Cxf拦截器简介
Cxf拦截器
目录
拦截器是Cxf的基础,Cxf中很多的功能都是由内置的拦截器来实现的,拦截器在Cxf中由Interceptor表示。Cxf的拦截器包括入拦截器和出拦截器,所有的入拦截器或出拦截器构成了一个拦截器链,它们可以作用在Server端也可以作用在Client端。当需要使用拦截器链处理消息的时候,Cxf会确保对应的消息被拦截器链里面的每一个拦截器都执行一遍。拦截器链在Cxf中由InterceptorChain接口表示,有兴趣的朋友可以看一下org.apache.cxf.phase.PhaseInterceptorChain类的public synchronized boolean doIntercept(Message message)方法,它是拦截器链拦截Message的实现。当客户端发送一个请求到服务端时会经过客户端的出拦截器和服务端的入拦截器;当服务端对客户端的请求进行响应时对应的响应消息会经过服务端的出拦截器和客户端的入拦截器。
1.1 自定义拦截器
Cxf的拦截器是通过org.apache.cxf.interceptor.Interceptor<T extends Message>接口表示的,其*定义了两个方法,具体如下。
public interface Interceptor<T extends Message> {
void handleMessage(T message) throws Fault;
void handleFault(T message);
}
Cxf在正常调用InterceptorChain的时候会依次调用其中包含的Interceptor的handleMessage方法,当调用某一个Interceptor出错时,将依次回调已经回调过的Interceptor的handleFault方法,而且它们的调用顺序与调用handleMessage的顺序是反的。
通常我们在实现自定义Interceptor的时候不会直接实现Interceptor接口,而且Cxf也不允许我们直接实现Interceptor接口,暂时对它没有支持。Interceptor有一个子接口PhaseInterceptor,顾名思义就是分阶段的拦截器。Cxf将消息的发送、接收的过程分为很多个阶段,如send、receive、read、write等,具体可以参考类org.apache.cxf.phase.Phase,其中定义了Cxf支持的所有拦截阶段的常量。Cxf要求我们在定义一个拦截器的时候必须指定当前拦截器需要作用的阶段。Cxf已经为我们提供了一个实现了PhaseInterceptor接口的抽象实现类org.apache.cxf.phase.AbstractPhaseInterceptor<T extends Message>,而且建议我们通过继承AbstractPhaseInterceptor来实现自定义拦截器。AbstractPhaseInterceptor中没有定义默认的空构造方法,所以子类在实现的时候必须在构造方法中调用父类的某一个构造方法。以下是一个非常简单的继承自AbstractPhaseInterceptor的自定义拦截器的实现。
public class LogInterceptor extends AbstractPhaseInterceptor<Message> {
private static final Logger logger = Logger.getLogger(LogInterceptor.class);
public LogInterceptor() {
super(Phase.RECEIVE);//针对Receive这个阶段进行拦截
}
@Override
public void handleMessage(Message message) throws Fault {
logger.info(message.toString());
}
}
1.2 配置拦截器
自定义的拦截器配置好后我们需要进行相应的配置,好让Cxf知道我们需要在什么地方使用我们的自定义拦截器。拦截器的配置可以通过代码进行程序化的配置,也可以通过注解进行配置,还可以通过Cxf的配置文件进行配置。拦截器链InterceptorChain由许多Interceptor组成,Cxf中定义了一个接口InterceptorProvider,通过该接口可以获取到与当前对象绑定的拦截器链里面的所有拦截器,当我们需要往某对象现有的拦截器链里面添加拦截器的时候我们就可以往通过InterceptorProvider获取到的对应拦截器列表中添加相应的拦截器来实现。InterceptorProvider的定义如下。
public interface InterceptorProvider {
List<Interceptor<?extends Message>>getInInterceptors();
List<Interceptor<?extends Message>>getOutInterceptors();
List<Interceptor<?extends Message>>getInFaultInterceptors();
List<Interceptor<?extends Message>>getOutFaultInterceptors();
}
1.2.1程序化配置
程序化配置就是直接通过实现了InterceptorProvider接口的对象获取到对应的拦截器列表,然后往其中添加Interceptor即可。实现了InterceptorProvider接口的类很多,包括EndpointImpl、CxfBusImpl、ServiceImpl、ClientImpl等,想了解更多实现了InterceptorProvider接口的类可以查看Cxf的API文档(http://cxf.apache.org/javadoc/latest-2.7.x/org/apache/cxf/interceptor/InterceptorProvider.html)。以下是一段将我们自定义的LogInterceptor添加到服务端所发布的服务的拦截器链中的示例代码。
public class Server {
private final static String ADDRESS = "http://localhost:8080/test/jaxws/services/HelloWorld";
public static void main(String args[]) {
HelloWorldImplhw = new HelloWorldImpl();
Interceptor<Message>logInterceptor = new LogInterceptor();
JaxWsServerFactoryBean jwsFactory = new JaxWsServerFactoryBean();
jwsFactory.setAddress(ADDRESS); //指定WebService的发布地址
jwsFactory.setServiceClass(HelloWorld.class);//WebService对应的类型
jwsFactory.setServiceBean(hw);//WebService对应的实现对象
jwsFactory.getInInterceptors().add(logInterceptor);//往入拦截器链里面添加拦截器
//往出拦截器链里面添加拦截器
jwsFactory.getOutInterceptors().add(logInterceptor);
//往错误处理的入拦截器链中添加拦截器
jwsFactory.getInFaultInterceptors().add(logInterceptor);
//往错误处理的出拦截器链中添加拦截器
jwsFactory.getOutFaultInterceptors().add(logInterceptor);
jwsFactory.create();
}
}
使用其它方式在服务端发布Service或者在Client端需要往某个对象的拦截器链中添加拦截器的用法和上述示例中的类似。有一点需要注意的是,我们实现的PhaseInterceptor是必须指定要拦截的阶段的,我们如果往一个拦截器链里面添加一个根本不会发生的阶段,那么对应的拦截器是不会被执行的,如往出拦截器链里面添加一个Receive阶段的拦截器。如果希望添加我们的拦截器到所有的服务端服务或客户端,那么我们就可以创建一个Bus对象,然后往Bus对象的对应拦截器链中添加对应的拦截器,再在创建服务端和客户端时指定使用的Bus为我们自己创建的那个。示例代码如下。
public class Client {
private final static String ADDRESS = "http://localhost:8080/test/jaxws/services/HelloWorld";
public static void main(String args[]) throwsHelloWorldException {
//获取默认的bus
Bus bus = BusFactory.getDefaultBus();
//往bus的入拦截器链中添加拦截器
bus.getInInterceptors().add(new LogInterceptor());
JaxWsProxyFactoryBean jwpFactory = new JaxWsProxyFactoryBean();
jwpFactory.setBus(bus);//指定bus
jwpFactory.setAddress(ADDRESS);
jwpFactory.setServiceClass(HelloWorld.class);
HelloWorldhw = (HelloWorld)jwpFactory.create();
String response = hw.sayHi("world");
System.out.println(response);
}
}
在上述代码中我们通过BusFactory.getDefaultBus()方法获取了一个默认的Bus对象,实际上在Cxf的很多类的内部构造中需要获取Bus的时候,如果没有指定Bus对象,都会通过BusFactory的getDefaultBus()方法获取,所以如果是在一个Service发布的代码中通过上述方式获取到一个默认的Bus对象,然后往其中加入对应的拦截器的话,那很可能会给其它Service也加上;如果每一个Service的发布都是同样的逻辑的话,那对应的Bus上的拦截器就会同时有多个对象存在。
1.2.2注解配置
Cxf为四种类型的拦截器都提供了对应的注解,以方便用户直接在SEI上进行配置,对应的注解如下。
Ø org.apache.cxf.interceptor.InInterceptors
Ø org.apache.cxf.interceptor.InFaultInterceptors
Ø org.apache.cxf.interceptor.OutInterceptors
Ø org.apache.cxf.interceptor.OutFaultInterceptors
每个注解都对应有两个属性,String[]类型的interceptors和Class<? extends Interceptor<? extends Message>>[]类型的classes,其中interceptors用来指定需要配置的拦截器的全名称,而classes则用来指定需要配置的拦截器的类。以下是一个在SEI上通过@InInterceptor配置了入拦截器LogInterceptor的示例。
@InInterceptors(classes={LogInterceptor.class})
@WebService
public interface HelloWorld {
public String sayHi(String who);
}
如果在配置的时候既使用了classes属性配置,又使用了interceptors属性配置,那么两个配置都会生效。如下代码就相当于我们配置了两个自定义的拦截器LogInterceptor到HelloWorld服务的入拦截器链中。
@InInterceptors(classes={LogInterceptor.class}, interceptors={"com.tiantian.cxftest.interceptor.LogInterceptor"})
@WebService
public interface HelloWorld {
public String sayHi(String who);
}
使用注解的方式配置其它拦截器的方式是类似的。使用注解在服务端的SEI上配置的拦截器会作用在服务端,如果客户端与服务端不在一起,需要单独在客户端上配置拦截器,也可以直接在客户端对应的SEI上通过上述四个注解进行配置,方法是一样的。
1.2.3Cxf配置文件配置
Cxf配置文件是Cxf与Spring进行整合时使用的,不管是使用simple约束还是jaxws约束,它们都为四种类型的拦截器配置定义对应的元素配置,具体如下。
Ø inInterceptors
Ø inFaultInterceptors
Ø outInterceptors
Ø outFaultInterceptors
使用的时候如果是在服务端进行配置,则只需要配置在server标签下即可,如果是在客户端进行配置,则只需要在client标签下配置即可,用法是一样的。下面是在服务端配置拦截器的示例。
<beansxmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:jaxws="http://cxf.apache.org/jaxws"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">
<bean id="logInterceptor" class="com.tiantian.cxftest.interceptor.LogInterceptor"/>
<jaxws:server address="/HelloWorld">
<jaxws:serviceBean>
<bean class="com.tiantian.cxftest.sample.jaxws.HelloWorldImpl"/>
</jaxws:serviceBean>
<!--入拦截器 -->
<jaxws:inInterceptors>
<ref bean="logInterceptor"/>
</jaxws:inInterceptors>
<!--入出错拦截器 -->
<jaxws:inFaultInterceptors>
<ref bean="logInterceptor"/>
</jaxws:inFaultInterceptors>
<!--出拦截器 -->
<jaxws:outInterceptors>
<ref bean="logInterceptor"/>
</jaxws:outInterceptors>
<!--出出错拦截器 -->
<jaxws:outFaultInterceptors>
<ref bean="logInterceptor"/>
</jaxws:outFaultInterceptors>
</jaxws:server>
</beans>
如果我们需要通过配置文件的方式给所有的服务端或客户端都加上相同的拦截器,那么我们就可以在对应的Bus上配置对应的拦截器。如果需要在Bus上配置拦截器,我们首先得引入Cxf的core Schema,之后我们就可以通过core Schema中的定义的bus标签来配置一个Bus对象及其对应的拦截器了。示例如下。
<beansxmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:jaxws="http://cxf.apache.org/jaxws"
xmlns:cxf="http://cxf.apache.org/core"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd
http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd">
<bean id="logInterceptor" class="com.tiantian.cxftest.interceptor.LogInterceptor"/>
<!--可以通过其bus属性指定需要使用的Bus,不指定则使用默认的 -->
<jaxws:server address="/HelloWorld">
<jaxws:serviceBean>
<bean class="com.tiantian.cxftest.sample.jaxws.HelloWorldImpl"/>
</jaxws:serviceBean>
</jaxws:server>
<!--默认Bus对象 -->
<cxf:bus id="bus">
<!--入拦截器 -->
<cxf:inInterceptors>
<ref bean="logInterceptor"/>
</cxf:inInterceptors>
<!--入出错拦截器 -->
<cxf:inFaultInterceptors>
<ref bean="logInterceptor"/>
</cxf:inFaultInterceptors>
<!--出拦截器 -->
<cxf:outInterceptors>
<ref bean="logInterceptor"/>
</cxf:outInterceptors>
<!--出出错拦截器 -->
<cxf:outFaultInterceptors>
<ref bean="logInterceptor"/>
</cxf:outFaultInterceptors>
</cxf:bus>
</beans>
如果一个Server或Client没有指定使用的Bus,则将使用默认的那一个。如果需要指定则可以通过它们的bus属性进行指定。
1.3 日志拦截器
在前文我们自己定义了一个日志拦截器LogInterceptor,其实一般我们大可不必定义自己的日志拦截器。因为Cxf已经为我们提供了两个日志拦截器。一个是针对入拦截器链的LoggingInInterceptor,一个是针对出拦截器链的LoggingOutInterceptor,分别对应于Receive和Send阶段,它们输出来的日志信息也是比较详细的,对开发工作比较有帮助。有兴趣的朋友可以去查看一下它们的源码。
除了日志拦截器以外,Cxf还内置了很多的Interceptor实现,有兴趣的朋友可以去查看一下Cxf的API文档http://cxf.apache.org/javadoc/latest-2.7.x/org/apache/cxf/interceptor/Interceptor.html。
1.4 参考文档
Ø http://cxf.apache.org/docs/interceptors.html
Ø http://cxf.apache.org/javadoc/latest-2.7.x/
(注:本文是基于Cxf2.7.6版本所写)
(注:原创文章,转载请注明出处。原文地址:http://elim.iteye.com/blog/2248620)
上一篇: rails3项目解析之3——redis
下一篇: RESTEasy 1.1 发布