代理模式的应用

静态代理

很多小伙伴去大城市打拼。来大城市第一件事就是租房,免不了和中介打交道,因为很多房东很忙,你根本找不到他。从这个场景中就可以抽象出来代理模式

ISubject:被访问者资源的抽象
SubjectImpl:被访问者具体实现类(房东)
SubjectProxy:被访问者的代理实现类(中介)

UML图如下
在这里插入图片描述
举个例子来理解一下这个设计模式
老板让记录一下用户服务的响应时间,用代理模式来实现这个功能。

public interface IUserService {
    public void request();
}
public class UserServiceImpl implements IUserService {
    @Override
    public void request() {
        System.out.println("this is userService");
    }
}
public class UserServiceProxy implements IUserService {

    private IUserService userService;

    public UserServiceProxy(IUserService userService) {
        this.userService = userService;
    }

    @Override
    public void request() {
        long startTime = System.currentTimeMillis();
        userService.request();
        System.out.println("reques cost :" + (System.currentTimeMillis() - startTime));
    }

    public static void main(String[] args) {
        IUserService userService = new UserServiceImpl();
        UserServiceProxy userServiceProxy = new UserServiceProxy(userService);
        // this is userService
        // reques cost :0
        userServiceProxy.request();
    }
}

代理模式不只有让代理类和原始类实现同一个接口,然后将原始类注入到代理类中这一种写法。如果原始类没有定义接口,并且原始类并不是我们维护的,我们此时就可以用继承的方式类实现代理模式,让代理类继承原始类,然后扩展功能。

public class UserService {

    public void request() {
        System.out.println("this is userService");
    }
}
public class UserServiceProxy extends UserService {

    @Override
    public void request() {
        long startTime = System.currentTimeMillis();
        super.request();
        System.out.println("reques cost :" + (System.currentTimeMillis() - startTime));
    }

    public static void main(String[] args) {
        UserService userService = new UserServiceProxy();
        // this is userService
        // reques cost :1
        userService.request();
    }
}

一切看起来都非常的美好,老板又发话了,把产品服务的响应时间也记录一下吧。又得写如下3个类

IProductService 
ProductServiceImpl 
ProductServiceProxy 

UserServiceProxy和ProductServiceProxy这两个代理类的逻辑都差不多,却还得写2次。其实这个还好,如果老板说,把现有系统的几十个服务的响应时间都记录一下吧,你是不是要疯了?这得写多少代理类啊?这就得用到我们后续提到的动态代理了。

总结一下,代理模式的实现方式有两种

  1. 代理类和原始类实现同一个接口,原始类注入到代理类
  2. 代理类继承原始类

动态代理

黑暗总是暂时的,终究会迎来黎明,在JDK1.3之后引入了一种称之为动态代理(Dynamic Proxy)的机制。使用该机制,我们可以为指定的接口在系统运行期间动态地生成代理对象,从而帮助我们走出最初使用静态代理实现AOP的窘境

动态代理的实现有两种方式

  1. 利用JDK实现动态代理,即调用Proxy.newProxyInstance()方法
  2. 使用CGLIB来实现动态代理

使用JDK实现动态代理,原始类必须实现某个接口,因为它是基于实现同一个接口的方式来实现的。而用CGLIB来实现动态代理,原始类有无实现接口都可以,因为它是基于继承的方式实现的

JDK动态代理

动态代理的实现主要由一个类和一个接口组成,即java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口。

让我们用动态代理来改造一下上面记录系统响应时间的功能。虽然要为IUserService和IProductService两种服务提供代理对象,但因为代理对象中要添加的横切逻辑是一样的。所以我们只需要实现一个InvocationHandler就可以了。代码如下

public class RequestCostInvocationHandler implements InvocationHandler {

    private Object target;

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

	/** 被代理对象的任何方法被执行时,都会先进入这个方法 */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (method.getName().equals("request")) {
            long startTime = System.currentTimeMillis();
            // 执行目标对象的方法
            method.invoke(target, args);
            System.out.println("reques cost :" + (System.currentTimeMillis() - startTime));
        }
        return null;
    }

    public static void main(String[] args) {

        // 3个参数解释如下
        // classloader,生成代理类
        // 代理类应该实现的接口
        // 实现InvocationHandler的切面类
        IUserService userService = (IUserService) Proxy.newProxyInstance(
        IUserService.class.getClassLoader(),
        new Class[]{IUserService.class},
        new RequestCostInvocationHandler(new UserServiceImpl()));

        IProductService productService = (IProductService) Proxy.newProxyInstance(
        IProductService.class.getClassLoader(), 
        new Class[]{IProductService.class},
        new RequestCostInvocationHandler(new ProductServiceImpl()));

        // this is userService
        // reques cost :0
        userService.request();

        // this is productService
        // reques cost :0
        productService.request();
    }

}

生成动态代理也很简单,调用Proxy.newProxyInstance()方法即可

public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)

三个参数如下:

loader:类加载器
interfaces: 代理类应该实现的接口
h:实现InvocationHandler接口的类,在里面增加代理逻辑

这个方法以及3个参数在面试中偶尔会被问到,动态代理在各大框架中用的确实很多了

UML图如下。Spring AOP就是用动态代理来实现的
在这里插入图片描述

CGLIB动态代理

CGLIB基于字节码技术为我们生成子类,不用我们自己去生成。用法如下

public class UserService {

    public void request() {
        System.out.println("welcome sir");
    }
}
public class RequestCtrlCallback implements MethodInterceptor {

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object object =  methodProxy.invokeSuper(o, objects);
        System.out.println("reques cost :" + (System.currentTimeMillis() - startTime));
        return object;
    }

    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(UserService.class);
        enhancer.setCallback(new RequestCtrlCallback());

        UserService proxy = (UserService)enhancer.create();
        // welcome sir
        // reques cost :25
        proxy.request();
    }
}

代理模式和装饰者模式的区别

装饰者模式主要是为被装饰的对象提供增强功能,而代理模式主要是对被代理对象的使用增加控制,并不提供增强功能

参考博客

[1]https://blog.csdn.net/javazejian/article/details/56267036#aspectj
[2]http://www.importnew.com/27772.html
[3]https://juejin.im/post/5ad3e6b36fb9a028ba1fee6a
[4]http://layznet.iteye.com/blog/1182924
[5]https://blog.csdn.net/fighterandknight/article/details/51200470
[6]https://www.cnblogs.com/cenyu/p/6289209.html
好文
[7]https://www.cnblogs.com/gonjan-blog/p/6685611.html
代理模式和装饰者模式的区别
[8]https://www.zhihu.com/question/41988550

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Java识堂

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值