静态代理
很多小伙伴去大城市打拼。来大城市第一件事就是租房,免不了和中介打交道,因为很多房东很忙,你根本找不到他。从这个场景中就可以抽象出来代理模式
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次。其实这个还好,如果老板说,把现有系统的几十个服务的响应时间都记录一下吧,你是不是要疯了?这得写多少代理类啊?这就得用到我们后续提到的动态代理了。
总结一下,代理模式的实现方式有两种
- 代理类和原始类实现同一个接口,原始类注入到代理类
- 代理类继承原始类
动态代理
黑暗总是暂时的,终究会迎来黎明,在JDK1.3之后引入了一种称之为动态代理(Dynamic Proxy)的机制。使用该机制,我们可以为指定的接口在系统运行期间动态地生成代理对象,从而帮助我们走出最初使用静态代理实现AOP的窘境
动态代理的实现有两种方式
- 利用JDK实现动态代理,即调用Proxy.newProxyInstance()方法
- 使用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.youkuaiyun.com/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.youkuaiyun.com/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