## **模式的定義**
代理模式是指客戶端并不直接調用實際的對象,而是通過調用代理,來間接的調用實際的對象。
## **靜態代理**
靜態代理就是需要手動編寫代理類,再編譯成源碼,在代碼里是真實存在的片段。
代碼模擬簡單的事物提交
1. 創建dao接口以及實現dao
```
public interface UserDao {
void add();
}
public class UserDaoImpl implements UserDao{
@Override
public void add() {
System.out.println("add");
}
}
```
2. 創建代理方法
```
public class UserDaoProxy implements UserDao {
private UserDao userDao;
public UserDaoProxy(UserDao target) {
this.userDao = target;
}
@Override
public void add() {
System.out.println("開始事物");
userDao.add();
System.out.println("提交事物");
}
}
```
3. 客戶端條用
```
public static void main(String[] args) {
UserDao userDao = new UserDaoImpl();
UserDaoProxy proxy = new UserDaoProxy(userDao);
proxy.add();
}
```
很簡單的實現了靜態代理處理事物的功能,但是假設我們100個dao就意味著需要創建100個proxy,很顯然代碼非常的冗余,JDK給我們提供了一種更好的技術,叫做JDK動態代理。
## **JDK動態代理**
動態代理有別于靜態代理,是根據代理的對象,動態創建代理類。這樣,就可以避免靜態代理中代理類接口過多的問題。動態代理是實現方式,是通過反射來實現的,借助Java自帶的`java.lang.reflect.Proxy`,通過固定的規則生成
其步驟如下:
1. 編寫一個委托類的接口,即靜態代理的(UserDao接口)
2. 實現一個真正的委托類,即靜態代理的(UserDaoImpl類)
3. 創建一個動態代理類,實現`InvocationHandler`接口,并重寫該`invoke`方法
4. 在測試類中,生成動態代理的對象。
步驟1和2和靜態代理的寫法一致
步驟3創建動態代理類:
```
public class TransHandler implements InvocationHandler {
private Object target;
public TransHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("開始事物");
Object invoke = method.invoke(target, args);
System.out.println("提交事務");
return invoke;
}
}
```
步驟4生成代理對象:
```
public static void main(String[] args) {
// 代理對象
UserDao userDao = new UserDaoImpl();
TransHandler transHandler = new TransHandler(userDao);
// 獲取代理對象的類加載器
ClassLoader loader = userDao.getClass().getClassLoader();
// 獲取代理對象的實現接口
Class<?>[] interfaces = userDao.getClass().getInterfaces();
UserDao userDaoImpl = (UserDao) Proxy.newProxyInstance(loader, interfaces, transHandler);
userDaoImpl.add();
}
```
## **CGLIB動態代理**
cglib動態代理是利用asm開源包,對代理對象類的class文件加載進來,通過修改其字節碼生成子類來處理。
使用CGLIB動態代理實現事物功能
1. maven中添加CGLIB依賴
```
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
```
2. 創建動態代理類
```
public class CglibProxy implements MethodInterceptor {
// 代理的對象
private Object target;
public Object getInstance(Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
// 指定cglib生成的子類繼承的父類
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
public Object intercept(Object o, Method method, Object[] args, MethodProxy proxy) {
System.out.println("開啟事物");
Object invoke = proxy.invoke(target, args);
System.out.println("提交事物");
return invoke;
}
}
```
3. 客戶端生成創建代理對象
```
public static void main(String[] args) {
UserDao userDao = new UserDaoImpl();
CglibProxy cglibProxy = new CglibProxy();
UserDao userDaoImpl = (UserDao) cglibProxy.getInstance(userDao);
userDaoImpl.add();
}
```
cglib使用底層字節碼技術,動態創建代理對象(UserDao)的子類,并重寫父類的方法。
## **JDK動態代理和CGLIB動態代理的區別**
| JDK動態代理 | CGLIB動態代理 |
| --- | --- |
| 利用反射機制生成一個實現代理接口的匿名類 | 通過動態字節碼技術創建虛擬子類實現 |
| 代理對象類需要實現接口 | 無需實現接口 |
Spring AOP采用哪種動態代理模式呢?
1. 代理對象實現了接口,默認情況下會采用JDK動態代理來實現AOP,例如Service層,Dao層等
2. 代理對象沒實現接口,會采用cglib動態代理模式實現AOP,例如Controller層