靜态代理
很多小夥伴去大城市打拼。來大城市第一件事就是租房,免不了和中介打交道,因為很多房東很忙,你根本找不到他。從這個場景中就可以抽象出來代理模式
ISubject:被通路者資源的抽象
SubjectImpl:被通路者具體實作類(房東)
SubjectProxy:被通路者的代理實作類(中介)
UML圖如下
舉個例子來了解一下這個設計模式
老闆讓記錄一下使用者服務的響應時間,用代理模式來實作這個功能。
public interface IUserService {
public void request();
}
public class UserServiceImpl implements IUserService {
@Override
public void request() {
System.out.println("this is userService");
}
}
public class UserServiceProxy implements IUserService {
private IUserService userService;
public UserServiceProxy(IUserService userService) {
this.userService = userService;
}
@Override
public void request() {
long startTime = System.currentTimeMillis();
userService.request();
System.out.println("reques cost :" + (System.currentTimeMillis() - startTime));
}
public static void main(String[] args) {
IUserService userService = new UserServiceImpl();
UserServiceProxy userServiceProxy = new UserServiceProxy(userService);
// this is userService
// reques cost :0
userServiceProxy.request();
}
}
代理模式不隻有讓代理類和原始類實作同一個接口,然後将原始類注入到代理類中這一種寫法。如果原始類沒有定義接口,并且原始類并不是我們維護的,我們此時就可以用繼承的方式類實作代理模式,讓代理類繼承原始類,然後擴充功能。
public class UserService {
public void request() {
System.out.println("this is userService");
}
}
public class UserServiceProxy extends UserService {
@Override
public void request() {
long startTime = System.currentTimeMillis();
super.request();
System.out.println("reques cost :" + (System.currentTimeMillis() - startTime));
}
public static void main(String[] args) {
UserService userService = new UserServiceProxy();
// this is userService
// reques cost :1
userService.request();
}
}
一切看起來都非常的美好,老闆又發話了,把産品服務的響應時間也記錄一下吧。又得寫如下3個類
IProductService
ProductServiceImpl
ProductServiceProxy
UserServiceProxy和ProductServiceProxy這兩個代理類的邏輯都差不多,卻還得寫2次。其實這個還好,如果老闆說,把現有系統的幾十個服務的響應時間都記錄一下吧,你是不是要瘋了?這得寫多少代理類啊?這就得用到我們後續提到的動态代理了。
總結一下,代理模式的實作方式有兩種
- 代理類和原始類實作同一個接口,原始類注入到代理類
- 代理類繼承原始類
動态代理
黑暗總是暫時的,終究會迎來黎明,在JDK1.3之後引入了一種稱之為動态代理(Dynamic Proxy)的機制。使用該機制,我們可以為指定的接口在系統運作期間動态地生成代理對象,進而幫助我們走出最初使用靜态代理實作AOP的窘境
動态代理的實作有兩種方式
- 利用JDK實作動态代理,即調用Proxy.newProxyInstance()方法
- 使用CGLIB來實作動态代理
使用JDK實作動态代理,原始類必須實作某個接口,因為它是基于實作同一個接口的方式來實作的。而用CGLIB來實作動态代理,原始類有無實作接口都可以,因為它是基于繼承的方式實作的
JDK動态代理
動态代理的實作主要由一個類和一個接口組成,即java.lang.reflect.Proxy類和java.lang.reflect.InvocationHandler接口。
讓我們用動态代理來改造一下上面記錄系統響應時間的功能。雖然要為IUserService和IProductService兩種服務提供代理對象,但因為代理對象中要添加的橫切邏輯是一樣的。是以我們隻需要實作一個InvocationHandler就可以了。代碼如下
public class RequestCostInvocationHandler implements InvocationHandler {
private Object target;
public RequestCostInvocationHandler(Object target) {
this.target = target;
}
/** 被代理對象的任何方法被執行時,都會先進入這個方法 */
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("request")) {
long startTime = System.currentTimeMillis();
// 執行目标對象的方法
method.invoke(target, args);
System.out.println("reques cost :" + (System.currentTimeMillis() - startTime));
}
return null;
}
public static void main(String[] args) {
// 3個參數解釋如下
// classloader,生成代理類
// 代理類應該實作的接口
// 實作InvocationHandler的切面類
IUserService userService = (IUserService) Proxy.newProxyInstance(
IUserService.class.getClassLoader(),
new Class[]{IUserService.class},
new RequestCostInvocationHandler(new UserServiceImpl()));
IProductService productService = (IProductService) Proxy.newProxyInstance(
IProductService.class.getClassLoader(),
new Class[]{IProductService.class},
new RequestCostInvocationHandler(new ProductServiceImpl()));
// this is userService
// reques cost :0
userService.request();
// this is productService
// reques cost :0
productService.request();
}
}
生成動态代理也很簡單,調用Proxy.newProxyInstance()方法即可
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
三個參數如下:
loader:類加載器
interfaces: 代理類應該實作的接口
h:實作InvocationHandler接口的類,在裡面增加代理邏輯
這個方法以及3個參數在面試中偶爾會被問到,動态代理在各大架構中用的确實很多了
UML圖如下。Spring AOP就是用動态代理來實作的
CGLIB動态代理
CGLIB基于位元組碼技術為我們生成子類,不用我們自己去生成。用法如下
public class UserService {
public void request() {
System.out.println("welcome sir");
}
}
public class RequestCtrlCallback implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
long startTime = System.currentTimeMillis();
Object object = methodProxy.invokeSuper(o, objects);
System.out.println("reques cost :" + (System.currentTimeMillis() - startTime));
return object;
}
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class);
enhancer.setCallback(new RequestCtrlCallback());
UserService proxy = (UserService)enhancer.create();
// welcome sir
// reques cost :25
proxy.request();
}
}