基础回顾
JDK是怎么创建代理对象的
JDK代理只能针对接口进行代理,因而被代理类必须先实现一个接口
public interface ActionService {
/**
* who做something事情
* @param who
*/
void dosomething(String who);
}
danceAction 实现类
public class DanceActionService implements ActionService {
@Override
public void dosomething(String who) {
System.out.println(who+"跳舞ing...");
}
}
public class JDKProxyTest {
public static void main(String[] args) throws InterruptedException {
final ActionService actionService = new DanceActionService();
Object proxyInstance = Proxy
.newProxyInstance(DanceActionService.class.getClassLoader(), DanceActionService.class.getInterfaces(),
(proxy, method, arguments) -> {
// 可以在这里植入我们的逻辑
return method.invoke(actionService, arguments);
});
System.out.println(proxyInstance.getClass());
((ActionService)proxyInstance).dosomething("我");
System.out.println(Proxy.isProxyClass(proxyInstance.getClass()));
Thread.sleep(9999999999L);
}
}
运行结果最终会打印出:“我跳舞ing…”
至此我不想解释太多,因为这段代码真的很基础,不明白的同学先去看看相关资料的。
// 上面一段代码到底做了什么,实则是在内存中生成一份class,代理对象则是该内存class的实例,生成的class文件如下:
package com.sun.proxy;
import com.atguigu.test.advisor.ActionService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0
extends Proxy
implements ActionService {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public $Proxy0(InvocationHandler invocationHandler) {
super(invocationHandler);
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m3 = Class.forName("com.atguigu.test.advisor.ActionService").getMethod("dosomething", Class.forName("java.lang.String"));
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
}
catch (NoSuchMethodException noSuchMethodException) {
throw new NoSuchMethodError(noSuchMethodException.getMessage());
}
catch (ClassNotFoundException classNotFoundException) {
throw new NoClassDefFoundError(classNotFoundException.getMessage());
}
}
public final boolean equals(Object object) {
try {
return (Boolean)this.h.invoke(this, m1, new Object[]{object});
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final String toString() {
try {
return (String)this.h.invoke(this, m2, null);
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final int hashCode() {
try {
return (Integer)this.h.invoke(this, m0, null);
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final void dosomething(String string) {
try {
// this.h 指的是父类Proxy中InvocationHandler对象
this.h.invoke(this, m3, new Object[]{string});
return;
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
}
重点关注dosomething方法,this.h 指的是父类Proxy中InvocationHandler对象,this指向代理对象,m3反射方法,new Object[]{string}是我们方法的参数。
CGLIB代理
除了官方的JDK代理,民间还有CGLIB代理方式,cglib框架底层基于java asm字节码技术,在过去经常听闻jdk代理比cglib代理要慢,事实真的是这样吗?在文末我们将实际测试,一般来说官方的是会随着jdk版本的升级而不断优化的,说xxx比xxx优秀,也许官方早就做了优化,就行jdk1.6之前的synchronize和reentranLock锁一样。
- JDK代理要求被代理者至少实现一个接口,而cglib代理则没有这一要求,cglib会生成一个类继承目标类。
- JDK代理需要借助目标对象创建代理,如上文需要先实例化目标对象,而cglib代理不需要。
callback
为了理解cglib,需要先明白cglib的callback。callback是cglib对创建代理对象方式的逻辑抽象概念,总共分为六种。其次还有callbackFilter,供我们实现接口,定义那些方法该被拦截。
- FixedValue
- InvocationHandler
- LazyLoader
- MethodInterceptor
- Dispatcher
- NoOp 接口声明了一个单例对象,该代理不对被代理类执行任何操作
其中最常用的是MethodInterceptor callback ,它允许我们完全自定义拦截方式,不同的callback,需要实现的invoke方法也不一样,大家可以查阅下api。由于我们经常使用Spring,spring自己集成callback的组件,在这六大callback基础上还增加了ConditionalCallback 条件callback,决定哪些方法会被拦截,其本质也是结合callback和callbackFilter实现,本文也是基于spring cglib组件运行的。
CGLIB的使用
- 引入spring-core核心依赖
- 编写目标类
/**
*
* 目标类(需要被代理的类)
*@author Evan
*@since 2020/6/12 23:19
*/
public class DanceActionService implements ActionService {
@Override
public void dosomething(String who) {
System.out.println(who+"跳舞ing...");
}
}
- 代理类测试
/**
* Enhancer 生成代理对象测试
*@author Evan
*@since 2020/6/13 19:23
*/
public class EnhancerTest {
public static void main(String[] args) throws InterruptedException {
final DanceActionService delegate = new DanceActionService();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(DanceActionService.class);
//cglib提供了6种callback
// enhancer.setCallback(NoOp.INSTANCE);
enhancer.setCallback(new MethodInterceptor() {
@Override
/**
* @param Object obj cglib生成的代理对象
* @param Method arguments 目标对象方法,例如,doSomething
* @param Object[] objects 参数
* @param MethodProxy methodProxy 代理对象方法
*/
public Object intercept(Object obj, Method method, Object[] arguments, MethodProxy methodProxy)
throws Throwable {
// 植入我们的逻辑
System.out.println(methodProxy.getSignature().getName()+"方法被调用");
// 调用目标方法
return methodProxy.invokeSuper(obj,arguments);
// 和jdk代理不同,不要这里调用反射方法,因为这里的obj不是目标对象,而是代理对象
// 如果非要这样用,得想办法把目标对象传进来
// return method.invoke(delegate,arguments);
}
});
DanceActionService proxy = (DanceActionService) enhancer.create();
proxy.dosomething("我");
}
}
接下来 我们看一下,生成的代理类究竟是什么样子的?
package com.atguigu.test.advisor;
import com.atguigu.test.advisor.DanceActionService;
import java.lang.reflect.Method;
import org.springframework.cglib.core.ReflectUtils;
import org.springframework.cglib.core.Signature;
import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.Factory;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
public class DanceActionService$$EnhancerByCGLIB$$c0a31786
extends DanceActionService
implements Factory {
private boolean CGLIB$BOUND;
public static Object CGLIB$FACTORY_DATA;
private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
private static final Callback[] CGLIB$STATIC_CALLBACKS;
private MethodInterceptor CGLIB$CALLBACK_0;
private static Object CGLIB$CALLBACK_FILTER;
private static final Method CGLIB$dosomething$0$Method;
private static final MethodProxy CGLIB$dosomething$0$Proxy;
private static final Object[] CGLIB$emptyArgs;
private static final Method CGLIB$equals$1$Method;
private static final MethodProxy CGLIB$equals$1$Proxy;
private static final Method CGLIB$toString$2$Method;
private static final MethodProxy CGLIB$toString$2$Proxy;
private static final Method CGLIB$hashCode$3$Method;
private static final MethodProxy CGLIB$hashCode$3$Proxy;
private static final Method CGLIB$clone$4$Method;
private static final MethodProxy CGLIB$clone$4$Proxy;
public DanceActionService$$EnhancerByCGLIB$$c0a31786() {
DanceActionService$$EnhancerByCGLIB$$c0a31786 danceActionService$$EnhancerByCGLIB$$c0a31786 = this;
DanceActionService$$EnhancerByCGLIB$$c0a31786.CGLIB$BIND_CALLBACKS(danceActionService$$EnhancerByCGLIB$$c0a31786);
}
static {
DanceActionService$$EnhancerByCGLIB$$c0a31786.CGLIB$STATICHOOK1();
}
public final boolean equals(Object object) {
MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
if (methodInterceptor == null) {
DanceActionService$$EnhancerByCGLIB$$c0a31786.CGLIB$BIND_CALLBACKS(this);
methodInterceptor = this.CGLIB$CALLBACK_0;
}
if (methodInterceptor != null) {
Object object2 = methodInterceptor.intercept(this, CGLIB$equals$1$Method, new Object[]{object}, CGLIB$equals$1$Proxy);
return object2 == null ? false : (Boolean)object2;
}
return super.equals(object);
}
public final String toString() {
MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
if (methodInterceptor == null) {
DanceActionService$$EnhancerByCGLIB$$c0a31786.CGLIB$BIND_CALLBACKS(this);
methodInterceptor = this.CGLIB$CALLBACK_0;
}
if (methodInterceptor != null) {
return (String)methodInterceptor.intercept(this, CGLIB$toString$2$Method, CGLIB$emptyArgs, CGLIB$toString$2$Proxy);
}
return super.toString();
}
public final int hashCode() {
MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
if (methodInterceptor == null) {
DanceActionService$$EnhancerByCGLIB$$c0a31786.CGLIB$BIND_CALLBACKS(this);
methodInterceptor = this.CGLIB$CALLBACK_0;
}
if (methodInterceptor != null) {
Object object = methodInterceptor.intercept(this, CGLIB$hashCode$3$Method, CGLIB$emptyArgs, CGLIB$hashCode$3$Proxy);
return object == null ? 0 : ((Number)object).intValue();
}
return super.hashCode();
}
protected final Object clone() throws CloneNotSupportedException {
MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
if (methodInterceptor == null) {
DanceActionService$$EnhancerByCGLIB$$c0a31786.CGLIB$BIND_CALLBACKS(this);
methodInterceptor = this.CGLIB$CALLBACK_0;
}
if (methodInterceptor != null) {
return methodInterceptor.intercept(this, CGLIB$clone$4$Method, CGLIB$emptyArgs, CGLIB$clone$4$Proxy);
}
return super.clone();
}
public Object newInstance(Callback[] arrcallback) {
DanceActionService$$EnhancerByCGLIB$$c0a31786.CGLIB$SET_THREAD_CALLBACKS(arrcallback);
DanceActionService$$EnhancerByCGLIB$$c0a31786 danceActionService$$EnhancerByCGLIB$$c0a31786 = new DanceActionService$$EnhancerByCGLIB$$c0a31786();
DanceActionService$$EnhancerByCGLIB$$c0a31786.CGLIB$SET_THREAD_CALLBACKS(null);
return danceActionService$$EnhancerByCGLIB$$c0a31786;
}
public Object newInstance(Callback callback) {
DanceActionService$$EnhancerByCGLIB$$c0a31786.CGLIB$SET_THREAD_CALLBACKS(new Callback[]{callback});
DanceActionService$$EnhancerByCGLIB$$c0a31786 danceActionService$$EnhancerByCGLIB$$c0a31786 = new DanceActionService$$EnhancerByCGLIB$$c0a31786();
DanceActionService$$EnhancerByCGLIB$$c0a31786.CGLIB$SET_THREAD_CALLBACKS(null);
return danceActionService$$EnhancerByCGLIB$$c0a31786;
}
public Object newInstance(Class[] arrclass, Object[] arrobject, Callback[] arrcallback) {
DanceActionService$$EnhancerByCGLIB$$c0a31786 danceActionService$$EnhancerByCGLIB$$c0a31786;
DanceActionService$$EnhancerByCGLIB$$c0a31786.CGLIB$SET_THREAD_CALLBACKS(arrcallback);
Class[] arrclass2 = arrclass;
switch (arrclass.length) {
case 0: {
danceActionService$$EnhancerByCGLIB$$c0a31786 = new DanceActionService$$EnhancerByCGLIB$$c0a31786();
break;
}
default: {
throw new IllegalArgumentException("Constructor not found");
}
}
DanceActionService$$EnhancerByCGLIB$$c0a31786.CGLIB$SET_THREAD_CALLBACKS(null);
return danceActionService$$EnhancerByCGLIB$$c0a31786;
}
public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] arrcallback) {
CGLIB$STATIC_CALLBACKS = arrcallback;
}
public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] arrcallback) {
CGLIB$THREAD_CALLBACKS.set(arrcallback);
}
public final void dosomething(String string) {
MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
if (methodInterceptor == null) {
DanceActionService$$EnhancerByCGLIB$$c0a31786.CGLIB$BIND_CALLBACKS(this);
methodInterceptor = this.CGLIB$CALLBACK_0;
}
if (methodInterceptor != null) {
Object object = methodInterceptor.intercept(this, CGLIB$dosomething$0$Method, new Object[]{string}, CGLIB$dosomething$0$Proxy);
return;
}
super.dosomething(string);
}
public void setCallback(int n, Callback callback) {
switch (n) {
case 0: {
this.CGLIB$CALLBACK_0 = (MethodInterceptor)callback;
break;
}
}
}
public void setCallbacks(Callback[] arrcallback) {
Callback[] arrcallback2 = arrcallback;
DanceActionService$$EnhancerByCGLIB$$c0a31786 danceActionService$$EnhancerByCGLIB$$c0a31786 = this;
this.CGLIB$CALLBACK_0 = (MethodInterceptor)arrcallback[0];
}
public Callback getCallback(int n) {
MethodInterceptor methodInterceptor;
DanceActionService$$EnhancerByCGLIB$$c0a31786.CGLIB$BIND_CALLBACKS(this);
switch (n) {
case 0: {
methodInterceptor = this.CGLIB$CALLBACK_0;
break;
}
default: {
methodInterceptor = null;
}
}
return methodInterceptor;
}
public Callback[] getCallbacks() {
DanceActionService$$EnhancerByCGLIB$$c0a31786.CGLIB$BIND_CALLBACKS(this);
DanceActionService$$EnhancerByCGLIB$$c0a31786 danceActionService$$EnhancerByCGLIB$$c0a31786 = this;
return new Callback[]{this.CGLIB$CALLBACK_0};
}
public static MethodProxy CGLIB$findMethodProxy(Signature signature) {
String string = ((Object)signature).toString();
switch (string.hashCode()) {
case -508378822: {
if (!string.equals("clone()Ljava/lang/Object;")) break;
return CGLIB$clone$4$Proxy;
}
case 866574608: {
if (!string.equals("dosomething(Ljava/lang/String;)V")) break;
return CGLIB$dosomething$0$Proxy;
}
case 1826985398: {
if (!string.equals("equals(Ljava/lang/Object;)Z")) break;
return CGLIB$equals$1$Proxy;
}
case 1913648695: {
if (!string.equals("toString()Ljava/lang/String;")) break;
return CGLIB$toString$2$Proxy;
}
case 1984935277: {
if (!string.equals("hashCode()I")) break;
return CGLIB$hashCode$3$Proxy;
}
}
return null;
}
static void CGLIB$STATICHOOK1() {
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
Class<?> class_ = Class.forName("com.atguigu.test.advisor.DanceActionService$$EnhancerByCGLIB$$c0a31786");
Class<?> class_2 = Class.forName("com.atguigu.test.advisor.DanceActionService");
CGLIB$dosomething$0$Method = ReflectUtils.findMethods(new String[]{"dosomething", "(Ljava/lang/String;)V"}, class_2.getDeclaredMethods())[0];
CGLIB$dosomething$0$Proxy = MethodProxy.create(class_2, class_, "(Ljava/lang/String;)V", "dosomething", "CGLIB$dosomething$0");
class_2 = Class.forName("java.lang.Object");
Method[] arrmethod = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, class_2.getDeclaredMethods());
CGLIB$equals$1$Method = arrmethod[0];
CGLIB$equals$1$Proxy = MethodProxy.create(class_2, class_, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1");
CGLIB$toString$2$Method = arrmethod[1];
CGLIB$toString$2$Proxy = MethodProxy.create(class_2, class_, "()Ljava/lang/String;", "toString", "CGLIB$toString$2");
CGLIB$hashCode$3$Method = arrmethod[2];
CGLIB$hashCode$3$Proxy = MethodProxy.create(class_2, class_, "()I", "hashCode", "CGLIB$hashCode$3");
CGLIB$clone$4$Method = arrmethod[3];
CGLIB$clone$4$Proxy = MethodProxy.create(class_2, class_, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4");
}
private static final void CGLIB$BIND_CALLBACKS(Object object) {
block2: {
Object object2;
block3: {
DanceActionService$$EnhancerByCGLIB$$c0a31786 danceActionService$$EnhancerByCGLIB$$c0a31786 = (DanceActionService$$EnhancerByCGLIB$$c0a31786)object;
if (danceActionService$$EnhancerByCGLIB$$c0a31786.CGLIB$BOUND) break block2;
danceActionService$$EnhancerByCGLIB$$c0a31786.CGLIB$BOUND = true;
object2 = CGLIB$THREAD_CALLBACKS.get();
if (object2 != null) break block3;
object2 = CGLIB$STATIC_CALLBACKS;
if (CGLIB$STATIC_CALLBACKS == null) break block2;
}
danceActionService$$EnhancerByCGLIB$$c0a31786.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])object2)[0];
}
}
final void CGLIB$dosomething$0(String string) {
super.dosomething(string);
}
final Object CGLIB$clone$4() throws CloneNotSupportedException {
return super.clone();
}
final boolean CGLIB$equals$1(Object object) {
return super.equals(object);
}
final int CGLIB$hashCode$3() {
return super.hashCode();
}
final String CGLIB$toString$2() {
return super.toString();
}
}
利用Spring代理工厂创建自己的代理对象
前面介绍了创建代理对象的两种方式,那么我们想要在spring应用中怎么创建自己的代理对象呢?当然你完全可以按照上面说的两种方式创建好代理对象,然后定义BeanPostProcess组件,在后初始化方法里把目标的对象替换成代理对象,然后注册到容器中,当然我们还可以做得更好,spring本身就提供了代理工厂类,接下来我们看一下如何实现。
Spring中面向Aop编程有一组抽象的概念:
- 执行点(Executepoint) - 类初始化,方法调用。
- 连接点(Joinpoint) - 执行点+方位的组合,可确定Joinpoint,比如类开始初始化前,类初始化后,方法调用前,方法调用后。
- 切点(Pointcut) - 在众多执行点中,定位感兴趣的执行点。Executepoint相当于数据库表中的记录,而Pointcut相当于查询条件。
- 增强(Advice) - 织入到目标类连接点上的一段程序代码。除了一段程序代码外,还拥有执行点的方位信息。
- 目标对象(Target) - 增强逻辑的织入目标类
- 引介(Introduction) - 一种特殊的增强(advice),它为类添加一些额外的属性和方法,动态为业务类添加其他接口的实现逻辑,让业务类成为这个接口的实现类。
- 代理(Proxy) - 一个类被AOP织入后,产生一个结果类,它便是融合了原类和增强逻辑的代理类。
- 切面(Aspect) - 切面由切点(Pointcut)和增强(Advice/Introduction)组成,既包括横切逻辑定义,也包括连接点定义。
为了创建代理我们需要向spring提供Advisor,advisor是一种特殊的Aspect只有一个切点和一个增强。
定义advisor
// @Component 在main函数中测试,因此不需要
public class LogAdvisor implements PointcutAdvisor,BeanFactoryAware {
@Override
public Pointcut getPointcut() {
return new Pointcut() {
@Override
public ClassFilter getClassFilter() {
return ClassFilter.TRUE;
}
@Override
public MethodMatcher getMethodMatcher() {
return new MethodMatcher() {
@Override
public boolean matches(Method method, Class<?> targetClass) {
//只拦截了mylogin方法
String methodName = method.getName();
if ("mylogin".equals(methodName)) {
return true;
}
return false;
}
@Override
public boolean isRuntime() {
return false;
}
@Override
public boolean matches(Method method, Class<?> targetClass, Object... args) {
return false;
}
};
}
};
}
@Override
public Advice getAdvice() {
return new MethodBeforeAdvice() {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("BeforeAdvice实现,在目标方法被调用前调用,目标方法是:" + method.getDeclaringClass().getName() + "."
+ method.getName());
}
};
}
@Override
public boolean isPerInstance() {
return true;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
}
}
main函数中测试
public class ProxyTest {
public static void main(String[] args) {
// 创建代理方式
AccountService accountService = new AccountService();
AdvisedSupport support = new AdvisedSupport();
support.addAdvisor(new LogAdvisor());
// 设目标类
support.setTarget(accountService);
ProxyCreatorSupport proxyCreatorSupport = new ProxyCreatorSupport();
AopProxyFactory aopProxyFactory = proxyCreatorSupport.getAopProxyFactory();
AopProxy aopProxy = aopProxyFactory.createAopProxy(support);
AccountService proxy = (AccountService) aopProxy.getProxy();
proxy.mylogin();
}
}
接下来我们看下源码是怎么创建代理对象的?
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.");
}
//重点看这里,使用jdk代理,如果目标类是接口或者是已经被JDK代理过
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
isProxyClass源码
public static boolean isProxyClass(Class<?> cl) {
isAssignableFrom判断目标对象所属的类是不是Proxy类或者是其子类,由于jdk代理生成的类都会继承Proy
return Proxy.class.isAssignableFrom(cl) && proxyClassCache.containsValue(cl);
}
结论:
- 如果目标类是接口或者其实例是JDK代理对象,则使用JDK代理。
- 其它情况使用cglib代理。
cglib 和jdk代理的性能比较
在我的window笔记本,jdk1.8 ,jdk代理和cglib代理速度是相差无几的。