代理模式(对原有方法进行增强)
基本介绍
类图
通过调用代理对象来间接使用模板对象的方法,代理对象和目的对象需要实现相同的接口或者继承相同的父类
代理对象会对原有方法进行补充
静态代理(手动编写代理类)
我们自己手动创建一个代理对象,和目标类实现相同的接口或者父类,然后对目标类的每一个方法进行改写
编写一个接口:
public interface ITeacher {
void teach();
}
编写目标类:
public class Teacher implements ITeacher{
@Override
public void teach() {
System.out.println("授课");
}
}
编写代理类:
public class TeacherProxy implements ITeacher{
ITeacher teacher;
TeacherProxy(ITeacher teacher){
this.teacher=teacher;
}
@Override
public void teach() {
System.out.println("开始代理");
teacher.teach();
System.out.println("代理结束");
}
}
客户端调用:
public class Client {
public static void main(String[] args) {
ITeacher teacher=new TeacherProxy(new Teacher());
teacher.teach();
}
}
客户端调用时,就感觉是在调用目标对象一样
静态代理的优点是灵活,缺点是如果目标类的方法很多,我们要为每个方法都编写代理方法,代码量很庞大,并且当目标类增加方法时,目标类和代理类都需要修改,代理类和目标类的耦合度很大,为了解决这个问题我们可以使用动态代理
JDK动态代理
目标对象需要实现一个接口,而代理对象由JDK帮我们生成不需要我们来实现接口
类图:
JVM利用反射机制生成代理对象,我们通过调用代理对象来间接调用目标对象的方法
编写一个代理工厂来为实现了接口的方法生成代理对象(包括已经被代理的对象)
实现:
和静态代理一样需要实现一个接口,对接口包含的所有方法进行代理(包括toString等Object里的方法)
代理对象工厂:
public class ProxyFactory {
Object getProxyInstance(Object target){
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开始代理");
Object result = method.invoke(target, args);
System.out.println("代理结束");
return result;
}
}
);
}
}
对所有传入的对象进行动态代理,实现原理是调用Proxy.newProxyInstance方法,这个方法需要三个参数:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
ClassLoader loader:目标对象的类加载器,直接使用target.getClass().getClassLoader()即可
Class<?>[] interfaces:目标对象实现的接口,直接使用target.getClass().getInterfaces()即可
InvocationHandler h:方法具体的增强逻辑,我们可以创建一个匿名内部类,也可以使用Lamda表达式
这样这个工厂可以为任何方法创建代理对象,代理的逻辑定义在内部类中
CGLIB动态代理
不需要实现接口,使用性能很高的ASM细节吗生成框架,常用于各种AOP框架
被代理的类不能是final的
使用的是拦截器机制
实现
创建目标类:
public class Teacher {
void teach(){
System.out.println("上课");
}
}
创建代理工厂:
public class ProxyFactory implements MethodInterceptor {
private static final ProxyFactory proxyFactory=new ProxyFactory();
private final ThreadLocal<Object> targetMap=new ThreadLocal<>();
private ProxyFactory(){}
public static ProxyFactory getProxyFactory(){
return proxyFactory;
}
public Object getProxyInstance(Object target){
targetMap.set(target);
Enhancer enhancer=new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("CGLIB 动态代理");
System.out.println(o.getClass());
Object result = methodProxy.invoke(targetMap.get(),objects);
methodProxy.invokeSuper(o,objects);
method.invoke(targetMap.get(),objects);
targetMap.remove();
System.out.println("代理结束");
return result;
}
}
调用目标方法有三种:
methodProxy.invoke(targetMap.get(),objects);
methodProxy.invokeSuper(o,objects);
method.invoke(targetMap.get(),objects);
targetMap.get()得到的是被代理的原对象,而参数中的o是提前暴露的代理对象,一定不要视图直接使用o,否则会爆栈
method是原原方法,methodProxy是代理方法
创建客户端测试:
public class Client {
private static final ProxyFactory proxyFactory=ProxyFactory.getProxyFactory();
public static void main(String[] args) {
Teacher teacher = (Teacher) proxyFactory.getProxyInstance(new Teacher());
teacher.teach();
}
}