学过spring的人应该都知道,spring里有一项很牛逼的技能就是AOP,面相切面编程,用的原理就是java的动态代理机制。spring的aop例子可以参照下面博客,说的很清楚,我就不写了,今天只来实现一下java的动态代理。
还是以昨天的博客,上网代理为例子。首先是上网接口:
/** * IsubJect: 上网接口(被代理类的接口) * * @author xuejupo [email protected] * * create in 2015-12-23 下午1:31:13 * */ interface IsubJect { public void request(String url); }
上网类:
/** * MySubject: 具体上网类(被代理类) * * @author xuejupo [email protected] * * create in 2015-12-23 下午1:33:21 * */ public class MySubject implements IsubJect { @Override public void request(String url) { // TODO Auto-generated method stub System.out.println("您打开了网址:" + url); } }
好了,上网类就算实现了,并且已经发布到现场版本了,不能轻易修改了。那么我们想对上网操作做修改怎么办? 使用动态代理:
首先定义几个接口,就像spring中的MyBeforeAdvice等接口一样:
/** * Before: 动态代理中,在执行方法前做的事,相当于aop中的MyBeforeAdvice接口 * @author xuejupo [email protected] * create in 2015-12-23 下午2:34:53 * */ interface Before{ void beforTheMonth(); } /** * after:动态代理中,在执行方法后做的事,相当于aop中的MyAfterAdvice接口 * @author xuejupo [email protected] * create in 2015-12-23 下午2:36:51 * */ interface After{ void afterTheMonth(); } /** * interTheMethod: 动态代理中,在执行方法的时候,转执行该方法,相当于aop中的MyMethodInterceptor接口 * @author xuejupo [email protected] * create in 2015-12-23 下午2:38:27 * */ interface InterTheMethod{ Object theOtherMethod(Object objcet,Method method, Object[] args); }
好了,定义好接口以后,就应该实现这些接口,扩充我们的业务:
/** * BeforClass: 动态代理中前置方法 * @author xuejupo [email protected] * create in 2015-12-24 下午8:09:40 * */ class BeforClass implements Before { @Override public void beforTheMonth() { // TODO Auto-generated method stub System.out.println("动态代理before方法:做好上网前准备-----"); } } /** * AfterClass:动态代理中后置方法 * @author xuejupo [email protected] * create in 2015-12-24 下午8:10:04 * */ class AfterClass implements After { @Override public void afterTheMonth() { // TODO Auto-generated method stub System.out.println("动态代理after方法:上网完成记得结账-----"); } } /** * innerClass:动态代理中插入方法 * @author xuejupo [email protected] * create in 2015-12-24 下午8:10:12 * */ class innerClass implements InterTheMethod { @Override public Object theOtherMethod(Object object,Method method, Object[] args) { // TODO Auto-generated method stub System.out.println("动态代理inner方法:执行该方法就不一定执行原来的方法了,在这个方法里判断:"); if(args[0].equals("youku.com")){ System.out.println("幸好我用代理,发现你小子看优酷了!禁止访问!!!"); }else{ try { System.out.println("原来不是看优酷呀,放过你了,继续看吧:"); method.invoke(object, args); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InvocationTargetException e) { // TODO Auto-generated catch block e.printStackTrace(); } } return null; } }
既然是模拟aop,aop里面是怎么做的? 在配置文件里面配相应的参数,使得在执行主要方法之前或者之后,能够执行我们的逻辑方法。应该有个类实现模拟aop中读取xml参数的行为,太麻烦了。。 做一个bean类,分别用几个get方法模拟一下读取xml文件中读取相应类的方法:
package InvolvedMode.proxy.dynamicProxy; import java.util.ArrayList; import java.util.List; /** * DynamicXmlBean : * * @author xuejupo [email protected] * * create in 2015-12-23 下午1:44:44 */ public class DynamicXmlBean { /** * @Fields beanMap : 模拟aop中xml配置中的bean配置 */ private static List<Class<?>> beanList = new ArrayList<Class<?>>(); //初始化 static{ try{ beanList.add(Class.forName("InvolvedMode.proxy.dynamicProxy.BeforClass")); beanList.add(Class.forName("InvolvedMode.proxy.dynamicProxy.AfterClass")); beanList.add(Class.forName("InvolvedMode.proxy.dynamicProxy.innerClass")); }catch(Exception e){ e.fillInStackTrace(); } } /** * getBeforeClass: 获取before方法(在真正方法前应该执行的类) * @param name * @return * Class<?> 返回类型 */ public static Class<?> getBeforeClass(){ for(Class<?> c:beanList){ if(Before.class.isAssignableFrom(c)){ return c; } } return null; } /** * getAfterClass: 获取after方法(在真正方法执行之后执行的方法) * @return * Class<?> 返回类型 */ public static Class<?> getAfterClass(){ for(Class<?> c:beanList){ if(After.class.isAssignableFrom(c)){ return c; } } return null; } /** * getInnerClass: 获取inner方法(在真正方法执行时,转而执行的方法) * @return * Class<?> 返回类型 */ public static Class<?> getInnerClass(){ for(Class<?> c:beanList){ if(InterTheMethod.class.isAssignableFrom(c)){ return c; } } return null; } }
真正的动态代理类,实现java自带的InvocationHandler接口:
package InvolvedMode.proxy.dynamicProxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; /** * Dproxy:动态代理类 * * @author xuejupo [email protected] create in 2015-12-23 下午2:09:16 * */ class Dproxy implements InvocationHandler { // 要代理的对象 private Object obj = null; // 我要代理谁 public Dproxy(Object _obj) { this.obj = _obj; } // 调用被代理的方法 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("---------------进入代理类了:"); Object result = null; this.beforeMethod(); // 其次,查找DynamicXmlBean中的inner类,如果存在,就不执行该方法 Class<?> inner = DynamicXmlBean.getInnerClass(); if (inner == null) { result = method.invoke(this.obj, args); } else { result = this.inner(inner, method, args); } this.afterMethod(); return result; } /** * beforeMethod: 代码执行前的方法 void 返回类型 */ private void beforeMethod() { // 首先,查找DynamicXmlBean中的before类(模拟aop中查找xml中的MyBeforeAdvice接口) Class<?> before = DynamicXmlBean.getBeforeClass(); if (before != null) { this.before(before); } } /** * afterMethod: 代码执行完成后的方法(如果结果不依赖此,可以改成线程的方式) * void 返回类型 */ private void afterMethod() { // 方法执行完成后,查找DynamicXmlBean中的after类(模拟aop中查找xml中的MyAfterAdvice接口) Class<?> after = DynamicXmlBean.getAfterClass(); if (after != null) { this.after(after); } } /** * before: 代码执行前方法 * * @param c * void 返回类型 */ private void before(Class<?> c) { try { Before b = (Before) c.newInstance(); b.beforTheMonth(); } catch (InstantiationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * inner: 代码执行转换方法 * * @param c * void 返回类型 */ private Object inner(Class<?> c, Method method, Object[] args) { Object result = null; try { InterTheMethod b = (InterTheMethod) c.newInstance(); result = b.theOtherMethod(this.obj, method, args); } catch (InstantiationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } return result; } /** * after: 代码执行后的方法 * * @param c * void 返回类型 */ private void after(Class<?> c) { try { After b = (After)c.newInstance(); b.afterTheMonth(); } catch (InstantiationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
可以看到,动态代理里面没有针对特定产品的代码,即动态代理是对产品解耦的。无论你需要多少代理,一个动态代理就够了,你要做的就是多配置几个xml参数和多实现几个Before或者After接口,简单粗暴。
测试类:
package InvolvedMode.proxy.dynamicProxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; /** * AOP例子:http://honda418.iteye.com/blog/339468 * http://www.blogjava.net/javadragon/archive/2006/12/03/85115.html * DynamicProxyTest: 动态代理测试类 * * @author xuejupo [email protected] * * create in 2015-12-23 上午10:25:03 */ public class DynamicProxyTest { public static void main(String[] args) { IsubJect sub = new MySubject(); InvocationHandler handler = new Dproxy(sub); // 创建代理对象 IsubJect ob = (IsubJect) Proxy.newProxyInstance( sub.getClass().getClassLoader(), sub.getClass().getInterfaces(), handler); ob.request("youku.com"); ob.request("youku.com.cn"); } }
结果:
---------------进入代理类了: 动态代理before方法:做好上网前准备----- 动态代理inner方法:执行该方法就不一定执行原来的方法了,在这个方法里判断: 幸好我用代理,发现你小子看优酷了!禁止访问!!! 动态代理after方法:上网完成记得结账----- ---------------进入代理类了: 动态代理before方法:做好上网前准备----- 动态代理inner方法:执行该方法就不一定执行原来的方法了,在这个方法里判断: 原来不是看优酷呀,放过你了,继续看吧: 您打开了网址:youku.com.cn 动态代理after方法:上网完成记得结账-----