设计模式-代理模式(Java)

介绍

代理模式通俗来讲就是为被代理的对象提供一种功能增强的方法,比如说你想为你的业务代码增加日志的功能,那么就可以通过创建代理对象来处理日志的功能,而被代理的对象就只需要关注自己业务代码的实现。代理模式将不属于业务逻辑的代码交给了代理对象来实现,使用代理模式的好处就是在不破坏被代理对象业务代码的前提下,对被代理对象的方法实现功能增强。

代理模式的实现分为静态代理和动态代理:

  1. 静态代理:一个代理类只能替一个代理主题(接口)代理工作
  2. 动态代理:一个代理工作处理器,可以替多个代理主题代理工作,只有代理工作内容一样就可以。

其中动态代理在Java中又有JDK动态代理和CGLB动态代理两种实现。

下面通过实际的代码案例来分别介绍静态代理和动态代理。

静态代理

案例:假设我们要给添加用户和添加商品的业务增加日志的功能。我们将添加的动作抽象为一个接口,然后分别定义添加用户和添加商品的实现类。

静态代理的实现步骤如下:

  1. 首先定义一个统一的接口:AddDao;
  2. 分别为添加商品和添加用户实现AddDao接口:UserAddDaoImpl、GoodsAddDaoImpl;
  3. 定义实现日志功能的代理类,同样实现AddDao接口;
  4. 创建代理对象,同时传入需要被代理的对象;
  5. 调用代理对象的方法;
public class StaticProxy {
    public static void main(String[] args) {
        // 创建UserAdd的代理对象
        AddDao userAddProxyDao = new StaticProxyAddDao(new UserAddDaoImpl());
        // 执行代理对象的方法
        userAddProxyDao.add();
        // 创建GoodsAdd的代理对象
        AddDao goodsAddProxyDao = new StaticProxyAddDao(new GoodsAddDaoImpl());
        // 执行代理对象的方法
        goodsAddProxyDao.add();
    }

}


interface AddDao {
    /**
     * 添加操作,可以实现User的添加或Goods的添加
     */
    void add();
}

class UserAddDaoImpl implements AddDao {

    @Override
    public void add() {
        System.out.println("执行添加User的业务代码...");
    }
}

class GoodsAddDaoImpl implements AddDao {

    @Override
    public void add() {
        System.out.println("执行添加Goods的业务代码...");
    }
}

class StaticProxyAddDao implements AddDao {
    /** 持有被代理者的引用,因为核心业务逻辑仍然交给被代理者自己完成 */
    private AddDao dao;

    public StaticProxyAddDao(AddDao dao) {

        this.dao = dao;
    }

    /**
     * 代理对象实现日志功能的增强
     */
    @Override
    public void add() {
        System.out.println("log: add方法开始执行");
        long start = System.currentTimeMillis();

        dao.add();

        long end = System.currentTimeMillis();
        System.out.println("log: 运行时间:" + (end-start));
        System.out.println("log: add方法执行结束");
    }
}

运行结果:

log: add方法开始执行
执行添加User的业务代码...
log: 运行时间:0
log: add方法执行结束
log: add方法开始执行
执行添加Goods的业务代码...
log: 运行时间:0
log: add方法执行结束

动态代理

动态代理分为JDK动态代理和CGLIB动态代理。

两者之间的特点:

  1. JDK的动态代理机制只能代理实现了接口的类,而没有实现接口的类就不能实现JDK的动态代理;
  2. cglib动态代理是针对类来实现代理的,它的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强。使用cglib实现动态代理,完全不受代理类必须实现接口的限制。
  3. cglib底层采用ASM字节码生成框架,使用字节码技术生成代理类,比使用Java反射效率要高。

JDK动态代理

案例:为两个不同的接口A和B的实现类增加日志输出功能。

JDK动态代理实现步骤:

  1. 定义代理类,实现InvocationHandler接口,并重写invoke方法
  2. 调用Proxy.newProxyInstance方法来生成代理对象

newProxyInstance方法需要的参数有三个:

参数1:被代理者的类加载器对象:ClassLoader loader

参数2:被代理对象实现的接口们: Class<?>[] interfaces

参数3:实现InvocationHandler接口的类对象:InvocationHandler h

具体请看案例代码的实现:

public class JDKDynamicProxy {
    public static void main(String[] args) {
        /* 创建接口A的代理对象 */
        A a = new AImpl();
        // 1.参数1:被代理者的类加载器对象
        ClassLoader loader = a.getClass().getClassLoader();
        // 2.参数2:被代理对象实现的接口们
        Class<?>[] interfaces = a.getClass().getInterfaces();
        // 3.参数3:实现InvocationHandler接口的类对象
        JDKPorxy jdkPorxyHandler = new JDKPorxy(a);
        // 4.创建代理对象
        A aProxy = (A) Proxy.newProxyInstance(loader, interfaces, jdkPorxyHandler);
        // 5.调用方法
        aProxy.a();

        /* 创建接口B的代理对象 */
        B b = new BImpl();
        // 1.参数1:被代理者的类加载器对象
        ClassLoader loader2 = b.getClass().getClassLoader();
        // 2.参数2:被代理对象实现的接口们
        Class<?>[] interfaces2 = b.getClass().getInterfaces();
        // 3.参数3:实现InvocationHandler接口的类对象
        JDKPorxy jdkPorxyHandler2 = new JDKPorxy(b);
        // 4.创建代理对象
        B bProxy = (B) Proxy.newProxyInstance(loader2, interfaces2, jdkPorxyHandler2);
        // 5.调用方法
        bProxy.b();
    }

}

interface A {
    void a();
}

interface B {
    void b();
}

class AImpl implements A {

    @Override
    public void a() {
        System.out.println("执行A业务代码 ...");
    }
}

class BImpl implements B {

    @Override
    public void b() {
        System.out.println("执行B业务代码 ...");
    }
}

class JDKPorxy implements InvocationHandler {
    /** 被代理的对象 */
    private Object target;

    public JDKPorxy(Object target) {
        this.target = target;
    }

    /**
     *  这个方法不是程序调用的,是一会执行代理类对象的方法时自动调用
     * @param proxy 代理类的对象
     * @param method 被代理者要执行的方法
     * @param args 被代理者要执行的方法需要的实参列表
     * @return 被代理者要执行的方法的返回结果
     * @throws
     *
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("log:" + method.getName() + "方法开始执行");
        long start = System.currentTimeMillis();

        Object value = method.invoke(target, args);

        long end = System.currentTimeMillis();
        System.out.println("log:" + "运行时间:" + (end-start));
        System.out.println("log:" + method.getName() + "方法执行结束");

        return value;
    }
}

输出结果:

log:a方法开始执行
执行A业务代码 ...
log:运行时间:0
log:a方法执行结束
log:b方法开始执行
执行B业务代码 ...
log:运行时间:0
log:b方法执行结束

CGLIB动态代理

案例:为UserService中的getUserName方法增加日志功能。

CGLIB动态代理实现步骤:

  1. 定义代理类CglibProxy,实现MethodInterceptor接口,并实现intercept方法
  2. 提供获取动态代理类对象的方法getTargetProxy

具体请看代码的实现:

public class CglibDynamicProxy {
    public static void main(String[] args) {
        // 1.创建被代理的对象
        UserService c = new UserService();
        // 2.创建实现MethodInterceptor接口的对象
        CglibProxy cglibProxy = new CglibProxy(c);
        // 3.创建代理实例
        UserService cProxy = (UserService) cglibProxy.getTargetProxy();
        // 4.调用方法
        System.out.println("打印返回结果:" + cProxy.getUserName(1001L));
    }
}
class UserService {
    public String getUserName(Long userId) {
        System.out.println("执行业务代码: 获取用户名...");
        return  userId + ":UserName";
    }
}

class CglibProxy implements MethodInterceptor {
    private Object target;

    public CglibProxy(Object target) {
        this.target = target;
    }

    /**
     *
     * @param proxy 代理对象,CGLib动态生成的代理类实例
     * @param method 目标对象的方法,上文中实体类所调用的被代理的方法引用
     * @param args 目标对象方法的参数列表,参数值列表
     * @param methodProxy 代理对象的方法,生成的代理类对方法的代理引用
     */
    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("log:" + method.getName() + "方法开始执行");
        long start = System.currentTimeMillis();

        Object value = method.invoke(target, args);

        long end = System.currentTimeMillis();
        System.out.println("log:" + "运行时间:" + (end-start));
        System.out.println("log:" + method.getName() + "方法执行结束");

        return value;
    }

    public Object getTargetProxy() {
        // Enhancer类是cglib中的一个字节码增强器,它可以方便的为你所要处理的类进行扩展
        Enhancer eh = new Enhancer();
        // 1.将目标对象所在的类作为Enhancer类的父类
        eh.setSuperclass(target.getClass());
        // 2.通过实现MethodInterceptor实现方法回调
        eh.setCallback(this);
        // 3. 创建代理实例
        return eh.create();
    }
}

 结果输出:

log:getUserName方法开始执行
执行业务代码: 获取用户名...
log:运行时间:0
log:getUserName方法执行结束
打印返回结果:1001:UserName

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值