JDK动态代理
最近也在疯狂的补基础知识,因此写这篇博客为了方便以后自己查看。
拦截器
- 由于动态代理一般都比较难理解,程序设计者会设计一个拦截器接口供开发者使用, 开发者只要知道拦截器接口的方法、含义和作用即可,无须知道动态代理是怎么实现的。 用 JDK 动态代理来实现一个拦截器的逻辑,为此先定义拦截器接口
import java.lang.reflect.Method; public interface Interceptor { /** * 当返回为true时,则反射真实对象的方法; * 当返回为false时,则调用around方法。 * @param proxy 代理对象 * @param target 真实对象 * @param method 方法 * @param args 运行方法参数 * @return */ public boolean before(Object proxy, Object target, Method method,Object[] args); /** * 在before方法返回为false时,则调用around方法。 * @param proxy * @param target * @param method * @param args * @return */ public void around(Object proxy, Object target,Method method,Object[] args); /** * 在反射真实对象方法或者around方法执行之后,调用after方法 * @param proxy * @param target * @param method * @param args * @return */ public void after(Object proxy, Object target,Method method,Object[] args); }
- 创建这个Interceptor的实现类-----MyInterceptor
package com.yl.interceptor; import java.lang.reflect.Method; /** * 拦截器实现类 */ public class Mylnterceptor implements Interceptor{ @Override public boolean before(Object proxy, Object target, Method method, Object[] args) { System.out.println("反射方法前逻辑---判断用户是否处于登录状态---用户未登录,操作拦截"); return false; //不反射被代理对象原有方法 } @Override public void around(Object proxy, Object target, Method method, Object[] args) { System.out.println("取代了被代理对象的方法---页面转发为指定界面,比如登录界面"); } @Override public void after(Object proxy, Object target, Method method, Object[] args) { System.out.println("反射方法后逻辑---记录本次异常操作"); } }
它实现了所有的 Interceptor 接口方法,使用JDK动态代理,就可以实现这些方法在适当时的调用逻辑
- 在JDK动态代理中使用拦截器以及test用例
这里有两个属性,一个是target,它是真实对象;另一个是字符串interceptorClass,它是一个拦截器的权限的类名。这里解释一下代码执行的步骤:package com.yl.interceptor; import com.yl.jdk.HelloWorld; import com.yl.jdk.HelloWorldImpl; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * 在JDK动态代理中使用拦截器 */ public class InterceptorJdkProxy implements InvocationHandler { private Object target = null; //真实对象 private String interceptorClass = null; //拦截器全限定名 public InterceptorJdkProxy(Object target, String interceptorClass) { this.target = target; this.interceptorClass = interceptorClass; } /** * 绑定委托对象并返回一个【代理占位】 * @param target 真实对象 * @param interceptorClass * @return 代理对象【占位】 */ public static Object bind(Object target,String interceptorClass) { //取得代理对象 return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InterceptorJdkProxy(target,interceptorClass)); } /** * 通过代理对象调用方法,首先进入这个方法 * @param proxy 代理对象 * @param method 方法,被调用方法 * @param args 方法的参数 * @return * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (interceptorClass == null) { //没有设置拦截器则直接反射原有方法 return method.invoke(target,args); } Object result = null; //通过反射生成拦截器 Interceptor interceptor = (Interceptor) Class.forName(interceptorClass).newInstance(); //调用前置方法 if (interceptor.before(proxy,target,method,args)) { //返回true,反射原有对象的方法 result = method.invoke(target,args); }else { //返回false,执行around方法 interceptor.around(proxy,target,method,args); } //调用后置方法 interceptor.after(proxy,target,method,args); return result; } public static void main(String[] args) { //注册拦截器 HelloWorld proxy = (HelloWorld) InterceptorJdkProxy.bind(new HelloWorldImpl(), "com.yl.interceptor.Mylnterceptor"); //不注册拦截器 HelloWorld proxy2 = (HelloWorld) InterceptorJdkProxy.bind(new HelloWorldImpl(),null); /** * 结果 * 反射方法前逻辑---判断用户是否处于登录状态---用户未登录,操作拦截 * 取代了被代理对象的方法---页面转发为指定界面,比如登录界面 * 反射方法后逻辑---记录本次异常操作 */ proxy.sayHelloWorld(); /** * 结果 * Hello World ! */ proxy2.sayHelloWorld(); } }
第 1 步,在 bind 方法中用 JDK 动态代理绑定了一个对象,然后返回代理对象。 第 2 步, 如果没有设置拦截器, 则直接反射真实对象的方法,然后结束,否则进行第 3 步。 第 3 步,通过反射生成拦截器,并准备使用它。 第 4 步,调用拦截器的 before 方法,如果返回为 true,反射原来的方法;否则运行拦 截器的 around 方法。 第 5 步,调用拦截器的 after 方法。 第 6 步,返回结果。
- HelloWorld接口
/** * JDK动态代理:必须借助一个接口才能产生代理对象,因此必须先定义接口。 */ public interface HelloWorld { public void sayHelloWorld(); }
- 提供HelloWorld的实现类来实现接口
/** * 提供HelloWorld的实现类来实现接口 */ public class HelloWorldImpl implements HelloWorld{ @Override public void sayHelloWorld() { System.out.println("Hello World !"); } }