简述SpringAop以及拦截器和过滤器
简述
AOP是面向切面编程(Aspect-Oriented Programming)的简称。它不是一项技术,和平常说的OOP(Object-Oriented Programming)一样,它是一种编程思想。这里不再做更多的名词解释。上图:
从这个丑陋的图中可以看出,利用AOP后,OOP的Objects 都可以只专注于自己的事情,而不需要去管用户是否登录以及记录本次操作日志的事情了。 而且关于用户的判断以及日志记录的代码也只需要一份,不再关心需要将这些额外的操作加在哪里。
实现
aop的实现主要有两种方式,一种是通过回调函数,另一种是代理。
1、回调
web中常见的通过回调的方式实现的aop有Filter(过滤器)和Interceptor(拦截器)。首先附上一张图,看一下在运用springMVC时,一个请求的部分生命周期。
(1)、客户端发起请求到服务器,服务器(这里以tomcat为例)会接受到请求,经过内部一系列包装以后,会生成编程时候需要用到的HttpServletRequest和HttpServletResponse。这其中的包装细节,这里不多说,贴个学习地址:https://blog.csdn.net/sunyunjie361/article/details/60126398。
(2)(11)(3)(10)、tomcat在StandardWrapperValve.invoke()方法中并不是直接调用dispatherServlet(分发器),而是将项目中注册的FilterList和dispatherServlet一起构造一个ApplicationFilterChain对象,再调用filterChain.doFilter(request,response)。在FilterChain中,会依次回调所有的Filter的doFilter方法,每个方法又通过调用FilterChain的doFilter来继续往下走,直到调用DispathServlet后,再逆向执行每个fitler中在调用FilterChain的doFilter后的代码。逻辑大致分这么几部,但具体的实现这里不做具体研究。有机会再写一篇关于tomcat的文章再具体说这个。
这个FilterChain是个挺有意思的算法,简单版本的:
public class myChain implements FilterChain { int pos = 0; List<Filter> list = new ArrayList<Filter>(); @Override public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { Filter filter = list.get(pos++); filter.doFilter(request,response,this); } } class MyFitler implements Filter{ @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { chain.doFilter(request,response); } @Override public void destroy() { } }
Filter(SpringBoot版本)示例:
@WebFilter @Order(Ordered.HIGHEST_PRECEDENCE) public class CrosFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException { HttpServletResponse httpServletResponse = (HttpServletResponse) response; //这里填写你允许进行跨域的主机ip httpServletResponse.setHeader("Access-Control-Allow-Origin", "*"); //允许的访问方法 httpServletResponse.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT, OPTIONS, DELETE, PATCH"); //Access-Control-Max-Age 用于 CORS 相关配置的缓存 httpServletResponse.setHeader("Access-Control-Max-Age", "3600"); httpServletResponse.setHeader("Access-Control-Allow-Headers", "content-type,sessionid,x-requested-with"); if(!HttpUtils.isOptionsRequest(request)){ filterChain.doFilter(request, response); } } @Override public void destroy() { } }
(4)(5)、在DispatherServlet第一次被调用的时候,它会先执行initStrategies(context) 方法,改方法会初始化分发器所需要的一些参数。包括了HandleMappings。获取需要初始化的handleMapping很简单,直接调用BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);方法,这个方法返回注册在SPringIOC中且实现了HandlerMapping接口的对象。当然handleAdapter也是这样做的。
当DispatherServlet执行doService时,它会遍历的HandleMapping,依次调用每个handlerMapping的getHandler()。每个handleMapping根据自己的需求重写getHandle或者getHandlerInternal()方法。在该方法中,根据reqeust判断是否是该自己处理的请求,如果不是,返回null,遍历继续,如果是自己该处理的请求,则返回this,同时遍历也就结束了。当然这里有个重要的事情就是:如果对于同一个Request有多个HandleMapping符合时,只会执行第一个。而第一个也不是根据注册时的顺序来的,是根据AnnotationAwareOrderComparator.sort(this.handlerMappings)方法来排序。这个方法其实就是@Order注解了。
HandlerExecutionChain在返回之前,会进行一些配置,其中就包括了适配Interceptor。这里以RequestMappingHandlermapping(springMVC中用于处理Controller的处理器)为例,它会根据url匹配规则,HandlerExecutionChain中会保存所有matches方法返回true的MappedInterceptor和直接实现HandleInterceptor的Interceptors。实现的具体请看代码:
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) { HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ? (HandlerExecutionChain) handler : new HandlerExecutionChain(handler)); String lookupPath = this.urlPathHelper.getLookupPathForRequest(request); for (HandlerInterceptor interceptor : this.adaptedInterceptors) { if (interceptor instanceof MappedInterceptor) { MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor; if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) { chain.addInterceptor(mappedInterceptor.getInterceptor()); } } else { chain.addInterceptor(interceptor); } } return chain; }
下面是adaptedInterceptors列表的初始化
protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) { mappedInterceptors.addAll( BeanFactoryUtils.beansOfTypeIncludingAncestors( getApplicationContext(), MappedInterceptor.class, true, false).values()); }
它会加载所有的MappedInterceptor以及其子类的SpringBean。然而,我们并不能简单的通过编写一个继承自MappedInterceptor的实体类来实现一个拦截器,因为MappedInterceptor.class是用final修饰的。这是一个装饰者模式的设计。所以,我们需要两部来编写一个Interceptor。首先编写一个实现了HandlerInterceptor接口的类,然后将其一个实例加上urlPattern来装饰为一个mappedInterceptor,并交由springIOC管理即可。
当然也可以用xml配置的方式来实现,让容器在启动时就加载Interceptor,这样就省去配置mappedInterceptor了:
<mvc:interceptors> <!--定义一个handleInterceptor , 拦截所有的请求 --> <bean class="com.host.app.web.interceptor.AllInterceptor"/> <!--定义一个mappedInterceptor 装饰了path属性-->
<mvc:interceptor> <mvc:mapping path="/test/number.do"/> <bean class="com.host.app.web.interceptor.LoginInterceptor"/> </mvc:interceptor> </mvc:interceptors>
当然,最终SpringMVC还是会把handleInterceptor变成mappedInterceptor。
protected void initInterceptors() { if (!this.interceptors.isEmpty()) {
//遍历handleInterceptor for (int i = 0; i < this.interceptors.size(); i++) { Object interceptor = this.interceptors.get(i); if (interceptor == null) { throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null"); }
//通过adaptInterceptor方法将handlerInterceptor转化为MappedInterceptor,并加入到adaptedInterceptors中 this.adaptedInterceptors.add(adaptInterceptor(interceptor)); } } }
在SpringBoot中,我们也有两种方式配置Interceptor,第一种还是配置mappedInterceptor,第二种是自定义WebMvcConfigurerAdapter适配器,在适配器中添加Interceptor。
@SpringBootApplication @EnableTransactionManagement @ServletComponentScan public class DemoApplication extends WebMvcConfigurerAdapter { protected static final Logger logger = LoggerFactory.getLogger(DemoApplication.class);
//通过重写这个方法来添加拦截器 @Override public void addInterceptors(InterceptorRegistry registry) {
//添加一个拦截器,并为它配置urlPath。 这是java中不常用的火车头式写法,不要被迷惑,注意addInterceptor方法的返回值 registry.addInterceptor(new ValidatorInterception()).addPathPatterns("/**"); registry.addWebRequestInterceptor(new TestWebRequestInterceptor()).addPathPatterns("/**"); super.addInterceptors(registry); } public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); logger.error("CONGRATULATIONS!! demo effective!"); } }
(6)(7)(8)(9) 在DispatherServlet中,获得了HandlerExecutionChain以后,通过回调来实现AOP,代码如下:(去掉了其他与拦截器无关的代码,源码在DispatherServlet.doDispatch()方法中。)
try { ModelAndView mv = null; Exception dispatchException = null; try {//获取handler mappedHandler = getHandler(processedRequest);//获取adapter HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); // 执行所有Interceptor的preHandle()方法 对应图中第6步
if (!mappedHandler.applyPreHandle(processedRequest, response)) { return; } // 通过适配器执行contoller 对于图中第 7 、8在这方法中执行,这里不具体研究 mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); //通过视图解析器解析modleAndView 对应途中第9步
applyDefaultViewName(processedRequest, mv); //执行所有Interceptor的PostHandle()方法 也在第9步
mappedHandler.applyPostHandle(processedRequest, response, mv); } catch (Exception ex) { ... } }catch (Throwable err) { ... } finally { //执行Interceptors的afterCompletion()方法
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response); }
通过代码发现,handleInterceptor接口中的三个方法都有被回调一次;其中:afterCompletion方法是一定会执行的,一般情况下preHandle方法也会执行,但PostHandle方法则不一定执行。
示例:
public class ValidatorInterception implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception { if(HttpUtils.isOptionsRequest(httpServletRequest)){ return true; } HandlerMethod method = (HandlerMethod)o; Method method1 = method.getMethod(); List<Object> objs = new ArrayList<Object>(); MethodParameter[] methodParameters = method.getMethodParameters(); for(int i = 0 ; i < methodParameters.length; i++){ MyValidator parameterAnnotation = methodParameters[i].getParameterAnnotation(MyValidator.class); String parameter = httpServletRequest.getParameter(methodParameters[i].getParameterName()); if(!Objects.isNull(parameterAnnotation)){ } } Parameter[] parameters = method1.getParameters(); for(int i = 0 ; i < parameters.length; i++){ MyValidator annotation = parameters[i].getAnnotation(MyValidator.class); if(!Objects.isNull(annotation)){ /* objs.add()*/ } } return true; } @Override public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception { } }
通过回调的方式,这里主要讲的就是Filter和Interceptor,从以上可以发现一个问题,那就是通过回调的方式实现的AOP需要一个回调的步骤,耦合度较高,如果框架没有提供这个回调的过程,那我们编写的时候是很难做到的。如果觉得这种方式有点low,那接下来我们就讨论一下另一种实现方式:
2、代理
首先说一下代理模式:
定义:为另一个对象提供一个替身或者占位符以控制对这个对象的访问。
名词解释啥的,本人不擅长。上面这句话引用自《Head First 设计模式 》。多说无益,上代码:
//代理对象和实际对象一样都继承共同接口
public class PersonBeanProxy implements Person{ //代理对象
PersonBean personBean; public SimpleProxy(PersonBean personBean) { this.personBean = personBean; } //控制对象的访问 public void say() { personBean.say(); } } interface Person{ void say(); } //被代理对象的类 class PersonBean implements Person{ String name; public PersonBean(String name) { this.name = name; } @Override public void say() { System.out.println("my name is "+name); } }
这是静态代理的一个简单例子,这个模式很容易和装饰者模式以及适配器模式混淆,其实细细品味,概念上还是能分出个所以然来。笔者觉得这需要个人理解,很难解释。
嗯哼~ 大好时光,何必纠结这些名词解释,说点实在的。在java中,动态代理模式已经有两种现成的实现,一种是jdk自带的(是不是很激动,jdk自带了,不用纠结怎么去实现了),而另一种就是CGlib了。至于动态代理的具体实现,这里就不说了。后面会出SpringIOC相关知识,再细说代理。接下来,我们直接说spring如何运用代理来实现AOP的。
SpringAop
这里简单花了一下springAop实现的流程图:
默认情况下springIOC再启动后会利用DefaultListableBeanFactory来实例化所有的bean。在实例化bean的时候,springIOC中会有多个beanPostProcessors(实例化bean后的处理器),也就是说大部分的bean实际上都是经过代理后的代理对象,而不是实际的对象。所以在debug调试中经常会看到一些bean的名字结尾是$proxy或者$cglibProxy等等,说明它们都是经过包装后的代理类对象。源码再DefaultListableBeanFactory.class的父类AbstractAutowireCapableBeanFactory.class中:
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
result = beanProcessor.postProcessAfterInitialization(result, beanName);
if (result == null) {
return result;
}
}
return result;
}
在众多的处理器中,包括了一个AnnotationAwareAspectJAutoProxyCreator,它其实也只是众多代理bean类处理器中的一个,它主要用来助理@AspectJ自动代理的处理器。在它的内部存储了用@AspectJ注解标注的advisor(通知器),这里有个有趣的事情,其实这些处理器和通知器本身也是Bean,后面学习SpringIOC的时候,再细细研究这个。
这里给出部分类图:
在AnnotationAwareAspectJAutoProxyCreator内,会先构建一个ProxyFactory对象。源码:
ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.copyFrom(this); if (!proxyFactory.isProxyTargetClass()) { if (shouldProxyTargetClass(beanClass, beanName)) { proxyFactory.setProxyTargetClass(true); } else { evaluateProxyInterfaces(beanClass, proxyFactory); } } Advisor[] advisors = buildAdvisors(beanName, specificInterceptors); for (Advisor advisor : advisors) { proxyFactory.addAdvisor(advisor); } proxyFactory.setTargetSource(targetSource); customizeProxyFactory(proxyFactory); proxyFactory.setFrozen(this.freezeProxy); if (advisorsPreFiltered()) { proxyFactory.setPreFiltered(true); }
ProxyFactory内部持有一个DefaultAopProxyFactory对象,利用DefaultAopProxyFactory.createAopProxy(AdvisedSupport config)来创建代理对象。源码:
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) { Class<?> targetClass = config.getTargetClass(); if (targetClass == null) { throw new AopConfigException("TargetSource cannot determine target class: " + "Either an interface or a target is required for proxy creation."); } if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) { return new JdkDynamicAopProxy(config); } return new ObjenesisCglibAopProxy(config); } else { return new JdkDynamicAopProxy(config); } }
这里第一个判断:代理服务器设置是否应该执行积极的优化,第二个判断:是否启动cglib,第三个为判断:bean是不是继承了SpringProxy。其中前两个判断可以利用参数来配置:
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean" p:interceptorNames="before" p:target-ref="userServiceImpl" p:optimize="false" p:proxyTargetClass="false" > </bean>
当然还有其他的一些判断,主要是因为两种aop的实现是不同的,jdk代理是基于接口,也就是说生成的代理类是实现了传入的接口,而cglib是基于类的,它生成代理类是继承了传入的类。所以两种实现所需的参数不同,也就适用于不同的情况。
先了解一下jdk版本的。Proxy类提供了newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h) 方法来获取代理对象。 它返回的对象实现了接口的所有方法,这样对这个代理对象的访问就像访问一个实例一样简单,而每个方法的具体实现,实际上jdk的Proxy是没有的。它并不会去管代理对象的方法该实现怎样的逻辑,而是通过调用传入的InvocationHandler 的的invoke方法来执行具体的细节。而invoke方法是需要用户自己实现的。当然在spring框架中,spring是已经实现了的。源码:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { MethodInvocation invocation; Object oldProxy = null; boolean setProxyContext = false; TargetSource targetSource = this.advised.targetSource; Class<?> targetClass = null; Object target = null; try { Object retVal;// Get the interception chain for this method. List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); // Check whether we have any advice. If we don't, we can fallback on direct // reflective invocation of the target, and avoid creating a MethodInvocation. if (chain.isEmpty()) { // We can skip creating a MethodInvocation: just invoke the target directly // Note that the final invoker must be an InvokerInterceptor so we know it does // nothing but a reflective operation on the target, and no hot swapping or fancy proxying. Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args); retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse); } else { // We need to create a method invocation... invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain); // Proceed to the joinpoint through the interceptor chain. retVal = invocation.proceed(); }return retVal; } finally { ... }
这里就会执行我们aop中的代码了。由于spring的aop是可以多层的嵌套的,这里也是通过链式调用来完成逐层调用。
然后来看下spring自己的cglilb实现:个人感觉cglib的实现就是比较暴力的了,它先创建Enhancer 对象,源码:
Enhancer enhancer = createEnhancer(); if (classLoader != null) { enhancer.setClassLoader(classLoader); if (classLoader instanceof SmartClassLoader && ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) { enhancer.setUseCache(false); } } enhancer.setSuperclass(proxySuperClass); enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised)); enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE); enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader)); Callback[] callbacks = getCallbacks(rootClass);
然后直接生产字节码的类文件,再调用classCLoad加载该类,然后生产一个代理对象。生产的类文件我还没办法找到,不过收藏了一篇 https://www.jianshu.com/p/9a61af393e41?from=timeline&isappinstalled=0
@Aspect @Configuration public class ValidatorAop { @Autowired Validator validator; @Pointcut("execution(public * com.example.controller..*.*(..))") public void validate(){} @Before("validate()") @After("") @Around("") public void validating(JoinPoint point)throws Throwable{ Object[] args = point.getArgs(); Method method = ((MethodSignature) point.getSignature()).getMethod(); Annotation[][] parameterAnnotations = method.getParameterAnnotations(); Before annotation1 = method.getAnnotation(Before.class); annotation1.value(); Parameter[] parameters = method.getParameters(); for(int i = 0 ; i < parameters.length; i++){ Parameter parameter = parameters[i]; Object value = args[i]; MyValidator annotation = parameter.getAnnotation(MyValidator.class); if(!Objects.isNull(annotation)){ ValidatorByType(args, i, value, annotation); } } } private void ValidatorByType(Object[] args, int i, Object value, MyValidator annotation) { if(Map.class.isAssignableFrom(value.getClass())){ Map<Object,Object> newValue = (Map<Object,Object>) value; for(Map.Entry<Object,Object> entry : newValue.entrySet()){ Object value1 = entry.getValue(); if(!value1.getClass().isPrimitive() && !(value1 instanceof String)){ validatedUseHibernateValidator(value1, annotation); } } }else if(Collection.class.isAssignableFrom(value.getClass())){ Collection newValue = (Collection) value; Iterator iterator = newValue.iterator(); while (iterator.hasNext()){ validatedUseHibernateValidator(iterator.next(), annotation); } }else{ validatedUseHibernateValidator(args[i], annotation); } } private void validatedUseHibernateValidator(Object value, MyValidator annotation) { Set<ConstraintViolation<Object>> validate = validator.validate(value, annotation.value()); if(!CollectionUtils.isEmpty(validate)){ throw new ConstraintViolationException(validate); } } }
两种实现的总结:
不管是通过方法回调还是代理的方式,再使用的时候都需要遵守一些规范,Fitler和Interceptor都需要实现指定的接口,而代理的方式是通过注解来注册通知器,但是代理的方式就显得特别灵活,可以随意控制拦截的目标,而回调的方式拦截的只能是再回调方法调用的地方了。但对于新手来说,拦截器和过滤器如果用代理的方式来实现的话,压根就不知道该拦截谁,这就很尴尬。即使初级开发,也很多不知道该如何去拦截。所以Filter和Interceptor是框架为我们提供的一个便利,不让我们迷失在框架的源码中。