代理模式,现在基本每个框架都在用,所以弄懂动态代理很有必要,我们先看一个代理模式图
根据上面的图我们来实现JDK的动态代理:
1、创建Subject接口;
2、创建实现Subject的实现子类RealSubject;
3、创建实现InvocationHandler的实现子类;
4、创建RealSubject的代理类;
5、通过代理类去执行实现接口的子类的方法;
一、创建Subject接口
public interface Subject { public void request(); }
二、创建实现Subject的实现子类RealSubject
public class RealSubject implements Subject { @Override public void request() { System.out.println("the request method of RealSubject"); } }
三、创建实现InvocationHandler的实现子类
public class CustomInvocationHandler implements InvocationHandler { // 目标对象 private Object target; public CustomInvocationHandler() { } public CustomInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { before(); Object obj = method.invoke(target, args); after(); return obj; } /** * 前置通知 */ private void before() { System.out.println("-- 插入前置通知代码 --"); } /** * 后置通知 */ private void after() { System.out.println("-- 插入后置通知代码 --"); } }
四、创建代理并且通过代理类的调用去执行实际实现接口子类的方法
public class Test { public static void main(String[] args) throws Exception { // 通过反射获取被代理类 Class<?> targetClazz = Class.forName("com.jjx.proxy.RealSubject"); // 生成代理对象 Subject subjectProxy = (Subject) Proxy.newProxyInstance( Test.class.getClassLoader(), // 类加载器 targetClazz.getInterfaces(), // 代理类所需要实现的接口(即实现类所实现的所有接口,这是个接口数组) new CustomInvocationHandler(targetClazz.newInstance())); // 调用处理器去调用接口实现类的方法 // 通过代理去执行接口实现类的方法 subjectProxy.request(); } }
上面就基本上完成了动态代理,但是我们在SpringMVC中都知道类中的方法还有注解(Annotation)的,这里我们就上级一下,来实现有注解的方法,这里就简单的实现一下类似Advice的注解,注解代码如下:
@Retention(RetentionPolicy.RUNTIME) public @interface MyTransaction { public AdviceType value(); }
这里有一个AdviceType是什么呢?这个是个自定义的枚举,用来枚举列出advice的前置通知、后置通知和环绕通知,下面是AdviceType枚举类的代码:
public enum AdviceType { BEFORE("before"), AFTER("after"), AROUND("around"); private String name; /** * @return the name */ public String getName() { return name; } /** * @param name the name to set */ public void setName(String name) { this.name = name; } private AdviceType(String name) { this.name = name; } }
现在也把接口改一下,改后的代码如下:
public interface Subject { // 在此方法执行之前,执行前置通知 public void needBeforeAdvice(); // 在此方法执行之后,执行后置通知 public void needAfterAdvice(); // 在此方法执行之前,执行前置通知;在此方法执行之后,执行后置通知 public void needAllAdvice(); // 此方法不执行任何通知 public void noAdvice(); }
实现接口的实现子类代码如下:
public class RealSubject implements Subject { @Override @MyTransaction(AdviceType.BEFORE) // 在needBeforeAdvice方法执行之前执行前置通知 public void needBeforeAdvice() { System.out.println("the mothed of needBeforeAdvice in the RealSubject class"); } @Override @MyTransaction(AdviceType.AFTER) // 在needAfterAdvice方法执行之后执行后置通知 public void needAfterAdvice() { System.out.println("the mothed of needAfterAdvice in the RealSubject class"); } @Override @MyTransaction(AdviceType.AROUND) // 在needAllAdvice方法执行之前,执行前置通知,该方法之后执行后置通知 public void needAllAdvice() { System.out.println("the mothed of needAllAdvice in the RealSubject class"); } @Override public void noAdvice() { // 该方法不做任何处理 System.out.println("the mothed of noAdvice in the RealSubject class"); } }
我们在来看看InvocationHandler实现子类是如何实现的,代码如下:
public class CustomInvocationHandler implements InvocationHandler { // 目标对象 private Object target; public CustomInvocationHandler() { } public CustomInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 获取被代理类的方法(method是接口中的方法,不是实现类的方法,所以要获取实现类中的实现方法) Method targetMethod = target.getClass().getMethod(method.getName(), method.getParameterTypes()); // 获取注解对象 MyTransaction annotation = targetMethod.getAnnotation(MyTransaction.class); Object obj; // 返回对象 // 如果该方法存在MyTransaction注解 if (annotation != null) { // 获取注解对象属性value的值,因为value值是一个AdviceType枚举类型,通过getName()方法可以获取枚举的值 String value = annotation.value().getName(); // 如果注解中的值为before,就执行before()方法 if (AdviceType.BEFORE.getName().equals(value)) { before(); // 执行相应的目标方法 obj = method.invoke(target, args); return obj; } // 如果注解中的值为around,就在执行方法之前执行before()方法,在执行方法之后执行after() if (AdviceType.AROUND.getName().equals(value)) { before(); // 执行相应的目标方法 obj = method.invoke(target, args); after(); return obj; } // 如果注解中的值为after,就执行after()方法 if (AdviceType.AFTER.getName().equals(value)) { // 执行相应的目标方法 obj = method.invoke(target, args); after(); return obj; } } else { // 如果该方法没有MyTransaction注解 // 执行相应的目标方法 obj = method.invoke(target, args); return obj; } return null; } /** * 前置通知 */ private void before() { System.out.println("-- 插入前置通知代码 --"); } /** * 后置通知 */ private void after() { System.out.println("-- 插入后置通知代码 --"); } }
通过代理去调用这些方法,代码如下:
public class Test { public static void main(String[] args) throws Exception { // 通过反射获取被代理类 Class<?> targetClazz = Class.forName("com.jjx.proxy.RealSubject"); // 生成代理对象 Subject subjectProxy = (Subject) Proxy.newProxyInstance( Test.class.getClassLoader(), // 类加载器 targetClazz.getInterfaces(), // 代理类所需要实现的接口(即实现类所实现的所有接口,这是个接口数组) new CustomInvocationHandler(targetClazz.newInstance())); // 调用处理器去调用接口实现类的方法 // 通过代理去执行接口实现类的方法 subjectProxy.needBeforeAdvice(); System.out.println("=========================="); subjectProxy.needAllAdvice(); System.out.println("=========================="); subjectProxy.needAfterAdvice(); System.out.println("=========================="); subjectProxy.noAdvice(); } }
得到的结果如下:
-- 插入前置通知代码 -- the mothed of needBeforeAdvice in the RealSubject class ========================== -- 插入前置通知代码 -- the mothed of needAllAdvice in the RealSubject class -- 插入后置通知代码 -- ========================== the mothed of needAfterAdvice in the RealSubject class -- 插入后置通知代码 -- ========================== the mothed of noAdvice in the RealSubject class这就达到了我们想要的结果,当然我这里只是简单的一个实现,只是用于自己巩固一下知识,希望没有误导到别人。