1、代理是Java經常使用的設計模式,代理類經過調用被代理類的相關方法,並對相關方法進行加強。加入一些非業務性代碼,好比事務、日誌、報警發郵件等操做。java
2、jdk靜態代理設計模式
一、業務接口框架
/** * 業務接口 * @author pc * */ public interface UserService { // 增長一個用戶 public void addUser(); // 編輯帳戶 public void editUser(); }
二、業務實現類性能
/** * 業務實現類 * @author pc * */ public class UserServiceImpl implements UserService { public void addUser() { System.out.println("增長一個用戶。。。"); } public void editUser() { System.out.println("編輯一個用戶。。。"); } }
三、代理類測試
/** * 代理類 * * @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("事務開始。。。"); // 調用委託類的方法; userImpl.addUser(); System.out.println("處理結束。。。"); } public void editUser() { System.out.println("代理類方法,進行了加強。。。"); System.out.println("事務開始。。。"); // 調用委託類的方法; userImpl.editUser(); System.out.println("事務結束。。。"); } }
四、測試類this
public static void main(String[] args) { UserServiceImpl userImpl = new UserServiceImpl(); UserServiceProxy proxy = new UserServiceProxy(userImpl); proxy.addUser(); System.out.println("----------分割線----------"); proxy.editUser(); }
五、結果.net
代理類方法,進行了加強。。。 事務開始。。。 增長一個用戶。。。 處理結束。。。 ----------分割線---------- 代理類方法,進行了加強。。。 事務開始。。。 編輯一個用戶。。。 事務結束。。。
3、jdk動態代理設計
一、業務接口代理
/** * 業務接口 * @author pc * */ public interface UserService { // 增長一個用戶 public void addUser(); // 編輯帳戶 public void editUser(); }
二、業務接口實現類日誌
/** * 業務接口實現類 * @author pc * */ public class UserServiceImpl implements UserService { public void addUser() { System.out.println("增長一個用戶。。。"); } public void editUser() { System.out.println("編輯一個用戶。。。"); } }
三、代理類
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; } /** * 建立代理實例 * @return * @throws Throwable */ public Object getProxy() throws Throwable { return Proxy.newProxyInstance(Thread.currentThread() .getContextClassLoader(), this.target.getClass() .getInterfaces(), this); // 這樣寫只返回了目標對象,沒有生成代理對象。 // return target; } /** * 實現InvocationHandler接口方法 * 執行目標對象的方法,並進行加強 */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; System.out.println("代理類方法,進行了加強。。。"); System.out.println("事務開始。。。"); // 執行目標方法對象 result = method.invoke(target, args); System.out.println("事務結束。。。"); return result; } }
四、測試類
public class Test { /** * jdk動態代理會生成一個動態代理類,生成相應的字節碼,而後經過ClassLoader加載字節碼。 * 該實例繼承了Proxy類,並實現了業務接口,在實現的方法裏經過反射調用了InvocationHandler接口實現類 * 的invoke()回調方法。 * @param args * @throws Throwable */ public static void main(String[] args) throws Throwable { UserService userService = new UserServiceImpl(); ServiceInvocationHandler handler = new ServiceInvocationHandler(userService); // 根據目標生成代理對象 UserService proxy = (UserService) handler.getProxy(); proxy.addUser(); // proxy.editUser(); } }
五、測試結果
代理類方法,進行了加強。。。 事務開始。。。 增長一個用戶。。。 事務結束。。。
4、cglib動態代理
須要引入cglib的jar包,
在pom.xml加入依賴:
<!-- https://mvnrepository.com/artifact/cglib/cglib --> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>2.2.2</version> </dependency>
一、業務類,沒有實現接口
/** * 業務類 * 沒有實現接口 * 若是類是final的,則無法生成代理對象,報錯。 * 若是方法是final的,代理無效 * @author pc * */ public class UserServiceImpl { public void addUser() { System.out.println("增長一個用戶。。。"); } public void editUser() { System.out.println("編輯一個用戶。。。"); } }
二、代理類
import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; /** * 使用Cglib動態代理 * @author pc * */ public class UserServiceCglib implements MethodInterceptor{ private Object target; /** * 建立代理實例 * @param target * @return */ public Object getInstance(Object target){ this.target = target; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(this.target.getClass()); // 設置回調方法 enhancer.setCallback(this); // 建立代理對象 return enhancer.create(); } /** * 實現MethodInterceptor接口要重寫的方法。 * 回調方法 */ 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("事務結束。。。"); return result; } }
三、測試類
public class TestCglib { public static void main(String[] args) { UserServiceCglib cglib = new UserServiceCglib(); UserServiceImpl bookFacadeImpl = (UserServiceImpl)cglib.getInstance(new UserServiceImpl()); bookFacadeImpl.addUser(); // bookFacadeImpl.editUser(); } }
四、結果:
事務開始。。。 增長一個用戶。。。 事務結束。。。
五、若是業務實現類被定義成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)
5、總結
一、原理
jdk靜態代理實現比較簡單,通常是直接代理對象直接包裝了被代理對象。
jdk動態代理是接口代理,被代理類A須要實現業務接口,業務代理類B須要實現InvocationHandler接口。
jdk動態代理會根據被代理對象生成一個繼承了Proxy類,並實現了該業務接口的jdk代理類,該類的字節碼會被傳進去的ClassLoader加載,建立了jdk代理對象實例,
jdk代理對象實例在建立時,業務代理對象實例會被賦值給Proxy類,jdk代理對象實例也就有了業務代理對象實例,同時jdk代理對象實例經過反射根據被代理類的業務方法建立了相應的Method對象m(可能有多個)。當jdk代理對象實例調用業務方法,如proxy.addUser();這個會先把對應的m對象做爲參數傳給invoke()方法(就是invoke方法的第二個參數),調用了jdk代理對象實例的invoke()回調方法,在invoke方法裏面再經過反射來調用被代理對象的由於方法,即result = method.invoke(target, args);。
cglib動態代理是繼承代理,經過ASM字節碼框架修改字節碼生成新的子類,重寫並加強方法的功能。
二、優缺點
jdk靜態代理類只能爲一個被代理類服務,若是須要代理的類比較多,那麼會產生過多的代理類。jdk靜態代理在編譯時產生class文件,運行時無需產生,可直接使用,效率好。
jdk動態代理必須實現接口,經過反射來動態代理方法,消耗系統性能。可是無需產生過多的代理類,避免了重複代碼的產生,系統更加靈活。
cglib動態代理無需實現接口,經過生成子類字節碼來實現,比反射快一點,沒有性能問題。可是因爲cglib會繼承被代理類,須要重寫被代理方法,因此被代理類不能是final類,被代理方法不能是final。
所以,cglib的應用更加普遍一點。
參考:http://blog.csdn.net/fighterandknight/article/details/51200470
http://blog.csdn.net/jiankunking/article/details/52143504