Spring源码解析之Spring AOP 获取 Proxy

2023-07-18 14:53:20 浏览数 (2)

下面我们来看看 Spring 的 AOP 的一些相关代码是怎么得到 Proxy 的,让我们我们先看看 AOP 和 Spring AOP 的一些基本概念: Advice: 通知,制定在连接点做什么,在 Sping 中,他主要描述 Spring 围绕方法调用注入的额外的行为,Spring 提供的通知类型有:

代码语言:javascript复制
1before advice,AfterReturningAdvice,ThrowAdvice,MethodBeforeAdvice

这些都是 Spring AOP 定义的接口类,具体的动作实现需要用户程序来完成。 Pointcut: 切点,其决定一个 advice 应该应用于哪个连接点,也就是需要插入额外处理的地方的集合,例如,被某个 advice 作为目标的一组方法。Spring pointcut 通常意味着标示方法,可以选择一组方法调用作为 pointcut,Spring 提供了具体的切点来给用户使用,比如正则表达式切点 JdkRegexpMethodPointcut 通过正则表达式对方法名进行匹配,其通过使用 AbstractJdkRegexpMethodPointcut 中的对MethodMatcher 接口的实现来完成 pointcut 功能:

代码语言:javascript复制
 1public final boolean matches(Method method, Class targetClass) { 
 2    //这里通过反射得到方法的全名 
 3    String patt = method.getDeclaringClass().getName()   "."   method.getName(); 
 4    for (int i = 0; i < this.patterns.length; i  ) { 
 5        // 这里是判断是否和方法名是否匹配的代码 
 6        boolean matched = matches(patt, i); 
 7        if (matched) { 
 8            for (int j = 0; j < this.excludedPatterns.length; j  ) { 
 9                boolean excluded = matchesExclusion(patt, j); 
10                if(excluded) { 
11                    return false; 
12                } 
13            } 
14            return true; 
15        } 
16    } 
17    return false; 
18} 

在 JDKRegexpMethodPointcut 中通过 JDK 中的正则表达式匹配来完成 pointcut 的最终确定:

代码语言:javascript复制
1protected boolean matches(String pattern, int patternIndex) { 
2    Matcher matcher = this.compiledPatterns[patternIndex].matcher(pattern); 
3    return matcher.matches(); 
4} 

Advisor: 当我们完成额外的动作设计(advice)和额外动作插入点的设计(pointcut)以后,我们需要一个对象把他们结合起来,这就是通知器 - advisor,定义应该在哪里应用哪个通知。Advisor 的实现有:DefaultPointcutAdvisor 他有两个属性 advice 和 pointcut 来让我们配置 advice和 pointcut。

接着我们就可以通过 ProxyFactoryBean 来配置我们的代理对象和方面行为,在 ProxyFactoryBean 中有 interceptorNames 来配置已经定义好的通知器-advisor,虽然这里的名字叫做 interceptNames,但实际上是供我们配置 advisor 的地方,具体的代理实现通过 JDK 的Proxy 或者 CGLIB 来完成。因为 ProxyFactoryBean 是一个 FactoryBean,在 ProxyFactoryBean 中我们通过 getObject()可以直接得到代理对象:

代码语言:javascript复制
 1public Object getObject() throws BeansException { 
 2    //这里初始化通知器链 
 3    initializeAdvisorChain(); 
 4    if (isSingleton()) { 
 5        //根据定义需要生成单件的 Proxy 
 6        return getSingletonInstance(); 
 7    }else { 
 8        ...
 9        //这里根据定义需要生成 Prototype 类型的 Proxy 
10        return newPrototypeInstance(); 
11    } 
12} 

我们看看怎样生成单件的代理对象:

代码语言:javascript复制
 1private synchronized Object getSingletonInstance() { 
 2    if (this.singletonInstance == null) { 
 3        this.targetSource = freshTargetSource(); 
 4        if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) { 
 5            // 这里设置代理对象的接口 
 6            setInterfaces(ClassUtils.getAllInterfacesForClass(this.targetSource.getTargetClass())); 
 7        } 
 8        // Eagerly initialize the shared singleton instance. 
 9        super.setFrozen(this.freezeProxy); 
10        // 注意这里的方法会使用 ProxyFactory 来生成我们需要的 Proxy 
11        this.singletonInstance = getProxy(createAopProxy()); 
12        // We must listen to superclass advice change events to recache the singleton 
13        // instance if necessary. 
14        addListener(this); 
15    } 
16    return this.singletonInstance; 
17} 
18
19//使用 createAopProxy 放回的 AopProxy 来得到代理对象。 
20protected Object getProxy(AopProxy aopProxy) { 
21    return aopProxy.getProxy(this.beanClassLoader); 
22} 

ProxyFactoryBean 的父类是 AdvisedSupport,Spring 使用 AopProxy 接口把 AOP 代理的实现与框架的其他部分分离开来;在AdvisedSupport 中通过这样的方式来得到 AopProxy,当然这里需要得到 AopProxyFactory 的帮助 - 下面我们看到 Spring 为我们提供的实现,来帮助我们方便的从 JDK 或者 cglib 中得到我们想要的代理对象:

代码语言:javascript复制
1protected synchronized AopProxy createAopProxy() { 
2    if (!this.isActive) { 
3        activate(); 
4    } 
5    return getAopProxyFactory().createAopProxy(this); 
6} 

而在 ProxyConfig 中对使用的 AopProxyFactory 做了定义:

代码语言:javascript复制
1//这个 DefaultAopProxyFactory 是 Spring 用来生成 AopProxy 的地方, 
2//当然了它包含 JDK 和 Cglib 两种实现方式。 
3private transient AopProxyFactory aopProxyFactory = new DefaultAopProxyFactory(); 

其中在 DefaultAopProxyFactory 中是这样生成 AopProxy 的:

代码语言:javascript复制
 1public AopProxy createAopProxy(AdvisedSupport advisedSupport) throws AopConfigException { 
 2    //首先考虑使用 cglib 来实现代理对象,当然如果同时目标对象不是接口的实现类的话 
 3    if (advisedSupport.isOptimize() || advisedSupport.isProxyTargetClass() || 
 4        advisedSupport.getProxiedInterfaces().length == 0) { 
 5        //这里判断如果不存在 cglib 库,直接抛出异常。 
 6        if (!cglibAvailable) { 
 7            throw new AopConfigException( 
 8            "Cannot proxy target class because CGLIB2 is not available. "   
 9            "Add CGLIB to the class path or specify proxy interfaces."); 
10        } 
11        // 这里使用 Cglib 来生成 Proxy,如果 target 不是接口的实现的话,返回 cglib 类型的 AopProxy 
12        return CglibProxyFactory.createCglibProxy(advisedSupport); 
13    } 
14    else { 
15        // 这里使用 JDK 来生成 Proxy,返回 JDK 类型的 AopProxy 
16        return new JdkDynamicAopProxy(advisedSupport); 
17    } 
18} 

于是我们就可以看到其中的代理对象可以由 JDK 或者 Cglib 来生成,我们看到 JdkDynamicAopProxy 类和 Cglib2AopProxy 都实现的是AopProxy 的接口,在 JdkDynamicAopProxy 实现中我们可以看到 Proxy 是怎样生成的:

代码语言:javascript复制
 1public Object getProxy(ClassLoader classLoader) { 
 2    if (logger.isDebugEnabled()) { 
 3        Class targetClass = this.advised.getTargetSource().getTargetClass(); 
 4        logger.debug("Creating JDK dynamic proxy"   
 5        (targetClass != null ? " for ["   targetClass.getName()   "]" : "")); 
 6    } 
 7    Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised); 
 8    findDefinedEqualsAndHashCodeMethods(proxiedInterfaces); 
 9    //这里我们调用 JDK Proxy 来生成需要的 Proxy 实例 
10    return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this); 
11} 

这样用 Proxy 包装 target 之后,通过 ProxyFactoryBean 得到对其方法的调用就被 Proxy 拦截了, ProxyFactoryBean 的 getObject()方法得到的实际上是一个 Proxy 了,我们的 target 对象已经被封装了。对 ProxyFactoryBean 这个工厂 bean 而言,其生产出来的对象是封装了目标对象的代理对象

0 人点赞