title: “理解代理模式”
url: “https://wsk1103.github.io/”
tags:
- 設計模式
是什麼
代理模式屬於結構型模式。給某一個對象提供一個代理,並且使用該代理的對象來訪問原來的對象。
代理模式一般分爲靜態代理和動態代理,動態代理又分爲JDK代理和CGLIB代理。
優缺點
優點
在不修改原來類的情況下,增強類的功能。
缺點
系統更復雜。
靜態代理
以 spring 的事務 @translation 爲例。
1. 先創建一個用戶的增刪改查的接口和其實現類。
/**
* @author wsk1103
* @date 2019/7/22
* @description 用戶類
*/
public interface UserService {
/**
* 更新
*/
void update();
}
/**
* @author wsk1103
* @date 2019/7/22
* @description 描述
*/
public class UserServiceImpl implements UserService{
@Override
public void update() {
System.out.println("update......");
}
}
一般來說,這就是一個數據庫的更新操作了。
2. 靜態代理實現事務
但是如果我們想要爲其加入 事務控制 ,又不想修改原來的類,那就可以試試靜態代理。
/**
* @author wsk1103
* @date 2019/7/22
* @description 靜態代理
*/
public class TranslationProxy implements UserService {
private UserService userService;
public TranslationProxy(UserService service) {
this.userService = service;
}
@Override
public void update() {
start();
userService.update();
commit();
}
private void start() {
System.out.println("start translation");
}
private void commit() {
System.out.println("commit translation");
}
}
3. 測試和輸出結果
/**
* @author wsk1103
* @date 2019/7/22
* @description 描述
*/
public class Main {
public static void main(String[] args) {
System.out.println("不使用靜態代理");
UserService userService = new UserServiceImpl();
userService.update();
System.out.println("-------");
System.out.println("使用靜態代理");
UserService target = new UserServiceImpl();
UserService proxy = new TranslationProxy(target);
proxy.update();
System.out.println("--------");
}
}
不使用靜態代理
update......
-------
使用靜態代理
start translation
update......
commit translation
--------
優點
既達到了功能的增強,並且沒有破壞入侵源代碼。
缺點
代理類和委託類實現相同的接口,同時要實現相同的方法。這樣就出現了大量的代碼重複。如果接口增加一個方法,除了所有實現類需要實現這個方法外,所有代理類也需要實現此方法。增加了代碼維護的複雜度。
JDK動態代理
還是以 事務 爲例子。
JDK動態代理主要涉及:java.lang.reflect.Proxy
1. 實現動態代理
import java.lang.reflect.Proxy;
/**
* @author wsk1103
* @date 2019/7/22
* @description 動態代理
*/
public class TranslationProxyFactory {
//被代理的對象
private Object target;
public TranslationProxy(Object o) {
this.target = o;
}
private void start() {
System.out.println("start translation");
}
private void commit() {
System.out.println("commit translation");
}
public Object getProxyInstance() {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
(proxy1, method1, args1) -> {
start();
//執行目標對象方法
Object returnValue = method1.invoke(target, args1);
commit();
return returnValue;
}
);
}
}
2. 測試和結果
public class Main {
public static void main(String[] args) {
System.out.println("不使用靜態代理");
UserService userService = new UserServiceImpl();
userService.update();
System.out.println("-------");
System.out.println("使用動態代理");
UserService target = new UserServiceImpl();
UserService proxy = (UserService) new TranslationProxyFactory(target).getProxyInstance();
proxy.update();
System.out.println("--------");
}
}
不使用靜態代理
update......
-------
使用動態代理
start translation
update......
commit translation
--------
優點
解耦
缺點
基於Java反射機制實現,代理對象不需要實現接口,但是目標對象一定要實現接口,否則不能用動態代理
CGLIB動態代理
需要引入新的包 CGLIB 。如果是使用spring的話就不需要,因爲spring中默認會引入了。
- Cglib是一個強大的高性能的代碼生成包,它可以在運行期擴展java類與實現java接口.它廣泛的被許多AOP的框架使用,例如Spring AOP和synaop,爲他們提供方法的interception(攔截)
- Cglib包的底層是通過使用字節碼處理框架ASM來轉換字節碼並生成新的子類.
1. maven 引入 cglib
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.12</version>
</dependency>
2. CGLIB動態代理工廠
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* @author wsk1103
* @date 2019/7/22
* @description 動態代理
*/
public class TranslationProxy implements MethodInterceptor {
//被代理的對象
private Object target;
public TranslationProxy(Object o) {
this.target = o;
}
private void start() {
System.out.println("start translation");
}
private void commit() {
System.out.println("commit translation");
}
public Object getProxyInstance() {
//1.工具類
Enhancer en = new Enhancer();
//2.設置父類
en.setSuperclass(target.getClass());
//3.設置回調函數
en.setCallback(this);
//4.創建子類(代理對象)
return en.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
start();
Object result = method.invoke(target, objects);
commit();
return result;
}
}
測試和輸出
package com.wsk.utils;
/**
* @author wsk1103
* @date 2019/7/22
* @description 描述
*/
public class Main {
public static void main(String[] args) {
System.out.println("不使用代理");
UserService userService = new UserServiceImpl();
userService.update();
System.out.println("-------");
System.out.println("使用動態代理");
UserService target = new UserServiceImpl();
UserService proxy = (UserService) new TranslationProxy(target).getProxyInstance();
proxy.update();
System.out.println("--------");
}
}
不使用代理
update......
-------
使用動態代理
start translation
update......
commit translation
--------
優點
解耦
缺點
代理的類不能爲final,否則報錯;目標對象的方法如果爲final/static,那麼就不會被攔截,即不會執行目標對象額外的業務方法.
java中的應用
- java.lang.reflect.Proxy
- Apache Commons Proxy
- Mocking frameworks Mockito, Powermock, EasyMock
參考:
https://github.com/iluwatar/java-design-patterns/tree/master/proxy
https://segmentfault.com/a/1190000009235245#articleHeader7