Java 动态代理

一、基本使用方式

代理两要素:

1、要代理的接口, 动态代理只支持对接口提供代理。

2、一个处理接口方法调用的java.lang.reflect.InvocationHandler接口。

public class LoggingInvocationHandler implements InvocationHandler{
  private static final Logger LOGGER = Logger.getLogger(LoggingInvocationHandler.class);
  private Object receiverObject;
  public LoggingInvocationHandler(Object object){
    this.receiverObject = object;
  }
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
    LOGGER.log(Level.INFO, "调用方法" + method.getName() + " : 参数为 " + Arrays.deepToString(args);
    return method.invoke(receiverObject, args);
  }
}
有了InvocationHandler接口的实现之后,就可以创建和使用动态代理。

public static void useProxy(){
  String str = "Hello World";
  LoggingInvocationHandler handler = new LoggingInvocationHandler(str);
  ClassLoader cl = SimpleProxy.class.getClassLoader();
  Comparable obj = (Comparable) Proxy.newProxyInstance( c1, new Class[]{Comparable.class} , handler);
  obj.compareTo("Good");
}
为任何接口及其实现类创建代理的工厂方法。

public static<T> ? makeProxy(Class<T> intf, final T object){
  LogginInvocationHandler handler = new LoggingInvocationHandler(object);
  ClassLoader c1 = object.getClass().getClassLoader();
  return (T) Proxy.newProxyInstance( c1, new Class<?>[]{ intf } , handler);
}
然后使用

public static void useGenericProxy(){
  String str = "Hello World";
  Comparable proxy = makeProxy(Comparable.class, str);
  proxy.compareTo("God");
  List<String> list = new ArrayList<String>();
  list = makeProxy(List.class, list);
  list.add("Hello");
}

二、代理某个类的所有接口
public static Object proxyAll (final Object object){
  LoggingInvocationHandler handler = new LoggingInvocationHandler(object);
  ClassLoader cl = object.getClass().getClassLoader();
  Class<?>[] interfaces = object.getClass().getInterfaces();
  return Proxy.newProxyInstance( cl, interfaces, handler);
}

三、代理类

上面的 Proxy.newProxyInstance方法直接创建动态代理的对象, 是一个快捷创建对象的捷径。

Proxy.getProxyClass方法获取代理类,newProxyInstance方法得到的代理对象是通过反射API调用代理类的构造方法获得。

代理类的构造方法只有一个参数,InvocationHandler接口的实现,

Proxy.isProxy 判断一个java类是否是代理类。

Proxy.getProxyClass只会创建一个代理类,之后都是缓存的。

public void proxyMultipleInterfaces() throws Throwable{
  List<String> receiverObj = new ArrayList<String>();
  ClassLoader cl = MultipleInterfacesProxy.class.getClassLoader();
  LoggingInvocationHandler handler = new LoggingInvocationHandler(receiverObj);
  Class<?> proxyClass = Proxy.getProxyClass( cl, new Class<?>[]{List.class, Set.class});//调用方法,和顺序有关。
  Object proxy = proxyClass.getConstructor(new Class[]{InvocationHandler.class}).new Instance(new Object[]{handler})
  List list = (List) proxy;
  list.add("Hello");
  Set set = (Set) proxy;
  set.add("World"); // 调用的是List接口的add,因为getProxyClass 方法,List在set之前。
}

四、案例

动态代理除了上面AOP方式的功能外,

还能够偷梁换柱,换掉参数,换掉返回,甚至换掉实现的方法。

下面是,新版本API适配旧版本API的例子

public interface GreetV1{
  String greet(String name, String gender) throws GreetException;
}
public interface GreetV2{//接口更新后
  String greet(String username)
}
public class GreetAdapter implements InvocationHandler{
  private GreetV1 v1;
  public GreetAdapter(GreetV1 v1){ // 通过构造方法获得旧版本
    this.v1 = v1;
  } 
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
    String methodName = method.getName();
    if("greet".equals(methodName){// 只处理greet的方法。
       String username = (String)args[0];
       Stringa name = ...;
       String gender = ...;
       try{
          Method greetmethodv1 = GreetV1.class.getMethod(methodName, new Class<?>[]{ Stiring.class, String.class});
          return greetmethodV1.invoke(greetV1, new Object[]{name, gender});
       }catch(InvocationTargetException e){
         Throwable cause = e.getCause();
         if(cause != null &&cause instanceof GreetException){
              throw new GreetRuntimeException(cause);
           }
           throw e;
       }
    }else {
       return method.invoke(greetV1, args);
     }
  }
}
public static GreetV2 adaptGreet(GreetV1 greet){
  GreetAdapter adapter = new GreetAdapter(greet);
  ClassLoader cl = greet.getClass().getClassLoader();
  return (GreetV2)Proxy.newProxyInstance( cl, new Class<?>[]{GreetV2.class}, adapter);
}
在实际使用中,如果遇到GreetV1接口的实现,只需通过adaptGreet方法转换为GreetV2接口

再按照GreetV2接口的方式来使用,GreetV1 接口可以继续在遗留代码中使用。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值