什麼是動態代理?Spring中有哪幾種實現?有什麼區別?
答: 1. 可以任意的控制任意對象的執行過程,這個對象的執行過程可以由客戶端靈活的指定
2. 兩種
3. jdk和CGLib
JDK
1. JDK version <= 1.6 的時候實現InvocationHandler,重寫invoke,自定義初始化對象(method.invoke(Object,args))
2.利用攔截器(必須要實現InvocationHandler接口)加上反射機制生成一個代理接口的匿名類,在具體方法前調用InvocationHandler來處理
CGlib
1.實現MethodInterceptor 重寫intercept (methodProxy.invokeSuper(Object,args))
2.CGLib利用ASM框架,對代理對象生成的class文件加載進來,通過修改其字節碼文件生成子類來處理
2.CGLib不能對聲明final的類和方法進行代理,因爲CGlib原理是動態生成被代理類的子類
3.完全不受代理類必須實現接口類的限制,採用的是接口繼承的方式
JDK默認提供的代理例子:
接口(inteface)
public interface JdkHello {
public String hello();
public String world();
}
正常實現類(class)
/**
* @author youshang
*/
public class JdkHelloImpl implements JdkHello {
@Override
public String hello() {
return "hello";
}
@Override
public String world() {
return "world";
}
}
代理實現類(Proxy Class)
@Log4j2
public class JdkHelloProxy implements JdkHello {
private JdkHello jdkHello = new JdkHelloImpl();
@Override
public String hello() {
log.info("通過動態代理執行hello");
return jdkHello.hello();
}
@Override
public final String world() {
log.info("通過動態代理執行world");
return jdkHello.world();
}
}
代理類(Proxy)
/**
* JDK1.6+ 底層提供的動態代理
*/
@Log4j2
public class JdkProxy {
private Object object;
public JdkProxy(Object object){
this.object = object;
}
/**
* 封裝代理方法,取代實現InvocationHandler
* @return
*/
public Object getProxyInstance() {
long startTime = System.currentTimeMillis();
return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), (proxy, method, args) -> {
DateTime startDate = DateUtil.date(startTime);
log.info(String.format("開始操作你的需求: %s ",startDate));
//進行代理
Object invoke = method.invoke(object, args);
long endTime = System.currentTimeMillis();
log.info(String.format("操作此需求總共耗時: %s 毫秒",(endTime- startTime)));
return invoke;
});
}
// (proxy, method ,args) -> method.invoke(proxy,args)
// =
// new InvocationHandler() {
// @Override
// public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// return method.invoke(object,args);
// }
}
代理類(Proxy)
/**
* JDK1.6+ 底層提供的動態代理
*/
@Log4j2
public class JdkProxy {
private Object object;
public JdkProxy(Object object){
this.object = object;
}
/**
* 封裝代理方法,取代實現InvocationHandler
* @return
*/
public Object getProxyInstance() {
long startTime = System.currentTimeMillis();
return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), (proxy, method, args) -> {
DateTime startDate = DateUtil.date(startTime);
log.info(String.format("開始操作你的需求: %s ",startDate));
//進行代理
Object invoke = method.invoke(object, args);
long endTime = System.currentTimeMillis();
log.info(String.format("操作此需求總共耗時: %s 毫秒",(endTime- startTime)));
return invoke;
});
}
// (proxy, method ,args) -> method.invoke(proxy,args)
// =
// new InvocationHandler() {
// @Override
// public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// return method.invoke(object,args);
// }
}
測試代理類(Test)
/**
* 測試jdk動態代理
*/
public class JdkProxyTest {
@Test
public void test(){
//通過代理類實現調用JdkHelloImpl實現類
JdkHello jdkHello = new JdkHelloProxy();
//執行代理操作
JdkHello jdkHelloProxy = (JdkHello)new JdkProxy(jdkHello).getProxyInstance();
System.out.println(jdkHelloProxy.hello());
//經過final修飾的方法
System.out.println(jdkHelloProxy.world());
}
}
CGLib代理測試例子
代理目標類(class)
public class CglibServiceImpl{
public String say() {
return "say";
}
public String useCase() {
return "useCase";
}
/**
* final修飾的方法,CGLib不能代理
* @return
*/
public final String example(){
return "勞資的東西你別碰。。。";
}
}
代理類(Proxy Class)
/**
* 實現CGLib核心接口 MethodInterceptor
*/
@Log4j2
public class CglibProxy implements MethodInterceptor {
private Long startTime;
public CglibProxy(){
startTime = System.currentTimeMillis();
}
/**
* 回調接口的方法
* 回調接口的方法執行的條件是:代理對象執行目標方法時會調用回調接口的方法
* @param obj
* @param method
* @param args
* @param proxy
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
log.info(String.format("方法名: %s",method.getName()));
log.info(String.format("開始執行日期: %s ", DateUtil.date(startTime)));
Object o = proxy.invokeSuper(obj, args);
long endTime = System.currentTimeMillis();
log.info(String.format("方法總共執行時間: %s 毫秒",endTime - startTime));
return o;
}
}
測試CGlib代理方法
@Test
public void test(){
Enhancer enhancer = new Enhancer();
//將目標類設置爲父類,cglib動態代理增強的原理就是子類增強父類,cglib不能增強目標類爲final的類和方法
enhancer.setSuperclass(CglibServiceImpl.class);
//設置當前類的具體子類(允許爲null)
enhancer.setClassLoader(CglibServiceImpl.class.getClassLoader());
//設置回調接口,這裏的MethodInterceptor實現類回調接口,而我們又實現了MethodInterceptor
enhancer.setCallback(new CglibProxy());
//create()方法用於創建cglib動態代理對象
CglibServiceImpl cglibService = (CglibServiceImpl)enhancer.create();
//實現CGLib代理調用方法
System.out.println(cglibService.say());
System.out.println(cglibService.useCase());
//通過final修飾的方法,CGlib沒辦法代理
System.out.println(cglibService.example());
}