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;
- }
- @Override
- public 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
- {
- // 假设就是一个简单的没有属性的bean
- return 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
- {
- @Override
- public 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();
- }
- }