Spring的MethodBeforeAdvice实现原理探究&&实现一个简单的Advice
来源:互联网 发布:水平位移监测数据分析 编辑:程序博客网 时间:2024/05/16 14:58
一、类结构
MethodBeforeAdvice整个过程大致就是使用的jdk提供的动态代理的接口InvocationHandler和Proxy
所以首先在这讨论下动态代理在这里是如何使用的
1、获得动态代理动态生成的类($Proxy0.class)
参考博文:http://sin90lzc.iteye.com/blog/1037448
Field field = System.class.getDeclaredField("props"); field.setAccessible(true); Properties props = (Properties) field.get(null); props.put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
2、反编译$Proxy0.class
Frontend 2.0 plus(cracked)下载(download)
反编译得到的java文件,查看生成的源代码,
//该类为动态代理Proxy生成的,最后getBean也是获取的该类的对象,//但是无法在编译阶段访问,所以只能够转成接口类型或父类型访问相关的方法//这里实现Seller接口是Spring配置文件中指明的,实现的SpringProxy接口是Marker Interface,主要就是标志这是一个Spring生成的代理类//然后实现Advised接口,可以通过Advised接口获取代理配置信息public final class $Proxy0 extends Proxy implements Seller, SpringProxy, Advised{ public $Proxy0(InvocationHandler invocationhandler)//构造函数,构建的时候传递一个invocationHandler处理后续的方法调用 { super(invocationhandler); //此处的invocationHandler实际为JdkDynamicAopProxy的实例}.....//拿两个比较典型的看看该类是如何工作的 //该方法使用invocationHandler调用m2//m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);public final String toString() { try { return (String)super.h.invoke(this, m2, null); } catch(Error _ex) { } catch(Throwable throwable) { throw new UndeclaredThrowableException(throwable); } }//添加通知的方法 // m18 = Class.forName("org.springframework.aop.framework.Advised").getMethod("addAdvice", new Class[] { // Integer.TYPE, Class.forName("org.aopalliance.aop.Advice") // }); public final void addAdvice(int i, Advice advice) throws AopConfigException { try { super.h.invoke(this, m18, new Object[] { Integer.valueOf(i), advice }); return; } catch(Error _ex) { } catch(Throwable throwable) { throw new UndeclaredThrowableException(throwable); } }//需要代理的目标类的方法//m3 = Class.forName("com.iss.spring.advice.Seller").getMethod("sell", new Class[] { // Class.forName("java.lang.String") // }); public final void sell(String s) { try { super.h.invoke(this, m3, new Object[] { s }); return; } catch(Error _ex) { } catch(Throwable throwable) { throw new UndeclaredThrowableException(throwable); } }......}
3.调用过程
通过上述源代码以及类图可以发现生成的$Proxy0这个类,也就是最后返回给用户的类对象,
给用户提供封装好的使用接口中这些方法直接调用,而不必自己去调用InvocationHandler
可以将该对象的引用强转为target接口类型,调用目标方法
这里的过程是调用了$Proxy0的sell方法,$Proxy0通过查找Seller接口找到方法,获取Method对象
然后传递给invocationHandler进行调用,
Seller seller = (Seller)context.getBean("proxy");seller.sell("Genke");
也可以把该对象的引用强转为Advised来获取代理配置信息
这里的过程是调用了$Proxy0的getTargetClass方法,$Proxy0通过查找Advised接口找到方法,获取Method对象
然后传递给invocationHandler进行调用,
Advised advised = (Advised) context.getBean("proxy");System.out.println(advised.getTargetClass());
InvocationHandler判断是否是Advised接口的,如果是就直接执行而不去管有通知
如果不是Advised接口的方法,则先获取通知链或者拦截器,执行完所有拦截器后将会执行目标对象的该方法,
注明:这里讨论的是MethodBeforeAdvice,几种Advice的执行顺序与目标对象的方法执行顺序见Spring的四种Advice源码探究
二、实现一个简单的通知(Advice)
package com.iss.spring.myadvice;import java.lang.reflect.Method;/** * 提供一个通知的接口 * @author Administrator * */public interface Advice{public void before(Object target,Method method,Object[] args);}
Bean.java
package com.iss.spring.myadvice;/** * 维护bean信息 * @author Administrator * */public class Bean{private String id;private String clazz; /*..........getter and setter..........*/}
Configuration.java
package com.iss.spring.myadvice;import java.util.List;/** * 该接口用来维护相关的配置文件信息 * @author Administrator * */public interface Configuration{public List<String> getProxyInterfaces();public List<String> getInterceptorNames();public String getTarget();}
ConfigurationImpl.java
package com.iss.spring.myadvice;import java.util.ArrayList;import java.util.List;/** * 实现Configuration,维护配置信息 * 继承Bean,维护bean本身信息 * @author Administrator * */public class ConfigurationImpl extends Bean implements Configuration{private List<String> proxyInterfaces;private List<String> interceptorNames;private String target;private List<Class> Interfaces;private List<Object> Interceptor;private Object targetSource;public ConfigurationImpl(){proxyInterfaces = new ArrayList<String>();interceptorNames = new ArrayList<String>();Interfaces = new ArrayList<Class>();Interceptor = new ArrayList<Object>();}/*...getter and setter...*/}
MyHandler.java
package com.iss.spring.myadvice;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;/** * 代理类,执行$Proxy0传递过来的方法 * @author Administrator * */public class MyHandler implements InvocationHandler{private Configuration config;public MyHandler(Configuration config){this.config = config;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable{if (config == null)return null;if(method.getDeclaringClass()==Configuration.class) //如果是调用获取配置信息则直接执行,忽略拦截器{return method.invoke(config,args);}if (config instanceof ConfigurationImpl){for (Object interceptor : ((ConfigurationImpl) config) //需要执行所有的拦截器或通知.getInterceptor()){if (interceptor instanceof Advice) //如果该拦截器类实现了Advice接口则调用该拦截器的before方法{((Advice) interceptor).before(((ConfigurationImpl) config).getTargetSource(),method, args);}}}return method.invoke(((ConfigurationImpl)config).getTargetSource(), args);//执行目标对象的目标方法}}
XmlContext.java
package com.iss.spring.myadvice;import java.io.IOException;import java.lang.reflect.Proxy;import java.util.ArrayList;import java.util.List;import org.dom4j.Document;import org.dom4j.DocumentException;import org.dom4j.Element;import org.dom4j.io.SAXReader;import org.springframework.core.io.ClassPathResource;/** * 使用Dom4j解析Xml,保存至Configuration中 * * @author Administrator * */public class XmlContext{private static final String SPRING_CONFIGURATION_IMPL_CLASS = "com.iss.spring.myadvice.ConfigurationImpl";private String path;private Document doc;private List<Bean> beans;public XmlContext(String path) throws DocumentException, IOException{this.path = path;try{doc = new SAXReader().read(path);}catch (Exception e){doc = new SAXReader().read(new ClassPathResource(path).getFile());// 在path上未找到则在classpath上找}beans = new ArrayList<Bean>();parse();}/** * 在xml中配置的id属性,通过该id找到对应的Bean * @param name bean id * @return */private Bean getBeanById(String name){for (Bean bean : beans){if (bean.getId().equals(name)){return bean;}}return null;}/** * 提供给用户调用的getBean,如果是配置class为代理类则返回$Proxy0代理对象 * 否则返回bean对象本身 * @param name bean id * @return * @throws ClassNotFoundException * @throws InstantiationException * @throws IllegalAccessException */public Object getBean(String name) throws ClassNotFoundException,InstantiationException, IllegalAccessException{Bean bean = getBeanById(name);if (bean == null)System.out.println(name);if (bean.getClazz().equals(SPRING_CONFIGURATION_IMPL_CLASS)){ConfigurationImpl config = (ConfigurationImpl) bean;List<Class> interfaces = new ArrayList<Class>();for (String i : config.getProxyInterfaces()){interfaces.add(Class.forName(i));}interfaces.add(Configuration.class);config.setInterfaces(interfaces); // 设置所有接口List<Object> interceptors = new ArrayList<Object>();for (String i : config.getInterceptorNames()){interceptors.add(getBean(i));}config.setInterceptor(interceptors); // 设置所有拦截器config.setTargetSource(getBean(config.getTarget())); // 设置目标对象return Proxy.newProxyInstance(config.getClass().getClassLoader(),interfaces.toArray(new Class[0]), new MyHandler(config)); // 返回生成的中间类$Proxy0对象,调用函数会经MyHandler处理}else{// 假设就是一个简单的没有属性的beanreturn Class.forName(getBeanById(name).getClazz()).newInstance();}}/** * 开始解析,获取xml所有配置信息 */private void parse(){if (doc == null)return;Element root = doc.getRootElement();for (Object obj : root.elements()){Element element = (Element) obj;if (element.getName().equals("bean"))parseBean(element);}}/** * 解析单个bean节点 * @param bean */private void parseBean(Element bean){Bean obj = null;if (bean.attributeValue("class").equals(SPRING_CONFIGURATION_IMPL_CLASS)){obj = new ConfigurationImpl();ConfigurationImpl temp = (ConfigurationImpl) obj;for (Object property : bean.elements("property")){parseProperty((Element) property, temp);}}else{obj = new Bean();}obj.setId(bean.attributeValue("id"));obj.setClazz(bean.attributeValue("class"));beans.add(obj);}/** * 解析单个property节点 * @param property * @param config */private void parseProperty(Element property, ConfigurationImpl config){if (property.attributeValue("name").equals("proxyInterfaces")){for (Object value : property.element("list").elements("value")){Element v = (Element) value;config.getProxyInterfaces().add(v.getText());}}else if (property.attributeValue("name").equals("interceptorNames")){for (Object value : property.element("list").elements("value")){Element v = (Element) value;config.getInterceptorNames().add(v.getText());}}else if (property.attributeValue("name").equals("target")){/* * 先找local,如果local为null,则找bean */String bean = property.element("ref").attributeValue("local") == null ? property.element("ref").attributeValue("bean") : property.element("ref").attributeValue("local");config.setTarget(bean);}}}
测试实例:
beans.xml
<bean id="BreadSeller" class="com.iss.spring.advice.BreadSeller"></bean><bean id="MyAdvice" class="com.iss.spring.advice.MyAdvice"></bean><bean id="proxy" class="com.iss.spring.myadvice.ConfigurationImpl"><property name="proxyInterfaces"><list><value>com.iss.spring.advice.Seller</value></list></property><property name="interceptorNames"><list><value>MyAdvice</value></list></property><property name="target"><ref local="BreadSeller" /></property></bean>
MyAdvice.java
public class MyAdvice implements Advice{@Overridepublic void before(Object target, Method method, Object[] args){System.out.println(target+" is say hello to "+args[0] + " and then "+method);}}
main
public static void main(String[] args){try{XmlContext context = new XmlContext("beans.xml");Seller seller = (Seller)context.getBean("proxy");//Seller接口就定义个sell(String)方法seller.sell("Genke");Configuration config = (Configuration)context.getBean("proxy");System.out.println(config.getTarget());}catch (Exception e){e.printStackTrace();}}
- Spring的MethodBeforeAdvice实现原理探究&&实现一个简单的Advice
- Spring的MethodBeforeAdvice实现原理探究&&实现一个简单的Advice
- spring-aop之MethodBeforeAdvice实现
- Spring的四种Advice源码探究
- 探究HashMap的实现原理
- Spring AOP Advice接口-MethodBeforeAdvice+AfterReturningAdvice
- ARC下实现一个NSSting类的简单探究
- Introduction的Advice的实现
- spring aop advice注解实现的几种方式
- Spring Cache的原理和简单实现
- 自己简单实现Spring的IOC原理
- 一个简单的Spring实现的例子
- 深入探究MODVERSIONS的实现原理
- 深入探究HashMap的实现原理
- 实现MethodBeforeAdvice接口问题
- hibernate+spring的一个简单分页实现
- hibernate+spring的一个简单分页实现
- hibernate+spring的一个简单分页实现
- 快速配置你的VIM_for_CPP_IDE
- 第五章 - 图像形态学 - 基于图像金字塔的图像分割(cvPyrSegmentation)
- AJAX检验用户是否存在
- Yii实现伪静态方法
- 如何在命令行下运行PHP脚本[带参数]
- Spring的MethodBeforeAdvice实现原理探究&&实现一个简单的Advice
- linux内核中的一些有趣的宏
- poj/pku 2594 (最小路径覆盖)
- HTML:框架
- 旋转卡壳---凸多边形间最小距离
- Silverlight 4 报表打印的简单实现
- java.util.ResourceBundle使用详解
- ExtJs中dataReader的使用方法
- 精通SQL-结构化查询语言详解之聚合分析