一.創(chuàng)建代理的三要素:
- 原始對(duì)象
- 額外功能
- 代理對(duì)象和原始對(duì)象實(shí)現(xiàn)相同的接口
打個(gè)比方,我去賣房子,然后我要帶客戶去看房子;可是有一天,我不想每天都帶那么多的客戶去看房子,我該怎么辦呢?找中介.讓中介代替我,領(lǐng)著客戶去看房子.那么中介需要偽裝成是房東(此時(shí)就要重寫原始對(duì)象中的方法).
二.JDK動(dòng)態(tài)代理:
Proxy.newProxyInstance(classloader,interfaces,invocationHandler)
2.1 invocationHandler介紹:
2.1.1 MethodInterceptor攔截器回顧:
我們?cè)趯W(xué)習(xí)spring動(dòng)態(tài)代理之MethodInterceptor攔截器的時(shí)候,需要實(shí)現(xiàn)MethodInterceptor接口,此時(shí)重寫了接口中的invoke方法,invoke參數(shù)中有一個(gè)MethodInvocation,它此時(shí)代表額外功能增加的那個(gè)原始方法,代碼如下:
public class Arroud implements MethodInterceptor {
/**
* spring動(dòng)態(tài)代理之MethodInterceptor攔截器
* @param methodInvocation :額外功能增加的那個(gè)原始方法,如:register(),login()
* @return: 因?yàn)槊總€(gè)方法的返回值都不一樣,所以需要object類來接受
* @throws Throwable
*/
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("額外功能----");
//表示:該原始方法執(zhí)行了,如register,login方法
Object proceed = methodInvocation.proceed();
System.out.println("方法的返回值:object="+proceed);
return proceed;
}
}
我們會(huì)發(fā)現(xiàn),這個(gè)methodInvocation是被spring進(jìn)行封裝了的,但是我們的JDK的代理還是原生的.
2.1.2 InvocationHandler介紹和演示:
invocationHandler是一個(gè)接口.我們需要重寫invoke方法,從而來增加額外的功能
/**
* 2.額外功能
*
*
*/
final InvocationHandler invocationHandler=new InvocationHandler() {
/**
* 為什么是method.invoke? 因?yàn)閯?dòng)態(tài)代理. 省去了service.register,service.login()
*
* @param proxy 代理對(duì)象
* @param method 原始對(duì)象的原始方法
* @param args 原始方法的參數(shù)
* @return 原始方法的返回值
* @throws Throwable
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//2.1 新增額外功能
System.out.println("jdk proxy...");
//2.2 執(zhí)行原始對(duì)象的原始方法
Object invoke = method.invoke(userService, args);
return invoke;
}
};
語(yǔ)法糖: jdk1.8之后,我們可以省略掉上面的final修飾符
三. interfaces介紹:
代理創(chuàng)建3要素的第三條,代理對(duì)象和原始對(duì)象需要實(shí)現(xiàn)相同的接口,這里的interfaces就是實(shí)現(xiàn)的接口.
此時(shí),我們?cè)趺传@取到這個(gè)接口呢?通過getClass().getInterfaces()可以獲取到類的所有接口定義.
四. classloader介紹:
4.1 類加載器怎么獲取呢?
(1).我們知道,一個(gè)user.java文件,通過編譯成user.class的字節(jié)碼文件,這里每個(gè)類的class文件,自動(dòng)會(huì)分配與之對(duì)應(yīng)的classloader類加載器;
(2).類加載器會(huì)把user.class字節(jié)碼文件放入到JVM虛擬機(jī)中;
(3).jvm虛擬機(jī)如何去創(chuàng)建一個(gè)user對(duì)象呢?此時(shí)肯定需要拿到user的class對(duì)象才能去創(chuàng)建user對(duì)象.此時(shí)也是通過類加載器去生成Class對(duì)象.
此時(shí),問題來了,我現(xiàn)在是動(dòng)態(tài)代理呀,我拿不到具體的.java文件,我也沒有具體的.class文件呀,順其自然的,我也就沒有對(duì)應(yīng)的類加載器呀,我該怎么辦?
答: 去借一個(gè)
五.最終的JDK動(dòng)態(tài)代理代碼如下:
package com.baizhiedu.jdl;
import com.baizhiedu.proxy.User;
import com.baizhiedu.proxy.UserService;
import com.baizhiedu.proxy.UserServiceImpl;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* JDK動(dòng)態(tài)代理
*/
public class TestJdkProxy {
public static void main(String[] args) {
//1.創(chuàng)建原始對(duì)象UserService
final UserService userService = new UserServiceImpl();
/**
* 2.額外功能
*
*
*/
final InvocationHandler invocationHandler=new InvocationHandler() {
/**
* 為什么是method.invoke? 因?yàn)閯?dòng)態(tài)代理. 省去了service.register,service.login()
*
* @param proxy 代理對(duì)象
* @param method 原始對(duì)象的原始方法
* @param args 原始方法的參數(shù)
* @return 原始方法的返回值
* @throws Throwable
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//2.1 新增額外功能
System.out.println("jdk proxy...");
//2.2 執(zhí)行原始對(duì)象的原始方法
Object invoke = method.invoke(userService, args);
return invoke;
}
};
/**
* 3.代理對(duì)象和原始對(duì)象實(shí)現(xiàn)相同的接口 interfaces:原始對(duì)象所實(shí)現(xiàn)的接口
* 先借用userService的classLoader TestJdkProxy也可以
*/
UserService service = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), invocationHandler);
service.login(new User());
service.register("Nisy",20);
}
}
5.1 運(yùn)行結(jié)果:
jdk proxy...
登錄功能的核心代碼...
jdk proxy...
注冊(cè)功能的核心代碼...