在看到拦截器的时候,大家一定会想到另外一个词,就是过滤器。两者到底有什么区别呢?过滤器,从字面的意思理解就是过滤用的,当很多请求过来的时候,我们对其进行过滤,满足一定条件的时候,才放行。在Java中,过滤器是使用Filter实现的,实现原理都是基于回调函数的。最常见的过滤器的应用就是字符编码的过滤、用户信息验证的过滤等。拦截器呢,就是用来拦截的,可以在方法的执行时,添加一些其他的信息,拦截器是使用Interceptor实现的,实现原理是基于Java的反射机制的。最常见的拦截器的应用有:添加访问日志、性能监控等。今天我们就来看看怎么用Spring的拦截器为方法添加访问日志。
首先,我们创建一个拦截器类,由于我们需要拦截的是方法,所以,就继承MethodInterceptor类。
import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 监控日志拦截器 * @author lizhiyang * */ public class MonitorLogInterceptor implements MethodInterceptor { private Logger logger = LoggerFactory.getLogger(MonitorLogInterceptor.class); public Object invoke(MethodInvocation methodinvocation) throws Throwable { Method method = methodinvocation.getMethod(); //方法执行前输出 logger.info("methodIn:methodName="+method.getName()); try { //执行方法 return methodinvocation.proceed(); } finally { //方法执行后输出 logger.info("methodOut:methodName="+method.getName()); } } }
在方法的执行前和执行后都加上日志输出。
接着,有的时候,我们可能需要自定义多个拦截器,这个时候,我们需要有一个拦截器链,把这些拦截器都串起来。
import java.util.ArrayList; import java.util.List; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; /** * 拦截器链 * @author lizhiyang * */ public class InterceptorChain implements MethodInterceptor { private List<MethodInterceptor> chains; public Object invoke(MethodInvocation methodinvocation) throws Throwable { InterceptorChainSupport support = new InterceptorChainSupport(methodinvocation, new ArrayList<MethodInterceptor>(chains)); return support.proceed(); } public List<MethodInterceptor> getChains() { return chains; } public void setChains(List<MethodInterceptor> chains) { this.chains = chains; } }
里面我们用到了一个类:InterceptorChainSupport,他的实现如下:
import java.lang.reflect.AccessibleObject; import java.lang.reflect.Method; import java.util.List; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; public class InterceptorChainSupport implements MethodInvocation { private MethodInvocation proxy; private List<MethodInterceptor> chains; public InterceptorChainSupport(MethodInvocation proxy,List<MethodInterceptor> chains) { this.proxy = proxy; this.chains = chains; } public MethodInvocation getProxy() { return proxy; } public void setProxy(MethodInvocation proxy) { this.proxy = proxy; } public List<MethodInterceptor> getChains() { return chains; } public void setChains(List<MethodInterceptor> chains) { this.chains = chains; } public Object[] getArguments() { return proxy.getArguments(); } public AccessibleObject getStaticPart() { return proxy.getStaticPart(); } public Object getThis() { return proxy.getThis(); } public Object proceed() throws Throwable { //如果拦截器链不空,则继续执行拦截器 if(chains != null && chains.size() > 0) { //递归调用,一直调用到拦截器链的最后一个 return (chains.remove(0)).invoke(this); } else { return proxy.proceed(); } } public Method getMethod() { return proxy.getMethod(); } }
InterceptorChainSupport是真正来处理拦截器链的,遍历执行所有的拦截器。在InterceptorChain中构造InterceptorChainSupport的时候要特别注意,一定要new一个新的List来存放chains,否则,会造成调用链只能执行一次的情况。
此处的执行过程是这样的:当调用相应的方法时,调用InterceptorChain.invoke()----->InterceptorChainSupport.proceed()---->***Interceptor.invoke()------>InterceptorChainSupport().proceed()------......---->真正的方法处理---->方法之后的拦截处理。
最后,我们在spring的配置文件中,来配置拦截器。
<!-- 配置日志监控拦截器 --> <bean id="monitorLogInterceptor" class="com.demo.interceptor.MonitorLogInterceptor" /> <!-- 配置拦截器链,保存所有的拦截器 --> <bean id="interceptorChain" class="com.demo.interceptor.InterceptorChain"> <property name="chains"> <list> <ref bean="monitorLogInterceptor"/> </list> </property> </bean> <!-- 配置拦截器和需要拦截的bean --> <bean id="serviceProxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <property name="interceptorNames"> <list> <value>interceptorChain</value> </list> </property> <property name="beanNames"> <value>*Service</value> </property> </bean>
这样配置之后,在spring容器加载的时候,spring就知道了执行*Service类中的方法时候,需要应用interceptorChain中的拦截器。
这个时候呢,就有了一个问题,如果我不想给这个类的每个方法都进行拦截,只拦截一部分呢?这个时候我们可以借助注解来实现。为需要拦截的方法上加上注解。
首先我们创建一个注解类,MonitorLog。
/** * 1.RetentionPolicy.SOURCE ——只在源代码级别保留,编译时就会被忽略 2.RetentionPolicy.CLASS ——编译时被保留,在class文件中存在,但JVM将会忽略 3.RetentionPolicy.RUNTIME —— 被JVM保留,所以他们能在运行时被JVM或其他使用反射机制的代码所读取和使用. * */ @Retention(RetentionPolicy.RUNTIME) /** * 该注解应用于方法 */ @Target(ElementType.METHOD) /** * 指明被注解的类会自动继承,如果我们把注解放在接口的方法上,那么实现该接口的类也会被继承该注解 */ @Inherited /** * Documented 注解表明这个注解应该被 javadoc工具记录. */ @Documented public @interface MonitorLog { }
然后我们在需要拦截的方法上天剑@MonitorLog注解。
public interface UserService { @MonitorLog public boolean insertUser(UserModel user); public UserModel getUser(int userId); public String test(); public void user(String name); }
我们现在还需要修改MonitorLogInterceptor类,只有添加@MonitorLog的方法才进行拦截,其他的不拦截。
public class MonitorLogInterceptor implements MethodInterceptor { private Logger logger = LoggerFactory.getLogger(MonitorLogInterceptor.class); public Object invoke(MethodInvocation methodinvocation) throws Throwable { Method method = methodinvocation.getMethod(); //获取方法的MonitorLog注解 MonitorLog log = method.getAnnotation(MonitorLog.class); boolean bLog = false; //该方法存在MonitorLog注解,则输出日志 if(log != null) { bLog = true; //方法执行前输出 logger.info("methodIn:methodName="+method.getName()); } try { //执行方法 return methodinvocation.proceed(); } finally { if(bLog) { //方法执行后输出 logger.info("methodOut:methodName="+method.getName()); } } } }
以上,就是spring拦截器的一个简单的应用。当然了,我们也可以使用spring的aop标签,来进行具体的配置。
版权声明:本文为博主原创文章,未经博主允许不得转载。