×

aop

Spring源码之Aop、SpringMVC执行流程源码分析

我的笔记 我的笔记 发表于2019-08-07 20:46:19 浏览3199 评论2

2人参与发表评论

SpringAop源码分析

AOP简介

概念

切面(Aspect) :官方的抽象定义为“一个关注点的模块化,这个关注点可能会横切多个对象”。
连接点(Joinpoint) :程序执行过程中的某一行为。
通知(Advice) :“切面”对于某个“连接点”所产生的动作。
切入点(Pointcut) :匹配连接点的断言,在AOP中通知和一个切入点表达式关联。
目标对象(Target Object) :被一个或者多个切面所通知的对象。
AOP代理(AOP Proxy) 在Spring AOP中有两种代理方式,JDK动态代理和CGLIB代理。

通知(Advice)类型
前置通知(Before advice) :在某连接点(JoinPoint)之前执行的通知,但这个通知不能阻止连接点前的执行。ApplicationContext中在<aop:aspect>里面使用<aop:before>元素进行声明。
后通知(After advice) :当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。ApplicationContext中在<aop:aspect>里面使用<aop:after>元素进行声明。
返回后通知(After return advice) :在某连接点正常完成后执行的通知,不包括抛出异常的情况。ApplicationContext中在<aop:aspect>里面使用<after-returning>元素进行声明。
环绕通知(Around advice) :包围一个连接点的通知,类似Web中Servlet规范中的Filter的doFilter方法。可以在方法的调用前后完成自定义的行为,也可以选择不执行。ApplicationContext中在<aop:aspect>里面使用<aop:around>元素进行声明。
抛出异常后通知(After throwing advice) : 在方法抛出异常退出时执行的通知。 ApplicationContext中在<aop:aspect>里面使用<aop:after-throwing>元素进行声明。

切入点表达式 :如execution(* com.spring.service.*.*(..))

特点

1、降低模块之间的耦合度

2、使系统容易扩展

3、更好的代码复用。

流程说明

1)AOP标签的定义解析刘彻骨肯定是从NamespaceHandlerSupport的实现类开始解析的,这个实现类就是AopNamespaceHandler。至于为什么会是从NamespaceHandlerSupport的实现类开始解析的,这个的话我想读者可以去在回去看看Spring自定义标签的解析流程,里面说的比较详细。

2)要启用AOP,我们一般会在Spring里面配置<aop:aspectj-autoproxy/>  ,所以在配置文件中在遇到aspectj-autoproxy标签的时候我们会采用AspectJAutoProxyBeanDefinitionParser解析器

3)进入AspectJAutoProxyBeanDefinitionParser解析器后,调用AspectJAutoProxyBeanDefinitionParser已覆盖BeanDefinitionParser的parser方法,然后parser方法把请求转交给了AopNamespaceUtils的registerAspectJAnnotationAutoProxyCreatorIfNecessary去处理

4)进入AopNamespaceUtils的registerAspectJAnnotationAutoProxyCreatorIfNecessary方法后,先调用AopConfigUtils的registerAspectJAnnotationAutoProxyCreatorIfNecessary方法,里面在转发调用给registerOrEscalateApcAsRequired,注册或者升级AnnotationAwareAspectJAutoProxyCreator类。对于AOP的实现,基本是靠AnnotationAwareAspectJAutoProxyCreator去完成的,它可以根据@point注解定义的切点来代理相匹配的bean。

5)AopConfigUtils的registerAspectJAnnotationAutoProxyCreatorIfNecessary方法处理完成之后,接下来会调用useClassProxyingIfNecessary() 处理proxy-target-class以及expose-proxy属性。如果将proxy-target-class设置为true的话,那么会强制使用CGLIB代理,否则使用jdk动态代理,expose-proxy属性是为了解决有时候目标对象内部的自我调用无法实现切面增强。

6)最后的调用registerComponentIfNecessary 方法,注册组建并且通知便于监听器做进一步处理。

 

创建AOP代理

上面说到AOP的核心逻辑是在AnnotationAwareAspectJAutoProxyCreator类里面实现,那么我们先来看看这个类的层次关系

流程说明

1) spring 容器启动,每个bean的实例化之前都会先经过AbstractAutoProxyCreator类的postProcessAfterInitialization()这个方法,然后接下来是调用wrapIfNecessary方法。

image.png

public Object   postProcessAfterInitialization(Object bean, String beanName) throws BeansException {

           if (bean != null) {

                 Object cacheKey = getCacheKey(bean.getClass(), beanName);

                 if (!this.earlyProxyReferences.contains(cacheKey)) {

                      return wrapIfNecessary(bean, beanName, cacheKey);

                 }

           }

           return bean;

      }

protected Object   wrapIfNecessary(Object bean, String beanName, Object cacheKey) {

           if (this.targetSourcedBeans.contains(beanName)) {

                 return bean;

           }

           if (this.nonAdvisedBeans.contains(cacheKey)) {

                 return bean;

           }

           if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {

                 this.nonAdvisedBeans.add(cacheKey);

                 return bean;

           }

 

           // Create proxy if we have advice.

           Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);

           if (specificInterceptors != DO_NOT_PROXY) {

                 this.advisedBeans.add(cacheKey);

                 Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));

                 this.proxyTypes.put(cacheKey, proxy.getClass());

                 return proxy;

           }

 

           this.nonAdvisedBeans.add(cacheKey);

           return bean;

      }

创建代理对象

protected Object   createProxy(

              Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {

 

         ProxyFactory proxyFactory = new ProxyFactory();

         // Copy our properties (proxyTargetClass   etc) inherited from ProxyConfig.

         proxyFactory.copyFrom(this);

 

         if (!shouldProxyTargetClass(beanClass, beanName)) {

              // Must allow for introductions; can't   just set interfaces to

              // the target's interfaces only.

              Class<?>[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass, this.proxyClassLoader);

              for (Class<?> targetInterface : targetInterfaces) {

                   proxyFactory.addInterface(targetInterface);

              }

         }

 

         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);

         }

 

         return proxyFactory.getProxy(this.proxyClassLoader);

     }

 

 

 

image.png

SpringMVC执行流程

 

Spring MVC工作流程图

image.png

image.png

Spring工作流程描述

      1. 用户向服务器发送请求,请求被Spring 前端控制Servelt DispatcherServlet捕获;

      2. DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI)。然后根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain对象的形式返回;

      3. DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter。(附注:如果成功获得HandlerAdapter后,此时将开始执行拦截器的preHandler(...)方法

       4.  提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)。 在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:

      HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息

      数据转换:对请求消息进行数据转换。如String转换成Integer、Double等

      数据根式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等

      数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中

      5.  Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象;

      6.  根据返回的ModelAndView,选择一个适合的ViewResolver(必须是已经注册到Spring容器中的ViewResolver)返回给DispatcherServlet ;

      7. ViewResolver 结合Model和View,来渲染视图

      8. 将渲染结果返回给客户端。

Spring工作流程描述

    为什么Spring只使用一个Servlet(DispatcherServlet)来处理所有请求?

     详细见J2EE设计模式-前端控制模式

    Spring为什么要结合使用HandlerMapping以及HandlerAdapter来处理Handler?

    符合面向对象中的单一职责原则,代码架构清晰,便于维护,最重要的是代码可复用性高。如HandlerAdapter可能会被用于处理多种Handler。


我的笔记博客版权我的笔记博客版权