Spring AOP之AopProxy代理对象
在Spring的AOP模块,一个主要的部分是代理对象的生成,可以通过ProxyFactoryBean来完成,它封装了主要代理对象的生成过程。在这个生成过程中,可以使用JDK的Proxy和CGLIB两种生成情况。
JDK动态代理与CGLIB区别
- jdk动态代理是利用反射机制生成的一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
- CGLIB动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。
- 如果目标对象实现了接口,默认情况下采用JDK的动态代理实现AOP,但可以强制性使用CGLIB实现AOP
- 如果目标对象没有实现接口,必须采用cglib库,
Tip: 什么是匿名类
即没有名称的类,其名称由Java编译器给出,一般为:外部类名称+$+匿名类顺序,名称也就是其他地方不能引用,不能实例化,只用一次,当然也就不能有构造器。
- 匿名类可以继承父类的方法,也可以重写父类的方法。
- 匿名类可以访问外部类的成员变量和方法,匿名类的类体不可以声明为static成员变量和static方法。
- 匿名类由于是一个new的结果,所以其实可以赋值给一个父类对象。
配置ProxyFactoryBean
- 定义通知器Advisor,这个通知器应该作为一个Bean来定义,定义了需要对目标对象进行增强的切面行为,也就是Advice通知。
- 定义ProxyFactoryBean,作为另一个Bean来定义,它是封装AOP功能的主要类。需要设定相关属性。
- interceptorNames: 设置为需要定义的通知器,要通过使用代理对象的拦截器机制起作用的。
- target: 是需要用AOP通知器中的切面应用来增强的对象。
生成AopProxy代理对象
在ProxyFactoryBean中,需要为target目标对象生成Proxy代理对象,从而为AOP横切面的编织做好准备。从FactoryBean中获取对象,是以getObject()方法作为入口完成的。在该方法中,首先对通知器链进行初始化,封装了一系列的拦截器,这些拦截器都要从配置中读取,然后为代理对象的生成做好准备。在生成代理对象时,因为Spring中有singleton类型和prototype类型这两种不同的Bean,所以要对代理对象的生成做一个区分。
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容器中取得的通知器加入到拦截器链中。
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中。
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框架来判断需要代理的接口
@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
protected Object getProxy(AopProxy aopProxy) {
return aopProxy.getProxy(this.proxyClassLoader); }
protected final synchronized AopProxy createAopProxy() {
if (!this.active) {
activate();
}
return getAopProxyFactory().createAopProxy(this); }
监听调用AdvisedSupportListener实现类的activated方法
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来生成。
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代理对象
- JdkDynamicAopProxy
@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代理对象的回调方法。
- CGLIB生成代理对象
在该篇文章中就不讲解了,感兴趣的可以百度搜索。
注:本文大多数是对《Spring技术内幕》的阅读整理。