动态代理
代理模式
- 代理模式:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户类和目标对象之间起到中介的作用
- 使用代理对象,是为了在不修改目标对象的基础上,实现对功能的增强。
- 代理模式的作用的另一种表述:
(1)功能增强:在你原有的功能上,增加了额外的功能。
(2)控制访问:代理类不让你访问目标。例如:商家不让用户访问厂家。 - 客户类真正想要访问的对象是目标对象,但客户类真正可以访问的对象是代理对象。客户类对目标对象的访问是通过访问代理对象来实现的。 当然,代理类和目标类要实现同一个接口。
- 示例:A 类想调用 C 类中的方法,但 C 类不允许 A 类调用,但 B 类可以调用 C 类中的方法。于是 A 类就通过 B 类调用 C 类的方法。因此 B 就是 C 的代理,A 通过 代理 B 访问 C。
- 图示:
静态代理
- 静态代理的定义:我们需要手动为目标类编写一个代理类
// 创建接口
interface Person{
void doWork();
}
// 创建目标类
class Student implements Person{
@Override
public void doWork() { System.out.println("学习"); }
}
// 静态代理
class StaticProxy implements Person{
private Person person;
public StaticProxy(Person person){ this.person = person; }
@Override
public void doWork() {
System.out.println("doSome before");
person.doWork();
System.out.println("doSome after");
}
}
public class Test {
public static void main(String[] args) {
Student student = new Student();
StaticProxy proxy = new StaticProxy(student);
proxy.doWork();
}
}
- 优点:实现简单;容易理解
- 缺点:
(1)在目标类比较多的时候,会产生大量的代理类。
(2)当接口中功能增加或修改时,影响的目标类和代理类会比较多,都需要修改。
动态代理
- 动态代理的定义:在程序运行过程中,通过 jdk 的反射机制自动生成代理类对象
- 优点:(1)不用创建代理类文件
(2)代理的目标类是活动的,可设置的。 - 动态代理可以做什么:
在不改变原来目标方法的前提下,可以在代理中增强自己的功能代码。 - 动态代理的实现方式通常有两种:使用 JDK 动态代理和通过 CGLIB 动态代理。
(1)JDK 的动态代理:JDK 的动态代理是基于 java 的反射机制实现的。使用过 JDK 中接口和类实现代理对象的动态创建。jdk 的动态代理要求目标对象必须实现接口,这是 java 设计上的要求。java 语言通过 java.lang.reflect 包提供的三个类支持代理模式:Proxy、Method 和 InvocationHandler。
(2)CGLIB 动态代理:CGLIB 是一个开源项目。CGLIB 代理的生成原理是生成目标类的子类,而子类是增强过的,这个子类对象就是代理对象。所以,使用 CGLIB 生成动态代理,要求目标类必须能够被继承,即不能是 final 的类。
jdk 动态代理的实现
- 反射包 java.lang.reflect,里面有三个类支持代理模式:InvocationHandler、Method、Proxy。
InvocationHandler 接口
- InvocationHandler 接口(调用处理器):该接口里面只有一个方法 invoke()。invoke():表示代理对象要执行的功能代码,你的代理类要完成的功能就写在 invoke() 方法中。
- 代理类的功能(主要做两件事情):
(1)调用目标方法,执行目标方法的功能。
(2)功能增强,在目标方法调用时,增强额外的功能。 - InvocationHandler 接口中的 invoke() 方法:
参数:
Object proxy:表示 jdk 创建的代理对象,无需赋值。
Method method:表示目标类中的方法,无需赋值。
Object[] args:目标类中方法的参数。
public Object invoke(Object proxy, Method method, Object[] args)
- 怎么使用 InvocationHandler 接口:
(1)创建类实现接口 InvocationHandler。
(2)重写 invoke() 方法,把原来静态代理中代理类要完成的功能,写在这里。
Method 类
- Method 类:表示方法的,其实就是目标类中的方法。
- 作用:通过 Method 可以执行某个目标类中的方法,method.invoke(目标对象,方法的参数)。
Proxy 类
- Proxy 类:是用来创建代理对象的。
- 使用的方法:newProxyInstance()。
参数:
ClassLoader loader:类加载器,负责向内存中加载对象的。使用反射机制获取目标对象的 ClassLoader。
Class<?>[] interfaces:接口,目标对象实现的接口。也是使用反射机制获取的。
InvocationHandler h:我们自己写的,代理类要完成的功能。
返回值:就是代理类对象
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
实现动态代理的步骤
(1)创建接口,定义目标类要完成的功能。
(2)创建目标类实现接口。
(3)创建 InvocationHandler 接口的实现类,在 invoke() 方法中完成代理类的功能。(主要干两件事情:1、调用目标方法;2、功能增强)
(4)使用 Proxy 类的静态方法,创建代理对象,并把返回值转为接口类型。
// 创建接口
interface Person{
void doWork();
}
// 创建目标类
class Student implements Person{
@Override
public void doWork() {
System.out.println("学习");
}
}
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("doSome before");
Object invoke = method.invoke(target, args); // 执行目标类的方法,通过 Method 类实现
System.out.println("doSome after");
return invoke;
}
}
public class Test {
public static void main(String[] args) {
Student student = new Student();
MyInvocationHandler handler = new MyInvocationHandler(student);
// 生成代理类对象
Person proxy = (Person)Proxy.newProxyInstance(student.getClass().getClassLoader(),student.getClass().getInterfaces(), handler);
proxy.doWork();
}
}