一.模式定义
- 代理模式是常见的设计模式之一,代理模式给某一个对象提供一个代理对象,代理对象具备真实对象的功能,并代替真实对象完成相应操作,并能够在操作执行的前后,对操作进行增强处理,是一种对象结构型模式。
二.模式结构
-
Subject抽象类
定义了RealSubject和Proxy的共同接口,这样就在任何使用RealSubject的地方都可以使用Proxy。
public abstract class Subject {
public abstract void request();
}
-
RealSubject类
定义了Proxy所代表的真实实体。
public class RealSubject extends Subject {
@Override
public void request() {
System.out.println("真实的请求RealSubject");
}
}
-
Proxy类
代理类。一个代理类可以代理多个被委托者或被代理者,因此一个代理类具体代理哪个真实主题角色,是由场景类决定的。
public class Proxy extends Subject {
private RealSubject realSubject = null;
public Proxy() {
this.realSubject = new RealSubject();
}
@Override
public void request() {
this.before();
this.realSubject.request();
this.after();
}
//预处理
private void before() {
System.out.println("-------before------");
}
//善后处理
private void after() {
System.out.println("-------after-------");
}
}
- 代理模式又分为静态代理和动态代理。动态代理常用 JDK 代理和 CGLIB 代理。
静态代理
- 静态代理在使用时,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承相同父类。
- 由程序员创建或特定工具自动生成源代码,也就是在编译时就已经将接口、被代理类、代理类等确定下来。在程序运行之前,代理类的.class文件就已经生成。
- 静态代理主要是在代理类中将被代理类的对象关联起来,将代码写死,无法更改代理对象。
定义一个 Person 接口确认具体行为。
//定义 Person 接口
public interface Person {
void do();
}
定义 Student 类,实现具体业务逻辑(被代理类)
public class Student implements Person {
private String name;
public Student(String name){
this.name = name;
}
@Override
public void do() {
System.out.println(name + "写作业");
}
}
定义代理类 MonitorProxy ,持有一个 Student 类对象,外界可通过这个类来访问 Student 类的方法。
public class MonitorProxy implements Person {
private Student student;
public MonitorProxy(Student student){
this.student = student;
}
@Override
public void do() {
System.out.println("交作业!");
student.do();
}
}
通过 MonitorProxy 来代理执行 Student 操作。
public class ProxyDemoTest {
public static void main(String[] args) {
MonitorProxy monitor = new MonitorProxy(new Student("张三"));
monitor.do();
}
}
动态代理-jdk
- 在编译期间无法确定需要代理的类。运行期间动态生成代理类。
- 在 java 的 java.lang.reflect 包下提供了一个 Proxy 类和一个 InvocationHandler 接口,通过这个类和这个接口可以生成 JDK 动态代理类和动态代理对象。
- 只能为接口实现类创建代理对象
- 创建出来的代理都是java.lang.reflect.Proxy的子类
- 代理对象,不需要实现接口,但是目标对象要实现接口,否则不能用动态代理;
- 代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象;
定义一个接口规范。
public interface Person {
void do();
}
实现接口规范的业务类。
public class Student implements Person {
private String name;
public Student(String name){
this.name = name;
}
@Override
public void do() {
System.out.println(name + "写作业");
}
}
定义处理器,实现 InvocationHandler 接口,invoke 方法定义了代理需要做的事情
(事情处理器,执行目标对象的方法时,会触发事情处理器方法, 会把当前执行的目标对象方法作为参数传入
)
public class ProxyHandle<T> implements InvocationHandler {
T target;
public ProxyHandle(T target){
this.target = target;
}
/**
* proxy 表示代理对象
* method 表示正在执行的方法
* args 表示调用方法时的参数
* */
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理执行:" + method.getName() + "方法");
//执行代理方法
Object invoke = method.invoke(target, args);
return invoke;
}
}
动态生成代理对象
public class ProxyTest {
public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Person p = new Student("张三");
ProxyHandle<Person> personProxyHandle = new ProxyHandle<>(p);
//ClassLoader loader : 指定当前目标对象使用的类加载器, 获取加载器的方法固定;
// Class<?>[] interfaces: 目标对象实现的接口类型,使用泛型方法确认类型;
// personProxyHandle : 事情处理器,执行目标对象的方法时,会触发事情处理器方法, 会把当前执行的目标对象方法作为参数传入
Person o = (Person)Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, personProxyHandle);
o.do();
}
}
动态代理-cglib
1、静态代理和JDK代理模式都要求目标对象是实现一个接口,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候可使用目标对象子类来实现代理-这就是Cglib代理 ;
2、Cglib代理也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能扩展,有些书也将Cglib代理归属到动态代理;
3、Cglib是一个强大的高性能的代码生成包,它可以在运行期扩展java类与实现java接口。它广泛的被许多AOP的框架使用,例如SpringAOP,实现方法拦截;
4、在AOP编程中如何选择代理模式:
- 目标对象需要实现接口,用JDK代理;
- 目标对象不需要实现接口,用Cglib代理。
5、Cglib包的底层是通过使用字节码处理框架ASM来转换字节码并生成新的类。