创建AOP代理(中篇)

创建代理

在获取了所有对应bean的增强器后,便可以进行代理的创建了。

protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
            @Nullable Object[] specificInterceptors, TargetSource targetSource) {

        if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
            AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
        }

        ProxyFactory proxyFactory = new ProxyFactory();
        //获取当前类中的相关属性
        proxyFactory.copyFrom(this);
        //决定对于给定的bean是否应该使用targetClass而不是它的接口代理,检查ProxyTargetClass设置以及preserveTargetClass属性
        if (!proxyFactory.isProxyTargetClass()) {
            if (shouldProxyTargetClass(beanClass, beanName)) {
                proxyFactory.setProxyTargetClass(true);
            }
            else {
                evaluateProxyInterfaces(beanClass, proxyFactory);
            }
        }

        Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
        //加入增强器
        proxyFactory.addAdvisors(advisors);
        //设置要代理的类
        proxyFactory.setTargetSource(targetSource);
        //定制代理
        customizeProxyFactory(proxyFactory);
        //用来控制代理工厂被配置之后,是否还允许修改通知,缺省值为false(即在代理被配置以后,不允许修改代理的配置)
        proxyFactory.setFrozen(this.freezeProxy);
        if (advisorsPreFiltered()) {
            proxyFactory.setPreFiltered(true);
        }

        return proxyFactory.getProxy(getProxyClassLoader());
    }

对于代理类的创建及处理,Spring委托给了ProxyFactory去处理,而在上述函数中主要是对ProxyFactory的初始化操作,进而对真正的创建代理做准备,这些初始化操作包括如下内容:

(1)获取当前类中的属性;

(2)添加代理接口;

(3)封装Advisor并加入到ProxyFactory;

(4)设置要代理的类;

(5)当然在Spring中还为子类提供了定制的函数customizeProxyFactory,子类可以在此函数中进行对ProxyFactory的进一步的封装;

(6)进行获取代理操作;

其中,封装Advisor并加入到ProxyFactory中以及创建代理是两个相对繁琐的过程,可以通过ProxyFactory提供的addAdvisor方法直接将增强器置入代理创建工厂中,但是将拦截器封装为增强器还是需要一定的逻辑的。

protected Advisor[] buildAdvisors(@Nullable String beanName, @Nullable Object[] specificInterceptors) {
        //解析注册所有的interceptorName
        Advisor[] commonInterceptors = resolveInterceptorNames();

        List<Object> allInterceptors = new ArrayList<>();
        if (specificInterceptors != null) {
            //加入拦截器
            allInterceptors.addAll(Arrays.asList(specificInterceptors));
            if (commonInterceptors.length > 0) {
                if (this.applyCommonInterceptorsFirst) {
                    allInterceptors.addAll(0, Arrays.asList(commonInterceptors));
                }
                else {
                    allInterceptors.addAll(Arrays.asList(commonInterceptors));
                }
            }
        }
        if (logger.isTraceEnabled()) {
            int nrOfCommonInterceptors = commonInterceptors.length;
            int nrOfSpecificInterceptors = (specificInterceptors != null ? specificInterceptors.length : 0);
            logger.trace("Creating implicit proxy for bean '" + beanName + "' with " + nrOfCommonInterceptors +
                    " common interceptors and " + nrOfSpecificInterceptors + " specific interceptors");
        }

        Advisor[] advisors = new Advisor[allInterceptors.size()];
        for (int i = 0; i < allInterceptors.size(); i++) {
            //拦截器进行封装转化为Advisor
            advisors[i] = this.advisorAdapterRegistry.wrap(allInterceptors.get(i));
        }
        return advisors;
    }
public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
        //如果要封装的对象本身就是Advisor类型的那么无需再做过多处理
        if (adviceObject instanceof Advisor) {
            return (Advisor) adviceObject;
        }
        //因为此封装方法只是针对Advisor与Advice两种类型的数据有效,如果不是将不能封装
        if (!(adviceObject instanceof Advice)) {
            throw new UnknownAdviceTypeException(adviceObject);
        }
        Advice advice = (Advice) adviceObject;
        if (advice instanceof MethodInterceptor) {
            // 如果是MethodInterceptor类型则使用DefaultPointcutAdvisor封装
            return new DefaultPointcutAdvisor(advice);
        }
        //如果存在Advisor的适配器那么也同样需要进行封装
        for (AdvisorAdapter adapter : this.adapters) {
            // Check that it is supported.
            if (adapter.supportsAdvice(advice)) {
                return new DefaultPointcutAdvisor(advice);
            }
        }
        throw new UnknownAdviceTypeException(advice);
    }

由于Spring中涉及过多的拦截器,增强器、增强方法等方式对逻辑进行增强,所以非常有必要统一封装成Advisor来进行代理的创建,完成了增强的封装过程,那么解析最重要的一步就是代理的创建与获取了。

public Object getProxy(@Nullable ClassLoader classLoader) {
        return createAopProxy().getProxy(classLoader);
    }

1.创建代理

protected final synchronized AopProxy createAopProxy() {
        if (!this.active) {
            activate();
        }
        return getAopProxyFactory().createAopProxy(this);
    }
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);
        }
    }

上述函数就是代理的创建。Spring的代理方式有JDKProxy和CGLIB两种方式,那么Spring是如何选择代理方式的呢?我们从源码的角度来分析。

从第二段代码的if判断条件可以看到3个方面影响着Spring的判断。

  ❤ optimize:用来控制通过CGLIB创建的代理是否使用激进的优化策略。除非完全了解AOP代理如何处理优化,否则不推荐用户使用这个设置。目前这个属性仅用于CGLIB代理,对于JDK动态代理(缺省代理)无效;

  ❤ proxyTargetClass:这个属性为true时,目标类本身被代理而不是目标类的接口。如果这个属性值被设为true,CGLIB代理将会被创建,设置方式:

<aop:aspectj-autoproxy proxy-target-class="true"/>

  ❤ hasNoUserSuppliedProxyInterfaces:是否存在代理接口。

下面是对JDK和CGLIB方式的总结。

  ❤ 如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP;

  ❤ 如果目标对象实现了接口,可以强制使用CGLIB实现AOP;

  ❤ 如果目标对象没有实现接口,必须采用CGLIB库,Spring会自动在JDK动态代理和CGLIB之间转换;

如何强制使用CGLIB实现AOP?

(1)添加CGLIB库,Spring-HOME/cglib/*.jar。

(2)在Spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class="true"/>。

JDK动态代理与CGLIB字节码生成的区别?

  ❤ JDK动态代理只能对实现了接口的类生成代理,而不能针对类;

  ❤ CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,因为是继承,所以该类或方法最好不要声明成final。

2.获取代理

确定了使用哪种代理方式后便可以进行代理的创建了,但是创建之前有必要回顾一下两种方式的使用方法。

(1)JDK代理使用示例

创建业务接口,业务对外提供的接口,包含着业务可以对外提供的功能。

public interface UserService {

    public abstract void add();
}

创建业务接口实现类:

public class UserServiceImpl implements UserService {
    @Override
    public void add() {
        System.out.println("------add-----------------");
    }
}

创建自定义的InvocationHandler,用于对接口提供的方法进行增强:

public class MyInvocationHandler implements InvocationHandler {
    //目标对象
    private Object target;

    /**
     * 构造方法
     * @param target
     */
    public MyInvocationHandler(Object target){
        super();
        this.target = target;
    }

    //执行目标对象的方法
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //在目标对象的方法执行之前简单的打印一下
        System.out.println("------before------");

        //执行目标对象的方法
        Object result = method.invoke(target,args);

        //在目标对象的方法执行之后简单打印一下
        System.out.println("------after------");

        return result;
    }

    /**
     * 获取对象的代理对象
     * @return
     */
    public Object getProxy(){
        return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),target.getClass().getInterfaces(),this);
    }
}

测试,验证对于接口的增强是否起到作用。

public static void main(String[] args){
        //实例化目标对象
        UserService userService = new UserServiceImpl();
        //实例化InvocationHandler
        MyInvocationHandler handler = new MyInvocationHandler(userService);
        //根据目标生成对象
        UserService proxy = (UserService) handler.getProxy();
        //调用代理对象的方法
        proxy.add();
    }

运行结果如下:

------before------
------add-----------------
------after------

上述是AOP的一个简单的实现,在目标对象调用之前和 之后进行了增强。Spring的AOP实现其实也是用了Proxy和InvocationHandler这两个对象的。

再来回顾一下使用JDK代理的方式,在整个创建过程中,对于Invocationhandler的创建是最为核心的,在自定义InvocationHandler中需要重写3个函数。

  ❤ 构造函数:将代理的对象传入;

  ❤ invoke方法:此方法中实现了AOP的增强的所有逻辑;

  ❤ getProxy方法:此方法不会变,但是必不可少;

了解了JDKProxy的使用,那么我们来看看JDK代理在Spring中是如何实现的呢?继续之前的跟踪,到JDKDynamicAopProxy的getProxy方法中:

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

通过上面的示例可以知道JDKProxy的使用关键是创建自定义的InvocationHandler,而InvocationHandler中包含了函数getProxy,而当前方法正是完成了这个操作。由此可以推断,JDKDynamicAopProxy类中必然实现了InvocationHandler接口,同时,此类中还有函数invoke函数,并且AOP的核心逻辑都在其中,查看代码:

 1 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
 2         MethodInvocation invocation;
 3         Object oldProxy = null;
 4         boolean setProxyContext = false;
 5 
 6         TargetSource targetSource = this.advised.targetSource;
 7         Object target = null;
 8 
 9         try {
10             //equals方法的处理
11             if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
12                 return equals(args[0]);
13             }
14             //hash方法的处理
15             else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
16                 return hashCode();
17             }
18             else if (method.getDeclaringClass() == DecoratingProxy.class) {
19                 // There is only getDecoratedClass() declared -> dispatch to proxy config.
20                 return AopProxyUtils.ultimateTargetClass(this.advised);
21             }
22             else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
23                     method.getDeclaringClass().isAssignableFrom(Advised.class)) {
24                 // 使用代理配置对ProxyConfig的服务调用…
25                 return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
26             }
27 
28             Object retVal;
29             //有时候目标对象内部的自我调用将无法实施切面中的增强则需要通过此属性暴露代理
30             if (this.advised.exposeProxy) {
31                 oldProxy = AopContext.setCurrentProxy(proxy);
32                 setProxyContext = true;
33             }
34 
35             // Get as late as possible to minimize the time we "own" the target,
36             // in case it comes from a pool.
37             target = targetSource.getTarget();
38             Class<?> targetClass = (target != null ? target.getClass() : null);
39 
40             // 获取当前方法的拦截器链
41             List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
42 
43             if (chain.isEmpty()) {
44                 //如果没有发现任何拦截器那么直接调用切点方法
45                 Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
46                 retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
47             }
48             else {
49                 //将拦截器封装在ReflectiveMethodInvocation以便于使用其proceed进行链接表用拦截器
50                 invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
51                 //执行拦截器链
52                 retVal = invocation.proceed();
53             }
54 
55             // 返回结果
56             Class<?> returnType = method.getReturnType();
57             if (retVal != null && retVal == target &&
58                     returnType != Object.class && returnType.isInstance(proxy) &&
59                     !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
60                 retVal = proxy;
61             }
62             else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
63                 throw new AopInvocationException(
64                         "Null return value from advice does not match primitive return type for: " + method);
65             }
66             return retVal;
67         }
68         finally {
69             if (target != null && !targetSource.isStatic()) {
70                 // Must have come from TargetSource.
71                 targetSource.releaseTarget(target);
72             }
73             if (setProxyContext) {
74                 // Restore old proxy.
75                 AopContext.setCurrentProxy(oldProxy);
76             }
77         }
78     }

代码第22~26行代码:其中isAssignableFrom(Advised.class)方法:调用这个方法的class或者接口与参数Advised.class表示的类或者接口相同,或者是参数Advised.class表示的类或者接口的父类,则返回true。例如: 自身类.class.isAssignableFrom(自身类或子类.class)返回true。

上述的函数中最主要的工作就是创建了一个拦截器链,并使用ReflectiveMethodInvocation类进行了封装,而在ReflectiveMethodInvocation类的process方法中实现了拦截器的逐一调用,那么我们继续来了解,在process方法中是怎么实现对目标对象的调用前后进行增强的?

public Object proceed() throws Throwable {
        //    执行完所有增强后执行切点方法
        if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
            return invokeJoinpoint();
        }
        //获取下一个要执行的拦截器
        Object interceptorOrInterceptionAdvice =
                this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
        if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
            // 动态匹配
            InterceptorAndDynamicMethodMatcher dm =
                    (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
            Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
            if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
                return dm.interceptor.invoke(this);
            }
            else {
                // 不匹配则不执行拦截器
                return proceed();
            }
        }
        else {
            // 普通拦截器,直接调用拦截器
            return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
        }
    }

在process方法中,ReflectiveMethodInvocation中的主要职责是维护了链接调用的计数器,记录着当前调用链接的位置,以便于链接可以有序的进行下去,那么在这个方法中并没有我们之前设想的维护各种增强的顺序,而是将此工作委托给了各个增强器,使各个增强器在内部进行逻辑实现。

至此,已经将获取代理中的JDK Proxy讲解完毕了,接下来下篇文章将要讲述CGLIB的代理。

参考:《Spring源码深度解析》 郝佳 编著:

posted on 2019-01-08 10:31  AoTuDeMan  阅读(289)  评论(0编辑  收藏  举报

导航