zoukankan      html  css  js  c++  java
  • Spring之AOP原理、代码、使用详解(XML配置方式)

      Spring 的两大核心,一是IOC,另一个是AOP,本博客从原理、AOP代码以及AOP使用三个方向来讲AOP。先给出一张AOP相关的结构图,可以放大查看。

    一、Spring AOP 接口设计

      1、PointCut (连接点,定义匹配哪些方法)

      首先打开 Spring 的源码,查看 PointCut 接口设计:

    public interface Pointcut {
        ClassFilter getClassFilter();
        MethodMatcher getMethodMatcher();
        Pointcut TRUE = TruePointcut.INSTANCE;
    }

      该接口定义了2 个方法,一个成员变量。我们先看第一个方法:ClassFilter getClassFilter() ,该方法返回一个类过滤器,由于一个类可能会被多个代理类代理,于是Spring引入了责任链模式,另一个方法则是 MethodMatcher getMethodMatcher() ,表示返回一个方法匹配器,我们知道,AOP 的作用是代理方法,那么,Spirng 怎么知道代理哪些方法呢?必须通过某种方式来匹配方法的名称来决定是否对该方法进行增强,这就是 MethodMatcher 的作用。还有要给默认的 Pointcut 实例,该实例对于任何方法的匹配结果都是返回 true。

      我们关注一下 MethodMatcher 接口:

    public interface MethodMatcher {
        boolean matches(Method method, @Nullable Class<?> targetClass);
        boolean isRuntime();
        boolean matches(Method method, @Nullable Class<?> targetClass, Object... args);
        MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;
    }

      该接口定义了静态方法匹配器和动态方法匹配器。所谓静态方法匹配器,它仅对方法名签名(包括方法名和入参类型及顺序)进行匹配;而动态方法匹配器,会在运行期检查方法入参的值。静态匹配仅会判别一次,而动态匹配因为每次调用方法的入参都可能不一样,所以每次都必须判断。一般情况下,动态匹配不常用。方法匹配器的类型由isRuntime()返回值决定,返回false表示是静态方法匹配器,返回true表示是动态方法匹配器。

      总的来说, PointCut 和 MethodMatcher 是依赖关系,定义了AOP应该匹配什么方法以及如何匹配。

      2、Advice (通知,定义在链接点做什么)

      注意,Advice 接口只是一个标识,什么也没有定义,但是我们常用的几个接口,比如 BeforeAdvice,AfterAdvice,都是继承自它。我们关注一下 AfterAdvice 的子接口 AfterReturningAdvice :

    public interface AfterReturningAdvice extends AfterAdvice {
        void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable;
    }
      该接口定义了一个方法,afterReturning,参数分别是返回值,目标方法,参数,目标方法类,在目标方法执行之后会回调该方法。那么我们就可以在该方法中执行我们的切面逻辑,BeforeAdvice 也是一样的道理。

      3、Advisor (通知器,将 Advice 和 PointCut 结合起来)

      有了对目标方法的增强接口 Advice 和 如何匹配目标方法接口 PointCut 接口后,那么我们就需要用一个对象将他们结合起来,发挥AOP 的作用,所以Spring 设计了 Advisor(通知器),经过我们刚刚的描述,我们应该知道了,这个 Advisor 肯定依赖了 Advice 和 PointCut,我们看看接口设计:

    public interface Advisor {
        Advice EMPTY_ADVICE = new Advice() {};
        Advice getAdvice();
        boolean isPerInstance();
    }

      及其子接口:

    public interface PointcutAdvisor extends Advisor {
        Pointcut getPointcut();
    }

      以上三个接口关系如图所示:

            

     二、手写AOP示例

      我们直接定义以上三个接口的实现类,实现AOP(此方式是为了理解AOP原理的AOP写作方式,非日常用的AOP的XML实现方式)。

      1、Pointcut 接口实现

    package test;
    
    import java.lang.reflect.Method;
    import org.springframework.aop.ClassFilter;
    import org.springframework.aop.MethodMatcher;
    import org.springframework.aop.Pointcut;
    
    public class TestPointcut implements Pointcut {
    
        @Override
        public ClassFilter getClassFilter() {
            return ClassFilter.TRUE;
        }
    
        @Override
        public MethodMatcher getMethodMatcher() {
            return new MethodMatcher() {
    
                public boolean matches(Method method, Class<?> targetClass, Object[] args) {
                    if (method.getName().equals("test")) {
                        return true;
                    }
                    return false;
                }
    
                public boolean matches(Method method, Class<?> targetClass) {
                    if (method.getName().equals("test")) {
                        return true;
                    }
                    return false;
                }
    
                public boolean isRuntime() {
                    return true;
                }
            };
        }
    }

      只要方法名称是test则对该方法进行增强或者说拦截。

      2、AfterAdvice 实现

    package test;
    
    import java.lang.reflect.Method;
    import org.springframework.aop.AfterReturningAdvice;
    
    public class TestAfterAdvice implements AfterReturningAdvice {
    
        @Override
        public void afterReturning(Object returnValue, Method method,
                Object[] args, Object target) throws Throwable {
            System.out.println(
                    "after " + target.getClass().getSimpleName() + "." + method.getName() + "()");
        }
    }

      3、Advisor 通知器的实现

    package test;
    
    import org.aopalliance.aop.Advice;
    import org.springframework.aop.Pointcut;
    import org.springframework.aop.PointcutAdvisor;
    
    // 通知器
    public class TestAdvisor implements PointcutAdvisor {
        // 获取通知处理逻辑
        @Override
        public Advice getAdvice() {
            return new TestAfterAdvice();
        }
        @Override
        public boolean isPerInstance() {
            return false;
        }
        // 获取切入点
        @Override
        public Pointcut getPointcut() {
            return new TestPointcut();
        }
    }

      我们实现了 PointcutAdvisor 接口,返回我们刚才定义的两个类。完成了他们的组合。

      4、定义目标类 Targe

    package test;
    
    public class TestTarget {
    
        public void test() {
            System.out.println("target.test()");
        }
    
        public void test2() {
            System.out.println("target.test2()");
        }
    }

      该目标的实现是2个方法,分别打印自己的方法名。

      5、定义XML配置

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
    
        <bean id="testAdvisor" class="test.TestAdvisor"></bean>
        <bean id="testTarget" class="test.TestTarget"></bean>
        <bean id="testAOP" class="org.springframework.aop.framework.ProxyFactoryBean">
            <property name="targetName">
                <value>testTarget</value>
            </property>
            <property name="interceptorNames">
                <list>
                    <value>testAdvisor</value>
                </list>
            </property>
        </bean>
    </beans>
      我们定义了3个bean,上面两个testAdvisor、testTarget是我们刚刚定义的,下面一个ProxyFactoryBean 是什么呢?首先他是一个 FactoryBean,我们在学习 IOC 的时候知道, FactoryBean 是Spring 留给我们扩展用的,实现该接口的类可以自定类的各种功能。ProxyFactoryBean 当然也实现了自己的很多自定义功能。ProxyFactoryBean 也是Spring IOC 环境中创建AOP 应用的底层方法,Spring 正是通过它来实现对AOP的封装。这样我们更加接近Spring的底层设计。而该类需要注入两个属性一个目标类,一个拦截类,ProxyFactoryBean 会生成一个动态代理类来完成对目标方法的拦截。

      6、定义测试类

    package test;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.FileSystemXmlApplicationContext;
    
    public class TestAOP {
    
        public static void main(String[] args) {
            ApplicationContext applicationContext = new FileSystemXmlApplicationContext(
                    "spring-context/src/test/java/test/beans.xml");
            TestTarget target = (TestTarget) applicationContext.getBean("testAOP");
            target.test();
            System.out.println("----------------");
            target.test2();
        }
    }

      查看输出结果:

    target.test()
    after TestTarget.test()
    ----------------
    target.test2()

      可以看到因为我们只配置了在test名称的方法之后打印该方法的名称和该目标类的名称,而test2 则没有配置,因此也就没有打印。

    三、深入 AOP 源码实现

      我本地配置对service配置了AOP,代码如下:

    public class Test {
        public static void main(String[] args) {
            ApplicationContext context = 
                    new ClassPathXmlApplicationContext("classpath:aop/args/applicationContext.xml");
            TestService service = context.getBean("service", TestService.class);
            System.out.println(service.getClass().getName());
            service.test("test execution");
        }
    }

      在调用时,可以通过debug看到service返回的是一个JDK生成的代理对象:JdkDynamicAopProxy,而不是我们自己定义的一个TestServiceImpl的实例,也就是说, FactoryBean 确实能够在IOC容器中做一些定制化。

          

      配置了AOP的实例类在IOC阶段就已经注册了一个带有AOP功能的代理类,那么这个代理对象是如何生成的呢?

      首先进入抽象类 AbstractApplicationContext 的getBean 方法,从容器或获取 Bean,再调用 doGetBean 方法,看看该方法实现:

    //获取IoC容器中指定名称的Bean  
    public Object getBean(String name) throws BeansException {  
       //doGetBean才是真正向IoC容器获取被管理Bean的过程  
       return doGetBean(name, null, null, false);  
    }  
    //获取IoC容器中指定名称和类型的Bean  
    public <T> T getBean(String name, Class<T> requiredType) throws BeansException {  
       //doGetBean才是真正向IoC容器获取被管理Bean的过程  
       return doGetBean(name, requiredType, null, false);  
    }  
    //获取IoC容器中指定名称和参数的Bean  
    public Object getBean(String name, Object... args) throws BeansException {  
       //doGetBean才是真正向IoC容器获取被管理Bean的过程  
       return doGetBean(name, null, args, false);  
    }  
    //获取IoC容器中指定名称、类型和参数的Bean  
    public <T> T getBean(String name, Class<T> requiredType, Object... args) throws BeansException {  
    //doGetBean才是真正向IoC容器获取被管理Bean的过程  
       return doGetBean(name, requiredType, args, false);  
    }  
    //真正实现向IoC容器获取Bean的功能,也是触发依赖注入功能的地方  
    @SuppressWarnings("unchecked")  
    protected <T> T doGetBean(  
           final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)  
           throws BeansException {  
       //根据指定的名称获取被管理Bean的名称,剥离指定名称中对容器的相关依赖,如果指定的是别名,将别名转换为规范的Bean名称  
       final String beanName = transformedBeanName(name);  
       Object bean;  
       //先从缓存中取是否已经有被创建过的单态类型的Bean,对于单态模式的Bean整个IoC容器中只创建一次,不需要重复创建  
       Object sharedInstance = getSingleton(beanName);  
       //IOC容器创建单态模式Bean实例对象  
       if (sharedInstance != null && args == null) {  
           if (logger.isDebugEnabled()) {  
               //如果指定名称的Bean在容器中已有单态模式的Bean被创建,直接返回已经创建的Bean  
               if (isSingletonCurrentlyInCreation(beanName)) {  
                   logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +  
                           "' that is not fully initialized yet - a consequence of a circular reference");  
               }  
               else {  
                   logger.debug("Returning cached instance of singleton bean '" + beanName + "'");  
               }  
           }  
           //获取给定Bean的实例对象,主要是完成FactoryBean的相关处理  
           //注意:BeanFactory是管理容器中Bean的工厂,而FactoryBean是创建创建对象的工厂Bean,两者之间有区别  
           bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);  
       }  
       else {
        //缓存没有正在创建的单态模式Bean  
           //缓存中已经有已经创建的原型模式Bean,但是由于循环引用的问题导致实例化对象失败  
           if (isPrototypeCurrentlyInCreation(beanName)) {  
               throw new BeanCurrentlyInCreationException(beanName);  
           }  
           //对IoC容器中是否存在指定名称的BeanDefinition进行检查,首先检查是否能在当前的BeanFactory中获取的所需要的Bean
        //如果不能则委托当前容器的父级容器去查找,如果还是找不到则沿着容器的继承体系向父级容器查找  
           BeanFactory parentBeanFactory = getParentBeanFactory();  
           //当前容器的父级容器存在,且当前容器中不存在指定名称的Bean  
           if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {  
               //解析指定Bean名称的原始名称  
               String nameToLookup = originalBeanName(name);  
               if (args != null) {  
                   //委派父级容器根据指定名称和显式的参数查找  
                   return (T) parentBeanFactory.getBean(nameToLookup, args);  
               }  
               else {  
                   //委派父级容器根据指定名称和类型查找  
                   return parentBeanFactory.getBean(nameToLookup, requiredType);  
               }  
           }  
           //创建的Bean是否需要进行类型验证,一般不需要  
           if (!typeCheckOnly) {  
               //向容器标记指定的Bean已经被创建  
               markBeanAsCreated(beanName);  
           }  
            //根据指定Bean名称获取其父级的Bean定义,主要解决Bean继承时子类合并父类公共属性问题  
           final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);  
           checkMergedBeanDefinition(mbd, beanName, args);  
           //获取当前Bean所有依赖Bean的名称  
           String[] dependsOn = mbd.getDependsOn();  
           //如果当前Bean有依赖Bean  
           if (dependsOn != null) {  
               for (String dependsOnBean : dependsOn) {  
                   //递归调用getBean方法,获取当前Bean的依赖Bean  
                   getBean(dependsOnBean);  
                   //把被依赖Bean注册给当前依赖的Bean  
                   registerDependentBean(dependsOnBean, beanName);  
               }  
           }  
           //创建单态模式Bean的实例对象  
           if (mbd.isSingleton()) {  
           //这里使用了一个匿名内部类,创建Bean实例对象,并且注册给所依赖的对象  
               sharedInstance = getSingleton(beanName, new ObjectFactory() {  
                   public Object getObject() throws BeansException {  
                       try {  
                           //创建一个指定Bean实例对象,如果有父级继承,则合并子//类和父类的定义  
                           return createBean(beanName, mbd, args);  
                       }  
                       catch (BeansException ex) {  
                           //显式地从容器单态模式Bean缓存中清除实例对象  
                           destroySingleton(beanName);  
                           throw ex;  
                       }  
                   }  
               });  
               //获取给定Bean的实例对象  
               bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);  
           }  
           //IoC容器创建原型模式Bean实例对象  
           else if (mbd.isPrototype()) {  
               //原型模式(Prototype)是每次都会创建一个新的对象  
               Object prototypeInstance = null;  
               try {  
                   //回调beforePrototypeCreation方法,默认的功能是注册当前创//建的原型对象  
                   beforePrototypeCreation(beanName);  
                   //创建指定Bean对象实例  
                   prototypeInstance = createBean(beanName, mbd, args);  
               }  
               finally {  
                   //回调afterPrototypeCreation方法,默认的功能告诉IoC容器指//定Bean的原型对象不再创建了  
                   afterPrototypeCreation(beanName);  
               }  
               //获取给定Bean的实例对象  
               bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);  
           }  
           //要创建的Bean既不是单态模式,也不是原型模式,则根据Bean定义资源中配置的生命周期范围,选择实例化Bean的合适方法,这种在Web应用程序中比较常用
           //如:request、session、application等生命周期  
           else {  
               String scopeName = mbd.getScope();  
               final Scope scope = this.scopes.get(scopeName);  
               //Bean定义资源中没有配置生命周期范围,则Bean定义不合法  
               if (scope == null) {  
                   throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");  
               }  
               try {  
                   //这里又使用了一个匿名内部类,获取一个指定生命周期范围的实例  
                   Object scopedInstance = scope.get(beanName, new ObjectFactory() {  
                       public Object getObject() throws BeansException {  
                           beforePrototypeCreation(beanName);  
                           try {  
                               return createBean(beanName, mbd, args);  
                           }  
                           finally {  
                               afterPrototypeCreation(beanName);  
                           }  
                       }  
                   });  
                   //获取给定Bean的实例对象  
                   bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);  
               }  
               catch (IllegalStateException ex) {  
                   throw new BeanCreationException(beanName,  
                           "Scope '" + scopeName + "' is not active for the current thread; " +  
                           "consider defining a scoped proxy for this bean if you intend to refer to it from a singleton",  
                           ex);  
               }  
           }  
       }  
       //对创建的Bean实例对象进行类型检查  
       if (requiredType != null && bean != null && !requiredType.isAssignableFrom(bean.getClass())) {  
           throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());  
       }  
       return (T) bean;  
    }
      该方法会进入到第一个if块中的 bean = getObjectForBeanInstance(sharedInstance, name, beanName, null) 方法中,而 getObjectForBeanInstance 方法则会先判断缓存是否存在,如果不存在,则进入父类的 getObjectForBeanInstance 方法,我们看看该方法实现:
    //获取给定Bean的实例对象,主要是完成FactoryBean的相关处理 
    protected Object getObjectForBeanInstance(  
           Object beanInstance, String name, String beanName, RootBeanDefinition mbd) {  
       //容器已经得到了Bean实例对象,这个实例对象可能是一个普通的Bean,也可能是一个工厂Bean,如果是一个工厂Bean,则使用它创建一个Bean实例对象
       //如果调用本身就想获得一个容器的引用,则指定返回这个工厂Bean实例对象  
       //如果指定的名称是容器的解引用(dereference,即是对象本身而非内存地址),且Bean实例也不是创建Bean实例对象的工厂Bean  
       if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {  
           throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());  
       }  
       //如果Bean实例不是工厂Bean,或者指定名称是容器的解引用,调用者向获取对容器的引用,则直接返回当前的Bean实例  
       if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {  
           return beanInstance;  
       }  
       //处理指定名称不是容器的解引用,或者根据名称获取的Bean实例对象是一个工厂Bean,使用工厂Bean创建一个Bean的实例对象  
       Object object = null;  
       if (mbd == null) {  
           //从Bean工厂缓存中获取给定名称的Bean实例对象  
           object = getCachedObjectForFactoryBean(beanName);  
       }  
       //让Bean工厂生产给定名称的Bean对象实例  
       if (object == null) {  
           FactoryBean factory = (FactoryBean) beanInstance;  
           //如果从Bean工厂生产的Bean是单态模式的,则缓存  
           if (mbd == null && containsBeanDefinition(beanName)) {  
               //从容器中获取指定名称的Bean定义,如果继承基类,则合并基类相关属性  
               mbd = getMergedLocalBeanDefinition(beanName);  
           }  
           //如果从容器得到Bean定义信息,并且Bean定义信息不是虚构的,则让工厂Bean生产Bean实例对象  
           boolean synthetic = (mbd != null && mbd.isSynthetic());  
           //调用FactoryBeanRegistrySupport类的getObjectFromFactoryBean方法,实现工厂Bean生产Bean对象实例的过程  
           object = getObjectFromFactoryBean(factory, beanName, !synthetic);  
       }  
       return object;  
    }
      首先判断是否是 Bean 引用类型并且是否是 Factory 类型,很明显不是Bean 引用类型(Bean引用类型指的是IOC在解析XML文件 的时候,会有 ref 属性,而这个ref 对象还没有实例化,则暂时创建一个Bean引用类型的实例,用于在依赖注入的时候判断是否是Bean的属性类型,如果是,则从容器中取出,如果不是,则是基本类型,就直接赋值),然后进入下面的if判断,很明显会直接跳过。进入下面的 getCachedObjectForFactoryBean(beanName) 方法,从缓存中取出,很明显,第一次肯定返回null,继续向下,进入if块,重点在 object = getObjectFromFactoryBean(factory, beanName, !synthetic) 方法,我们进入该方法查看:
    //Bean工厂生产Bean实例对象  
    protected Object getObjectFromFactoryBean(FactoryBean factory, String beanName, boolean shouldPostProcess) {  
       //Bean工厂是单态模式,并且Bean工厂缓存中存在指定名称的Bean实例对象  
       if (factory.isSingleton() && containsSingleton(beanName)) {  
           //多线程同步,以防止数据不一致  
           synchronized (getSingletonMutex()) {  
               //直接从Bean工厂缓存中获取指定名称的Bean实例对象  
               Object object = this.factoryBeanObjectCache.get(beanName);  
               //Bean工厂缓存中没有指定名称的实例对象,则生产该实例对象  
               if (object == null) {  
                   //调用Bean工厂的getObject方法生产指定Bean的实例对象  
                   object = doGetObjectFromFactoryBean(factory, beanName, shouldPostProcess);  
                   //将生产的实例对象添加到Bean工厂缓存中  
                   this.factoryBeanObjectCache.put(beanName, (object != null ? object : NULL_OBJECT));  
               }  
               return (object != NULL_OBJECT ? object : null);  
           }  
       }  
       //调用Bean工厂的getObject方法生产指定Bean的实例对象  
       else {  
           return doGetObjectFromFactoryBean(factory, beanName, shouldPostProcess);  
       }  
    }  

      该方法还是先重缓存中取出,然后进入 doGetObjectFromFactoryBean(factory, beanName) 方法,我们看看该方法:

    //调用Bean工厂的getObject方法生产指定Bean的实例对象  
    private Object doGetObjectFromFactoryBean(  
           final FactoryBean factory, final String beanName, final boolean shouldPostProcess)  
           throws BeanCreationException {  
       Object object;  
       try {  
           if (System.getSecurityManager() != null) {  
               AccessControlContext acc = getAccessControlContext();  
               try {  
                   //实现PrivilegedExceptionAction接口的匿名内置类,根据JVM检查权限,然后决定BeanFactory创建实例对象  
                   object = AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {  
                       public Object run() throws Exception {  
                               //调用BeanFactory接口实现类的创建对象方法  
                               return factory.getObject();  
                           }  
                       }, acc);  
               }  
               catch (PrivilegedActionException pae) {  
                   throw pae.getException();  
               }  
           }  
           else {  
               //调用BeanFactory接口实现类的创建对象方法  
               object = factory.getObject();  
           }  
       }  
       catch (FactoryBeanNotInitializedException ex) {  
           throw new BeanCurrentlyInCreationException(beanName, ex.toString());  
       }  
       catch (Throwable ex) {  
           throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);  
       }  
       //创建出来的实例对象为null,或者因为单态对象正在创建而返回null  
       if (object == null && isSingletonCurrentlyInCreation(beanName)) {  
           throw new BeanCurrentlyInCreationException(  
                   beanName, "FactoryBean which is currently in creation returned null from getObject");  
       }  
       //为创建出来的Bean实例对象添加BeanPostProcessor后置处理器  
       if (object != null && shouldPostProcess) {  
           try {  
               object = postProcessObjectFromFactoryBean(object, beanName);  
           }  
           catch (Throwable ex) {  
               throw new BeanCreationException(beanName, "Post-processing of the FactoryBean's object failed", ex);  
           }  
       }  
       return object;  
    }
      该方法会直接进入 object = factory.getObject() 行,也就是 ProxyFactoryBean 的 getObject 方法,Spring 允许我们从写 getObject 方法来实现特定逻辑,继续看看该方法实现:
    public Object getObject() throws BeansException {
        initializeAdvisorChain();// 为代理对象配置Advisor链
        if (isSingleton()) {// 单例
            return getSingletonInstance();
        }
        else {
            if (this.targetName == null) {
                logger.warn("Using non-singleton proxies with singleton targets is often undesirable. " +
                        "Enable prototype proxies by setting the 'targetName' property.");
            }// 非单例
            return newPrototypeInstance();
        }
    }
      该方法很重要,首先初始化通知器链,然后获取单例,这里返回的就是我们最初看到的JDK动态代理。这里的初始化过滤器链的重要作用就是将通知连接起来,基本实现就是循环我们在配置文件中配置的通知器,按照链表的方式连接起来。然后判断是否是单例,然后我们着重关注下面的方法 getSingletonInstance();
    private synchronized Object getSingletonInstance() {
        if (this.singletonInstance == null) {
            this.targetSource = freshTargetSource();
            if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {
                // Rely on AOP infrastructure to tell us what interfaces to proxy.
                Class<?> targetClass = getTargetClass();
                if (targetClass == null) {
                    throw new FactoryBeanNotInitializedException("Cannot determine target class for proxy");
                }
                setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader));
            }
            // Initialize the shared singleton instance.
            super.setFrozen(this.freezeProxy);
            this.singletonInstance = getProxy(createAopProxy());
        }
        return this.singletonInstance;
    }
      该方法是同步方法,防止并发错误,因为有共享变量。首先返回一个包装过的目标对象,然后是否含有接口,我们的目标类实现了接口,因此截图也有展示interfaces,进入if块,从目标类中取出所有接口并设置接口。接下来重要的一行是 getProxy(createAopProxy()),先创建AOP,再获取代理。我们先看 crateAopProxy。
    protected final synchronized AopProxy createAopProxy() {
        if (!this.active) {
            activate();
        }
        return getAopProxyFactory().createAopProxy(this);
    }

      该方法返回一个AopProxy 类型的实例,我们看看该接口:

    public interface AopProxy {
        Object getProxy();
        Object getProxy(@Nullable ClassLoader classLoader);
    }

      该接口定义了两个重载方法,我们看看它有哪些实现:

            

      这是该接口的继承图,分别是 JdkDynamicAopProxy 动态代理和 CglibAopProxy 代理。而 JdkDynamicAopProxy 实现了 InvocationHandler 接口,如果熟悉Java 动态代理,应该熟悉该接口,实现了该接口的类并实现invoke方法,再代理类调用的时候,会回调该方法。实现动态代理。

      我们继续看 createAopProxy 方法,该方法主要逻辑是创建一个AOP 工厂,默认工厂是 DefaultAopProxyFactory,该类的 createAopProxy 方法则根据 ProxyFactoryBean 的一些属性来决定创建哪种代理:

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

      看到只要实现类接口,则创建JDK代理,否则则是Cglib代理。最终创建了一个 JdkDynamicAopProxy代理。

      我们回到 ProxyFactoryBean 类的 getProxy 方法,当 createAopProxy 返回一个JDK 代理的后,则调用 getProxy 方法获取一个代理对象,我们看看该方法的JdkDynamicAopProxy实现:

    public Object getProxy(@Nullable ClassLoader classLoader) {
            if (logger.isDebugEnabled()) {
                logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
            }
    
            Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
            this.findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
            return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
        }

      该方法就是用JDK自带的proxy返回一个proxy代理,接下来我们看看另一种Cglib的实现:

    @Override
    public Object getProxy(@Nullable ClassLoader classLoader) {
        if (logger.isDebugEnabled()) {
            logger.debug("Creating CGLIB proxy: target source is " + this.advised.getTargetSource());
        }
    
        try {
            Class<?> rootClass = this.advised.getTargetClass();
            Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");
    
            Class<?> proxySuperClass = rootClass;
            if (ClassUtils.isCglibProxyClass(rootClass)) {
                proxySuperClass = rootClass.getSuperclass();
                Class<?>[] additionalInterfaces = rootClass.getInterfaces();
                for (Class<?> additionalInterface : additionalInterfaces) {
                    this.advised.addInterface(additionalInterface);
                }
            }
    
            // Validate the class, writing log messages as necessary.
            validateClassIfNecessary(proxySuperClass, classLoader);
    
            // Configure 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);
            Class<?>[] types = new Class<?>[callbacks.length];
            for (int x = 0; x < types.length; x++) {
                types[x] = callbacks[x].getClass();
            }
            // fixedInterceptorMap only populated at this point, after getCallbacks call above
            enhancer.setCallbackFilter(new ProxyCallbackFilter(
                    this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
            enhancer.setCallbackTypes(types);
    
            // Generate the proxy class and create a proxy instance.
            return createProxyClassAndInstance(enhancer, callbacks);
        }
        catch (CodeGenerationException | IllegalArgumentException ex) {
            throw new AopConfigException("Could not generate CGLIB subclass of class [" +
                    this.advised.getTargetClass() + "]: " +
                    "Common causes of this problem include using a final class or a non-visible class",
                    ex);
        }
        catch (Throwable ex) {
            // TargetSource.getTarget() failed
            throw new AopConfigException("Unexpected AOP exception", ex);
        }
    }

      该方法基本上就是使用了Cglib 的库的一些API最后通过字节码生成代理类,比如 Enhancer 增强器,。总之,我们已经知道了Spring 是如何生成代理对象的,主要的通过 ProxyFactoryBean 来实现。

      最后,返回代理类,执行代理类的方法。完成切面编程。

    四、AOP代理类调用流程

      由3已经知道,AOP直接返回了一个代理类,那么这个代理类如何调用方法实现AOP的呢?
      以JdkDynamicAopProxy为例,通过debug可以知道在调用到具体的方法时,实际调用了JdkDynamicAopProxy类的invoke方法。

      1、JdkDynamicAopProxy.invoke()

    @Nullable
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object oldProxy = null;
        boolean setProxyContext = false;
        // 获取代理对象中的目标源对象。 相当于 Service 实现类。
        TargetSource targetSource = this.advised.targetSource;
        Object target = null;
    
        Integer var9;
        try {
            // 判断逻辑目的是避免代理对象执行出现RuntimeException
            if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
                Boolean var19 = this.equals(args[0]);
                return var19;
            }
    
            if (this.hashCodeDefined || !AopUtils.isHashCodeMethod(method)) {
                if (method.getDeclaringClass() == DecoratingProxy.class) {
                    Class var18 = AopProxyUtils.ultimateTargetClass(this.advised);
                    return var18;
                }
                //returnvalue, 定义返回结果数据的引用。
                Object retVal;
                if (!this.advised.opaque && method.getDeclaringClass().isInterface() && method.getDeclaringClass().isAssignableFrom(Advised.class)) {
                    retVal = AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
                    return retVal;
                }
    
                if (this.advised.exposeProxy) {
                    oldProxy = AopContext.setCurrentProxy(proxy);
                    setProxyContext = true;
                }
    
                //incaseitcomesfromapool. 目标对象获取。目标对象的类对象 
                target = targetSource.getTarget();
                Class<?> targetClass = target != null ? target.getClass() : null;
                // Get the interception chain for this method. 获取代理需要在目标方法执行前后,切入的拦截器链。 关注此方法
                List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
                if (chain.isEmpty()) {
                    // 如果代理对象没有需要切入的拦截器,执行目标对象中的方法。
                    Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
                    retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
                } else {
                    // 创建一个执行器,加入拦截信息,并按照顺序执行拦截代码和目标对象中的方法。
                    MethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
                    // 方法执行。按照顺序执行拦截代码和目标对象中的方法。关注此方法
                    retVal = invocation.proceed();
                }
    
                // Massage return value if necessary. 获取目标对象中方法的返回结果类
                Class<?> returnType = method.getReturnType();
                if (retVal != null && retVal == target && returnType != Object.class && returnType.isInstance(proxy) && !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
                    retVal = proxy;
                } else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
                    throw new AopInvocationException("Null return value from advice does not match primitive return type for: " + method);
                }
    
                Object var13 = retVal;
                return var13;
            }
    
            var9 = this.hashCode();
        } finally {
            if (target != null && !targetSource.isStatic()) {
                targetSource.releaseTarget(target);
            }
    
            if (setProxyContext) {
                AopContext.setCurrentProxy(oldProxy);
            }
    
        }
        return var9;
    }

      2、AdvisedSupport.getInterceptorsAndDynamicInterceptionAdvice

    public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {
        // 方法匹配信息, 获取 spring 容器中的缓存。
        AdvisedSupport.MethodCacheKey cacheKey = new AdvisedSupport.MethodCacheKey(method);
        // 从已知的缓存中获取方法缓存匹配信息。
        List<Object> cached = (List)this.methodCache.get(cacheKey);
        if (cached == null) {
            // 查询代理对象需要执行的拦截信息。 关注此方法
            cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(this, method, targetClass);
            // 保存缓存数据,为后续其他代码提供缓存内容。
            this.methodCache.put(cacheKey, cached);
        }
        return cached;
    }

      3、DefaultAdvisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice

    public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Advised config, Method method, @Nullable Class<?> targetClass) {
        List<Object> interceptorList = new ArrayList(config.getAdvisors().length);
        Class<?> actualClass = targetClass != null ? targetClass : method.getDeclaringClass();
        boolean hasIntroductions = hasMatchingIntroductions(config, actualClass);
        // 通知注册器。spring 容器会将配置好的所有通知使用注册器管理。
        AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
        Advisor[] var8 = config.getAdvisors();
        int var9 = var8.length;
    
        // 从配置信息中获取通知对象。
        for(int var10 = 0; var10 < var9; ++var10) {
            Advisor advisor = var8[var10];
            MethodInterceptor[] interceptors;
            if (advisor instanceof PointcutAdvisor) {
                PointcutAdvisor pointcutAdvisor = (PointcutAdvisor)advisor;
                if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
                    interceptors = registry.getInterceptors(advisor);
                    MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
                    if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) {
                        if (mm.isRuntime()) {
                            MethodInterceptor[] var15 = interceptors;
                            int var16 = interceptors.length;
    
                            for(int var17 = 0; var17 < var16; ++var17) {
                                MethodInterceptor interceptor = var15[var17];
                                interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
                            }
                        } else {
                            interceptorList.addAll(Arrays.asList(interceptors));
                        }
                    }
                }
            } else if (advisor instanceof IntroductionAdvisor) {
                IntroductionAdvisor ia = (IntroductionAdvisor)advisor;
                if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
                    interceptors = registry.getInterceptors(advisor);
                    interceptorList.addAll(Arrays.asList(interceptors));
                }
            } else {
                Interceptor[] interceptors = registry.getInterceptors(advisor);
                interceptorList.addAll(Arrays.asList(interceptors));
            }
        }
    
        return interceptorList;
    }

      4、ReflectiveMethodInvocation.proceed()

    public Object proceed() throws Throwable {
        // 开始执行代理方法。包含通知方法和目标对象中的真实方法。
        // 判断当前代理是否还有需要执行通知。如果没有通知,执行目标代码。
        if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
            return this.invokeJoinpoint();
        } else {
            Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
            if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
                InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher)interceptorOrInterceptionAdvice;
                return dm.methodMatcher.matches(this.method, this.targetClass, this.arguments) ? dm.interceptor.invoke(this) : this.proceed();
            } else {
                return ((MethodInterceptor)interceptorOrInterceptionAdvice).invoke(this);
            }
        }
    } 

      综合以上给出JdkDynamicAopProxy的时序图:

    五、AOP常见XML配置

      1、execution 表达式

      语法格式:execution(返回类型 包名.类名.方法名(参数表))

      如:execution(java.lang.String com.xxx.service.AService.test(java.lang.Integer))

      在类型 com.xxx.service.AService 中有方法 test,且参数为 Integer,返回类型为 String 时增加切面。

      如:execution(* com.xxx.AService.*(..)),代码:

    <bean id="service" class="com.sxt.aop.TestServiceImpl"></bean>
    <bean id="advice" class="com.sxt.aop.TestBeforeAdvice"></bean>
    <aop:config>
        <!-- com.sxt.aop包中任意类型任意方法都作为连接点。 -->
        <aop:pointcut expression="execution(* com.sxt.aop.*.*(..))" id="pointcut"/>
        <aop:advisor advice-ref="advice" pointcut-ref="pointcut"/>
    </aop:config>

      com.xxx.AService 类型中的任意方法,任意类型返回结果,参数表不限定,都增加切面。 应用:最常用 。也是相对最通用。根据方法执行的标准,定义切点。如:事务处理,日志处理。

      2、target表达式

      以目标对象作为切点的表达式定义方式。

      语法: target(包名.接口名)

      如: target(com.xxx.IA) - 所有实现了 IA 接口的实现类,作为代理的目标对象,会自动增加通知的织入,实现切面。代码:

    <bean id="service" class="com.sxt.aop.TestServiceImpl"></bean>
    <bean id="advice" class="com.sxt.aop.TestBeforeAdvice"></bean>
    <aop:config>
        <!-- 任意实现TestService接口的目标对象,都作为连接点 -->
        <aop:pointcut expression="target( com.sxt.aop.TestService )" id="pointcut"/>
        <aop:advisor advice-ref="advice" pointcut-ref="pointcut"/>
    </aop:config>

      应用:为某一个具体的接口实现提供的配置。如:登录 。登录的时候需要执行的附属逻辑是比较多的。在不同的业务流程中,附属逻辑也不同。如:电商中,可能在登录的时候,需要去执行购物车合并。

      3、this 表达式

      实现了某接口的代理对象,会作为切点。 和 target 很类似,但是更粗粒度,因为一个代理对象可能实现多个接口,意味着可以对应包含多个target。

      语法: this(包名.接口名),如代码:

    <bean id="service" class="com.sxt.aop.TestServiceImpl"></bean>
    <bean id="advice" class="com.sxt.aop.TestBeforeAdvice"></bean>
    <aop:config>
        <!-- 实现接口TestService的任意代理对象都作为连接点 -->
        <aop:pointcut expression="this( com.sxt.aop.TestService )" id="pointcut"/>
        <aop:advisor advice-ref="advice" pointcut-ref="pointcut"/>
    </aop:config>

      如: this(com.xxx.IA) - 代理对象 Proxy 如果实现了 IA 接口,则作为连接点。 应用:针对某个具体的代理提供的配置。比 target 切点粒度细致。因为目标对象可以多实现。代理对象可以针对目标对象实现的多个接口的某一个接口,提供特定的切点 。如:银 行中的登录,银行中的帐户种类非常多。且有交叉。如:借记卡,贷记卡,借记还贷卡,贷 记还贷卡等。可以针对还贷接口提供一个切点,做还贷信息的记录等。

      4、within 表达式

      以包作为目标,定义切点。

      语法: within(包名.*)- 代表在包中的任意接口或类型都作为切点。如代码:

    <bean id="service" class="com.sxt.aop.TestServiceImpl"></bean>
    <bean id="advice" class="com.sxt.aop.TestBeforeAdvice"></bean>
    <aop:config>
        <!-- com.sxt.aop包中的任意位置作为连接点 -->
        <aop:pointcut expression="within( com.sxt.aop.* )" id="pointcut"/>
        <aop:advisor advice-ref="advice" pointcut-ref="pointcut"/>
    </aop:config>

      应用: 针对某一个包提供的切点,粒度比 target 粗糙。如:某包中的所有接口都需要执行某附属逻辑。如:电商平台中的下订单。下订单服务中可能需 要特定的逻辑(时间戳校 验,库存检查等),这些逻辑,是其他业务线中不需要提供切面的。

      5、args 表达式

      以参数标准作为目标,定义切点。

      语法: args(类型,类型) - 代表方法的参数表符合要求的时候,作为切点。参数表是有顺序的。代码如:

    <bean id="service" class="com.sxt.aop.TestServiceImpl"></bean>
    <bean id="advice" class="com.sxt.aop.TestBeforeAdvice"></bean>
    <aop:config>
        <!-- 任意只有唯一参数,且参数类型为字符串的,都作为连接点 -->
        <aop:pointcut expression="args( java.lang.String )" id="pointcut"/>
        <aop:advisor advice-ref="advice" pointcut-ref="pointcut"/>
    </aop:config>

      应用:主要应用在参数校验中。如:登录的时候必须传递两个字符 串参数(登录名和密 码)。可以使用 args 来限定。 配合这 execution 实现。 如: execution( * xxxx.*.login(..) ) args(string,string)。 是使用频率最低的表达式。

    六、注解方式AOP概述

      而对于注解方式,通过分析源码我们知道注解方式和 XML 配置方式的底层实现都是一样的,都是通过继承 ProxyCreatorSupport 来实现的,不同的通过扩展不同的 Spring 提供的接口,XML 扩展的是FactoryBean 接口, 而注解方式扩展的是 BeanPostProcessor 接口,通过Spring 的扩展接口,能够对特定的Bean进行增强。而 AOP 正式通过这种方式实现的。

      大致流程为:

      纵观过程:实际就是为bean创建一个proxy,JDKproxy或者CGLIBproxy,然后在调用bean的方法时,会通过proxy来调用bean方法

        重点过程可分为:

        1)通过AspectJAutoProxyBeanDefinitionParser类将AnnotationAwareAspectJAutoProxyCreator注册到Spring容器中;

        2)AnnotationAwareAspectJAutoProxyCreator类的postProcessAfterInitialization()方法将所有有advice的bean重新包装成proxy;

        3)调用bean方法时通过proxy来调用,proxy依次调用增强器的相关方法,来实现方法切入。

    参考资料:

      SpringBoot2 | Spring AOP 原理深度源码分析

      深入理解Spring 之 源码剖析AOP(注解方式)

      Spring源码深度解析(AOP功能源码解析)

  • 相关阅读:
    [flash]准备添加动态加载flash,防止浏览者下载
    [Question]如何将韩文数据存入到数据库中,并在数据库中正确显示
    [other]毕业一年同学聚会
    [Question]要建立这样一个网站,应该怎么建立架构?
    [other]配置了一台K8平台的电脑
    Internet Explorer 7 Beta, Summer 2005
    [程序]粗制烂造的第一个windows应用程序
    [method]how to learn a language
    [other]昨天坐公交车没有买票!?
    五一长假你快乐吗?
  • 原文地址:https://www.cnblogs.com/jing99/p/11841159.html
Copyright © 2011-2022 走看看