1. 靜態(tài)代理VS動態(tài)代理
代理類可以增強被代理對象的方法??煞譃殪o態(tài)代理和動態(tài)代理。
1.1 靜態(tài)代理
靜態(tài)代理:源代碼中需要聲明代理類
public class TestStaticProxy {
public static void main(String[] args) {
IRegisterService iRegisterService = new RegisterServiceImpl();
IRegisterService proxy = new RegisterServiceProxy(iRegisterService);
proxy.register("RyanLee", "123");
}
}
interface IRegisterService {
void register(String name, String pwd);
}
class RegisterServiceImpl implements IRegisterService {
@Override
public void register(String name, String pwd) {
System.out.println(String.format("【向數(shù)據(jù)庫中插入數(shù)據(jù)】name:%s,pwd:%s", name, pwd));
}
}
class RegisterServiceProxy implements IRegisterService {
IRegisterService iRegisterService;
public RegisterServiceProxy(IRegisterService iRegisterService) {
this.iRegisterService = iRegisterService;
}
@Override
public void register(String name, String pwd) {
System.out.println("[Proxy]一些前置處理");
System.out.println(String.format("[Proxy]打印注冊信息:姓名:%s,密碼:%s", name, pwd));
iRegisterService.register(name, pwd);
System.out.println("[Proxy]一些后置處理");
}
}
執(zhí)行結(jié)果
[Proxy]一些前置處理
[Proxy]打印注冊信息:姓名:RyanLee,密碼:123
【向數(shù)據(jù)庫中插入數(shù)據(jù)】name:RyanLee,pwd:123
[Proxy]一些后置處理
1.2 動態(tài)代理
動態(tài)代理:無需聲明代理類。是使用反射和字節(jié)碼的技術(shù),在運行期創(chuàng)建指定接口或類的子類(即動態(tài)代理類)以及其實例對象的技術(shù)。通過動態(tài)代理技術(shù)可以無侵入地對代碼進行增強。


動態(tài)代理實現(xiàn)的方式主要有兩種:
1.JDK原生動態(tài)代理
2.CGLib動態(tài)代理(CG:Code Generation)
2.動態(tài)代理的實現(xiàn)方式
2.1 JDK原生動態(tài)代理
動態(tài)代理類和被代理類必須繼承同一個接口。動態(tài)代理只能對接口中聲明的方法進行代理。
2.1.1 Proxy
java.lang.reflect.Proxy是所有動態(tài)代理的父類。它通過靜態(tài)方法newProxyInstance()來創(chuàng)建動態(tài)代理的class對象和實例。
2.1.2 InvocationHandler
每一個動態(tài)代理實例都有一個關(guān)聯(lián)的InvocationHandler。通過代理實例調(diào)用方法,方法調(diào)用請求會被轉(zhuǎn)發(fā)給InvocationHandler的invoke方法。
2.1.3 栗子
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class TestJdkDynamicProxy {
public static void main(String[] args) {
IRegisterService iRegisterService = new RegisterServiceImpl();
InsertDataHandler insertDataHandler = new InsertDataHandler();
IRegisterService proxy = (IRegisterService)insertDataHandler.getProxy(iRegisterService);
proxy.register("RyanLee", "123");
}
}
class InsertDataHandler implements InvocationHandler {
Object obj;
public Object getProxy(Object obj){
this.obj = obj;
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
doBefore();
Object result = method.invoke(obj, args);
doAfter();
return result;
}
private void doBefore() {
System.out.println("[Proxy]一些前置處理");
}
private void doAfter() {
System.out.println("[Proxy]一些后置處理");
}
}
執(zhí)行結(jié)果
[Proxy]一些前置處理
【向數(shù)據(jù)庫中插入數(shù)據(jù)】name:RyanLee,pwd:123
[Proxy]一些后置處理
-
proxy類型:
2.2 CGLib動態(tài)代理
CGLib(Code Generation Library)是一個基于ASM的字節(jié)碼生成庫。它允許我們在運行時對字節(jié)碼進行修改或動態(tài)生成。CGLib通過繼承被代理類的方式實現(xiàn)代理。
2.2.1 Enhancer
Enhancer指定要代理的目標對象。通過create方法得到代理對象。通過代理實例調(diào)用非final方法,方法調(diào)用請求會首先轉(zhuǎn)發(fā)給MethodInterceptor的intercept
2.2.2 MethodInterceptor
通過代理實例調(diào)用方法,調(diào)用請求都會轉(zhuǎn)發(fā)給intercept方法進行增強。
2.2.3 栗子
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class TestCGLibDynamicProxy {
public static void main(String[] args) {
IRegisterService iRegisterService = new RegisterServiceImpl();
InsertDataInterceptor interceptor = new InsertDataInterceptor();
RegisterServiceImpl proxy = (RegisterServiceImpl) interceptor.getProxy(iRegisterService);
proxy.register("RyanLee", "123");
}
}
class InsertDataInterceptor implements MethodInterceptor {
Object target;
public Object getProxy(Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
// 回調(diào)方法
enhancer.setCallback(this);
// 創(chuàng)建代理對象
return enhancer.create();
}
private void doBefore() {
System.out.println("[Proxy]一些前置處理");
}
private void doAfter() {
System.out.println("[Proxy]一些后置處理");
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
doBefore();
Object result = methodProxy.invoke(target, objects);
doAfter();
return result;
}
}
執(zhí)行結(jié)果
[Proxy]一些前置處理
【向數(shù)據(jù)庫中插入數(shù)據(jù)】name:RyanLee,pwd:123
[Proxy]一些后置處理
-
proxy類型:
2.3 JDK動態(tài)代理 VS CGLib動態(tài)代理。
兩種動態(tài)代理的最大的區(qū)別是:
JDK動態(tài)代理要求被代理對象必須基于接口來實現(xiàn)。動態(tài)代理類和被代理類必須繼承同一個接口。動態(tài)代理只能對接口中聲明的方法進行代理。對那些沒有實現(xiàn)接口的bean。JDK動態(tài)代理無法代理。而CGLib通過繼承被代理類的方式實現(xiàn)代理。
- Spring 注解默認使用JDK動態(tài)代理來實現(xiàn)。也可以通過修改配值,改為使用CGLib動態(tài)代理來實現(xiàn)。因而建議Spring注解不要寫在Interface上。

