什么是代理模式
代理模式:簡(jiǎn)單理解就是主要對(duì)我們方法執(zhí)行之前與之后實(shí)現(xiàn)增強(qiáng)
代理模式應(yīng)用場(chǎng)景
- 日志的采集
- 權(quán)限控制
- 實(shí)現(xiàn)aop
- Mybatis mapper
- Spring的事務(wù)
- 全局捕獲異常
- Rpc遠(yuǎn)程調(diào)用接口
- 代理數(shù)據(jù)源
代理模式實(shí)現(xiàn)的原理
代理模式主要包含三個(gè)角色,即抽象主題角色(Subject)、委托類角色(被代理角色,Proxied)以及代理類角色(Proxy),如上圖所示:
抽象主題角色:可以是接口,也可以是抽象類;
委托類角色: 真實(shí)主題角色,業(yè)務(wù)邏輯的具體執(zhí)行者;
代理類角色:內(nèi)部含有對(duì)真實(shí)對(duì)象RealSubject的引用,負(fù)責(zé)對(duì)真實(shí)主題角色的調(diào)用,并在真實(shí)主題角色處理前后做預(yù)處理和后處理。
代理模式創(chuàng)建方式
靜態(tài)代理
靜態(tài)代理需要自己人工編寫(xiě)代理類代碼
基于接口實(shí)現(xiàn)方式如下:
public class OrderServiceProxy implements OrderService{
private OrderService orderService;
public OrderServiceProxy(OrderService orderService) {
this.orderService = orderService;
}
public String addOrder(String userName, String userPwd) {
System.out.println("使用靜態(tài)代理類打印日志開(kāi)始:userName:" + userName + "," + userPwd);
String result = orderService.addOrder(userName, userPwd);
System.out.println("使用靜態(tài)代理類打印日志結(jié)束:userName:" + userName + "," + userPwd);
return result;
}
}
public interface OrderService {
/**
* 需要被代理的方法
* @return
*/
String addOrder(String userName,String userPwd);
}
public class Test001 {
public static void main(String[] args) {
OrderService orderService = new OrderServiceProxy(new OrderServiceImpl());
orderService.addOrder("靜態(tài)代理測(cè)試","123456");
}
}
基于繼承的實(shí)現(xiàn)方式
public class OrderServiceProxy extends OrderServiceImpl {
private OrderService orderService;
public OrderServiceProxy(OrderService orderService) {
this.orderService = orderService;
}
public String addOrder(String userName, String userPwd) {
System.out.println("使用靜態(tài)代理類打印日志開(kāi)始:userName:" + userName + "," + userPwd);
String result = super.addOrder(userName, userPwd);
System.out.println("使用靜態(tài)代理類打印日志結(jié)束:userName:" + userName + "," + userPwd);
return result;
}
}
動(dòng)態(tài)代理與靜態(tài)代理的區(qū)別
動(dòng)態(tài)代理不需要寫(xiě)代理類對(duì)象,通過(guò)程序自動(dòng)生成,而靜態(tài)代理需要我們自己寫(xiě)代理類對(duì)象。
動(dòng)態(tài)代理
- 動(dòng)態(tài)代理是在實(shí)現(xiàn)階段不用關(guān)心代理類,而在運(yùn)行階段才指定哪一個(gè)對(duì)象。
- 動(dòng)態(tài)代理類的源碼是在程序運(yùn)行期間由JVM根據(jù)反射等機(jī)制動(dòng)態(tài)的生成 。
Jdk動(dòng)態(tài)代理
JDK動(dòng)態(tài)代理的一般步驟如下:
- 創(chuàng)建被代理的接口和類;
- 實(shí)現(xiàn)InvocationHandler接口,對(duì)目標(biāo)接口中聲明的所有方法進(jìn)行統(tǒng)一處理;
- 調(diào)用Proxy的靜態(tài)方法,創(chuàng)建代理類并生成相應(yīng)的代理對(duì)象;
實(shí)現(xiàn)原理:利用攔截器機(jī)制必須實(shí)現(xiàn)InvocationHandler接口中的invoke方法實(shí)現(xiàn)對(duì)我們的目標(biāo)方法增強(qiáng)。
public class JdkInvocationHandler implements InvocationHandler {
/**
* 目標(biāo)對(duì)象
*/
private Object target;
public JdkInvocationHandler(Object target) {
this.target = target;
}
/**
* @param proxy 使用jdk程序生成的代理類
* @param method 目標(biāo)方法
* @param args 方法需要傳遞的參數(shù)
* @return
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("使用Jdk動(dòng)態(tài)代理打印日志開(kāi)始" + args[0]);
Object result = method.invoke(target, args);
System.out.println("使用Jdk動(dòng)態(tài)代理打印日志結(jié)束" + args[1]);
return result;
}
/**
* 生成代理類
*
* @param <T>
* @return
*/
public <T> T getProxy() {
return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
}
/**
* 測(cè)試代理類
*
* @param <T>
* @return
*/
public static void main(String[] args) {
JdkInvocationHandler jdkInvocationHandler = new JdkInvocationHandler(new OrderServiceImpl());
OrderServiceImpl orderService = jdkInvocationHandler.getProxy();
orderService.addOrder("動(dòng)態(tài)代理", "測(cè)試動(dòng)態(tài)代理");
}
}
public interface OrderService {
void addOrder(String arg0, String arg1);
}
public class OrderServiceImpl implements OrderService {
public void addOrder(String arg0, String arg1) {
System.out.println(arg0+"-----"+arg1);
}
}
運(yùn)行結(jié)果
Connected to the target VM, address: '127.0.0.1:54763', transport: 'socket'
使用Jdk動(dòng)態(tài)代理打印日志開(kāi)始動(dòng)態(tài)代理測(cè)試
動(dòng)態(tài)代理測(cè)試-----測(cè)試動(dòng)態(tài)代理
使用Jdk動(dòng)態(tài)代理打印日志結(jié)束測(cè)試動(dòng)態(tài)代理
Disconnected from the target VM, address: '127.0.0.1:54763', transport: 'socket'
注意:繼承了Proxy類,實(shí)現(xiàn)了代理的接口,由于java不能多繼承,這里已經(jīng)繼承了Proxy類了,不能再繼承其他的類,所以JDK的動(dòng)態(tài)代理不支持對(duì)實(shí)現(xiàn)類的代理,只支持接口的代理。
CGLIB動(dòng)態(tài)代理
利用asm字節(jié)碼技術(shù),生成子類實(shí)現(xiàn)對(duì)目標(biāo)方法實(shí)現(xiàn)增強(qiáng)
CGLIB動(dòng)態(tài)代理實(shí)現(xiàn)方式
Maven依賴
<dependencies>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.12</version>
</dependency>
</dependencies>
實(shí)現(xiàn)一個(gè)業(yè)務(wù)類,注意,這個(gè)業(yè)務(wù)類并沒(méi)有實(shí)現(xiàn)任何接口:
package com.jpeony.spring.proxy.cglib;
public class HelloService {
public HelloService() {
System.out.println("HelloService構(gòu)造");
}
/**
* 該方法不能被子類覆蓋,Cglib是無(wú)法代理final修飾的方法的
*/
final public String sayOthers(String name) {
System.out.println("HelloService:sayOthers>>"+name);
return null;
}
public void sayHello() {
System.out.println("HelloService:sayHello");
}
}
自定義MethodInterceptor
package com.jpeony.spring.proxy.cglib;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* 自定義MethodInterceptor
*/
public class MyMethodInterceptor implements MethodInterceptor{
/**
* sub:cglib生成的代理對(duì)象
* method:被代理對(duì)象方法
* objects:方法入?yún)? * methodProxy: 代理方法
*/
@Override
public Object intercept(Object sub, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("======代理開(kāi)始======");
Object object = methodProxy.invokeSuper(sub, objects);
System.out.println("======代理結(jié)束======");
return object;
}
}
調(diào)用代理類測(cè)試代碼
package com.jpeony.spring.proxy.cglib;
import net.sf.cglib.core.DebuggingClassWriter;
import net.sf.cglib.proxy.Enhancer;
public class Client {
public static void main(String[] args) {
// 代理類class文件存入本地磁盤(pán)方便我們反編譯查看源碼
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\myWork");
// 通過(guò)CGLIB動(dòng)態(tài)代理獲取代理對(duì)象的過(guò)程
Enhancer enhancer = new Enhancer();
// 設(shè)置enhancer對(duì)象的父類
enhancer.setSuperclass(HelloService.class);
// 設(shè)置enhancer的回調(diào)對(duì)象
enhancer.setCallback(new MyMethodInterceptor());
// 創(chuàng)建代理對(duì)象
HelloService proxy= (HelloService)enhancer.create();
// 通過(guò)代理對(duì)象調(diào)用目標(biāo)方法
proxy.sayHello();
}
Jdk與Cglib動(dòng)態(tài)代理的區(qū)別
- Jdk動(dòng)態(tài)代理利用反射技術(shù)生成匿名的代理類走InvokeHandler回調(diào)方法實(shí)現(xiàn)增強(qiáng),同時(shí)也是一種基于接口的方式實(shí)現(xiàn)代理。
- Cglib動(dòng)態(tài)代理利用asm字節(jié)碼技術(shù)生成一個(gè)子類覆蓋其中的方法實(shí)現(xiàn)增強(qiáng),同時(shí)采用fastClass機(jī)制對(duì)整個(gè)代理類建立索引比反射效率要高
- 在Spring中如果需要被代理的對(duì)象如果實(shí)現(xiàn)了接口采用Jdk動(dòng)態(tài)代理,沒(méi)有實(shí)現(xiàn)接口則使用Cglib動(dòng)態(tài)代理。