一.什么是代理
代理其实就是一种常用的设计模式。这种通过,工程师编辑代理类代码zx nkl,.;/6yujhnm 089p-/’]=[’]’
.m,实现代理模式;在编译期就生成了代理类的方式通常也被称作静态代理。
- 代理模式的定义:
为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
/**
* 猫咪接口
*/
public interface Cat {
void eat();
void drink();
void wc();
}
/**
* 猫咪实体类:咪咪
* 实现猫咪接口
*/
class MiMi implements Cat {
@Override
public void eat() {
System.out.println("咪咪吃饭");
}
@Override
public void drink() {
System.out.println("咪咪喝水");
}
@Override
public void wc() {
System.out.println("咪咪上厕所");
}
}
/**
* 代理类:铲屎官
* 负责照顾咪咪
*/
class ProxyCat implements Cat {
MiMi mimi;
public ProxyCat() {
this.mimi = new MiMi();
System.out.println("实例化铲屎官");
}
@Override
public void eat() {
System.out.println("给咪咪倒猫粮");
mimi.eat();
}
@Override
public void drink() {
System.out.println("给咪咪倒水");
mimi.drink();
}
@Override
public void wc() {
mimi.wc();
System.out.println("给咪咪铲屎");
}
}
/**测试类*/
class Test{
public static void main(String[] args) {
ProxyCat proxyCat=new ProxyCat();
proxyCat.eat();
proxyCat.drink();
proxyCat.wc();
}
}
---------------------------------------------------------------------------------------------
实例化铲屎官
给咪咪倒猫粮
咪咪吃饭
给咪咪倒水
咪咪喝水
咪咪上厕所
给咪咪铲屎
代理模式UML图:代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。
为了保持行为的一致性,代理类和委托类通常会实现相同的接口,所以在访问者看来两者没有丝毫的区别。通过代理类这中间一层,能有效控制对委托类对象的直接访问,也可以很好地隐藏和保护委托类对象,同时也为实施不同控制策略预留了空间,从而在设计上获得了更大的灵活性。
二.Java动态代理
Java 动态代理机制以巧妙的方式近乎完美地实践了代理模式的设计理念,Java动态代理的实现方式主要有以下几种。
- 基于 JDK 实现动态代理。
- 基于CGlib 动态代理模式 。
- 基于 Aspectj 实现动态代理。
基于JDK实现动态代理
通过JDK提供的工具方法Proxy.newProxyInstance动态构建全新的代理类(继承Proxy类,并重写InvocationHandler方法 )字节码文件并实例化对象回调。(jdk动态代理是由java内部的反射机制来实例化代理对象,并代理的调用委托类方法)
/**
* 猫咪接口
*/
public interface Cat {
void eat();
void drink();
void wc();
}
/**
* 猫咪实体类:咪咪
* 实现猫咪接口
*/
class MiMi implements Cat {
@Override
public void eat() {
System.out.println("咪咪吃饭");
}
@Override
public void drink() {
System.out.println("咪咪喝水");
}
@Override
public void wc() {
System.out.println("咪咪上厕所");
}
}
/**
* 实现InvocationHandler接口的代理类
*/
class CatProxy implements InvocationHandler {
//需要代理的对象
Object mimi;
public CatProxy(Object mimi) {
super();
//通过有参构造器将需要代理的对象传入
this.mimi = mimi;
}
//重写invke方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//具体代理处理逻辑
if ("eat".equals(method.getName())) {
System.out.println("给咪咪倒猫粮");
method.invoke(mimi, args);
} else if ("drink".equals(method.getName())) {
System.out.println("给咪咪倒水");
method.invoke(mimi, args);
} else if ("wc".equals(method.getName())) {
method.invoke(mimi, args);
System.out.println("给咪咪铲屎");
}
return null;
}
}
/**测试类*/
class Test {
public static void main(String[] args) {
Cat cat = new MiMi();
CatProxy catProxy = new CatProxy(cat);
Class<? extends Cat> aClass = cat.getClass();
//通过Proxy.newProxyInstance方法动态生成字节码并装载实例化出对象
Cat mi = (Cat) Proxy.newProxyInstance(aClass.getClassLoader(), aClass.getInterfaces(), catProxy);
mi.eat();
mi.drink();
mi.wc();
}
}
------------------------------------------------------------------------------------
给咪咪倒猫粮
咪咪吃饭
给咪咪倒水
咪咪喝水
咪咪上厕所
给咪咪铲屎
JDK动态代理解析
在JDK的动态代理机制中,分别由一个接口和类组成,一个是 InvocationHandler接口、另一个则是 Proxy类,这一组类和接口是实现我们动态代理所必须用到的。
- InvocationHandler:每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由InvocationHandler这个接口的 invoke 方法来进行调用。
public Object invoke(Object proxy, Method method, Object[] args)
- 这个方法里包含三个参数分别是
proxy: - 指代代理所生成的那个真实对象
method: - 指代的是我们所要调用真实对象的某个方法的Method对象
args: - 指代的是调用真实对象某个方法时接受的参数
- Proxy:该类即为动态代理类。
newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandlerh)
loader:一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
interfaces:一个Interface对象的数组,表示的是我提供给代理对象的一组接口,由代理对象实现这组接口,这样我就能调用这组接口中的方法了
h:一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个处理对象上
JDK动态代理原理
- 通过实现 InvocationHandler 接口创建自己的调用处理器;
- 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
- 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;
- 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。
Proxy类,根据我们所提供的接口生成一个实现类,它就是动态代理类。
生成过程是:通过接口,便可以获知接口的所有信息(主要是方法的定义),也就能声明一个新的类型去实现该接口的所有方法,这些方法显然都是“虚”的,它需要调用另一个对象的方法。当然这个被调用的对象不能是接口对象,如果是接口对象,我们就没法增强了,等于饶了一圈又回来了。
所以它调用的是接口对象的包装类,这个包装类需要我们来实现,但是jdk给出了约束,它必须实现InvocationHandler,这个接口里面有个方法,它是所有被代理类的所有方法的调用入口(invoke),调用之前我们可以加自己的代码增强。
所以可以这么认为实现类代理了InvocationHandler,InvocationHandler代理了我们的包装类,两级代理。
动态代理就是要生成一个包装类对象,由于代理的对象是动态的,所以叫动态代理。由于我们需要增强,这个增强是需要留给开发人员开发代码的,因此代理类不能直接包含被代理对象,而是一个InvocationHandler,该InvocationHandler包含被代理对象,并负责分发请求给被代理对象,分发前后均可以做增强。从原理可以看出,JDK动态代理是“对象”的代理。
基于JDK实现动态代理的特点:
- 动态代理类:在程序运行时,通过反射机制动态生成。
- 动态代理类通常代理接口下的所有类。
- 动态代理事先不知道要代理的是什么,只有在运行的时候才能确定。
- 动态代理的调用处理程序必须事先InvocationHandler接口,及使用Proxy类中的newProxyInstance方法动态的创建代理类。
- Java动态代理只能代理接口,要代理类需要使用第三方的CLIGB等类库。