代理模式是java常用的设计模式,他的特征是代理类和委托类有同样的接口,代理类主要负责为委托类预处理消息,过滤消息等操作。
根据代理的创建时期,可以分为静态代理和动态代理两种,本文主要针对动态代理进行说明。
1. 首先JDK为我们提供了一个类Proxy,通过该类,我们可以很方便的创建出代理类对象,首先我们来看下通过Proxy创建的代理类具有哪些构造方法以及对于参数类型,通过执行下面的代码我们可以看到返回的代理对应的构造方法以及参数类型
package com.jacksoft.proxy;
import java.lang.reflect.Constructor;
import java.lang.reflect.Proxy;
import java.util.Collection;
/**
* 动态代理
* 通过对collection进行代理
* @author Jack
*
*/
public class ProxyTest {
public static void main(String[] args) {
Class clazz = Proxy.getProxyClass(Collection.class.getClassLoader(),new Class[]{Collection.class});
printConstructors(clazz);
}
/**
* 打印出构造方法以及对于参数类型
* 如:
* add(java.lang.String)
* @param clazz
*/
private static void printConstructors(Class clazz){
System.out.println("---------------------------构造方法--------------------------");
Constructor[] constructors = clazz.getConstructors();
StringBuilder sb = new StringBuilder();
for(Constructor constructor : constructors){
sb.append(constructor.getName());
sb.append("(");
Class[] parameterClass = constructor.getParameterTypes();
if(parameterClass != null){
for(Class c : parameterClass){
sb.append(c.getName()).append(",");
}
if(parameterClass.length != 0){
sb.deleteCharAt(sb.length()-1);
}
}
sb.append(")");
}
System.out.println(sb.toString());
}
}
通过上面的代码,得到的结果如下:
---------------------------构造方法--------------------------
$Proxy0(java.lang.reflect.InvocationHandler)
通过getProxyClass 这个方法返回的Class是$Proxy0,并不是我们传入的Collection,同时也只有一个带InvocationHandler参数的构造方法,所以接下来我们要对返回的Class通过反射来进行实例化操作,构造方法和对应参数都有了,很简单就能完成。由于InvocationHandler是接口,所以我们需要new一个实现出来传入。
/**
* 返回实例对象
* @param clazz
* @return
* @throws
* @throws SecurityException
*/
private static Object getInstance(Class clazz) throws Exception{
Constructor constructor = clazz.getConstructor(InvocationHandler.class);
return constructor.newInstance(new InvocationHandler(){
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
return null;
}
});
}
这里主要来看下IncocationHandler中的invoke方法,该方法就是我们在调用目标方法时,执行的方法,有三个参数,分别对应如下:
Object proxy:代理的对象。
Method method:要调用的方法
Object[] args:方法调用时所需要的参数
当我们调用目标方法时,实际上是通过Method反射去调用,所以我们要添加的代码也就是需要在这里体现。
2. 接下来我们也通过简单的代码来实现advise功能,也就是常说的织入
一般advise有四种,分别是方法执行前,执行后,执行前后,异常中,这里我简单定义两种,执行前和执行后
为了标准规范,这里需要定义一个接口
Advise:
package com.jacksoft.proxy;
/**
* Advise
* @author Jack
*
*/
public interface Advise {
/**
* 方法执行前
*/
public void beforMethod();
/**
* 方法执行后
*/
public void afterMethod();
}
然后再写一个对应的实现类
MyAdvise:
package com.jacksoft.proxy;
public class MyAdvise implements Advise {
@Override
public void beforMethod() {
System.out.println("-----------方法执行前-------------");
}
@Override
public void afterMethod() {
System.out.println("-----------方法执行后-------------");
}
}
这里只是简单的进行打印,可以根据实际的需求,还可以传入参数,比如对参数关键字进行替换之类的。
这里可以通过Proxy给我们提供的newProxyInstance方法来直接返回代理对象,写一个获取代理对象的工具类
ProxyUtils
package com.jacksoft.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 代理工具类
* @author Jack
*
*/
public class ProxyUtils {
public static Object getProxyInstance(final Object target,final Advise advise){
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
advise.beforMethod();
Object reVal = method.invoke(target, args);
advise.afterMethod();
return reVal;
}
});
}
}
这里在method.invoke前后都插入advise方法。
接下来再加上测试类,为了简单,这里我使用Colletion接口来生成代理对象
Client:
package com.jacksoft.proxy;
import java.util.ArrayList;
import java.util.Collection;
public class Client {
public static void main(String[] args) {
Advise advise = new MyAdvise();
Collection coll = (Collection) ProxyUtils.getProxyInstance(new ArrayList(), advise);
coll.add("1");
}
}
执行结果:
-----------方法执行前-------------
-----------方法执行后-------------
这样就将我们的代码横切到目标代码中,从而不影响目标源代码的逻辑,也很容易进行卸载,方便管理