SpringFrameworkSpring源码分析

Spring AOP从原理到源码(三)

2020-04-20  本文已影响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. 创建代理对象。

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

上一篇 下一篇

猜你喜欢

热点阅读