在Spring的AOP模块,一个主要的部分是代理对象的生成,可以通过ProxyFactoryBean来完成,它封装了主要代理对象的生成过程。在这个生成过程中,可以使用JDK的Proxy和CGLIB两种生成情况。

JDK动态代理与CGLIB区别

  1. jdk动态代理是利用反射机制生成的一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
  2. CGLIB动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
  3. 如果目标对象实现了接口,默认情况下采用JDK的动态代理实现AOP,但可以强制性使用CGLIB实现AOP
  4. 如果目标对象没有实现接口,必须采用cglib库,

Tip: 什么是匿名类
即没有名称的类,其名称由Java编译器给出,一般为:外部类名称+$+匿名类顺序,名称也就是其他地方不能引用,不能实例化,只用一次,当然也就不能有构造器。

  • 匿名类可以继承父类的方法,也可以重写父类的方法。
  • 匿名类可以访问外部类的成员变量和方法,匿名类的类体不可以声明为static成员变量和static方法。
  • 匿名类由于是一个new的结果,所以其实可以赋值给一个父类对象。

配置ProxyFactoryBean

  1. 定义通知器Advisor,这个通知器应该作为一个Bean来定义,定义了需要对目标对象进行增强的切面行为,也就是Advice通知。
  2. 定义ProxyFactoryBean,作为另一个Bean来定义,它是封装AOP功能的主要类。需要设定相关属性。
  • interceptorNames: 设置为需要定义的通知器,要通过使用代理对象的拦截器机制起作用的。
  • target: 是需要用AOP通知器中的切面应用来增强的对象。

生成AopProxy代理对象

在ProxyFactoryBean中,需要为target目标对象生成Proxy代理对象,从而为AOP横切面的编织做好准备。从FactoryBean中获取对象,是以getObject()方法作为入口完成的。在该方法中,首先对通知器链进行初始化,封装了一系列的拦截器,这些拦截器都要从配置中读取,然后为代理对象的生成做好准备。在生成代理对象时,因为Spring中有singleton类型和prototype类型这两种不同的Bean,所以要对代理对象的生成做一个区分。

1
2
3
4
5
6
7
8
9
10
11
12
13
public Object getObject() throws BeansException {
initializeAdvisorChain();
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();
}
}

首先为Proxy代理对象配置Advisor链,在initializeAdvisorChain()方法中执行。
在该方法中它会首先通过this.advisorChainInitialized来判断通知器链是否已经初始化了,如果已经初始化了,就直接返回。其他情况下,通过this.interceptorNames来要添加的通知器名,然后通过该名从IOC容器中取得的通知器加入到拦截器链中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
private synchronized void initializeAdvisorChain() throws AopConfigException, BeansException {
if (this.advisorChainInitialized) {
return;
}

if (!ObjectUtils.isEmpty(this.interceptorNames)) {
if (this.beanFactory == null) {
throw new IllegalStateException("No BeanFactory available anymore (probably due to serialization) " +
"- cannot resolve interceptor names " + Arrays.asList(this.interceptorNames));
}

// Globals can't be last unless we specified a targetSource using the property...
if (this.interceptorNames[this.interceptorNames.length - 1].endsWith(GLOBAL_SUFFIX) &&
this.targetName == null && this.targetSource == EMPTY_TARGET_SOURCE) {
throw new AopConfigException("Target required after globals");
}

// Materialize interceptor chain from bean names.
for (String name : this.interceptorNames) {
if (logger.isTraceEnabled()) {
logger.trace("Configuring advisor or advice '" + name + "'");
}

if (name.endsWith(GLOBAL_SUFFIX)) {
if (!(this.beanFactory instanceof ListableBeanFactory)) {
throw new AopConfigException(
"Can only use global advisors or interceptors with a ListableBeanFactory");
}
addGlobalAdvisor((ListableBeanFactory) this.beanFactory,
name.substring(0, name.length() - GLOBAL_SUFFIX.length()));
}

else {
// If we get here, we need to add a named interceptor.
// We must check if it's a singleton or prototype.
Object advice;
if (this.singleton || this.beanFactory.isSingleton(name)) {
// Add the real Advisor/Advice to the chain.
advice = this.beanFactory.getBean(name);
}
else {
// It's a prototype Advice or Advisor: replace with a prototype.
// Avoid unnecessary creation of prototype bean just for advisor chain initialization.
advice = new PrototypePlaceholderAdvisor(name);
}
addAdvisorOnChainCreation(advice, name);
}
}
}

this.advisorChainInitialized = true;
}

生成singleton的代理对象在getSingletonInstance()中完成
如果它还没有被创建,则lazily creating

在Spring代理目标target时,其实并不是直接创建一个目标target的对象实例的,而是通过一个TargetSource类型的对象对目标target进行封装,Spring Aop获取目标对象始终是通过TargetSource.getTarget()方法进行的。

proxy(代理对象)代理的不是target,而是TargetSource

那么问题来了:为什么SpringAOP代理不直接代理target,而需要通过代理TargetSource(target的来源,其内部持有target),间接代理target呢?

通常情况下,一个proxy(代理对象)只能代理一个target,每次方法调用的目标也是唯一固定的target。但是,如果让proxy代理TargetSource,可以使得每次方法调用的target实例都不同(当然也可以相同,这取决于TargetSource实现)。这种机制使得方法调用变得灵活,可以扩展出很多高级功能,如:target pool(目标对象池)、hot swap(运行时目标对象热替换),等等。

Spring内置了多种TargetSource

  • SingletonTargetSource
    从这个目标源取得的目标对象是单例的,成员变量target缓存了目标对象,每次getTarget()都是返回这个对象。
  • PrototypeTargetSource
    每次getTarget()将生成prototype类型的bean,即其生成的bean并不是单例的,因而使用这个类型的TargetSource时需要注意,封装的目标bean必须是prototype类型的。PrototypeTargetSource继承了AbstractBeanFactoryBasedTargetSource拥有了创建bean的能力。
  • CommonsPool2TargetSource
    里CommonsPool2TargetSource也就是池化的TargetSource,其基本具有平常所使用的“池”的概念的所有属性,比如:最小空闲数,最大空闲数,最大等待时间等等.
  • ThreadLocalTargetSource
    ThreadLocalTargetSource也就是和线程绑定的TargetSource,可以理解,其底层实现必然使用的是ThreadLocal

在上面简单介绍了有关TargetSource的有关知识,接下来将对getSingletonInstance()方法的有关步骤进行解释。

  • this.targetSource = freshTargetSource() 返回要在创建代理时使用的TargetSource.
    如果在interceptorNames列表的末尾没有指定目标,TargetSource将是该类的TargetSource成员。
    否则,我们将获得目标bean,并在必要时将其封装在TargetSource中。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private TargetSource freshTargetSource() {
if (this.targetName == null) {
if (logger.isTraceEnabled()) {
logger.trace("Not refreshing target: Bean name not specified in 'interceptorNames'.");
}
return this.targetSource;
}
else {
if (this.beanFactory == null) {
throw new IllegalStateException("No BeanFactory available anymore (probably due to serialization) " +
"- cannot resolve target with name '" + this.targetName + "'");
}
if (logger.isDebugEnabled()) {
logger.debug("Refreshing target with name '" + this.targetName + "'");
}
Object target = this.beanFactory.getBean(this.targetName);
return (target instanceof TargetSource ? (TargetSource) target : new SingletonTargetSource(target));
}
}
  • Class<?> targetClass = getTargetClass(); 根据AOP框架来判断需要代理的接口

    1
    2
    @Override @Nullable public Class<?> getTargetClass() {
    return this.targetSource.getTargetClass(); }
  • setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader)); 这里设置代理对象的接口

  • super.setFrozen(this.freezeProxy); 初始化共享单例实例 ,当一个配置被冻结时,不能对advice进行更改

  • this.singletonInstance = getProxy(createAopProxy()); 通过createAopProxy返回的AopProxy来生成需要的Proxy

1
2
protected Object getProxy(AopProxy aopProxy) {
return aopProxy.getProxy(this.proxyClassLoader); }
1
2
3
4
5
protected final synchronized AopProxy createAopProxy() {
if (!this.active) {
activate();
}
return getAopProxyFactory().createAopProxy(this); }

监听调用AdvisedSupportListener实现类的activated方法

1
2
3
4
5
6
private void activate() {
this.active = true;
for (AdvisedSupportListener listener : this.listeners) {
listener.activated(this);
}
}

具体的代理对象的生成,是在ProxyFactoryBean的基类AdvisedSupport的实现中借助AopProxyFactory完成的,这个代理对象要么从JDK中生成,要么借助CGLIB获得。

这个AopProxyFactory是在初始化函数中定义的,使用的是DefaultAopProxyFactor。

  • createAopProxy(AdvisedSupport config)
    在该方法中会判断采用不同的策略来生成AopProxy

如果targetClass是接口类,使用JDK来生成Proxy
如果不是接口类要生成Proxy,那么使用CGLIB来生成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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);
}
}

接下来分别介绍两种不同的方式来产生AopProxy代理对象

  1. JdkDynamicAopProxy
    1
    2
    3
    4
    5
    6
    7
    8
    9
    @Override public Object getProxy(@Nullable ClassLoader classLoader) {
    if (logger.isDebugEnabled()) {
    logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
    }
    # 获取AdvisedSupport类型对象的所有接口
    Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
    # 接口是否定义了 equals和hashcode方法 正常是没有的
    findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
    return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this); }

首先从advised对象中取得代理对象的代理接口配置,然后调用Proxy的newProxyInstance方法,得到最终的Proxy代理对象。

在生成代理对象时,需要指明三个参数,类加载器,代理接口和Proxy回调方法所在的对象。
在回调方法所在对象中,需要实现InvocationHandler接口,它定义了invoke方法,
对于JdkDynamimcAopProxy,它本身实现了InvocationHandler接口和invoke方法,这个invoke方法是Proxy代理对象的回调方法。

  1. CGLIB生成代理对象

在该篇文章中就不讲解了,感兴趣的可以百度搜索。

注:本文大多数是对《Spring技术内幕》的阅读整理。