上一篇博客中给大家介绍了AOP中的JDK动态代理。附上地址:
https://blog.csdn.net/bicheng4769/article/details/80028158
回顾一下上一篇的内容,JDK动态代理必须要求目标类实现接口才能使用。那么在AOP的使用中,我们貌似没有这个限制,所以这里AOP还有一种动态代理的机制:CGLIB动态代理。
什么是CGLIB
- CGLIB(Code Generation Library)是一个开源项目,是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口,其实就是cglib可以在运行时动态生成字节码。
- 使用CGLib实现动态代理,完全不受代理类必须实现接口的限制,而且CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类。(也就是通过继承去实现代理类,你目标类变成final,让人家怎么继承)
怎么去用CGLIB
- 拦截器:
MeMethodInterceptor
接口,实现MeMethodInterceptor(类似于JDK中的InvocationHandler
)。
MeMethodInterceptor 中仅一个方法Object intercept(Object var1, Method var2, Object[] var3, MethodProxy var4);
Object为由CGLib动态生成的代理类实例,Method为上文中实体类所调用的被代理的方法引用,Object[]为参数值列表,MethodProxy为生成的代理类对方法的代理引用。 - 字节码增强器:Java字节码增强指的是在Java字节码生成之后,对其进行修改,增强其功能,这种方式相当于对应用程序的二进制文件进行修改。通过
Enhancer
类去实现。
通过现象看本质(例子)
不需要接口的普通类:
package com.perf.Test.aop.cglib;
/**
* @author cj34920
* Date: 2018/04/21
*/
public class DayWork {
public void breakfast() {
System.out.println("吃早饭");
}
public void lunch() {
System.out.println("吃午饭");
}
public void dinner() {
System.out.println("吃完饭");
}
}
代理类实现MethodInterceptor接口,实现intercept方法:
package com.perf.Test.aop.cglib;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* @author cj34920
* Date: 2018/04/21
*/
public class CglibProxy implements MethodInterceptor {
private Object target;
private Class classz;
CglibProxy(Object o,Class classz)
{this.target = o;
this.classz = classz;}
public Object getNewProxy() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(classz);
//通过回调指定代理类。
enhancer.setCallback(CglibProxy.this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("吃饭之前洗手");
Object e = method.invoke(target, objects);
System.out.println("吃饭之后洗碗");
return e;
}
}
测试类:
package com.perf.Test.aop.cglib;
import org.springframework.aop.framework.ProxyFactoryBean;
/**
* @author cj34920
* Date: 2018/04/21
*/
public class DynamicProxy {
public static void main(String[] args) {
DayWork dayWork = new DayWork();
CglibProxy cglibProxy = new CglibProxy(dayWork, DayWork.class);
DayWork dayWorkProxy = (DayWork) cglibProxy.getNewProxy();
dayWorkProxy.breakfast();
dayWorkProxy.lunch();
dayWorkProxy.dinner();
System.out.println(dayWorkProxy.getClass());
ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
}
}
运行结果:
吃饭之前洗手
吃早饭
吃饭之后洗碗
吃饭之前洗手
吃午饭
吃饭之后洗碗
吃饭之前洗手
吃完饭
吃饭之后洗碗
class com.perf.Test.aop.cglib.DayWork$$EnhancerByCGLIB$$769d8723
看过例子之后,我们可以分析下这个输出的class。JDK动态代理输出的class 类似$proxy
。CGLIB动态代理输出的class 是类似目标类$$EnhancerByCGLIB$$
。反推过去,我们可以通过class来判断AOP是使用的哪种方式的代理。
两种代理方式在AOP的实践:
我们可以看到 CglibAopProxy 和jdkDynamicAopProxy 实现了AopProxy的接口。
AOP中对这两种代理的支持都是从ProxyFactoryBean
中的getObject
方法开始。
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();
}
}
最终是通过 调用ProxyCreatorSupport
这个类(实现AopProxyFactory
接口)中的createAopProxy
来创建代理类。
AOP如何判断使用哪种代理方式:
可以看到 最终的createAopProxy
实现类就是上面的DefaultAopProxyFactory
来实现的。所以千言万语,最终决定调用那种代理方式的依据是在DefaultAopProxyFactory
这个类的createAopProxy
方法中,附上代码:
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
@Override
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);
}
}
/**
* Determine whether the supplied {@link AdvisedSupport} has only the
* {@link org.springframework.aop.SpringProxy} interface specified
* (or no proxy interfaces specified at all).
*/
private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
Class<?>[] ifcs = config.getProxiedInterfaces();
return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0])));
}
}
Spring AOP 会动态选择使用 JDK 动态代理、CGLIB 来生成 AOP 代理。
Spring AOP 框架对 AOP 代理类的处理原则是:如果目标对象的实现类实现了接口(上述代码中的if (targetClass.isInterface() || Proxy.isProxyClass(targetClass))
),Spring AOP 将会采用 JDK 动态代理来生成 AOP 代理类;如果目标对象的实现类没有实现接口,Spring AOP 将会采用 CGLIB 来生成 AOP 代理类——不过这个选择过程对开发者完全透明、开发者也无需关心。
CGLIB和JDK动态代理的区别
JDK动态代理只能对实现了接口的类生成代理,而不能针对类 。
CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法 。
因为是继承,所以该类或方法不要声明成final ,final可以阻止继承和多态。
AOP强制使用CGLIB代理
配置文件中加入<aop:aspectj-autoproxy proxy-target-class=”true” />
强制AOP使用CGLIB代理。