动态代理与静态代理相比,其总体实现功能是相同的。
但静态代理中,需要在代理类里实例化实体类,每想增强一个类都需要在代理类中修改,不方便
动态代理使用了诸如反射等方法,可以在不修改代理类的情况下,新增加被代理的类,更方便使用。
java提供了专门实现动态代理的接口InvocationHandler
1. 新建类实现接口
2. 设置bind()方法,主要是用来返回相应增强过的实体类对象(其实是另一个对象Proxy0)
3. 实现invoke()方法,
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {...} 方法,要求三个参数。"参数 proxy 指代理类,method表示被代理的方法,args为 method 中的参数数组,返回值Object为代理实例的方法调用返回的值。这个抽象方法在代理类中动态实现"。
Object argObject = args[0];//拿到实体对象
beforeMethod(argObject);//before方法
Object object = method.invoke(target,args);//原方法
afterMethod();
return object;
搞定代理类之后,可以使用代理类增强原对象的方法
//得到增强的对象
Order2 orderServiceDynamicProxy2 = (Order2) new OrderServiceDynamicProxy(new Order2Impl()).bind();
orderServiceDynamicProxy2.justTest(1);
借鉴一个别人的例子
开发一个接口,包含两个方法,可以向指定的人问候“你好”或者“再见”。
public interface IHello {
void sayHello(String name);
void sayGoogBye(String name);
}
创建一个简单的类,实现这个IHello接口。
public class Helloimplements implements IHello {
@Override
public void sayHello(String name) {
System.out.println("Hello " + name);
}
@Override
public void sayGoogBye(String name) {
System.out.println(name+" GoodBye!");
}
}
消费这个实现类,迄今为止没什么特别的。
现在假设我们接到了这个需求:老板要求在该实现类每次问候某人时,必须把问候的细节记录到日志文件里。为了简单起见,我们在问候前打印下面的一行语句来模拟日志记录的动作。
System.out.println("问候之前的日志记录...");
您也许会说,这还不简单?直接修改Helloimplements的对应方法,把这行日志插入到对应方法即可。
然而,老板的要求是:不允许你修改原来的Helloimplements类。在现实场景中,Helloimplements可能是第三方的jar包提供的,我们没有办法修改代码。
您也许会说,我们可以用设计模式里的代理模式,即创建一个新的Java类作为代理类,同样实现IHello接口,然后将Helloimplements类的实例传入代理类。我们虽然被要求不允许修改Helloimplements的代码,但是可以把日志记录代码写在代理类里。完整代码如下:
public class StaticProxy implements IHello {
private IHello iHello;
public void setImpl(IHello impl){
this.iHello = impl;
}
@Override
public void sayHello(String name) {
System.out.println("问候之前的日志记录...");
iHello.sayHello(name);
}
@Override
public void sayGoogBye(String name) {
System.out.println("问候之前的日志记录...");
iHello.sayGoogBye(name);
}
static public void main(String[] arg) {
Helloimplements hello = new Helloimplements();
StaticProxy proxy = new StaticProxy();
proxy.setImpl(hello);
proxy.sayHello("Jerry");
}
}
这种做法能够实现需求:
下面我们再看如何用InvocationHandler实现同样的效果。
InvocationHandler是一个JDK提供的标准接口。看下面的代码:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class DynaProxyHello implements InvocationHandler {
private Object delegate;
public Object bind(Object delegate) {
this.delegate = delegate;
return Proxy.newProxyInstance(
this.delegate.getClass().getClassLoader(), this.delegate
.getClass().getInterfaces(), this);
}
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
Object result = null;
try {
System.out.println("问候之前的日志记录...");
// JVM通过这条语句执行原来的方法(反射机制)
result = method.invoke(this.delegate, args);
}
catch (Exception e) {
e.printStackTrace();
}
return result;
}
上面代码里的bind方法很想我之前代理类StaticProxy的setImpl方法,只不过这个bind方法的输入参数类型更加通用。日志记录的代码写在方法invoke里。
看看如何使用:
static public void main(String[] arg) {
DynaProxyHello helloproxy = new DynaProxyHello();
Helloimplements hello = new Helloimplements();
IHello ihello = (IHello) helloproxy.bind(hello);
ihello.sayHello("Jerry");
}
作者:JerryWangSAP
链接:https://www.jianshu.com/p/e575bba365f8
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
package proxy.staticproxy.dynamicproxy;
import proxy.Order;
import proxy.staticproxy.OrderServiceStaticProxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @program: ProxyDesignModen
* @description: 动态代理
* @author: Mr.Wang
* @create: 2019-05-06 15:51
**/
public class OrderServiceDynamicProxy implements InvocationHandler {
private Object target;
public OrderServiceDynamicProxy(Object target) {
this.target = target;
}
//绑定的方法
public Object bind() {
Class cls = target.getClass();
return Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object argObject = args[0];
beforeMethod(argObject);
Object object = method.invoke(target,args);
afterMethod();
return object;
}
private void beforeMethod(Object obj){
int userId = 0;
System.out.println("动态代理 before core");
if(obj instanceof Order){
//如果是order类
Order order = (Order)obj;
userId = order.getUserId();
int dbRouter = userId % 2;
System.out.println("动态代理分配到【db" + dbRouter + "】处理数据");
}
}
private void afterMethod() {
System.out.println("动态代理after");
}
}