1. 程式人生 > >java動態代理【一】

java動態代理【一】

bject print reat 映射 tor ted borde 需要 static

java動態代理的定義:為其他目標類的方法增加切面的邏輯,即在執行目標類方法的時候,先去執行一段如校驗檢測的邏輯代碼。java通俗一點就是生成一個繼承目標類的子類,並在每個調用方法都添加一段邏輯。
應用場景:當我們從別的項目遷移過來的代碼進行修改的時候,如果有一個需求是當要執行某個業務類的所有方法前,需要校驗其權限或其他的時候,如果這個類是源代碼,我們還可以在類的基礎上對每個方法區更改,但若是打包成jar包的類,若該類有接口還可以實現一個代理模式創建一個代理類,沒有接口就比較麻煩,但接口一旦多起來,那編寫的話也比較麻煩。這時我們就需要用到動態代理,由jdk動態創建一個proxy類,我們通過proxy類來調用目標類的方法。
動態代理的實現有三種方法:1、使用代理模式。 1)創建一個Star(明星)類,其實現了Speak,Eat接口

  1. package
    cglib;
  2. public class Star implements Speak,Eat{
  3. @Override
  4. public void say() {
  5. // TODO Auto-generated method stub
  6. System.out.println("i am a star");
  7. }
  8. @Override
  9. public void eat() {
  10. // TODO Auto-generated method stub
  11. System.out.println("star eats some fruit");
  12. }
  13. }
2)創建一個Broker(經紀人)類,其實現了Speak接口,如下代碼:
  1. package cglib;
  2. //裝飾器模式
  3. public class Broker implements Speak{
  4. private Speak speak;
  5. public Broker(Speak speak){
  6. this.speak=speak;
  7. }
  8. @Override
  9. public void say() {
  10. // TODO Auto-generated method stub
  11. speak.say();
  12. }
  13. }

3)測試代碼:
技術分享
輸出I am Star! 一個經紀人可以代理不同的明星表述,這在現實中是符合的。2、使用JDK動態代理(原理是通過映射,動態生成java字節碼Proxy類) 1)同樣以Star類為目標類,實現JDK動態代理需要實現InvocationHandler接口,如下:
  1. package cglib;
  2. import java.lang.reflect.InvocationHandler;
  3. import java.lang.reflect.Method;
  4. import java.lang.reflect.Proxy;
  5. public class JDKDynamicProxy implements InvocationHandler{
  6. private Object speak;
  7. private JDKDynamicProxy(Object speak){
  8. //傳入star對象
  9. this.speak=speak;
  10. }
  11. //創建一個靜態方法,生成代理類,
  12. public static Object newProxyInstance(Object speak){
  13. System.out.println(JDKDynamicProxy.class.getClassLoader().toString());
  14. return Proxy.newProxyInstance(JDKDynamicProxy.class.getClassLoader(),new Class[]{Speak.class,Eat.class}, new JDKDynamicProxy(speak));
  15. }
  16. //覆蓋InvocationHandler的invoke方法,從參數可以看出,這個方法明顯使用了jdk的方法映射來調用Star的方法,在調用Star的方法前後,我們都可以增加自己的邏輯代碼
  17. @Override
  18. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  19. // TODO Auto-generated method stub
  20. System.out.println("call the JDKDynamicProxy");
  21. return method.invoke(speak, args);
  22. }
  23. }

2)測試類
  1. package cglib;
  2. import java.lang.reflect.Constructor;
  3. public class ProxyTest {
  4. public static void main(String[] args) {
  5. //裝飾者模式
  6. Star star=new Star();
  7. Speak b=new Broker(star);
  8. b.say();
  9. //jdk動態代理模式
  10. Speak s=(Speak) JDKDynamicProxy.newProxyInstance(star);
  11. s.say();
  12. Eat e=(Eat) JDKDynamicProxy.newProxyInstance(star);
  13. e.eat();
  14. //CGlib動態代理
  15. Speak sp=CGProxy.newCGproxyInstance(Star.class);
  16. sp.say();
  17. }
  18. }
技術分享 可以看出在調用Star的say()方法之前,調用了JDKDynamicProxy 的invoke方法。 3、使用CGLib動態代理(原理使用了asm)
1)CGProxy類,因為是方法攔截,故實現的是MethodInterceptor 接口,下面是一個簡單的實現方法。
  1. package cglib;
  2. import java.lang.reflect.Method;
  3. import net.sf.cglib.proxy.Enhancer;
  4. import net.sf.cglib.proxy.MethodInterceptor;
  5. import net.sf.cglib.proxy.MethodProxy;
  6. public class CGProxy implements MethodInterceptor {
  7. public static <T extends Speak> Speak newCGproxyInstance(Class<T> superClass){
  8. Enhancer en=new Enhancer();
  9. en.setSuperclass(superClass);
  10. en.setCallback(new CGProxy());
  11. return (Speak)en.create();
  12. }
  13. @Override
  14. public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
  15. // TODO Auto-generated method stub
  16. System.out.println("call the CGProxy");
  17. return arg3.invokeSuper(arg0, arg2);
  18. }
  19. }
看代碼和JDK動態代理的寫法差不多,但其內部實現的原理卻不同。明顯的區別從代碼可以看出,JDK動態代理要傳入目標類的接口作為參數,所以目標類必須要有一個統一的接口,即如上的Speak,Eat接口,這就使得JDK動態代理有一定的局限性。而CGlib從代碼看出,傳入的是Class,不是接口,使用範圍更廣一些。


下一章探討一下JDK動態代理內部的實現原理。



null

java動態代理【一】