本文转载自微信公众号「小郎码知答」,作者simon郎。转载本文请联系小郎码知答公众号。
代理模式是给一个对象提供一个代理对象,并由代理对象控制对原对象的引用。
通俗来讲,代理模式就是我们所熟知的中介。
以我们熟知的商品代购为例:
商品代购
假如我们需要买一个物品,我们可以直接去工厂里购买;也可以找代购。
如果直接去工厂购买,我们在购买前需要对自己要买的物品做一些调研,然后去工厂直接去提货,这样什么事情都需要自己亲力亲为。
如果我们通过代购购买,我们只需要告诉代购我们需要什么,剩下的事情代购会帮我们处理(调研、拿货),最终给我们需要的相应的物品。
因此,代理模式的目标如下:
(1)通过引用代理对象的方式来间接访问目标对象,防止直接访问目标对象给系统带来不必要的复杂性。
(2)通过代理对象对原有的业务进行增强。
通常情况下,按照代理的创建时期,一般可以分为两种:
- 静态代理
静态代理是由程序员或者特定的工具自动生成的源代码,再对其编译,在程序运行之前,代理类编译的生成的.class文件就已经存在了
- 动态代理
动态代理是在程序运行时,通过反射机制动态创建而成。
1、静态代理模式
静态代理中的代理类和委托类的关系在运行前就确定了,如图所示:
静态代理
特别注意几个概念:
- 抽象对象
抽象对象声明了真实对象和代理对象的公共接口。
- 真实对象
代理对象所代表的真实对象,最终被引用的对象。
- 代理对象
包含真实对象进而操作真实对象,相当于访问者与真实对象直接的中介。
下面,我们来举个例子:
(1)创建服务类接口
- public interface BuyCar {
- void buycar();
- }
(2)服务实现类
- public class BuyCarImpl implements BuyCar{
- public void buycar() {
- System.out.println("买一辆奥迪");
- }
- }
(3)创建代理类
- public class BuyCarProxy implements BuyCar{
- private BuyCar buyCar;
- public BuyCarProxy(BuyCar buyCar){
- this.buyCar = buyCar;
- }
- public void buycar() {
- System.out.println("买车前的调研......");
- buyCar.buycar();
- System.out.println("买车后的保养......");
- }
- }
(4)编写测试类
- public class ProxyTest {
- public static void main(String[] args) {
- BuyCarImpl buyCar = new BuyCarImpl();
- BuyCarProxy buyCarProxy = new BuyCarProxy(buyCar);
- buyCarProxy.buycar();
- }
- }
优点:静态代理在不修改目标对象的前提下,可以通过代理对象对目标对象进行扩展。
代理类可以使得客户端不需要知道具体的实现类是什么,怎么做的,客户端只需知道代理即可(解耦合)
缺点:代理类和具体的实现类实现了相同的接口,代理类通过实现类实现了相同的方法。这样就出现了大量的代码重复。如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
代理对象只服务于一种类型的对象,如果要服务多类型的对象。势必要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。
2、动态代理模式
2.1 JDK自带
事实上,单一的代理是不存的,一个代理可以同时身兼数职。既可以代购车,也可以代购房。
在动态代理中我们不再需要手动的创建代理类,我们只需要一个动态处理器就可以了,而真正的代理对象由JDK运行时动态的创建。
(1)创建服务类接口
- //买车接口
- public interface BuyCar {
- void buycar();
- }
- //买房接口
- public interface BuyHouse {
- void buyHouse();
- }
(2)服务实现类
- //买车接口的实现类
- public class BuyCarImpl implements BuyCar {
- public void buycar() {
- System.out.println("买一辆奥迪");
- }
- }
- //买房接口的实现类
- public class BuyHouseImpl implements BuyHouse{
- public void buyHouse() {
- System.out.println("买一栋大别墅");
- }
- }
(3)动态代理类
- //通过实现 InvocationHandler 接口创建自己的调用处理器;
- public class ProxyHandler implements InvocationHandler {
- private Object object;
- //通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入。
- public ProxyHandler(Object object){
- this.object = object;
- }
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- System.out.println("Before invoke "+method.getName());
- method.invoke(object,args);
- System.out.println("After invoke "+method.getName());
- return null;
- }
- }
(4)测试类
- public class DynamicProxyTest {
- public static void main(String[] args){
- BuyHouse buyHouse = new BuyHouseImpl();
- BuyCar buyCar = new BuyCarImpl();
- InvocationHandler handler = new ProxyHandler(buyHouse);
- InvocationHandler handler1 = new ProxyHandler(buyCar);
- /**
- * 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类;
- */
- BuyHouse proxyHouse = (BuyHouse) Proxy.newProxyInstance(buyHouse.getClass().getClassLoader(), buyHouse.getClass().getInterfaces(),handler);
- BuyCar proxyCar = (BuyCar) Proxy.newProxyInstance(buyCar.getClass().getClassLoader(), buyCar.getClass().getInterfaces(),handler1);
- proxyHouse.buyHouse();
- proxyCar.buycar();
- }
- }
注意Proxy.newProxyInstance()方法接受三个参数:
- public static Object newProxyInstance(ClassLoader loader,
- Class<?>[] interfaces,
- InvocationHandler h)
ClassLoader loader:指定当前目标对象使用类加载器,获取加载器的方法是固定的。
Class[] interfaces:指定目标对象实现的接口的类型,使用泛型方式确认类型。
InvocationHandler h:指定动态处理器,执行目标对象的方法时,会触发事件处理器的方法。
2.2 CGLIB
CGLIB相比于JDK动态代理更加强大,JDK动态代理虽然简单易用,但是其有一个致命缺陷是,只能对接口进行代理。如果要代理的类为一个普通类、没有接口,那么Java动态代理就没法使用了。
在使用cglib前,需要先添加依赖。
- <dependency>
- <groupId>cglib</groupId>
- <artifactId>cglib</artifactId>
- <version>3.2.12</version>
- </dependency>
(1)目标类
- Dao
- public class Dao {
- public void update() {
- System.out.println("PeopleDao.update()");
- }
- }
- Dao1
- public class Dao1 {
- public void select(){
- System.out.println("PeopleDao.select");
- }
- }
(2)代理类
- public class DaoProxy implements MethodInterceptor {
- public Object intercept(Object object, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
- System.out.println("Befor Metod Invoke");
- methodProxy.invokeSuper(object,objects);
- System.out.println("After Method Invoke");
- return null;
- }
- }
参数解释:
- Object表示要进行增强的对象
- Method表示拦截的方法
- Object[]数组表示参数列表,基本数据类型需要传入其包装类型,如int-->Integer、long-Long、double-->Double
- MethodProxy表示对方法的代理,invokeSuper方法表示对被代理对象方法的调用
(3)测试
- public class CglibProxyTest {
- public static void main(String[] args) {
- DaoProxy daoProxy = new DaoProxy();
- Enhancer enhancer = new Enhancer();
- Enhancer enhancer1 = new Enhancer();
- //设置要继承的父类
- enhancer.setSuperclass(Dao.class);
- enhancer1.setSuperclass(Dao1.class);
- //设置回调方法
- enhancer.setCallback(daoProxy);
- enhancer1.setCallback(daoProxy);
- //创建动态代理类
- Dao dao = (Dao)enhancer.create();
- Dao1 dao1= (Dao1) enhancer1.create();
- dao.update();
- System.out.println("...................................");
- dao1.select();
- }
- }