淺談Java代理(jdk靜態(tài)代理、動(dòng)態(tài)代理和cglib動(dòng)態(tài)代理)
一、代理是Java常用的設(shè)計(jì)模式,代理類(lèi)通過(guò)調(diào)用被代理類(lèi)的相關(guān)方法,并對(duì)相關(guān)方法進(jìn)行增強(qiáng)。加入一些非業(yè)務(wù)性代碼,比如事務(wù)、日志、報(bào)警發(fā)郵件等操作。
二、jdk靜態(tài)代理
1、業(yè)務(wù)接口
/** * 業(yè)務(wù)接口 * @author pc * */ public interface UserService { // 增加一個(gè)用戶 public void addUser(); // 編輯賬戶 public void editUser(); }
2、業(yè)務(wù)實(shí)現(xiàn)類(lèi)
/** * 業(yè)務(wù)實(shí)現(xiàn)類(lèi) * @author pc * */ public class UserServiceImpl implements UserService { public void addUser() { System.out.println("增加一個(gè)用戶。。。"); } public void editUser() { System.out.println("編輯一個(gè)用戶。。。"); } }
3、代理類(lèi)
/**
* 代理類(lèi)
*
* @author pc
*
*/
public class UserServiceProxy implements UserService {
private UserServiceImpl userImpl;
public UserServiceProxy(UserServiceImpl countImpl) {
this.userImpl = countImpl;
}
public void addUser() {
System.out.println("代理類(lèi)方法,進(jìn)行了增強(qiáng)。。。");
System.out.println("事務(wù)開(kāi)始。。。");
// 調(diào)用委托類(lèi)的方法;
userImpl.addUser();
System.out.println("處理結(jié)束。。。");
}
public void editUser() {
System.out.println("代理類(lèi)方法,進(jìn)行了增強(qiáng)。。。");
System.out.println("事務(wù)開(kāi)始。。。");
// 調(diào)用委托類(lèi)的方法;
userImpl.editUser();
System.out.println("事務(wù)結(jié)束。。。");
}
}
4、測(cè)試類(lèi)
public static void main(String[] args) { UserServiceImpl userImpl = new UserServiceImpl(); UserServiceProxy proxy = new UserServiceProxy(userImpl); proxy.addUser(); System.out.println("----------分割線----------"); proxy.editUser(); }
5、結(jié)果
代理類(lèi)方法,進(jìn)行了增強(qiáng)。。。
事務(wù)開(kāi)始。。。
增加一個(gè)用戶。。。
處理結(jié)束。。。
----------分割線----------
代理類(lèi)方法,進(jìn)行了增強(qiáng)。。。
事務(wù)開(kāi)始。。。
編輯一個(gè)用戶。。。
事務(wù)結(jié)束。。。
三、jdk動(dòng)態(tài)代理
1、業(yè)務(wù)接口
/** * 業(yè)務(wù)接口 * @author pc * */ public interface UserService { // 增加一個(gè)用戶 public void addUser(); // 編輯賬戶 public void editUser(); }
2、業(yè)務(wù)接口實(shí)現(xiàn)類(lèi)
/** * 業(yè)務(wù)接口實(shí)現(xiàn)類(lèi) * @author pc * */ public class UserServiceImpl implements UserService { public void addUser() { System.out.println("增加一個(gè)用戶。。。"); } public void editUser() { System.out.println("編輯一個(gè)用戶。。。"); } }
3、代理類(lèi)
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * * @author pc * */ public class ServiceInvocationHandler implements InvocationHandler { // 目標(biāo)對(duì)象 private Object target; public ServiceInvocationHandler(Object target) { super(); this.target = target; } /** * 創(chuàng)建代理實(shí)例 * @return * @throws Throwable */ public Object getProxy() throws Throwable { return Proxy.newProxyInstance(Thread.currentThread() .getContextClassLoader(), this.target.getClass() .getInterfaces(), this); // 這樣寫(xiě)只返回了目標(biāo)對(duì)象,沒(méi)有生成代理對(duì)象。 // return target; } /** * 實(shí)現(xiàn)InvocationHandler接口方法 * 執(zhí)行目標(biāo)對(duì)象的方法,并進(jìn)行增強(qiáng) */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; System.out.println("代理類(lèi)方法,進(jìn)行了增強(qiáng)。。。"); System.out.println("事務(wù)開(kāi)始。。。"); // 執(zhí)行目標(biāo)方法對(duì)象 result = method.invoke(target, args); System.out.println("事務(wù)結(jié)束。。。"); return result; } }
4、測(cè)試類(lèi)
public class Test { /** * jdk動(dòng)態(tài)代理會(huì)生成一個(gè)動(dòng)態(tài)代理類(lèi),生成相應(yīng)的字節(jié)碼,然后通過(guò)ClassLoader加載字節(jié)碼。 * 該實(shí)例繼承了Proxy類(lèi),并實(shí)現(xiàn)了業(yè)務(wù)接口,在實(shí)現(xiàn)的方法里通過(guò)反射調(diào)用了InvocationHandler接口實(shí)現(xiàn)類(lèi) * 的invoke()回調(diào)方法。 * @param args * @throws Throwable */ public static void main(String[] args) throws Throwable { UserService userService = new UserServiceImpl(); ServiceInvocationHandler handler = new ServiceInvocationHandler(userService); // 根據(jù)目標(biāo)生成代理對(duì)象 UserService proxy = (UserService) handler.getProxy(); proxy.addUser(); // proxy.editUser(); } }
5、測(cè)試結(jié)果
代理類(lèi)方法,進(jìn)行了增強(qiáng)。。。
事務(wù)開(kāi)始。。。
增加一個(gè)用戶。。。
事務(wù)結(jié)束。。。
四、cglib動(dòng)態(tài)代理
需要引入cglib的jar包,
在pom.xml加入依賴(lài):
<!-- https://mvnrepository.com/artifact/cglib/cglib --> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>2.2.2</version> </dependency>
1、業(yè)務(wù)類(lèi),沒(méi)有實(shí)現(xiàn)接口
/** * 業(yè)務(wù)類(lèi) * 沒(méi)有實(shí)現(xiàn)接口 * 如果類(lèi)是final的,則沒(méi)法生成代理對(duì)象,報(bào)錯(cuò)。 * 如果方法是final的,代理無(wú)效 * @author pc * */ public class UserServiceImpl { public void addUser() { System.out.println("增加一個(gè)用戶。。。"); } public void editUser() { System.out.println("編輯一個(gè)用戶。。。"); } }
2、代理類(lèi)
import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; /** * 使用Cglib動(dòng)態(tài)代理 * @author pc * */ public class UserServiceCglib implements MethodInterceptor{ private Object target; /** * 創(chuàng)建代理實(shí)例 * @param target * @return */ public Object getInstance(Object target){ this.target = target; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(this.target.getClass()); // 設(shè)置回調(diào)方法 enhancer.setCallback(this); // 創(chuàng)建代理對(duì)象 return enhancer.create(); } /** * 實(shí)現(xiàn)MethodInterceptor接口要重寫(xiě)的方法。 * 回調(diào)方法 */ public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("事務(wù)開(kāi)始。。。"); Object result = proxy.invokeSuper(obj, args); System.out.println("事務(wù)結(jié)束。。。"); return result; } }
3、測(cè)試類(lèi)
public class TestCglib { public static void main(String[] args) { UserServiceCglib cglib = new UserServiceCglib(); UserServiceImpl bookFacadeImpl = (UserServiceImpl)cglib.getInstance(new UserServiceImpl()); bookFacadeImpl.addUser(); // bookFacadeImpl.editUser(); } }
4、結(jié)果:
事務(wù)開(kāi)始。。。
增加一個(gè)用戶。。。
事務(wù)結(jié)束。。。
5、如果業(yè)務(wù)實(shí)現(xiàn)類(lèi)被定義成final類(lèi),就會(huì)報(bào)以下錯(cuò)誤
Exception in thread "main" java.lang.IllegalArgumentException: Cannot subclass final class class cn.xx.xx.cgilb.UserServiceImpl at net.sf.cglib.proxy.Enhancer.generateClass(Enhancer.java:446) at net.sf.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25) at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:216) at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:377) at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:285) at cn.pconline.proxy.cgilb.UserServiceCglib.getInstance(UserServiceCglib.java:30) at cn.pconline.proxy.cgilb.TestCglib.main(TestCglib.java:7)
五、總結(jié)
1、原理
jdk靜態(tài)代理實(shí)現(xiàn)比較簡(jiǎn)單,一般是直接代理對(duì)象直接包裝了被代理對(duì)象。
jdk動(dòng)態(tài)代理是接口代理,被代理類(lèi)A需要實(shí)現(xiàn)業(yè)務(wù)接口,業(yè)務(wù)代理類(lèi)B需要實(shí)現(xiàn)InvocationHandler接口。
jdk動(dòng)態(tài)代理會(huì)根據(jù)被代理對(duì)象生成一個(gè)繼承了Proxy類(lèi),并實(shí)現(xiàn)了該業(yè)務(wù)接口的jdk代理類(lèi),該類(lèi)的字節(jié)碼會(huì)被傳進(jìn)去的ClassLoader加載,創(chuàng)建了jdk代理對(duì)象實(shí)例,
jdk代理對(duì)象實(shí)例在創(chuàng)建時(shí),業(yè)務(wù)代理對(duì)象實(shí)例會(huì)被賦值給Proxy類(lèi),jdk代理對(duì)象實(shí)例也就有了業(yè)務(wù)代理對(duì)象實(shí)例,同時(shí)jdk代理對(duì)象實(shí)例通過(guò)反射根據(jù)被代理類(lèi)的業(yè)務(wù)方法創(chuàng)建了相應(yīng)的Method對(duì)象m(可能有多個(gè))。當(dāng)jdk代理對(duì)象實(shí)例調(diào)用業(yè)務(wù)方法,如proxy.addUser();這個(gè)會(huì)先把對(duì)應(yīng)的m對(duì)象作為參數(shù)傳給invoke()方法(就是invoke方法的第二個(gè)參數(shù)),調(diào)用了jdk代理對(duì)象實(shí)例的invoke()回調(diào)方法,在invoke方法里面再通過(guò)反射來(lái)調(diào)用被代理對(duì)象的因?yàn)榉椒?,即result = method.invoke(target, args);。
cglib動(dòng)態(tài)代理是繼承代理,通過(guò)ASM字節(jié)碼框架修改字節(jié)碼生成新的子類(lèi),重寫(xiě)并增強(qiáng)方法的功能。
2、優(yōu)缺點(diǎn)
jdk靜態(tài)代理類(lèi)只能為一個(gè)被代理類(lèi)服務(wù),如果需要代理的類(lèi)比較多,那么會(huì)產(chǎn)生過(guò)多的代理類(lèi)。jdk靜態(tài)代理在編譯時(shí)產(chǎn)生class文件,運(yùn)行時(shí)無(wú)需產(chǎn)生,可直接使用,效率好。
jdk動(dòng)態(tài)代理必須實(shí)現(xiàn)接口,通過(guò)反射來(lái)動(dòng)態(tài)代理方法,消耗系統(tǒng)性能。但是無(wú)需產(chǎn)生過(guò)多的代理類(lèi),避免了重復(fù)代碼的產(chǎn)生,系統(tǒng)更加靈活。
cglib動(dòng)態(tài)代理無(wú)需實(shí)現(xiàn)接口,通過(guò)生成子類(lèi)字節(jié)碼來(lái)實(shí)現(xiàn),比反射快一點(diǎn),沒(méi)有性能問(wèn)題。但是由于cglib會(huì)繼承被代理類(lèi),需要重寫(xiě)被代理方法,所以被代理類(lèi)不能是final類(lèi),被代理方法不能是final。
因此,cglib的應(yīng)用更加廣泛一點(diǎn)。
以上這篇淺談Java代理(jdk靜態(tài)代理、動(dòng)態(tài)代理和cglib動(dòng)態(tài)代理)就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
SpringBoot actuator 健康檢查不通過(guò)的解決方案
這篇文章主要介紹了SpringBoot actuator 健康檢查不通過(guò)的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07SpringBoot Actuator未授權(quán)訪問(wèn)漏洞的排查和解決方法
Spring Boot Actuator 是開(kāi)發(fā)和管理生產(chǎn)級(jí) Spring Boot 應(yīng)用程序的重要工具,它可以幫助你確保應(yīng)用程序的穩(wěn)定性和性能,本文給大家介紹了SpringBoot Actuator未授權(quán)訪問(wèn)漏洞的排查和解決方法,需要的朋友可以參考下2024-05-05詳解SpringBoot定制@ResponseBody注解返回的Json格式
這篇文章主要介紹了詳解SpringBoot定制@ResponseBody注解返回的Json格式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11spring Boot打包部署到遠(yuǎn)程服務(wù)器的tomcat中
這篇文章主要給大家介紹了關(guān)于spring Boot打包部署到遠(yuǎn)程服務(wù)器的tomcat中的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-12-12