上一篇 <<<Java基礎(chǔ)-反射機(jī)制
下一篇 >>>Java基礎(chǔ)-字節(jié)碼技術(shù)
代理模式:使用代理對象完成用戶請求,屏蔽用戶對真實對象的訪問。
- 優(yōu)點:減少代碼冗余、提高代碼復(fù)用性、安全性、隱藏真實角色、非入侵
應(yīng)用場景
- Spring AOP
- 過濾器
- 自定義注解
- 全局捕獲異常
- 事務(wù)原理
- 日志收集打印
- 權(quán)限控制
- RPC遠(yuǎn)程調(diào)用
- 安全代理可以隱蔽真實角色
- Mybatis的Mapper
- 全局ID(LCN/Seata中)
分類
靜態(tài)代理:實現(xiàn)接口和實現(xiàn)集成兩種方式
動態(tài)代理:JDK代理(接口實現(xiàn))和CGLIB代理(繼承方式)
區(qū)別:靜態(tài)代理需要自己寫代理類,而動態(tài)代理不需要寫代理類。
靜態(tài)代理
- 接口實現(xiàn)
/**靜態(tài)代理:接口實現(xiàn)*/
OrderService orderService = new OrderServiceProxy1(new OrderServiceImpl());
orderService.order();
/**
* 接口實現(xiàn)方式
*/
public class OrderServiceProxy1 implements OrderService {
/**
* 代理對象
*/
private OrderService proxiedOrderService;
public OrderServiceProxy1(OrderService orderService) {
this.proxiedOrderService=orderService;
}
public void order() {
System.out.println("日志收集開始..");
proxiedOrderService.order();
System.out.println("日志收集結(jié)束..");
}
}
- 繼承實現(xiàn)
/**靜態(tài)代理:繼承實現(xiàn)*/
OrderService orderService = new OrderServiceProxy();
orderService.order();
/**
* 繼承方式
*/
public class OrderServiceProxy extends OrderServiceImpl {
@Override
public void order() {
System.out.println("日志收集開始..");
super.order();
System.out.println("日志收集結(jié)束..");
}
}
動態(tài)代理
1.JDK動態(tài)代理
執(zhí)行步驟
1.創(chuàng)建被代理的接口和類;
2.實現(xiàn)InvocationHandler接口,對目標(biāo)接口中聲明的所有方法進(jìn)行統(tǒng)一處理;
3.調(diào)用Proxy的靜態(tài)方法,創(chuàng)建代理類并生成相應(yīng)的代理對象;
1).核心原理 (代理類的生成、編譯及加載到j(luò)vm中)

a、生成代理類---該類實現(xiàn)了接口,并集成Proxy類【通過編譯之后可查看】
Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
b、執(zhí)行代理類的order等目標(biāo)方法,實際上是調(diào)用MyInvocationHandel h的invoke方法
缺點:jdk動態(tài)代理,必須是面向接口,目標(biāo)業(yè)務(wù)類必須實現(xiàn)接口
2).調(diào)試源碼
JdkInvocationHandler jdkInvocationHandler = new JdkInvocationHandler(new OrderServiceImpl());
OrderService proxy = jdkInvocationHandler.getProxy();
proxy.order();
/**
* JDK動態(tài)代理
*/
public class JdkInvocationHandler implements InvocationHandler {
/**
* 目標(biāo)代理對象
*/
public Object target;
public JdkInvocationHandler(Object target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(">>>日志收集開始>>>>");
// 執(zhí)行代理對象方法
Object reuslt = method.invoke(target, args);
System.out.println(">>>日志收集結(jié)束>>>>");
return reuslt;
}
/**
* 獲取代理對象接口
*
*/
public <T> T getProxy() {
return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
}
3).自動生成的代理類源碼
public final class $Proxy0 extends Proxy implements OrderService
{
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public $Proxy0(final InvocationHandler invocationHandler) {
super(invocationHandler);
}
public final void order() {
try {
super.h.invoke(this, $Proxy0.m3, null);
}
catch (Error | RuntimeException error) {
throw;
}
catch (Throwable t) {
throw new UndeclaredThrowableException(t);
}
}
static {
try {
$Proxy0.m3 = Class.forName("cn.jarye.service.OrderService").getMethod("order", (Class<?>[])new Class[0]);
}
catch (NoSuchMethodException ex) {
throw new NoSuchMethodError(ex.getMessage());
}
catch (ClassNotFoundException ex2) {
throw new NoClassDefFoundError(ex2.getMessage());
}
}
}

4).手寫JDK動態(tài)代理思路
a.使用java反射機(jī)制拼接$Proxy.java類的源代碼---參考Proxy.newProxyInstance方法
b.需要將$Proxy.java編譯成$Proxy.class
c.程序中直接讀取該class文件到內(nèi)存中
2.CGLIB動態(tài)代理
1).核心原理

利用asm字節(jié)碼開源包,對代理對象類的class文件加載進(jìn)來,通過修改其字節(jié)碼生成子類來處理。
2).調(diào)試源碼
CglibMethodInterceptor cglibMethodInterceptor = new CglibMethodInterceptor();
//相當(dāng)于生成代理類
Enhancer enhancer = new Enhancer();
// 設(shè)置代理類的付類
enhancer.setSuperclass(OrderServiceImpl.class);
// 設(shè)置回調(diào)對象
enhancer.setCallback(cglibMethodInterceptor);
// 創(chuàng)建代理對象
OrderServiceImpl orderServiceImpl = (OrderServiceImpl) enhancer.create();
orderServiceImpl.order();
public class CglibMethodInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("<<<<<日志收集開始...>>>>>>>");
Object reuslt = proxy.invokeSuper(obj, args);
System.out.println("<<<<<日志收集結(jié)束...>>>>>>>");
return reuslt;
}
}
3).自動生成的代理類源碼

-
索引機(jī)制
使用字節(jié)碼技術(shù)獲取當(dāng)前所有的方法,對每個方法加上一個索引,直接根據(jù)索引調(diào)用到目標(biāo)方法效率是比反射機(jī)制要高。
MethodProxy類的源碼:
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
try {
this.init();
MethodProxy.FastClassInfo fci = this.fastClassInfo;
return fci.f2.invoke(fci.i2, obj, args);
} catch (InvocationTargetException var4) {
throw var4.getTargetException();
}
}
-
索引計算方法
使用方法名稱+參數(shù)類型計算hash值,在根據(jù)hash值得出索引 - 代理類 (使用繼承方法)
public class OrderServiceImpl$$EnhancerByCGLIB$$279607fc extends OrderServiceImpl implements Factory
{
private static Object CGLIB$CALLBACK_FILTER;
private static final Method CGLIB$order$0$Method;
private static final MethodProxy CGLIB$order$0$Proxy;
static void CGLIB$STATICHOOK1() {
final Class<?> forName3;
CGLIB$order$0$Method = ReflectUtils.findMethods(new String[] { "order", "()V" }, (forName3 = Class.forName("com.jarye.service.impl.OrderServiceImpl")).getDeclaredMethods())[0];
CGLIB$order$0$Proxy = MethodProxy.create((Class)forName3, (Class)forName, "()V", "order", "CGLIB$order$0");
}
final void CGLIB$order$0() {
super.order();
}
public final void order() {
MethodInterceptor cglib$CALLBACK_2;
MethodInterceptor cglib$CALLBACK_0;
if ((cglib$CALLBACK_0 = (cglib$CALLBACK_2 = this.CGLIB$CALLBACK_0)) == null) {
CGLIB$BIND_CALLBACKS(this);
cglib$CALLBACK_2 = (cglib$CALLBACK_0 = this.CGLIB$CALLBACK_0);
}
if (cglib$CALLBACK_0 != null) {
cglib$CALLBACK_2.intercept((Object)this, OrderServiceImpl$$EnhancerByCGLIB$$279607fc.CGLIB$order$0$Method, OrderServiceImpl$$EnhancerByCGLIB$$279607fc.CGLIB$emptyArgs, OrderServiceImpl$$EnhancerByCGLIB$$279607fc.CGLIB$order$0$Proxy);
return;
}
super.order();
}
// 設(shè)置回調(diào)
public void setCallbacks(final Callback[] array) {
this.CGLIB$CALLBACK_0 = (MethodInterceptor)array[0];
}
static {
CGLIB$STATICHOOK1();
}
}
- 方法索引
public int getIndex(final String s, final Class[] array) {
Label_1079: {
switch (s.hashCode()) {
case 106006350: {
if (!s.equals("order")) {
break;
}
switch (array.length) {
case 0: {
return 7;
}
default: {
break Label_1079;
}
}
break;
}
}
}
return -1;
}
public Object invoke(final int n, final Object o, final Object[] array) throws InvocationTargetException {
final OrderServiceImpl$$EnhancerByCGLIB$$279607fc orderServiceImpl$$EnhancerByCGLIB$$279607fc = (OrderServiceImpl$$EnhancerByCGLIB$$279607fc)o;
try {
switch (n) {
case 7: {
orderServiceImpl$$EnhancerByCGLIB$$279607fc.order();
return null;
}
}
}
catch (Throwable t) {
throw new InvocationTargetException(t);
}
throw new IllegalArgumentException("Cannot find matching method/constructor");
}
4).手寫CGLIB動態(tài)代理思路
/**
* 直接生成代理類,然后設(shè)置callback方法
*/
MemberServiceImpl$$EnhancerByCGLIB$$600aa7a2 memberServiceImpl=
new MemberServiceImpl$$EnhancerByCGLIB$$600aa7a2();
// 設(shè)置回調(diào)
memberServiceImpl.setCallbacks(new CglibMethodInterceptor());
memberServiceImpl.addMember("jarye");
3.JDK和CGLIB動態(tài)代理
1).差異對比
| 對比點 | JDK動態(tài)代理 | CGLIB動態(tài)代理 |
|---|---|---|
| 代碼上 | 實現(xiàn)InvocationHandler接口,對目標(biāo)接口中聲明的所有方法進(jìn)行統(tǒng)一處理 | 實現(xiàn)MethodInterceptor接口的intercept方法 |
| 原理上 | 使用Java的反射技術(shù)生成繼承了Proxy類的動態(tài)匿名類,只能代理實現(xiàn)了接口的類, 沒有實現(xiàn)接口的類不能實現(xiàn)動態(tài)代理。 | 通過ASM字節(jié)碼處理框架來轉(zhuǎn)換字節(jié)碼并生成代理類的子類,子類重寫了被代理類中所有非final的方法,在子類中采用方法攔截的技術(shù)攔截所有父類方法的調(diào)用,順勢植入橫切邏輯。大部分功能實際上是ASM所提供的,Cglib只是封裝了ASM,簡化了ASM操作,實現(xiàn)了運行期生成新的class。 |
| 缺陷 | jdk動態(tài)代理,必須是面向接口,目標(biāo)業(yè)務(wù)類必須實現(xiàn)接口 | 對于被代理類中的final方法,無法進(jìn)行代理,因為子類中無法重寫final函數(shù) |
| 總結(jié) | jdk動態(tài)代理最終使用反射機(jī)制執(zhí)行目標(biāo)方法 | cglib根據(jù)索引找到目標(biāo)方法,然后通過super.目標(biāo)方法執(zhí)行 |
2).Cglib的效率比Jdk動態(tài)代理效率要高原因【jdk7開始JDK動態(tài)代理更加高效】
-
Jdk動態(tài)動態(tài)代理 走回調(diào)攔截 實現(xiàn)接口接口生成帶了類 使用反射技術(shù)執(zhí)行我們的目標(biāo)方法
a.拼接java源代碼
b.編譯為class文件
c.讀取去class文件到內(nèi)存中 -
Cglib動態(tài)代理 采用繼承的模式生成代理類 底層基于Asm字節(jié)碼技術(shù)實現(xiàn)生成代理類
a.生成class文件
b.讀取去class文件到內(nèi)存中
c、采用fastClass索引的機(jī)制執(zhí)行我們的目標(biāo)方法
2).代理模式在Spring中的應(yīng)用
- 如果目標(biāo)對象實現(xiàn)了接口,默認(rèn)情況下會采用JDK的動態(tài)代理實現(xiàn)AOP,但可以強(qiáng)制使用CGLIB實現(xiàn)
- 如果目標(biāo)對象沒有實現(xiàn)了接口,必須采用CGLIB庫,spring會自動在JDK動態(tài)代理和CGLIB之間轉(zhuǎn)換
Spring中的@Async注解使用不當(dāng)會導(dǎo)致失效,請參考:@Async失效之謎
相關(guān)文章鏈接:
<<<23種常用設(shè)計模式總覽
<<<裝飾模式(Decorator Pattern)
<<<觀察者模式(Observer Pattern)
<<<單例模式(Singleton Pattern)
<<<責(zé)任鏈模式(Chain of Responsibility Pattern)
<<<策略模式(Strategy Pattern)
<<<模板方法模式(Template Pattern)
<<<外觀/門面模式(Facade Pattern)
<<<建造者模式(Builder Pattern)
<<<適配器模式(Adapter Pattern)
<<<原型模式(Prototype Pattern)
<<<工廠相關(guān)模式(Factory Pattern)