【小家Spring】详解Spring AOP中底层代理模式之JdkDynamicAopProxy和CglibAopProxy(ObjenesisCglibAopProxy)的源码分析
每篇一句
纸上得来终觉浅,建议多多动手
前言
在前面的博文我们了解到,Spring所有的代理AopProxy
的创建最后都是ProxyCreatorSupport#createAopProxy
这个方法,而这个方法如下:
protected final synchronized AopProxy createAopProxy() {
if (!this.active) {
activate();
}
return getAopProxyFactory().createAopProxy(this);
}
显然它又是调用了AopProxyFactory#createAopProxy
方法,它的唯一实现为DefaultAopProxyFactory
。
它做了一个简单的逻辑判断:若实现类接口,使用JdkDynamicAopProxy
最终去创建,否则交给ObjenesisCglibAopProxy
。
最终拿到AopProxy
后,调用AopProxy#getProxy()
就会拿到这个代理对象,从而进行相应的工作了。
我们基本有一共共识就是:默认情况下,若我们实现了接口,就实用JDK动态代理,若没有就实用CGLIB。那么就下来,就具体看看关乎到代理对象的创建、执行的一个具体过程原理
AopProxy:Aop代理接口
它是一个AOP代理的抽象接口。提供了两个方法,让我们可以获取对应 配置的AOP对象的代理:
public interface AopProxy {
//Create a new proxy object. Uses the AopProxy's default class loader ClassUtils.getDefaultClassLoader()
Object getProxy();
Object getProxy(@Nullable ClassLoader classLoader);
}
它的继承关系也很简单,就是接下来我们要说的那几个
环境构建
为了更好的做源码分析,因此此处我构建一个非常简单的Spring AOP的环境,来跟踪一下它的来龙去脉
public class Main {
public static void main(String[] args) {
ProxyFactory proxyFactory = new ProxyFactory(new Demo());
proxyFactory.addAdvice((MethodBeforeAdvice) (method, args1, target) ->
System.out.println("你被拦截了:方法名为:" + method.getName() + " 参数为--" + Arrays.asList(args1)));
DemoInterface demo = (DemoInterface) proxyFactory.getProxy();
//你被拦截了:方法名为:hello 参数为--[]
//this demo show
demo.hello();
System.out.println(proxyFactory.getTargetClass()); //class com.fsx.maintest.Demo
System.out.println(proxyFactory.getTargetSource()); //SingletonTargetSource for target object [[email protected]]
System.out.println(Arrays.asList(proxyFactory.getProxiedInterfaces())); //[interface com.fsx.maintest.DemoInterface]
System.out.println(Arrays.asList(proxyFactory.getAdvisors())); //[org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [Pointcut.TRUE]; advice [com.fsx.maintest.Main$$Lambda$2/[email protected]]]
// 获取类型,看看是JDK代理还是cglib的
System.out.println(demo instanceof Proxy); //true 所有的JDK代理都是继承自Proxy的
System.out.println(demo instanceof SpringProxy); //true
System.out.println(demo.getClass()); //class com.fsx.maintest.$Proxy0
System.out.println(Proxy.isProxyClass(demo.getClass())); //true
System.out.println(AopUtils.isCglibProxy(demo)); //false
//测试Advised接口、DecoratingProxy的内容
Advised advised = (Advised) demo;
System.out.println(Arrays.asList(advised.getProxiedInterfaces())); //[interface com.fsx.maintest.DemoInterface]
System.out.println(Arrays.asList(advised.getAdvisors())); //[org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [Pointcut.TRUE]; advice [com.fsx.maintest.Main$$Lambda$2/[email protected]]]
System.out.println(advised.isExposeProxy()); //false
System.out.println(advised.isFrozen()); //false
//System.out.println(advised.removeAdvice(null));
DecoratingProxy decoratingProxy = (DecoratingProxy) demo;
System.out.println(decoratingProxy.getDecoratedClass()); //class com.fsx.maintest.Demo
System.out.println("-----------------------------------------------------");
// Object的方法 ==== 所有的Object方法都不会被AOP代理 这点需要注意
System.out.println(demo.equals(new Object()));
System.out.println(demo.hashCode());
System.out.println(demo.getClass());
// 其余方法都没被拦截 只有toString()被拦截了 咋回事呢?它也不符合切点表达式的要求啊 看下面的解释吧
// 你被拦截了:方法名为:hello 参数为--[]
// [email protected]
System.out.println(demo.toString());
}
}
interface DemoInterface {
void hello();
}
class Demo implements DemoInterface {
@Override
public void hello() {
System.out.println("this demo show");
}
}
因为本文只讲述代理的创建,所以使用最为简单的
ProxyFactory
来创建代理,排除其它的干扰因素
JdkDynamicAopProxy
如上搭建的环境,生成的就是JDK的动态代理对象。这里面逻辑比较干净简单了:
- setTarget和setInterfaces交给
ProxyCreatorSupport
-
addAdvice
:此处就是个Advice前置通知增强器。最终会被包装成DefaultPointcutAdvisor
交给ProxyCreatorSupport
-
getProxy()
才是今天的关键,他内部会去new出来一个JdkDynamicAopProxy
,然后调用其getProx()
获取到动态代理出来的对象
那么接下来,就深入它的源码看看究竟:
// 我们发现它自己就实现了了InvocationHandler,所以处理器就是它自己。会实现invoke方法
// 它还是个final类 默认是包的访问权限
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
private static final Log logger = LogFactory.getLog(JdkDynamicAopProxy.class);
/** 这里就保存这个AOP代理所有的配置信息 包括所有的增强器等等 */
private final AdvisedSupport advised;
// 标记equals方法和hashCode方法是否定义在了接口上=====
private boolean equalsDefined;
private boolean hashCodeDefined;
public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {
Assert.notNull(config, "AdvisedSupport must not be null");
// 内部再校验一次:必须有至少一个增强器 和 目标实例才行
if (config.getAdvisors().length == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
throw new AopConfigException("No advisors and no TargetSource specified");
}
this.advised = config;
}
@Override
public Object getProxy() {
return getProxy(ClassUtils.getDefaultClassLoader());
}
// 真正创建JDK动态代理实例的地方
@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
}
// 这部很重要,就是去找接口 我们看到最终代理的接口就是这里返回的所有接口们(除了我们自己的接口,还有Spring默认的一些接口) 大致过程如下:
//1、获取目标对象自己实现的接口们(最终肯定都会被代理的)
//2、是否添加`SpringProxy`这个接口:目标对象实现对就不添加了,没实现过就添加true
//3、是否新增`Adviced`接口,注意不是Advice通知接口。 实现过就不实现了,没实现过并且advised.isOpaque()=false就添加(默认是会添加的)
//4、是否新增DecoratingProxy接口。传入的参数decoratingProxy为true,并且没实现过就添加(显然这里,首次进来是会添加的)
//5、代理类的接口一共是目标对象的接口+上面三个接口SpringProxy、Advised、DecoratingProxy(SpringProxy是个标记接口而已,其余的接口都有对应的方法的)
//DecoratingProxy 这个接口Spring4.3后才提供
Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
// 第三个参数传的this,处理器就是自己嘛 到此一个代理对象就此new出来啦
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
// 找找看看接口里有没有自己定义equals方法和hashCode方法,这个很重要 然后标记一下
// 注意此处用的是getDeclaredMethods,只会找自己的
private void findDefinedEqualsAndHashCodeMethods(Class<?>[] proxiedInterfaces) {
for (Class<?> proxiedInterface : proxiedInterfaces) {
Method[] methods = proxiedInterface.getDeclaredMethods();
for (Method method : methods) {
if (AopUtils.isEqualsMethod(method)) {
this.equalsDefined = true;
}
if (AopUtils.isHashCodeMethod(method)) {
this.hashCodeDefined = true;
}
// 小技巧:两个都找到了 就没必要继续循环勒
if (this.equalsDefined && this.hashCodeDefined) {
return;
}
}
}
}
// 对于这部分代码和采用CGLIB的大部分逻辑都是一样的,Spring对此的解释很有意思:
// 本来是可以抽取出来的,使得代码看起来更优雅。但是因为此会带来10%得性能损耗,所以Spring最终采用了粘贴复制的方式各用一份
// Spring说它提供了基础的套件,来保证两个的执行行为是一致的。
//proxy:指的是我们所代理的那个真实的对象;method:指的是我们所代理的那个真实对象的某个方法的Method对象args:指的是调用那个真实对象方法的参数。
// 此处重点分析一下此方法,这样在CGLIB的时候,就可以一带而过了~~~因为大致逻辑是一样的
@Override
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 它是org.aopalliance.intercept这个包下的 AOP联盟得标准接口
MethodInvocation invocation;
Object oldProxy = null;
boolean setProxyContext = false;
TargetSource targetSource = this.advised.targetSource;
Object target = null;
try {
//“通常情况”Spring AOP不会对equals、hashCode方法进行拦截增强,所以此处做了处理
// equalsDefined为false(表示自己没有定义过eequals方法) 那就交给代理去比较
// hashCode同理,只要你自己没有实现过此方法,那就交给代理吧
// 需要注意的是:这里统一指的是,如果接口上有此方法,但是你自己并没有实现equals和hashCode方法,那就走AOP这里的实现
// 如国接口上没有定义此方法,只是实现类里自己@Override了HashCode,那是无效的,就是普通执行吧
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
return equals(args[0]);
}
else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
return hashCode();
}
// 下面两段做了很有意思的处理:DecoratingProxy的方法和Advised接口的方法 都是是最终调用了config,也就是this.advised去执行的~~~~
else if (method.getDeclaringClass() == DecoratingProxy.class) {
// There is only getDecoratedClass() declared -> dispatch to proxy config.
return AopProxyUtils.ultimateTargetClass(this.advised);
}
else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
method.getDeclaringClass().isAssignableFrom(Advised.class)) {
// Service invocations on ProxyConfig with the proxy config...
return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
}
// 这个是最终该方法的返回值~~~~
Object retVal;
//是否暴露代理对象,默认false可配置为true,如果暴露就意味着允许在线程内共享代理对象,
//注意这是在线程内,也就是说同一线程的任意地方都能通过AopContext获取该代理对象,这应该算是比较高级一点的用法了。
// 这里缓存一份代理对象在oldProxy里~~~后面有用
if (this.advised.exposeProxy) {
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
//通过目标源获取目标对象 (此处Spring建议获取目标对象靠后获取 而不是放在上面)
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
// 获取作用在这个方法上的所有拦截器链~~~ 参见DefaultAdvisorChainFactory#getInterceptorsAndDynamicInterceptionAdvice方法
// 会根据切点表达式去匹配这个方法。因此其实每个方法都会进入这里,只是有很多方法得chain事Empty而已
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
if (chain.isEmpty()) {
// 若拦截器为空,那就直接调用目标方法了
// 对参数进行适配:主要处理一些数组类型的参数,看是表示一个参数 还是表示多个参数(可变参数最终到此都是数组类型,所以最好是需要一次适配)
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
// 这句代码的意思是直接调用目标方法~~~
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
}
else {
// 创建一个invocation ,此处为ReflectiveMethodInvocation 最终是通过它,去执行前置加强、后置加强等等逻辑
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// 此处会执行所有的拦截器链 交给AOP联盟的MethodInvocation去处理。当然实现还是我们Spring得ReflectiveMethodInvocation
retVal = invocation.proceed();
}
// 获取返回值的类型
Class<?> returnType = method.getReturnType();
if (retVal != null && retVal == target &&
returnType != Object.class && returnType.isInstance(proxy) &&
!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
// 一些列的判断条件,如果返回值不为空,且为目标对象的话,就直接将目标对象赋值给retVal
retVal = proxy;
}
// 返回null,并且还不是Void类型。。。抛错
else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
throw new AopInvocationException(
"Null return value from advice does not match primitive return type for: " + method);
}
return retVal;
}
finally {
// 释放~~
if (target != null && !targetSource.isStatic()) {
targetSource.releaseTarget(target);
}
// 把老的代理对象重新set进去~~~
if (setProxyContext) {
AopContext.setCurrentProxy(oldProxy);
}
}
}
// AOP帮我们实现的CgLib方法
@Override
public boolean equals(@Nullable Object other) {
if (other == this) {
return true;
}
if (other == null) {
return false;
}
JdkDynamicAopProxy otherProxy;
if (other instanceof JdkDynamicAopProxy) {
otherProxy = (JdkDynamicAopProxy) other;
}
else if (Proxy.isProxyClass(other.getClass())) {
InvocationHandler ih = Proxy.getInvocationHandler(other);
if (!(ih instanceof JdkDynamicAopProxy)) {
return false;
}
otherProxy = (JdkDynamicAopProxy) ih;
}
else {
// Not a valid comparison...
return false;
}
// If we get here, otherProxy is the other AopProxy.
return AopProxyUtils.equalsInProxy(this.advised, otherProxy.advised);
}
// AOP帮我们实现的HashCode方法
@Override
public int hashCode() {
return JdkDynamicAopProxy.class.hashCode() * 13 + this.advised.getTargetSource().hashCode();
}
}
细节:
- 除了实现类里自己写的方法(接口上没有的),其余方法统一都会进入代理得
invoke()
方法里面。只是invoke上做了很多特殊处理,比如DecoratingProxy
和Advised
等等的方法,都是直接执行了。 - object的方法中,toString()方法会被增强(至于为何,我至今还没找到原因,麻烦的知道的给个答案) 因为我始终不知道
AdvisedSupport#methodCache
这个字段事什么把toString()方法缓存上的,打断点都没跟踪上 - 生成出来的代理对象,Spring默认都给你实现了接口:
SpringProxy、DecoratingProxy、Advised
CglibAopProxy
ObjenesisCglibAopProxy
环境我们只需要让Demo不再实现接口就成了~
public class Main {
public static void main(String[] args) {
ProxyFactory proxyFactory = new ProxyFactory(new Demo());
proxyFactory.addAdvice((MethodBeforeAdvice) (method, args1, target) -> {
System.out.println("你被拦截了:方法名为:" + method.getName() + " 参数为--" + Arrays.asList(args1));
}
);
Demo demo = (Demo) proxyFactory.getProxy();
//你被拦截了:方法名为:hello 参数为--[]
//this demo show
demo.hello();
}
}
// 不要再实现接口,就会用CGLIB去代理
class Demo {
public void hello() {
System.out.println("this demo show");
}
}
这个两个放在一起说,因为ObjenesisCglibAopProxy
继承自CglibAopProxy
,它只重写了createProxyClassAndInstance
方法:
// 它是Spring4.0之后提供的
class ObjenesisCglibAopProxy extends CglibAopProxy {
// 下面有解释,另外一种创建实例的方式(可议不用空的构造函数哟)
private static final SpringObjenesis objenesis = new SpringObjenesis();
public ObjenesisCglibAopProxy(AdvisedSupport config) {
super(config);
}
// 创建一个代理得实例
@Override
protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
Class<?> proxyClass = enhancer.createClass();
Object proxyInstance = null;
// 如果为true,那我们就采用objenesis去new一个实例~~~
if (objenesis.isWorthTrying()) {
try {
proxyInstance = objenesis.newInstance(proxyClass, enhancer.getUseCache());
} catch (Throwable ex) {
logger.debug("Unable to instantiate proxy using Objenesis, " +
"falling back to regular proxy construction", ex);
}
}
// 若果还为null,就再去拿到构造函数(指定参数的)
if (proxyInstance == null) {
// Regular instantiation via default constructor...
try {
Constructor<?> ctor = (this.constructorArgs != null ?
proxyClass.getDeclaredConstructor(this.constructorArgTypes) :
proxyClass.getDeclaredConstructor());
// 通过此构造函数 去new一个实例
ReflectionUtils.makeAccessible(ctor);
proxyInstance = (this.constructorArgs != null ?
ctor.newInstance(this.constructorArgs) : ctor.newInstance());
} catch (Throwable ex) {
throw new AopConfigException("Unable to instantiate proxy using Objenesis, " +
"and regular proxy instantiation via default constructor fails as well", ex);
}
}
((Factory) proxyInstance).setCallbacks(callbacks);
return proxyInstance;
}
}
本来要想使用ASM和CGLIB,我们是需要引入cglib相关的jar包的。但是从Spring3.2以后,我们就不用再单独因此此Jar了,因为Spring已经帮我们集成在Spring-core里面了
那么接下来,我们只需要看看CglibAopProxy
内容即可,他处理的是核心内容。
class CglibAopProxy implements AopProxy, Serializable {
// 它的两个getProxy()相对来说比较简单,就是使用CGLIB的方式,利用Enhancer创建了一个增强的实例
// 这里面比较复杂的地方在:getCallbacks()这步是比较繁琐的
// setCallbackFilter就是看看哪些方法需要拦截、哪些不需要~~~~
@Override
public Object getProxy() {
return getProxy(null);
}
// CGLIB重写的这两个方法
@Override
public boolean equals(Object other) {
return (this == other || (other instanceof CglibAopProxy &&
AopProxyUtils.equalsInProxy(this.advised, ((CglibAopProxy) other).advised)));
}
@Override
public int hashCode() {
return CglibAopProxy.class.hashCode() * 13 + this.advised.getTargetSource().hashCode();
}
// 最后,所有的被代理得类的所有的方法调用,都会进入DynamicAdvisedInterceptor#intercept这个方法里面来(相当于JDK动态代理得invoke方法)
// 它实现了MethodInterceptor接口
private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {
private final AdvisedSupport advised;
public DynamicAdvisedInterceptor(AdvisedSupport advised) {
this.advised = advised;
}
@Override
@Nullable
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
Object target = null;
// 目标对象源
TargetSource targetSource = this.advised.getTargetSource();
try {
if (this.advised.exposeProxy) {
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// 拿到目标对象
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
// 一样的,也是拿到和这个方法匹配的 所有的增强器、通知们 和JDK Proxy中是一样的
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
// 没有增强器,同时该方法是public得 就直接调用目标方法(不拦截)
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = methodProxy.invoke(target, argsToUse);
}
else {
// CglibMethodInvocation这里采用的是CglibMethodInvocation,它是`ReflectiveMethodInvocation`的子类 到这里就和JDK Proxy保持一致勒
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
}
retVal = processReturnType(proxy, target, method, retVal);
return retVal;
}
finally {
if (target != null && !targetSource.isStatic()) {
targetSource.releaseTarget(target);
}
if (setProxyContext) {
AopContext.setCurrentProxy(oldProxy);
}
}
}
@Override
public boolean equals(Object other) {
return (this == other ||
(other instanceof DynamicAdvisedInterceptor &&
this.advised.equals(((DynamicAdvisedInterceptor) other).advised)));
}
/**
* CGLIB uses this to drive proxy creation.
*/
@Override
public int hashCode() {
return this.advised.hashCode();
}
}
}
细节:
- 和JDK的一样,Object的方法,只有toString()会被拦截
- 生成出来的代理对象,Spring默认都给你实现了接口:
SpringProxy、DecoratingProxy、Advised
- 它和JDK不同的是,比如equals和hashCode等方法根本就不会进入intecept方法,而是在
getCallbacks()
那里就给特殊处理掉了
Objenesis:另一种实例化对象的方式
它专门用来创建对象,即使你没有空的构造函数,都木有问题~~ 可谓非常的强大
它不使用构造方法创建Java对象,所以即使你有空的构造方法,也是不会执行的。
Objenesis是一个Java的库,主要用来创建特定的对象。
由于不是所有的类都有无参构造器又或者类构造器是private,在这样的情况下,如果我们还想实例化对象,class.newInstance是无法满足的。
使用ObjenesisStd
public class MainTest {
public static void main(String[] args) throws Exception {
Objenesis objenesis = new ObjenesisStd();
// 它竟然创建成功了
MyDemo myDemo = objenesis.newInstance(MyDemo.class);
System.out.println(myDemo); //[email protected]
System.out.println(myDemo.code); //null 特别注意:这里是null,而不是10
// 若直接这样创建 就报错 java.lang.InstantiationException: com.fsx.maintest.MyDemo
System.out.println(MyDemo.class.newInstance());
}
}
class MyDemo {
public String code = "10";
public MyDemo(String code) {
this.code = code;
}
}
实用ObjectInstantiator
public static void main(String[] args) throws Exception {
Objenesis objenesis = new ObjenesisStd();
// 相当于生成了一个实例创建的工厂,接下来就可以很方便得创建实例了
// 如果你要创建多个实例,建议这么来创建
ObjectInstantiator<MyDemo> instantiator = objenesis.getInstantiatorOf(MyDemo.class);
MyDemo myDemo1 = instantiator.newInstance();
MyDemo myDemo2 = instantiator.newInstance();
System.out.println(myDemo1);
System.out.println(myDemo1.code); //null
System.out.println(myDemo2);
}
使用SpringObjenesis
这是Spring对Objenesis
接口的一个实现。由Spring4.2之后提供的(ObjenesisCglibAopProxy
可是Spring4.0就有了哦)
基本实用上,我们只需要换个实现就成:
Objenesis objenesis = new SpringObjenesis();
Spring为我们提供了一个isWorthTrying()
方法:
// 是否需要尝试:也就是说,它是否还没有被使用过,或者已知是否有效。方法返回true,表示值得尝试
// 如果配置的Objenesis Instantiator策略被确定为不处理当前JVM。或者系统属性"spring.objenesis.ignore"值设置为true,表示不尝试了
// 这个在ObjenesisCglibAopProxy创建代理实例的时候用到了。若不尝试使用Objenesis,那就还是用老的方式用空构造函数吧
public boolean isWorthTrying() {
return (this.worthTrying != Boolean.FALSE);
}
Objenesis Vs class.newInstance
从以上代码可以发现class构造器需要参数,而Objenesis可以绕过去, Objenesis主要应用场景:
- 序列化,远程调用和持久化 -对象需要实例化并存储为到一个特殊的状态,而没有调用代码
- 代理,AOP库和Mock对象 -类可以被子类继承而子类不用担心父类的构造器。
- 容器框架 -对象可以以非标准的方式被动态实例化(比如Spring就是容器框架)。
Enhancer:CGLIB增强器
也是位于cglib相关的包内。org.springframework.cglib.proxy
CGLIB是一个强大的高性能的代码生成包。它被许多AOP的框架(例如Spring AOP)使用,为他们提供方法的interception(拦截)
CGLIB包的底层是通过使用一个小而快的
字节码
处理框架ASM,来转换字节码并生成新的类。不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉
public class MainTest {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(MyDemo.class);
// 注意此处得MethodInterceptor是cglib包下的 AOP联盟里还有一个MethodInterceptor
enhancer.setCallback((MethodInterceptor) (o, method, args1, methodProxy) -> {
System.out.println(method.getName() + "---方法拦截前");
// 此处千万不能调用method得invoke方法,否则会死循环的 只能使用methodProxy.invokeSuper 进行调用
//Object result = method.invoke(o, args1);
Object result = methodProxy.invokeSuper(o, args1);
System.out.println(method.getName() + "---方法拦截后");
return result;
});
//MyDemo myDemo = (MyDemo) enhancer.create(); // 这里是要求必须有空的构造函数的
MyDemo myDemo = (MyDemo) enhancer.create(new Class[]{String.class}, new Object[]{"fsx"});
// 直接打印:默认会调用toString方法以及hashCode方法 此处都被拦截了
System.out.println(myDemo);
//System.out.println(myDemo.code);
}
}
class MyDemo {
public String code = "10";
public MyDemo(String code) {
this.code = code;
}
}
输出:
toString---方法拦截前
hashCode---方法拦截前
hashCode---方法拦截后
toString---方法拦截后
com.fsx.maintest.MyDemo$$EnhancerByCGLIB$$b07b3819@7960847b
fsx
这样我们就简单的实现了,对一个对象进行增强。
还有一种创建代理实例的方式,就是我们只用Enhancer把Class类型创建出来,然后创建实例的工作交给Objenesis 这样我们就拜托了对构造函数的依赖
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(MyDemo.class);
// 如国实用createClass方式来创建代理的实例 是不能直接添加callback得
//enhancer.setCallback();
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setStrategy(new DefaultGeneratorStrategy());
enhancer.setCallbackFilter(new CallbackHelper(MyDemo.class, null) {
@Override
protected Object getCallback(Method method) {
return (MethodInterceptor) (o, method1, args1, methodProxy) -> {
System.out.println(method1.getName() + "---方法拦截前");
// 此处千万不能调用method得invoke方法,否则会死循环的 只能使用methodProxy.invokeSuper 进行调用
//Object result = method.invoke(o, args1);
Object result = methodProxy.invokeSuper(o, args1);
System.out.println(method1.getName() + "---方法拦截后");
return result;
};
}
});
enhancer.setCallbackTypes(new Class[]{MethodInterceptor.class});
// 这里我们只生成Class字节码,并不去创建对象
Class clazz = enhancer.createClass();
// 创建对象的操作交给
Objenesis objenesis = new SpringObjenesis();
MyDemo myDemo = (MyDemo) objenesis.newInstance(clazz);
System.out.println(myDemo);
System.out.println(myDemo.code);
}
输出:
com.fsx.maintest.MyDemo$$EnhancerBySpringCGLIB$$6558edaa@5700d6b1
null
这样即使你没有空的构造函数,我依然可议给你创建一个实例。
CGLIB整个过程如下
- Cglib根据父类,Callback, Filter 及一些相关信息生成key
- 然后根据key 生成对应的子类的二进制表现形式
- 使用ClassLoader装载对应的二进制,生成Class对象,并缓存
- 最后实例化Class对象,并缓存
生成二进制Class的方法
针对不同场景, CGlib准备了不同的Class生成方法
二进制文件存在在哪儿?
放在byte数组中,下面这行代码就截取于方法AbstractClassGenerator.create(Object key)
byte[] b = strategy.generate(this);
然后通过 ReflectUtils.defineClass(className, b, loader)
生成对应的Class实例,并缓存入cache2
Cglib如何把二进制Load生成的Class
上面说了,事ReflectUtils.defineClass
这个方法:
public static Class defineClass(String className, byte[] b, ClassLoader loader) throws Exception {
return defineClass(className, b, loader, PROTECTION_DOMAIN);
}
public static Class defineClass(String className, byte[] b, ClassLoader loader, ProtectionDomain protectionDomain) throws Exception {
Object[] args;
Class c;
if (DEFINE_CLASS != null) {
args = new Object[]{className, b, new Integer(0), new Integer(b.length), protectionDomain};
c = (Class)DEFINE_CLASS.invoke(loader, args);
} else {
if (DEFINE_CLASS_UNSAFE == null) {
throw new CodeGenerationException(THROWABLE);
}
args = new Object[]{className, b, new Integer(0), new Integer(b.length), loader, protectionDomain};
c = (Class)DEFINE_CLASS_UNSAFE.invoke(UNSAFE, args);
}
Class.forName(className, true, loader);
return c;
}
总结
JdkDynamicAopProxy
入口方法是动态代理的 invoke() 方法,CGLIB 使用的是 DynamicAdvisedInterceptor.intercept()
方法
JdkDynamicAopProxy
使用的MethodInvocation
是: ReflectiveMethodInvocation
子类,
CGLIB 使用的是CglibMethodInvocation
它俩都是
ProxyMethodInvocation
接口的实现类。并且CglibMethodInvocation
是继承自ReflectiveMethodInvocation
的
知识交流
若群二维码失效,请加微信号(或者扫描下方二维码):fsx641385712。
并且备注:“java入群” 字样,会手动邀请入群