一、类结构
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();
}
}