java怎么拦截某个对象
252
2022-09-06
代理模式的应用
静态代理
很多小伙伴去大城市打拼。来大城市第一件事就是租房,免不了和中介打交道,因为很多房东很忙,你根本找不到他。从这个场景中就可以抽象出来代理模式
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(); }}
代理模式和装饰者模式的区别
装饰者模式主要是为被装饰的对象提供增强功能,而代理模式主要是对被代理对象的使用增加控制,并不提供增强功能
参考博客
[2] [4] 好文 代理模式和装饰者模式的区别 [8]https://zhihu.com/question/41988550
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~