一、基本使用方式
代理两要素:
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 接口可以继续在遗留代码中使用。