基礎回顧
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代理速度是相差無幾的。