动态代理模式和责任链模式
动态代理的意义在于生成一个占位(又称代理对象),来代理真实对象,从而控制真实对象的访问。
先来谈谈什么是代理模式。假设这样一个场景,你的公司是一家软件公司,你是以为软件工程师。客户带着需求去找公司显然不会直接和你谈,而是找商务谈,此时客户认为商务就代表公司。
客户(调用者)——>商务(代理对象)——>软件工程师(真实对象)
显然客户是通过商务区访问软件工程师的,那么商务的意义在于干什么呢?
商务可以进行谈判,比如项目启动前的商务谈判,软件的价格、交付、进度的时间节点等,或者项目完成后的商务追讨收账款。商务也有可能在开发软件之前谈判失败,此时商务会根据公司的规矩区结束和客户的合作关系,这些都不用软件工程师来处理。
因此,代理的作用就是,在真实对象访问之前或者之后加入相应的逻辑,或者根据其他规则控制是否使用真是对象,显然在中国例子里商务控制了客户对软件工程师的访问。
上面的论述,商务和软件工程师是代理和被代理的关系,客户是经过商务区访问软件工程师的。此时客户是程序中的调用者,商务就是代理对象,软件工程师就是真实对象。
我们需要在调用者调用对象之前产生一个代理对象,而这个代理对象需要和真实对象建立代理关系,所以代理必须分为两个步骤:
1. 代理对象和真实对象建立代理关系
2. 实现代理对象的代理逻辑方法
在Java中又多种动态代理技术,比如JDK\CGLIB\Javassist\ASM,其中最常用的动态代理技术有两种:
一种是JDK动态代理,这是JDK自带的功能。
另一种是CGLIB,这是第三方提供的一个技术。
Spring常用JDK和CGLIB,而MyBatis还是用了javassist。
JDK动态代理
JDK动态代理是java.lang.reflect.*包提供的方式,它必须借助一个接口才能产生代理对象,所以先定义接口。
//定义接口
public interface HelloWorld {
public void sayHelloWorld();
}
//实现接口
public class HelloWorldImpl implements HelloWorld{
@Override
public void sayHelloWorld() {
// TODO Auto-generated method stub
System.out.println("Hello World");
}
}
先要建立起代理对象和真实服务对象的关系,然后实现代理逻辑。
在JDK动态代理里,要实现代理逻辑类必须区实现java.lang.reflect.InvocationHander接口,它里面定义了一个invoke方法,并提供接口数组用于下挂代理对象。
importjava.lang.reflect.InvocationHandler;
importjava.lang.reflect.Method;
importjava.lang.reflect.Proxy;
public class JdkProxyExample implements InvocationHandler{
//真实对象
private Object target = null;
/*
* 建立代理对象和真实对象的代理关系,并返回代理对象
*@param target 真实对象
*@return 代理对象
* */
public Object bind(Object target) {
this.target = target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/sTODO Autohmethod stub
System.out.println("进入代理逻辑方法");
System.out.println("在调度真实对象之前的服务");
Object obj = method.invoke(target, args);
System.out.println("在调度真实对象之后的服务");
return obj;
}
}
1. 建立代理对象和真实对象的关系。
这里是用bind方法区完成的。
其中newProxyInstance方法包含了三个参数。
第一个是类加载器,采用了target本身的类加载器
第二个是把生成的动态代理对象下挂在哪些接口的下面,这个写法就是放在target是实现的接口下。HelloWorldImpl对象的接口显然就是HelloWorld,代理对象可以这么申明:
HelloWorld proxy = xxxx;
第三个是实现方法逻辑的代理类,this表示当前对象,它必须包含实现InvocationHandler接口的invoke方法,它就是代理逻辑方法的现实方法。
2. 实现代理逻辑方法。
Invoke方法可以实现代理逻辑
三个参数含义:
proxy,代理对象就是bind方法生成的对象
Method,当前调度的方法
Args,调度方法的参数
Proxy相当于商务对象,target相当于软件工程师对象,bind方法就是建立商务和软件工程师代理关系的方法。而invoke就是商务逻辑,它控制软件工程的访问。
测试
public class TestJdkProxy {
public static void main(String[] args) {
JdkProxyExample jdk = new JdkProxyExample();
//绑定关系,因为挂在接口hellword下,所以声明代理对象helloworld proxy
HelloWorld proxy = (HelloWorld) jdk.bind(new HelloWorldImpl());
proxy.sayHelloWorld();;
}
}
结果