美文网首页SpringFrameworkSpring源码分析
Spring AOP从原理到源码(三)

Spring AOP从原理到源码(三)

作者: 李不言被占用了 | 来源:发表于2020-04-20 09:13 被阅读0次

    接着上一节Spring AOP从原理到源码(二),本节关注spring aop创建代理对象的过程。

    Spring AOP的ProxyFactory

    本文希望传递的是掌握spring aop的方法,而不是抠源码,因为源码本身并没有什么难度,掌握的方法以后,基本上都能看懂。

    学习源码的方法一般是从两种:

    1. 自上而下
    2. 自下而上
      其实这跟我们设计软件的思路是一样的。自上而下可以让你避免抠细节,可以从大局上知道功能的整体情况,整体流程;自下而上则是先了解每个组件的细节以后,再将所有组件整合成完成的功能。两种方法其实各有利弊。

    刚开始看spring aop源码的时候也是采用自上而下的,从EnableAspectJAutoProxy注解开始,了解了aop的整体流程。但是总感觉不够深刻,因为发现里面有很多的细节(概念):AdviceAdvisorPointcutAdvisedProxyConfgAspectJ*AspectJAfterAfterAdviceAspectJAfterReturningAfterAdvice……)……被搞得有点凌乱

    于是决定从ProxyFactoryProxyFactoryBean开始。

    先从两段测试代码开始:

    import org.junit.Test;
    import org.springframework.aop.framework.ProxyFactory;
    
    /**
     * <br/>
     *
     * @author
     * @version V1.0
     * @email
     * @date 2020-04-17 16:41
     */
    public class ProxyFactoryTest {
    
        @Test
        public void test() {
            IMyService target = new MyServiceImpl();
            ProxyFactory factory = new ProxyFactory(target);
    
            // 注意这里添加的是Advice
            factory.addAdvice(new MyMethodBeforeAdvice());
    
            IMyService proxy = (IMyService) factory.getProxy();
            System.out.println(proxy.doService("aop"));
        }
    
        @Test
        public void test1() {
            IMyService target = new MyServiceImpl();
            ProxyFactory factory = new ProxyFactory(target);
    
            // 注意这里添加的是Advisor
            factory.addAdvisor(new DoServiceMethodPointcutAdvisor());
            IMyService proxy = (IMyService) factory.getProxy();
            System.out.println(proxy.doService("aop"));
    
            System.out.println("-------------------我是无情的分割线------------------");
            System.out.println(proxy.anotherService("你觉得我会被拦截吗?"));
        }
    }
    

    IMyService.java

    public interface IMyService {
        String doService(String msg);
        String anotherService(String msg);
    }
    

    MyMethodBeforeAdvice.java

    public class MyMethodBeforeAdvice implements MethodBeforeAdvice {
        @Override
        public void before(Method method, Object[] args, Object target) throws Throwable {
            System.out.println(String.format("%s#before正在拦截%s方法", getClass().getSimpleName(), method));
        }
    }
    

    MyServiceImpl.java

    public class MyServiceImpl implements IMyService {
        @Override
        public String doService(String msg) {
            System.out.println("MyServiceImpl#doService方法正在被调用");
            return "Hello, " + msg;
        }
    
        @Override
        public String anotherService(String msg) {
            System.out.println("这是MyServiceImpl#anotherService方法正在被调用");
            return "Hello, " + msg;
        }
    }
    
    public class DoServiceMethodPointcutAdvisor implements PointcutAdvisor {
        @Override
        public Pointcut getPointcut() {
            return new Pointcut() {
                @Override
                public ClassFilter getClassFilter() {
                    return new ClassFilter() {
                        @Override
                        public boolean matches(Class<?> clazz) {
                            return clazz.equals(MyServiceImpl.class);
                        }
                    };
                }
    
                @Override
                public MethodMatcher getMethodMatcher() {
                    return new MethodMatcher() {
                        @Override
                        public boolean matches(Method method, Class<?> targetClass) {// 只拦截doService方法
                            return method.getName().equalsIgnoreCase("doService");
                        }
    
                        @Override
                        public boolean isRuntime() {
                            return false;
                        }
    
                        @Override
                        public boolean matches(Method method, Class<?> targetClass, Object... args) {
                            return false;
                        }
                    };
                }
            };
        }
    
        @Override
        public Advice getAdvice() {
            return new MyMethodBeforeAdvice();
        }
    
        @Override
        public boolean isPerInstance() {
            return false;
        }
    }
    

    代码详解

    接着一行代码带着大家看:
    前面说过AOP的核心过程,所以,看源码其实分两步看即可:(划重点、划重点、划重点)

    1. 代理对象的创建
    2. 调用方法的拦截。

    1. 代理对象的创建

    new ProxyFactory(target);

    /**
        * Create a new ProxyFactory.
        * <p>Will proxy all interfaces that the given target implements.
        * @param target the target object to be proxied
        */
    public ProxyFactory(Object target) {
        // 把被代理对象封装到SingletonTargetSource里,没什么特别的逻辑
        setTarget(target);
        // 拿到被代理对象的所有接口
        setInterfaces(ClassUtils.getAllInterfaces(target));
        // 以上两段都没什么特别的逻辑,就是保存配置,为什么说是配置呢?
        // 因为ProxyFactory是ProxyConfig的子类,它肯定要保存代理相关的配置,才能创建出代理对象
    }
    这一段的核心就是把被代理的对象保存起来。
    

    factory.addAdvice(new MyMethodBeforeAdvice());

    @Override
    public void addAdvice(Advice advice) throws AopConfigException {
        // 插入一个通知,Advice就是特定连接点上的执行东西
        // 此处不必过于抠术语,简单理解就是插入了一个拦截器,拦截器在你执行方法的时候会进行拦截,然后增强
        // pos为advice待插入的位置,其实就是末尾!
        int pos = this.advisors.size();
        addAdvice(pos, advice);
    }
    
    /**
        * Cannot add introductions this way unless the advice implements IntroductionInfo.
        */
    @Override
    public void addAdvice(int pos, Advice advice) throws AopConfigException {
        Assert.notNull(advice, "Advice must not be null");
        /**
            * 一点补充知识:
            *   IntroductionAdvisor类级别的拦截(类中所有方法都会被拦截)。
            *   PointcutAdvisor可以实现方法级别的拦截。
            *   DynamicMethodMatcher方便实现根据方法参数拦截
            */
        // 这里先不纠结,直接看else
        if (advice instanceof IntroductionInfo) {
            // We don't need an IntroductionAdvisor for this kind of introduction:
            // It's fully self-describing.
            addAdvisor(pos, new DefaultIntroductionAdvisor(advice, (IntroductionInfo) advice));
        }
        else if (advice instanceof DynamicIntroductionAdvice) {
            // We need an IntroductionAdvisor for this kind of introduction.
            throw new AopConfigException("DynamicIntroductionAdvice may only be added as part of IntroductionAdvisor");
        }
        else {
            // 把advice封装成DefaultPointcutAdvisor
            /**
                * 再次插入一点背景知识:
                *   Advisor(或者说PointcutAdvisor)是Pointcut+Advice的结合体
                *   Advice定义在连接点做什么。
                *   Pointcut是用来匹配切入点的:它能触发类匹配(ClassFilter)+方法匹配(MethodMather)
                */
            addAdvisor(pos, new DefaultPointcutAdvisor(advice));
        }
    }
    
    @Override
    public void addAdvisor(int pos, Advisor advisor) throws AopConfigException {
        // 先不关注
        if (advisor instanceof IntroductionAdvisor) {
            validateIntroductionAdvisor((IntroductionAdvisor) advisor);
        }
        // 加入到list里
        addAdvisorInternal(pos, advisor);
    }
    
    private void addAdvisorInternal(int pos, Advisor advisor) throws AopConfigException {
        Assert.notNull(advisor, "Advisor must not be null");
        if (isFrozen()) {
            throw new AopConfigException("Cannot add advisor: Configuration is frozen.");
        }
        if (pos > this.advisors.size()) {
            throw new IllegalArgumentException(
                    "Illegal position " + pos + " in advisor list with size " + this.advisors.size());
        }
        // 添加到末尾,没啥好说的。
        // 不知道出于什么考虑advisors用的是LinkedList
        // 不过把advisor添加到末尾倒是效率非常高的,因为LinkedList内部保存了last指针(引用)
        this.advisors.add(pos, advisor);
        updateAdvisorArray();
        // 拦截器发生改变的时候需要把缓存清一下,不然方法被拦截就不正确了
        adviceChanged();
    }
    

    整一段的核心逻辑就是把Advice封装成DefaultPointcutAdvisor加入到List里。

    factory.getProxy();

    /**
        * Create a new proxy according to the settings in this factory.
        * <p>Can be called repeatedly. Effect will vary if we've added
        * or removed interfaces. Can add and remove interceptors.
        * <p>Uses a default class loader: Usually, the thread context class loader
        * (if necessary for proxy creation).
        * @return the proxy object
        */
    public Object getProxy() {
        // 由具体的AopProxy来创建代理对象
        return createAopProxy().getProxy();
    }
    

    分两段看:

    1. createAopProxy():
    /**
        * Subclasses should call this to get a new AOP proxy. They should <b>not</b>
        * create an AOP proxy with {@code this} as an argument.
        */
    protected final synchronized AopProxy createAopProxy() {
        // 当第一个代理被创建的时候active被设置成true,并且通知listener(s)
        // 观察者模式
        if (!this.active) {
            activate();
        }
        // 真正创建的工作是交给DefaultAopProxyFactory#createAopProxy的,传入的参数是this,代表AdvisedSupport(ProxyConfig)
        return getAopProxyFactory().createAopProxy(this);
    }
    
    @Override
    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        // 做了这些配置,或者被代理对象(target)没有实现接口,就要cglib来创建代理对象。
        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 {
            // 有实现的接口,就用JDK来创建代理
            // 需要关注的就是JdkDynamicAopProxy持有config对象和它本身实现了InvocationHandler,
            // 所以拦截方法的时候会被调用invoke方法,不明白的先恶补JDK的动态代理原理。
            return new JdkDynamicAopProxy(config);
        }
    }
    
    1. getProxy()
      这里会有多个实现,此处关注的是JdkDynamicAopProxy#getProxy
    
    
    @Override
    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);
        // 对equals和hashcode方法进行标记,不是重点
        findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
        // 创建出代理对象,InvocationHandler指向自己(this),所以拦截方法的时候会触发invoke调用
        // 至此,代理对象被创建出来,创建过程就算完成了。
        // 剩下的就是关注调用过程的方法拦截了。
        return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
    }
    

    至此,代理对象已经创建出来了。

    小结

    简单总结一下:

    1. 封装被代理对象;
    2. 添加Advice,封装成Advisor
    3. 创建代理对象。

    下一节一起看看方法调用的拦截过程是怎么实现的。
    转载请说明出处

    相关文章

      网友评论

        本文标题:Spring AOP从原理到源码(三)

        本文链接:https://www.haomeiwen.com/subject/xozkihtx.html