设计模式之代理模式
通过增加代理来解耦A与C之间的调用,这样可以封装原来C调用A的一些相关细节,转换成C直接调用B中封装后的代理方法,则等同于访问A。对于WebService的远程调用时,如果我们使用添加Web引用的方式,那么WebService会为我们自动生成代理类的
所谓代理,就是代表某个真实对象,为另一个对象提供一个替身或占位符以控制对这个对象的访问。
JDK动态代理概念
运行时动态生成一个实际对象的代理,能过代理进行控制访问
与静态代理相比:
动态代理类的字节码在程序运行时由Java反射机制动态生成,无需程序员手工编写它的源代码。动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展 性,因为Java 反射机制可以生成任意类型的动态代理类
JDK的动态代理是建立在接口编程上,如果一个类没有实现接口,则JDK无法生成相对应的代理类,并且只有接口中的方法能够被代理
应用:
连接池的实现,调用close方法只是放回连接池而不是并闭,这样就可以使用代理代管close方法,如果方法名是close就使用自定义处理方法
AOP的实现,在调用方法前后调用其他处理方法
JDK动态代理实现
通过 java.lang.reflect.Proxy中的newProxyInstance方法创建一个代理类
InvocationHandler
接口自定义处理逻辑
接口:
public interface PersonBean {
String getName();
void setName(String name);
}
实现类:
public class PersonBeanImpl implements PersonBean {
private String name;
@Override
public String getName() {
// TODO Auto-generated method stub
return this.name;
}
@Override
public void setName(String name) {
// TODO Auto-generated method stub
this.name = name;
}
}
自定义处理器:
public class PersonInvocationHandler implements InvocationHandler {
private Object target;
public PersonInvocationHandler(Object target){
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("dynamic invoke begin...");
Object obj = method.invoke(target, args);
//Object obj = method.invoke(proxy, args); //传proxy这种写法有bug,java.lang.reflect.InvocationTargetException
System.out.println("before dynamic end...");
return obj;
}
}
测试代码:
public class Test {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
ProxyGeneratorUtils.writeProxyClassToHardDisk("d:/$Proxy11.class");
PersonBean realPersonBean = new PersonBeanImpl();
PersonInvocationHandler personInvocationHandler = new PersonInvocationHandler(
realPersonBean);
//name=null 初始时为null
System.out.println("name=" + realPersonBean.getName());
PersonBean personBean = (PersonBean) Proxy.newProxyInstance(
PersonBeanImpl.class.getClassLoader(), PersonBeanImpl.class
.getInterfaces(), personInvocationHandler);
personBean.setName("aaa");
//name=aaa 对代理对象的操作,实际是对目标对象的操作
System.out.println("name=" + realPersonBean.getName());
}
}
原理:
Proxy.newProxyInstance时,首先通过Proxy. getProxyClass()加载类,主要代码
/*
* Generate the specified proxy class.
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces);
try {
proxyClass = defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
/*
* A ClassFormatError here means that (barring bugs in the
* proxy class generation code) there was some other
* invalid aspect of the arguments supplied to the proxy
* class creation (such as virtual machine limitations
* exceeded).
*/
throw new IllegalArgumentException(e.toString());
}
生成二进制字节码,然后再加载到JVM中
加载后,调用代理类的构造函数生成实例对象
Constructor cons = cl.getConstructor(constructorParams);
return (Object) cons.newInstance(new Object[] { h });
具体动态生成的字节码可以
byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy11", PersonBeanImpl.class.getInterfaces());
输出到本地磁盘,反编译查看
//代理类构造函数
public Proxy11()
throws
{
super(paramInvocationHandler);
}
//代理类中其中一个方法
public final String getName()
throws
{
try
{
return ((String)this.h.invoke(this, m3, null));
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
可以看到调用方法时,执行的是h.invoke(this, m3, null)
即自定义的InvocationHandler中的invoke方法
如果自定义中如下写:Object obj = method.invoke(proxy, args);
Proxy指向的是当前调用对象(代理类),则执行代理类中的method,
而代理类中的方法又是h.invoke(this, m3, null),这样一直递归调用下去
所以出现如下错误:
dynamic invoke begin...
dynamic invoke begin...
dynamic invoke begin...
dynamic invoke begin...
Exception in thread "main" java.lang.reflect.UndeclaredThrowableException
at $Proxy0.setName(Unknown Source)
at com.Test.main(Test.java:19)
Caused by: java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at com.PersonInvocationHandler.invoke(PersonInvocationHandler.java:18)
... 2 more
Caused by: java.lang.reflect.UndeclaredThrowableException
at $Proxy0.setName(Unknown Source)
... 7 more
Caused by: java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at com.PersonInvocationHandler.invoke(PersonInvocationHandler.java:18)
... 8 more
Caused by: java.lang.reflect.UndeclaredThrowableException
at $Proxy0.setName(Unknown Source)
... 13 more
处理方法,自定义处理器中的
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable
Object obj = method.invoke(target, args);
Target指向目标对象,即被代理的对象,这样调用的是实际对象的方法了
使用代理对象操作实际目标对象,在代理对象上调用setName方法,实际会在目标对象上执行setName方法
来源:https://www.cnblogs.com/xuruhong/p/3270832.html