zoukankan      html  css  js  c++  java
  • Spring注解版学习笔记——AOP

    1、介绍

      AOP:动态代理,指在程序运行期间动态的将某段代码切入到指定方法指定位置进行运行的编程方式,即所谓面向切面编程

    2、使用

    • 开启基于注解的aop模式;@EnableAspectJAutoProxy
    • 将业务逻辑组件和切面类都加入到容器中;告诉Spring哪个是切面类(@Aspect)
    • 在切面类上的每一个通知方法上标注通知注解,告诉Spring何时何地运行(切入点表达式)
     1 /**
     2  * 切面类
     3  * @author lfy
     4  * 
     5  * @Aspect: 告诉Spring当前类是一个切面类
     6  *
     7  */
     8 @Aspect
     9 public class LogAspects {
    10     
    11     //抽取公共的切入点表达式
    12     //1、本类引用
    13     //2、其他的切面引用
    14     @Pointcut("execution(public int com.atguigu.aop.MathCalculator.*(..))")
    15     public void pointCut(){};
    16     
    17     //@Before在目标方法之前切入;切入点表达式(指定在哪个方法切入)
    18     @Before("pointCut()")
    19     public void logStart(JoinPoint joinPoint){
    20         Object[] args = joinPoint.getArgs();
    21         System.out.println(""+joinPoint.getSignature().getName()+"运行。。。@Before:参数列表是:{"+Arrays.asList(args)+"}");
    22     }
    23     
    24     @After("com.atguigu.aop.LogAspects.pointCut()")
    25     public void logEnd(JoinPoint joinPoint){
    26         System.out.println(""+joinPoint.getSignature().getName()+"结束。。。@After");
    27     }
    28     
    29     //JoinPoint一定要出现在参数表的第一位
    30     @AfterReturning(value="pointCut()",returning="result")
    31     public void logReturn(JoinPoint joinPoint,Object result){
    32         System.out.println(""+joinPoint.getSignature().getName()+"正常返回。。。@AfterReturning:运行结果:{"+result+"}");
    33     }
    34     
    35     @AfterThrowing(value="pointCut()",throwing="exception")
    36     public void logException(JoinPoint joinPoint,Exception exception){
    37         System.out.println(""+joinPoint.getSignature().getName()+"异常。。。异常信息:{"+exception+"}");
    38     }
    39 
    40 }
    View Code
    1 public class MathCalculator {
    2     
    3     public int div(int i,int j){
    4         System.out.println("MathCalculator...div...");
    5         return i/j;    
    6     }
    7 
    8 }
    View Code
     1 @EnableAspectJAutoProxy
     2 @Configuration
     3 public class MainConfigOfAOP {
     4      
     5     //业务逻辑类加入容器中
     6     @Bean
     7     public MathCalculator calculator(){
     8         return new MathCalculator();
     9     }
    10 
    11     //切面类加入到容器中
    12     @Bean
    13     public LogAspects logAspects(){
    14         return new LogAspects();
    15     }
    16 }
    View Code

    3、原理

    组件源码一般阅读要点:1)看给容器中注册了个什么组件;2)这个组件什么时候工作,是不是后置处理器,3)容器功能是啥。

    3.1 看下@EnableAspectJAutoProxy 做了啥

     1 @Target({ElementType.TYPE})
     2 @Retention(RetentionPolicy.RUNTIME)
     3 @Documented
     4 @Import({AspectJAutoProxyRegistrar.class})
     5 public @interface EnableAspectJAutoProxy {
     6     boolean proxyTargetClass() default false;
     7 
     8     boolean exposeProxy() default false;
     9 }
    10 
    11 
    12 
    13 /**
    14  * Registers an {@link org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator
    15  * AnnotationAwareAspectJAutoProxyCreator} against the current {@link BeanDefinitionRegistry}
    16  * as appropriate based on a given @{@link EnableAspectJAutoProxy} annotation.
    17  *
    18  * @author Chris Beams
    19  * @author Juergen Hoeller
    20  * @since 3.1
    21  * @see EnableAspectJAutoProxy
    22  */
    23 class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
    24 
    25     /**
    26      * Register, escalate, and configure the AspectJ auto proxy creator based on the value
    27      * of the @{@link EnableAspectJAutoProxy#proxyTargetClass()} attribute on the importing
    28      * {@code @Configuration} class.
    29      */
    30     @Override
    31     public void registerBeanDefinitions(
    32             AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    33 
    34         AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
    35 
    36         AnnotationAttributes enableAspectJAutoProxy =
    37                 AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
    38         if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
    39             AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
    40         }
    41         if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
    42             AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
    43         }
    44     }
    45 
    46 }
    47 
    48 
    49     public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
    50         return registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, null);
    51     }
    52 
    53     public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
    54         return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
    55     }
    56 
    57 
    58     private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, Object source) {
    59         Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    60         if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
    61             BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
    62             if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
    63                 int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
    64                 int requiredPriority = findPriorityForClass(cls);
    65                 if (currentPriority < requiredPriority) {
    66                     apcDefinition.setBeanClassName(cls.getName());
    67                 }
    68             }
    69             return null;
    70         }
    71         RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
    72         beanDefinition.setSource(source);
    73         beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
    74         beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    75         registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
    76         return beanDefinition;
    77     }
    View Code

    可以看到@EnableAspectJAutoProxy 主要就是往 register(容器)里面注册一个  (AnnotationAwareAspectJAutoProxyCreator.class)的bean对象,beanName为:AUTO_PROXY_CREATOR_BEAN_NAME("org.springframework.aop.config.internalAutoProxyCreator")

    3.2 探索下 AnnotationAwareAspectJAutoProxyCreator 这个类

    AnnotationAwareAspectJAutoProxyCreator 是个长继承链的类,首先是需要确认他是否属于后置处理器的实现。列出继承链:

    AnnotationAwareAspectJAutoProxyCreator  ->  AspectJAwareAdvisorAutoProxyCreator  ->  AbstractAdvisorAutoProxyCreator  ->  AbstractAutoProxyCreator  

    implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware  关注后置处理器(在bean初始化完成前后做事情)、自动装配BeanFactory

     1     @Override
     2     public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
     3         Object cacheKey = getCacheKey(beanClass, beanName);
     4 
     5         if (beanName == null || !this.targetSourcedBeans.contains(beanName)) {
     6             if (this.advisedBeans.containsKey(cacheKey)) {
     7                 return null;
     8             }
     9             if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
    10                 this.advisedBeans.put(cacheKey, Boolean.FALSE);
    11                 return null;
    12             }
    13         }
    14 
    15         // Create proxy here if we have a custom TargetSource.
    16         // Suppresses unnecessary default instantiation of the target bean:
    17         // The TargetSource will handle target instances in a custom fashion.
    18         if (beanName != null) {
    19             TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
    20             if (targetSource != null) {
    21                 this.targetSourcedBeans.add(beanName);
    22                 Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
    23                 Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
    24                 this.proxyTypes.put(cacheKey, proxy.getClass());
    25                 return proxy;
    26             }
    27         }
    28 
    29         return null;
    30     }
    31 
    32     @Override
    33     public boolean postProcessAfterInstantiation(Object bean, String beanName) {
    34         return true;
    35     }
    36 
    37     @Override
    38     public PropertyValues postProcessPropertyValues(
    39             PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) {
    40 
    41         return pvs;
    42     }
    43 
    44     @Override
    45     public Object postProcessBeforeInitialization(Object bean, String beanName) {
    46         return bean;
    47     }
    48 
    49     /**
    50      * Create a proxy with the configured interceptors if the bean is
    51      * identified as one to proxy by the subclass.
    52      * @see #getAdvicesAndAdvisorsForBean
    53      */
    54     @Override
    55     public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    56         if (bean != null) {
    57             Object cacheKey = getCacheKey(bean.getClass(), beanName);
    58             if (!this.earlyProxyReferences.contains(cacheKey)) {
    59                 return wrapIfNecessary(bean, beanName, cacheKey);
    60             }
    61         }
    62         return bean;
    63     }
    View Code

    对每一个bean实例化——初始化前后,后置处理器的四种方法的执行顺序可以参考  Spring注解版学习笔记——bean的生命周期与Spring的钩子接口  ,大体可以简化为:

     postProcessBeforeInstantiation() :

    • 判断当前bean是否在advisedBeans中(保存了所有需要增强bean)
    • 判断当前bean是否是基础类型的Advice、Pointcut、Advisor、AopInfrastructureBean,或者是否是切面(@Aspect)
    • 是否需要跳过,获取候选的增强器(切面里面的通知方法)【List<Advisor> candidateAdvisors】,每一个封装的通知方法的增强器是 InstantiationModelAwarePointcutAdvisor;判断每一个增强器是否是 AspectJPointcutAdvisor 类型的;返回true

    postProcessAfterInstantiation():直接返回

    postProcessBeforeInitialization():直接返回

    postProcessAfterInitialization():

     1     protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
     2         if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
     3             return bean;
     4         }
     5         if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
     6             return bean;
     7         }
     8         if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
     9             this.advisedBeans.put(cacheKey, Boolean.FALSE);
    10             return bean;
    11         }
    12 
    13         // Create proxy if we have advice.
    14         Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    15         if (specificInterceptors != DO_NOT_PROXY) {
    16             this.advisedBeans.put(cacheKey, Boolean.TRUE);
    17             Object proxy = createProxy(
    18                     bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
    19             this.proxyTypes.put(cacheKey, proxy.getClass());
    20             return proxy;
    21         }
    22 
    23         this.advisedBeans.put(cacheKey, Boolean.FALSE);
    24         return bean;
    25     }
    26 
    27 
    28     @Override
    29     protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource targetSource) {
    30         List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
    31         if (advisors.isEmpty()) {
    32             return DO_NOT_PROXY;
    33         }
    34         return advisors.toArray();
    35     }
    36 
    37     /**
    38      * Find all eligible Advisors for auto-proxying this class.
    39      * @param beanClass the clazz to find advisors for
    40      * @param beanName the name of the currently proxied bean
    41      * @return the empty List, not {@code null},
    42      * if there are no pointcuts or interceptors
    43      * @see #findCandidateAdvisors
    44      * @see #sortAdvisors
    45      * @see #extendAdvisors
    46      */
    47     protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
    48         List<Advisor> candidateAdvisors = findCandidateAdvisors();
    49         List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    50         extendAdvisors(eligibleAdvisors);
    51         if (!eligibleAdvisors.isEmpty()) {
    52             eligibleAdvisors = sortAdvisors(eligibleAdvisors);
    53         }
    54         return eligibleAdvisors;
    55     }
    View Code

    可以看到:postProcessAfterInitialization的关键步骤是

    return wrapIfNecessary(bean, beanName, cacheKey);  

     判断是否返回增强类:

    • 获取当前bean的所有增强器(通知方法)  Object[]  specificInterceptors
      • 找到候选的所有的增强器(找哪些通知方法是需要切入当前bean方法的)
      • 获取到能在bean使用的增强器。
      • 给增强器排序
    • 保存当前bean在advisedBeans中;
    • 如果当前bean需要增强,创建当前bean的代理对象;
      • 获取所有增强器(通知方法)
      • 保存到proxyFactory
      • 创建代理对象:Spring自动决定 :JdkDynamicAopProxy(config);jdk动态代理; ObjenesisCglibAopProxy(config);cglib的动态代理;
    • 给容器中返回当前组件使用cglib增强了的代理对象;
    • 以后容器中获取到的就是这个组件的代理对象,执行目标方法的时候,代理对象就会执行通知方法的流程;

    3.3 被代理的目标方法的执行

    • 代理对象执行目标方法
    • CglibAopProxy.intercept();
      • 得到目标方法的拦截器链(增强器包装成拦截器MethodInterceptor)
      • 利用拦截器的链式机制,依次进入每一个拦截器进行执行;
      • 效果:正常执行:前置通知-》目标方法-》后置通知-》返回通知;出现异常:前置通知-》目标方法-》后置通知-》异常通知

    4、引申

  • 相关阅读:
    Interop.SQLDMO组件无法连接SQL2008
    关于数据连接配置connectionStrings的写法
    SQL锁表语句 (转摘)
    从思想到命运
    CIO:2013年OA选型六步走(摘)
    JS SCRIPT Confirm
    Silverlight 2 RTW中ToolTipService.ToolTip不继承父节点的DataContext的问题
    在Silverlight里实现类似WPF的UniformGrid
    尝试通过HttpWebRequest向TAOBAO批量发布商品及上传图片
    简单探照灯遮照效果的几个Silverlight程序(Silverlight 2.0)
  • 原文地址:https://www.cnblogs.com/demo12138/p/12655721.html
Copyright © 2011-2022 走看看