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

