代理模式是常见的设计模式之一,顾名思义,代理模式就是代理对象具备真实对象的功能,并代替真实对象完成相应操作,并能够在操作执行的前后,对操作进行增强处理。(为真实对象提供代理,然后供其他对象通过代理访问真实对象)
分为
-
静态代理
-
动态代理
-
jdk动态代理
-
cglib动态代理
-
静态代理
真实类和代理类要实现同一个接口,在代理类中实现真实类的方法同时可以进行真实类方法的增强处理,在一个代理类中就可以完成对多个真实对象的注入工作。
public interface IRentHouse {void rentHouse();
}
public class RentHouse implements IRentHouse {@Overridepublic void rentHouse() {System.out.println("实现租房");}
}
public class IntermediaryProxy implements IRentHouse {private IRentHouse iRent;public IntermediaryProxy(IRentHouse iRentHouse) {iRent=iRentHouse;}@Overridepublic void rentHouse() {System.out.println("交中介费");iRent.rentHouse();System.out.println("中介负责维修管理");}
}
//client测试类
public class TestStaticProxy {public static void main(String[] args) {//定义租房IRentHouse iRentHouse = new RentHouse();//定义中介IRentHouse intermediaryProxy = new IntermediaryProxy(iRentHouse);//中介租房intermediaryProxy.rentHouse();}
}
动态代理
从静态代理的代码中可以发现,静态代理的缺点显而易见,那就是当真实类的方法越来越多的时候,这样构建的代理类的代码量是非常大的,所以就引进动态代理.
动态代理允许使用一种方法的单个类(代理类)为具有任意数量方法的任意类(真实类)的多个方法调用提供服务,
JAVA 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为 java 语言的反射机制。
jdk动态代理(接口代理)
Jdk代理涉及到java.lang.reflect包中的InvocationHandler接口和Proxy类,核心方法是
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
jdk动态代理过程中实际上代理的是接口,是因为在创建代理实例的时候,依赖的是java.lang.reflect包中Proxy类的newProxyInstance方法,该方法的生效就恰恰需要这个参数;
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) throws IllegalArgumentException{……
}
下面以案例来说明jdk动态代理的完整过程:
//接口
public interface Person {void wakeup();void sleep();
}
//实现类1
public class Student implements Person{private String name;public Student() {}public Student(String name) {this.name = name;}@Overridepublic void wakeup() {System.out.println("学生"+name+"早晨醒来啦");}@Overridepublic void sleep() {System.out.println("学生"+name+"晚上睡觉啦");}
}
//代理类
public class JDKDynamicProxy implements InvocationHandler {private Object bean;public JDKDynamicProxy(Object bean) {this.bean=bean;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {String methodname=method.getName();if (methodname.equals("wakeup")){System.out.println("早安~~~");}else if(methodname.equals("sleep")){System.out.println("晚安~~~");}return method.invoke(bean,args);}
}
//测试类
public class TestJDKDynamicProxy {public static void main(String[] args) {JDKDynamicProxy proxy = new JDKDynamicProxy(new Student("张三"));//创建代理实例Person student = (Person) Proxy.newProxyInstance(proxy.getClass().getClassLoader(), new Class[]{Person.class}, proxy);student.wakeup();student.sleep();}
}
输出结果为
早安~~
学生张三早晨醒来啦
晚安~~
学生张三晚上睡觉啦
接口中的方法,以及代理类中重写的invoke方法,但是invoke()方法并不是显式调用的,是在创建代理实例的过程中生成的接口虚拟代理类中调用了invoke方法。(把Sproxy0的实例强制转换成对应接口类型的引用,然后执行接口方法,进而执行代理类中invoke ())
总结对比:
1.静态代理中,代理类和真实类实现的是同一个接口,重写同样的方法;jdk动态代理中,代理类和真实类关系不大,代理类实现无侵入式的代码扩展。
2.静态代理中当接口中方法增加的时候,在代理类代码量也会增加,显然是不妥的;jdk动态代理解决了这个问题,当业务增加,代理类的代码不会增加。
3.jdk动态代理实现的是jdk自带InvocationHandler接口,实现了这个接口的类也叫拦截器类,也叫代理类。
cglib动态代理
从上面可以看出,jdk动态代理的前提条件是,要有接口存在,那还有许多场景是没有接口的,这个时候就需要cglib动态代理了,CGLIB(Code Generation Library)是一个基于ASM的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成。CGLIB通过继承方式实现代理。cglib动态代理过程中生成的是实现类的子类,cglib是如何凭空创造的实现类的子类的,下面是测试代码
//所需的代理类
public class CglibProxy implements MethodInterceptor {private Enhancer enhancer=new Enhancer();private Object bean;public CglibProxy(Object bean) {this.bean = bean;}public Object getProxy(){//设置需要创建子类的类enhancer.setSuperclass(bean.getClass());enhancer.setCallback(this);return enhancer.create();}@Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {String methodName = method.getName();if (methodName.equals("wakeup")){System.out.println("早安~~~");}else if(methodName.equals("sleep")){System.out.println("晚安~~~");}return method.invoke(bean,objects);}
}
//测试类
public class TestCglibProxy {public static void main(String[] args) {//生成虚拟代理类的代码,本来虚拟代理子类是看不见的,//下面这句话的作用就是把执行过程中cglib增强后的class字节码文件System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\aop");CglibProxy proxy = new CglibProxy(new Cat("咪咪"));Cat cat = (Cat) proxy.getProxy();cat.wakeup();cat.sleep();}
}
总结:
cglib动态代理和jdk动态代理的区别显而易见,但是实现逻辑差不多,cglib代理类是通过实现MethodInterceptor,重写intercept方法,通过生成被代理类的子类来达到代理增强代码的目的;而Jdk代理是通过实现InvocationHandler,重写invoke方法,通过生成接口的代理类来达到代码增强的目的,所以jdk动态代理的实现需要接口,cglib则不需要,spring5.0以上以及springboot2.0以上默认采用cglib动态来实现AOP。