更新時(shí)間:2021-05-25 來(lái)源:黑馬程序員 瀏覽量:
IT就到黑馬程序員.gif)
JDK動(dòng)態(tài)代理是通過(guò)java.lang.reflect.Proxy 類(lèi)來(lái)實(shí)現(xiàn)的,我們可以調(diào)用Proxy類(lèi)的newProxyInstance()方法來(lái)創(chuàng)建代理對(duì)象。對(duì)于使用業(yè)務(wù)接口的類(lèi),Spring默認(rèn)會(huì)使用JDK動(dòng)態(tài)代理來(lái)實(shí)現(xiàn)AOP。
接下來(lái),通過(guò)一個(gè)案例來(lái)演示Spring中JDK動(dòng)態(tài)代理的實(shí)現(xiàn)過(guò)程,具體步驟如下。
(1)創(chuàng)建一個(gè)名為chapter03的Web項(xiàng)目,導(dǎo)入Spring框架所需JAR包到項(xiàng)目的lib目錄中,并發(fā)布到類(lèi)路徑下。
(2)在src目錄下,創(chuàng)建一個(gè)com.itheima.jdk包,在該包下創(chuàng)建接口UserDao,并在該接口中編寫(xiě)添加和刪除的方法,如文件1所示。
文件1 UserDao.java
package com.itheima.jdk;
public interface UserDao {
public void addUser();
public void deleteUser();
}(3)在com.itheima.jdk包中,創(chuàng)建UserDao接口的實(shí)現(xiàn)類(lèi)UserDaoImpl,分別實(shí)現(xiàn)接口中的方法,并在每個(gè)方法中添加一條輸出語(yǔ)句,如文件2所示。
文件2 UserDaoImpl.java
package com.itheima.jdk;
// 目標(biāo)類(lèi)
public class UserDaoImpl implements UserDao {
public void addUser() {
System.out.println("添加用戶(hù)");
}
public void deleteUser() {
System.out.println("刪除用戶(hù)");
}
}需要注意的是,本案例中會(huì)將實(shí)現(xiàn)類(lèi)UserDaoImpl作為目標(biāo)類(lèi),對(duì)其中的方法進(jìn)行增強(qiáng)處理。
(4)在src目錄下,創(chuàng)建一個(gè)com.itheima.aspect包,并在該包下創(chuàng)建切面類(lèi)MyAspect,在該類(lèi)中定義一個(gè)模擬權(quán)限檢查的方法和一個(gè)模擬記錄日志的方法,這兩個(gè)方法就表示切面中的通知,如文件3所示。
文件3 MyAspect.java
package com.itheima.aspect;
//切面類(lèi):可以存在多個(gè)通知Advice(即增強(qiáng)的方法)
public class MyAspect {
public void check_Permissions(){
System.out.println("模擬檢查權(quán)限...");
}
public void log(){
System.out.println("模擬記錄日志...");
}
}(5)在com.itheima.jdk包下,創(chuàng)建代理類(lèi)JdkProxy,該類(lèi)需要實(shí)現(xiàn)InvocationHandler接口,并編寫(xiě)代理方法。在代理方法中,需要通過(guò)Proxy類(lèi)實(shí)現(xiàn)動(dòng)態(tài)代理,如文件4所示。
文件4 JdkProxy.java
package com.itheima.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import com.itheima.aspect.MyAspect;
/**
* JDK代理類(lèi)
*/
public class JdkProxy implements InvocationHandler{
// 聲明目標(biāo)類(lèi)接口
private UserDao userDao;
// 創(chuàng)建代理方法
public Object createProxy(UserDao userDao) {
this.userDao = userDao;
// 1.類(lèi)加載器
ClassLoader classLoader = JdkProxy.class.getClassLoader();
// 2.被代理對(duì)象實(shí)現(xiàn)的所有接口
Class[] clazz = userDao.getClass().getInterfaces();
// 3.使用代理類(lèi),進(jìn)行增強(qiáng),返回的是代理后的對(duì)象
return Proxy.newProxyInstance(classLoader,clazz,this);
}
/*
* 所有動(dòng)態(tài)代理類(lèi)的方法調(diào)用,都會(huì)交由invoke()方法去處理
* proxy 被代理后的對(duì)象
* method 將要被執(zhí)行的方法信息(反射)
* args 執(zhí)行方法時(shí)需要的參數(shù)
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// 聲明切面
MyAspect myAspect = new MyAspect();
// 前增強(qiáng)
myAspect.check_Permissions();
// 在目標(biāo)類(lèi)上調(diào)用方法,并傳入?yún)?shù)
Object obj = method.invoke(userDao, args);
// 后增強(qiáng)
myAspect.log();
return obj;
}
}在文件4中,JdkProxy類(lèi)實(shí)現(xiàn)了InvocationHandler接口,并實(shí)現(xiàn)了接口中的invoke()方法,所有動(dòng)態(tài)代理類(lèi)所調(diào)用的方法都會(huì)交由該方法處理。在創(chuàng)建的代理方法createProxy()中,使用了Proxy類(lèi)的newProxyInstance()方法來(lái)創(chuàng)建代理對(duì)象。newProxyInstance()方法中包含三個(gè)參數(shù),其中第1個(gè)參數(shù)是當(dāng)前類(lèi)的類(lèi)加載器,第2個(gè)參數(shù)表示的是被代理對(duì)象實(shí)現(xiàn)的所有接口,第3個(gè)參數(shù)this代表的就是代理類(lèi)JdkProxy本身。在invoke()方法中,目標(biāo)類(lèi)方法執(zhí)行的前后,會(huì)分別執(zhí)行切面類(lèi)中的check_Permissions()方法和log()方法。
(6)在com.itheima.jdk包中,創(chuàng)建測(cè)試類(lèi)JdkTest。在該類(lèi)中的main()方法中創(chuàng)建代理對(duì)象和目標(biāo)對(duì)象,然后從代理對(duì)象中獲得對(duì)目標(biāo)對(duì)象userDao增強(qiáng)后的對(duì)象,最后調(diào)用該對(duì)象中的添加和刪除方法,如文件5所示。
文件5 JdkTest.java
package com.itheima.jdk;
public class JdkTest{
public static void main(String[] args) {
// 創(chuàng)建代理對(duì)象
JdkProxy jdkProxy = new JdkProxy();
// 創(chuàng)建目標(biāo)對(duì)象
UserDao userDao= new UserDaoImpl();
// 從代理對(duì)象中獲取增強(qiáng)后的目標(biāo)對(duì)象
UserDao userDao1 = (UserDao) jdkProxy.createProxy(userDao);
// 執(zhí)行方法
userDao1.addUser();
userDao1.deleteUser();
}
}執(zhí)行程序后,控制臺(tái)的輸出結(jié)果如圖1所示。

圖1 運(yùn)行結(jié)果
從圖1可以看出,userDao實(shí)例中的添加用戶(hù)和刪除用戶(hù)的方法已被成功調(diào)用,并且在調(diào)用前后分別增加了檢查權(quán)限和記錄日志的功能。這種實(shí)現(xiàn)了接口的代理方式,就是Spring中的JDK動(dòng)態(tài)代理。
JDK環(huán)境變量配置win10視頻教程【黑馬程序員】