0
点赞
收藏
分享

微信扫一扫

代理模式的应用


静态代理

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

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就是用动态代理来实现的

代理模式的应用_代理类_02

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();
}
}

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

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

参考博客


[2]http://www.importnew.com/27772.html
[4]http://layznet.iteye.com/blog/1182924
好文
代理模式和装饰者模式的区别
[8]https://www.zhihu.com/question/41988550


举报

相关推荐

0 条评论