为了增强某个类的方法,我们可以使用代理模式来处理。例如静态代理和动态代理
静态代理查看 静态代理
要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,全部采用静态代理方式,
将是一件非常麻烦的事情!所以我们就得使用动态代理。
动态代理,使用的 Proxy 类,在 java.lang.reflect 包下。
Proxy主要有以下方法
//返回指定代理实例的调用处理程序。
public static InvocationHandler getInvocationHandler(Object proxy)
//返回代理类的 java.lang.Class 对象,并向其提供类加载器和接口数组。
public static Class> getProxyClass(ClassLoader loader,Class>... interfaces)
//返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。
public static Object newProxyInstance(ClassLoader loader,
Class>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
上面三个方法中,我们使用动态代理,其实使用到的只有 newProxyInstance。其他方法可以不用关心。
这是一个接口,在 java.lang.reflect 包下。动态代理时候,代理方法的处理就是通过实现这个接口中的 invoke 方法来实现的。
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
上面的方法说明中,我们知道了,想要获取代理类,我们可以通过如下的方法来获取。需要说明的是,动态代理的前提是被代理类一点要有接口。
//返回代理类的 java.lang.Class 对象,并向其提供类加载器和接口数组。
public static Class> getProxyClass(ClassLoader loader,Class>... interfaces)
下面的代码我们只是获取代理类和打印它的方法,这里我们代理的是 Collection 接口
/**
* 获取代理类
*/
@Test
public void test1(){
//1.获取代理类
Class clazzProxy = Proxy.getProxyClass(Collection.class.getClassLoader(),Collection.class);
System.out.println(clazzProxy.getName());
//2.获取构造方法,并且打印出来
Constructor[] constructors = clazzProxy.getConstructors();
System.out.println("--------constructors list--------");
for (Constructor constructor : constructors) {
StringBuilder sb = new StringBuilder( constructor.getName() );
sb.append('(');
Class[] clazzParams = constructor.getParameterTypes();
for (Class clazzParam : clazzParams) {
sb.append(clazzParam.getName()).append(",");
}
//删除最后一个逗号
if(clazzParams!=null && clazzParams.length !=0){
sb.deleteCharAt(sb.length()-1);
}
sb.append(')');
System.out.println(sb.toString());
}
//3.获取方法,并且打印
Method[] methods = clazzProxy.getMethods();
System.out.println("--------methods list--------");
for (Method method : methods) {
StringBuilder sb = new StringBuilder( method.getName() );
sb.append('(');
Class[] clazzParams = method.getParameterTypes();
for (Class clazzParam : clazzParams) {
sb.append(clazzParam.getName()).append(",");
}
//删除最后一个逗号
if(clazzParams!=null && clazzParams.length !=0){
sb.deleteCharAt(sb.length()-1);
}
sb.append(')');
System.out.println(sb.toString());
}
}
结果
com.sun.proxy.$Proxy4
--------constructors list--------
com.sun.proxy.$Proxy4(java.lang.reflect.InvocationHandler)
--------methods list--------
add(java.lang.Object)
remove(java.lang.Object)
equals(java.lang.Object)
toString()
hashCode()
clear()
.....
其实这里我们这个处理中,我们就实现了动态代理。
这里我们通过获取代理类,然后获取使用它的构造方法,来创建一个代理实例。目标对象是 ArrayList。
实现的结果是,再调用ArrayList的方法时候,调用前和调用后都打印数据。
下面是实现代码
/**
* 通过构造函数来创建代理实例
*/
@Test
public void test2() throws Exception {
//1.创建一个代理类
Class clazzProxy = Proxy.getProxyClass(ArrayList.class.getClassLoader(),ArrayList.class.getInterfaces());
//2.获取构造方法
Constructor constructor = clazzProxy.getConstructor(InvocationHandler.class);
//3.通过构造方法创建实例
Collection arrayListProxy = (Collection) constructor.newInstance(new MyInvocationHandler(new ArrayList()));
// 4.添加数据
arrayListProxy.add("1");
arrayListProxy.add("2");
arrayListProxy.isEmpty();
arrayListProxy.remove("2");
}
public class MyInvocationHandler implements InvocationHandler{
private Object target;
public MyInvocationHandler(Object target){
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("----------------------------");
System.out.println(method.getName() + " : 方法调用之前----");
Object obj = method.invoke(target,args);
System.out.println(method.getName() + " : 方法掉用之后");
System.out.println("----------------------------");
return obj;
}
}
结果
----------------------------
add : 方法调用之前----
add : 方法掉用之后
----------------------------
----------------------------
add : 方法调用之前----
add : 方法掉用之后
----------------------------
----------------------------
isEmpty : 方法调用之前----
isEmpty : 方法掉用之后
----------------------------
----------------------------
remove : 方法调用之前----
remove : 方法掉用之后
----------------------------
上面我们实现了动态代理,但是如果每次都那么弄,麻烦得不要不要的。有没有简单的呢?肯定是有的,而且真是使用中,我们也只会使用这种方式来实现动态代理。
//返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。
public static Object newProxyInstance(ClassLoader loader,
Class>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
这里我们还是使用上面的 MyInvocationHandler 处理类,来动态代理 HashSet 类
@Test
public void test3(){
Collection arrayListProxy = (Collection)Proxy.newProxyInstance(HashSet.class.getClassLoader(),
HashSet.class.getInterfaces(),
new MyInvocationHandler(new HashSet()));
arrayListProxy.add("jjj");
}
结果
----------------------------
add : 方法调用之前----
add : 方法掉用之后
----------------------------
或者我们代码封装一下,实现传入我们目标实例,生成代理对象。代码如下
@Test
public void test5(){
Map map = (Map)getProxyObj(new HashMap());
map.put("name","xiaoming");
map.remove("name");
}
public Object getProxyObj(Object target){
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("----------------------------");
System.out.println(method.getName() + " : 方法调用之前----");
Object obj = method.invoke(target,args);
System.out.println(method.getName() + " : 方法掉用之后");
System.out.println("----------------------------");
return obj;
}
});
}
结果
----------------------------
put : 方法调用之前----
put : 方法掉用之后
----------------------------
----------------------------
remove : 方法调用之前----
remove : 方法掉用之后
----------------------------
https://github.com/wimingxxx/spring01/blob/master/src/com/qwm/spring2/c_proxy/Demo.java