zoukankan      html  css  js  c++  java
  • Spring AOP

    此文主要是对于spring中AOP实现的一些思考总结 图。

    一、先认识两个AOP功能类

      1、根据一个表达式判断一个Method是否匹配。

      2、根据拦截类类名和拦截器的方法名,获取指定拦截器方法Method。(拦截器即Advice)

    其实,在Spring中,还对MethodLocatingFactory提出了一个更高层次的抽象FactoryBean(无非就是说,这个Bean是一个工厂Bean,它的功能是做其他事情,而不是直接供用户使用。比如我们现在的MethodLocatingFactory就是用于生成一个指定的Method)。如下图(AspectInstanceFactory暂忽略,后面生成Advice会用到):

    补充:面试中可能会问到BeanFactory和FactoryBean的区别?

    二、讲解一个策略ReflectiveMethodInvocation(重要!重要!重要!)

      一个可以实现按一定规则(before、afterReturning、afterThrowing等)链式调用增强方法的策略ReflectiveMethodInvocation。如下图:

     

    三、代理工厂AopProxyFactory

      对以上的封装:
         AopConfig<-AopConfigSupport:包含有:advice拦截器集合、target目标对象。
            主要作用是提供一个方法:通过指定的方法获取其匹配的拦截器集合List<MethodInterceptor>。封装到DynamicAdvisedInterceptor中,最终传递给  ReflectiveMethodInvocation去链式处理。  
      最后封装到AopProxyFactory中,如下图所示:

    四、拦截器Advice的合成

      AOP中的重中之重,说白了就是根据配置文件生成拦截器,然后通过CGLIB或者JDK生成出代理对象。

      (CGLIB和JDK动态代理,可戳此文https://www.cnblogs.com/zomicc/p/12255510.html

      Advice的建立依赖于PointCut,先引入PointCut,PointCut如下图:

      在Spring中,生成一个对象是通过BeanDefinition,Advice也不例外。
         先介绍下构造Advice对应的BeanDefinition和构造Advice都需要哪些信息:
          Advice(Method adviceMethod,AspectJExpressionPointcut pointcut,AspectInstanceFactory adviceObjectFactory)
             参数一:拦截器执行方法;
             参数二:切入点(顾名思义,用于指定拦截器在哪个TargetMethod上执行);
             参数三:切面初始化工厂(用于获取拦截器对象)。
          Advice对应的BeanDefinition构造参数都有:
             参数一:GenericBeanDefinition(Class类型是MethodLocatingFactory,作用是在生成Advice时获取Method对象。为什么不直接是Method,是因为在xml

              解析阶段不进行其他逻辑操作)
             参数二:RuntimeReference(对应切入点)
             参数三:GenericBeanDefinition(Class类型是AspectInstanceFactory)

        以AspectJBeforeAdvice为例,介绍合成BeanDefinition的原理,如下图所示:

    至此,拦截器对应的BeanDefinition就已经定义好了,通过XmlBeanDefinitionReader解析即可将所有的合成BeanDefinition加入到BeanFactory。

    然后就是使用Ioc中的构造器注入即可生产出真实的拦截器对象Advice。

    需要注意的两点是:对于嵌套BeanDefinition需要进一步取值处理,对于FactoryBean类型的也需要获取到真实内容:(重要!重要!重要

     1 public Object resolveValueIfNecessary(Object value) {
     2         
     3         if (value instanceof RuntimeBeanReference) {
     4             RuntimeBeanReference ref = (RuntimeBeanReference) value;            
     5             String refName = ref.getBeanName();            
     6             Object bean = this.beanFactory.getBean(refName);                
     7             return bean;
     8             
     9         }else if (value instanceof TypedStringValue) {
    10             return ((TypedStringValue) value).getValue();
    11         } else if (value instanceof BeanDefinition) {//一、对于嵌套的BeanDefinition需要进一步取值
    12             // Resolve plain BeanDefinition, without contained name: use dummy name.
    13             BeanDefinition bd = (BeanDefinition) value;
    14             
    15             String innerBeanName = "(inner bean)" + bd.getBeanClassName() + "#" +
    16                     Integer.toHexString(System.identityHashCode(bd));
    17             
    18             return resolveInnerBean(innerBeanName, bd);
    19             
    20         } 
    21         else{
    22             return value;
    23         }    
    24     }
    25     private Object resolveInnerBean(String innerBeanName, BeanDefinition innerBd) {
    26     
    27         try {
    28             
    29             Object innerBean = this.beanFactory.createBean(innerBd);//二、获取内部BeanDefinition对应的Bean
    30             
    31             if (innerBean instanceof FactoryBean) {//三、如果是FactoryBean类型,则需要通过getObject()方法获取真实的内容
    32                 try {
    33                     return ((FactoryBean<?>)innerBean).getObject();
    34                 } catch (Exception e) {                    
    35                     throw new BeanCreationException(innerBeanName, "FactoryBean threw exception on object creation", e);
    36                 }                
    37             }
    38             else {
    39                 return innerBean;
    40             }
    41         }
    42         catch (BeansException ex) {
    43             throw new BeanCreationException(
    44                     innerBeanName,
    45                     "Cannot create inner bean '" + innerBeanName + "' " +
    46                     (innerBd != null && innerBd.getBeanClassName() != null ? "of type [" + innerBd.getBeanClassName() + "] " : "")
    47                     , ex);
    48         }
    49     }

    五、最终通过Bean生命周期的“钩子”函数AspectJAutoProxyCreator生成真实的代理对象

      本质上就是通过BeanPostProcessor的afterInitialization()方法对AopProxyFactory的进一步封装。AspectJAutoProxyCreator代码如下:

    public class AspectJAutoProxyCreator implements BeanPostProcessor {
        ConfigurableBeanFactory beanFactory;
        public Object beforeInitialization(Object bean, String beanName) throws BeansException {
            return bean;
        }
    
        public Object afterInitialization(Object bean, String beanName) throws BeansException {
            
            //如果这个Bean本身就是Advice及其子类,那就不要再生成动态代理了。
            if(isInfrastructureClass(bean.getClass())){
                return bean;
            }
            
            List<Advice> advices = getCandidateAdvices(bean);//获取可用拦截器
            if(advices.isEmpty()){
                return bean;
            }
            
            return createProxy(advices,bean);
        }
        
        private List<Advice> getCandidateAdvices(Object bean){
            
            List<Object> advices = this.beanFactory.getBeansByType(Advice.class);
            
            List<Advice> result = new ArrayList<Advice>();
            for(Object o : advices){            
                Pointcut pc = ((Advice) o).getPointcut();
                if(canApply(pc,bean.getClass())){
                    result.add((Advice) o);
                }
                
            }
            return result;
        }
        
        protected Object createProxy( List<Advice> advices ,Object bean) {
            
            
            AopConfigSupport config = new AopConfigSupport();
            for(Advice advice : advices){
                config.addAdvice(advice);//构造AopConfig(添加拦截器)
            }
            
            Set<Class> targetInterfaces = ClassUtils.getAllInterfacesForClassAsSet(bean.getClass());
            for (Class<?> targetInterface : targetInterfaces) {
                config.addInterface(targetInterface);
            }
            
            config.setTargetObject(bean);//构造AopConfig(添加目标对象)     
            
            AopProxyFactory proxyFactory = null;
            if(config.getProxiedInterfaces().length == 0){
                proxyFactory =  new CglibProxyFactory(config);//cglib代理
            } else{
                
                proxyFactory = new JdkAopProxyFactory(config);
            }  
            
            return proxyFactory.getProxy();//获取代理对象
            
            
        }
        
        protected boolean isInfrastructureClass(Class<?> beanClass) {
            boolean retVal = Advice.class.isAssignableFrom(beanClass);
            
            return retVal;
        }
        
        public void setBeanFactory(ConfigurableBeanFactory beanFactory) {
            this.beanFactory = beanFactory;
            
        }
        
        public static boolean canApply(Pointcut pc, Class<?> targetClass) {
            
            MethodMatcher methodMatcher = pc.getMethodMatcher();    
    
            Set<Class> classes = new LinkedHashSet<Class>(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
            classes.add(targetClass);
            for (Class<?> clazz : classes) {
                Method[] methods = clazz.getDeclaredMethods();            
                for (Method method : methods) {
                    if (methodMatcher.matches(method/*, targetClass*/)) {//有一个方法与切入点匹配,则该bean就是需要动态代理的对象
                        return true;
                    }
                }
            }
            return false;
        }
    
    }

      最后,Bean生命周期图附一张:

    AOP就这么多了,你品,你细品!

    "我们所要追求的,永远不是绝对的正确,而是比过去的自己更好"
  • 相关阅读:
    成为JAVA(高级)工程师
    JVM的内存区域划分以及垃圾回收机制
    XML
    String.valueOf
    JAVA书籍(2)
    JAVA书籍(1)
    深入JAVA线程池
    FileWriter与BufferedWriter
    获取下拉框的文本或值
    删除字符串最后一个字符的几种方法
  • 原文地址:https://www.cnblogs.com/zomicc/p/12247992.html
Copyright © 2011-2022 走看看