Spring的MethodBeforeAdvice实现原理探究&&实现一个简单的Advice

本文深入探讨了Spring AOP的实现原理,包括如何利用JDK动态代理创建代理类、反编译$Proxy0类的过程及如何实现自定义的通知(Advice)。通过具体的代码示例,展示了如何配置和使用Spring AOP。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、类结构

MethodBeforeAdvice整个过程大致就是使用的jdk提供的动态代理的接口InvocationHandler和Proxy

所以首先在这讨论下动态代理在这里是如何使用的

1、获得动态代理动态生成的类($Proxy0.class)

参考博文:http://sin90lzc.iteye.com/blog/1037448

[java]  view plain copy print ?
  1. Field field = System.class.getDeclaredField("props");      
  2. field.setAccessible(true);      
  3. Properties props = (Properties) field.get(null);      
  4. props.put("sun.misc.ProxyGenerator.saveGeneratedFiles""true");    


2、反编译$Proxy0.class

Frontend 2.0 plus(cracked)下载(download)

反编译得到的java文件,查看生成的源代码,


[java]  view plain copy print ?
  1. //该类为动态代理Proxy生成的,最后getBean也是获取的该类的对象,  
  2. //但是无法在编译阶段访问,所以只能够转成接口类型或父类型访问相关的方法  
  3. //这里实现Seller接口是Spring配置文件中指明的,实现的SpringProxy接口是Marker Interface,主要就是标志这是一个Spring生成的代理类  
  4. //然后实现Advised接口,可以通过Advised接口获取代理配置信息  
  5. public final class $Proxy0 extends Proxy   
  6.   
  7. implements Seller, SpringProxy, Advised  
  8.   
  9. {    
  10.   
  11. public $Proxy0(InvocationHandler invocationhandler)//构造函数,构建的时候传递一个invocationHandler处理后续的方法调用     
  12.   
  13. {      
  14.   
  15.     super(invocationhandler);    //此处的invocationHandler实际为JdkDynamicAopProxy的实例  
  16.   
  17. }  
  18.   
  19. .....  
  20. //拿两个比较典型的看看该类是如何工作的  
  21.  //该方法使用invocationHandler调用m2  
  22.   
  23. //m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);  
  24.   
  25. public final String toString()   
  26.   
  27.     {  
  28.   
  29.         try  
  30.   
  31.         {  
  32.   
  33.             return (String)super.h.invoke(this, m2, null);  
  34.   
  35.         }  
  36.   
  37.         catch(Error _ex) { }  
  38.   
  39.         catch(Throwable throwable)  
  40.   
  41.         {  
  42.   
  43.             throw new UndeclaredThrowableException(throwable);  
  44.   
  45.         }  
  46.   
  47.      }  
  48.   
  49.   
  50.   
  51. //添加通知的方法  
  52.   
  53.   
  54.   
  55.         //    m18 = Class.forName("org.springframework.aop.framework.Advised").getMethod("addAdvice", new Class[] {  
  56.   
  57.         //        Integer.TYPE, Class.forName("org.aopalliance.aop.Advice")  
  58.   
  59.          //   });  
  60.   
  61.     public final void addAdvice(int i, Advice advice)  
  62.   
  63.         throws AopConfigException  
  64.   
  65.     {  
  66.   
  67.         try  
  68.   
  69.         {  
  70.   
  71.             super.h.invoke(this, m18, new Object[] {  
  72.   
  73.                 Integer.valueOf(i), advice  
  74.   
  75.             });  
  76.   
  77.             return;  
  78.   
  79.         }  
  80.   
  81.         catch(Error _ex) { }  
  82.   
  83.         catch(Throwable throwable)  
  84.   
  85.         {  
  86.   
  87.             throw new UndeclaredThrowableException(throwable);  
  88.   
  89.         }  
  90.   
  91.     }  
  92.   
  93.   
  94.   
  95. //需要代理的目标类的方法  
  96.   
  97. //m3 = Class.forName("com.iss.spring.advice.Seller").getMethod("sell", new Class[] {  
  98.   
  99.     //            Class.forName("java.lang.String")  
  100.   
  101.     //        });  
  102.   
  103.     public final void sell(String s)  
  104.   
  105.     {  
  106.   
  107.         try  
  108.   
  109.         {  
  110.   
  111.             super.h.invoke(this, m3, new Object[] {  
  112.   
  113.                 s  
  114.   
  115.             });  
  116.   
  117.             return;  
  118.   
  119.         }  
  120.   
  121.         catch(Error _ex) { }  
  122.   
  123.         catch(Throwable throwable)  
  124.   
  125.         {  
  126.   
  127.             throw new UndeclaredThrowableException(throwable);  
  128.   
  129.         }  
  130.   
  131.     }  
  132.   
  133.   
  134.   
  135.   
  136.   
  137. ......}  

3.调用过程

通过上述源代码以及类图可以发现生成的$Proxy0这个类,也就是最后返回给用户的类对象,

给用户提供封装好的使用接口中这些方法直接调用,而不必自己去调用InvocationHandler

可以将该对象的引用强转为target接口类型,调用目标方法

这里的过程是调用了$Proxy0的sell方法,$Proxy0通过查找Seller接口找到方法,获取Method对象

然后传递给invocationHandler进行调用,

[java]  view plain copy print ?
  1. Seller seller = (Seller)context.getBean("proxy");  
  2. seller.sell("Genke");  


也可以把该对象的引用强转为Advised来获取代理配置信息

这里的过程是调用了$Proxy0的getTargetClass方法,$Proxy0通过查找Advised接口找到方法,获取Method对象

然后传递给invocationHandler进行调用,


[java]  view plain copy print ?
  1. Advised advised = (Advised) context.getBean("proxy");  
  2. System.out.println(advised.getTargetClass());  


InvocationHandler判断是否是Advised接口的,如果是就直接执行而不去管有通知

如果不是Advised接口的方法,则先获取通知链或者拦截器,执行完所有拦截器后将会执行目标对象的该方法,

注明:这里讨论的是MethodBeforeAdvice,几种Advice的执行顺序与目标对象的方法执行顺序见Spring的四种Advice源码探究


二、实现一个简单的通知(Advice)

Advice.java
[java]  view plain copy print ?
  1. package com.iss.spring.myadvice;  
  2.   
  3. import java.lang.reflect.Method;  
  4. /** 
  5.  * 提供一个通知的接口 
  6.  * @author Administrator 
  7.  * 
  8.  */  
  9. public interface Advice  
  10. {  
  11.     public void before(Object target,Method method,Object[] args);  
  12. }  


Bean.java
[java]  view plain copy print ?
  1. package com.iss.spring.myadvice;  
  2. /** 
  3.  * 维护bean信息 
  4.  * @author Administrator 
  5.  * 
  6.  */  
  7. public class Bean  
  8. {  
  9.     private String id;  
  10.     private String clazz;  
  11.         /*..........getter and setter..........*/  
  12. }  

Configuration.java
[java]  view plain copy print ?
  1. package com.iss.spring.myadvice;  
  2.   
  3. import java.util.List;  
  4.   
  5. /** 
  6.  * 该接口用来维护相关的配置文件信息 
  7.  * @author Administrator 
  8.  * 
  9.  */  
  10. public interface Configuration  
  11. {  
  12.     public List<String> getProxyInterfaces();  
  13.     public List<String> getInterceptorNames();  
  14.     public String getTarget();  
  15. }  

ConfigurationImpl.java
[java]  view plain copy print ?
  1. package com.iss.spring.myadvice;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.List;  
  5.   
  6. /** 
  7.  * 实现Configuration,维护配置信息 
  8.  * 继承Bean,维护bean本身信息 
  9.  * @author Administrator 
  10.  *  
  11.  */  
  12. public class ConfigurationImpl extends Bean implements Configuration  
  13. {  
  14.     private List<String> proxyInterfaces;  
  15.     private List<String> interceptorNames;  
  16.     private String target;  
  17.   
  18.     private List<Class> Interfaces;  
  19.     private List<Object> Interceptor;  
  20.     private Object targetSource;  
  21.       
  22.     public ConfigurationImpl()  
  23.     {  
  24.         proxyInterfaces = new ArrayList<String>();  
  25.         interceptorNames = new ArrayList<String>();  
  26.         Interfaces = new ArrayList<Class>();  
  27.         Interceptor = new ArrayList<Object>();  
  28.     }  
  29.     /*...getter and setter...*/  
  30. }  

MyHandler.java
[java]  view plain copy print ?
  1. package com.iss.spring.myadvice;  
  2.   
  3. import java.lang.reflect.InvocationHandler;  
  4. import java.lang.reflect.Method;  
  5.   
  6. /** 
  7.  * 代理类,执行$Proxy0传递过来的方法 
  8.  * @author Administrator 
  9.  * 
  10.  */  
  11. public class MyHandler implements InvocationHandler  
  12. {  
  13.     private Configuration config;  
  14.   
  15.     public MyHandler(Configuration config)  
  16.     {  
  17.         this.config = config;  
  18.     }  
  19.   
  20.     @Override  
  21.     public Object invoke(Object proxy, Method method, Object[] args)  
  22.             throws Throwable  
  23.     {  
  24.         if (config == null)  
  25.             return null;  
  26.   
  27.         if(method.getDeclaringClass()==Configuration.class//如果是调用获取配置信息则直接执行,忽略拦截器  
  28.         {  
  29.             return method.invoke(config,args);  
  30.         }  
  31.           
  32.         if (config instanceof ConfigurationImpl)  
  33.         {  
  34.             for (Object interceptor : ((ConfigurationImpl) config)  //需要执行所有的拦截器或通知  
  35.                     .getInterceptor())  
  36.             {  
  37.                 if (interceptor instanceof Advice) //如果该拦截器类实现了Advice接口则调用该拦截器的before方法  
  38.                 {  
  39.                     ((Advice) interceptor).before(  
  40.                             ((ConfigurationImpl) config).getTargetSource(),  
  41.                             method, args);  
  42.                 }  
  43.             }  
  44.         }  
  45.   
  46.         return method.invoke(((ConfigurationImpl)config).getTargetSource(), args);//执行目标对象的目标方法  
  47.   
  48.     }  
  49.   
  50. }  

XmlContext.java

[java]  view plain copy print ?
  1. package com.iss.spring.myadvice;  
  2.   
  3. import java.io.IOException;  
  4. import java.lang.reflect.Proxy;  
  5. import java.util.ArrayList;  
  6. import java.util.List;  
  7.   
  8. import org.dom4j.Document;  
  9. import org.dom4j.DocumentException;  
  10. import org.dom4j.Element;  
  11. import org.dom4j.io.SAXReader;  
  12. import org.springframework.core.io.ClassPathResource;  
  13.   
  14. /** 
  15.  * 使用Dom4j解析Xml,保存至Configuration中 
  16.  *  
  17.  * @author Administrator 
  18.  *  
  19.  */  
  20. public class XmlContext  
  21. {  
  22.     private static final String SPRING_CONFIGURATION_IMPL_CLASS = "com.iss.spring.myadvice.ConfigurationImpl";  
  23.     private String path;  
  24.     private Document doc;  
  25.     private List<Bean> beans;  
  26.   
  27.     public XmlContext(String path) throws DocumentException, IOException  
  28.     {  
  29.         this.path = path;  
  30.         try  
  31.         {  
  32.             doc = new SAXReader().read(path);  
  33.         }  
  34.         catch (Exception e)  
  35.         {  
  36.             doc = new SAXReader().read(new ClassPathResource(path).getFile());// 在path上未找到则在classpath上找  
  37.         }  
  38.         beans = new ArrayList<Bean>();  
  39.         parse();  
  40.     }  
  41.   
  42.     /** 
  43.      * 在xml中配置的id属性,通过该id找到对应的Bean 
  44.      * @param name bean id 
  45.      * @return 
  46.      */  
  47.     private Bean getBeanById(String name)  
  48.     {  
  49.         for (Bean bean : beans)  
  50.         {  
  51.             if (bean.getId().equals(name))  
  52.             {  
  53.                 return bean;  
  54.             }  
  55.         }  
  56.         return null;  
  57.     }  
  58.     /** 
  59.      * 提供给用户调用的getBean,如果是配置class为代理类则返回$Proxy0代理对象 
  60.      * 否则返回bean对象本身 
  61.      * @param name bean id 
  62.      * @return 
  63.      * @throws ClassNotFoundException 
  64.      * @throws InstantiationException 
  65.      * @throws IllegalAccessException 
  66.      */  
  67.     public Object getBean(String name) throws ClassNotFoundException,  
  68.             InstantiationException, IllegalAccessException  
  69.     {  
  70.         Bean bean = getBeanById(name);  
  71.         if (bean == null)  
  72.             System.out.println(name);  
  73.         if (bean.getClazz().equals(SPRING_CONFIGURATION_IMPL_CLASS))  
  74.         {  
  75.             ConfigurationImpl config = (ConfigurationImpl) bean;  
  76.             List<Class> interfaces = new ArrayList<Class>();  
  77.             for (String i : config.getProxyInterfaces())  
  78.             {  
  79.                 interfaces.add(Class.forName(i));  
  80.             }  
  81.             interfaces.add(Configuration.class);  
  82.             config.setInterfaces(interfaces); // 设置所有接口  
  83.   
  84.             List<Object> interceptors = new ArrayList<Object>();  
  85.             for (String i : config.getInterceptorNames())  
  86.             {  
  87.                 interceptors.add(getBean(i));  
  88.             }  
  89.             config.setInterceptor(interceptors); // 设置所有拦截器  
  90.   
  91.             config.setTargetSource(getBean(config.getTarget())); // 设置目标对象  
  92.   
  93.             return Proxy.newProxyInstance(config.getClass().getClassLoader(),  
  94.                     interfaces.toArray(new Class[0]), new MyHandler(config)); // 返回生成的中间类$Proxy0对象,调用函数会经MyHandler处理  
  95.         }  
  96.         else  
  97.         {  
  98.             // 假设就是一个简单的没有属性的bean  
  99.             return Class.forName(getBeanById(name).getClazz()).newInstance();  
  100.         }  
  101.   
  102.     }  
  103.   
  104.     /** 
  105.      * 开始解析,获取xml所有配置信息 
  106.      */  
  107.     private void parse()  
  108.     {  
  109.         if (doc == null)  
  110.             return;  
  111.         Element root = doc.getRootElement();  
  112.         for (Object obj : root.elements())  
  113.         {  
  114.             Element element = (Element) obj;  
  115.             if (element.getName().equals("bean"))  
  116.                 parseBean(element);  
  117.         }  
  118.     }  
  119.     /** 
  120.      * 解析单个bean节点 
  121.      * @param bean 
  122.      */  
  123.     private void parseBean(Element bean)  
  124.     {  
  125.         Bean obj = null;  
  126.         if (bean.attributeValue("class")  
  127.                 .equals(SPRING_CONFIGURATION_IMPL_CLASS))  
  128.         {  
  129.             obj = new ConfigurationImpl();  
  130.             ConfigurationImpl temp = (ConfigurationImpl) obj;  
  131.   
  132.             for (Object property : bean.elements("property"))  
  133.             {  
  134.                 parseProperty((Element) property, temp);  
  135.             }  
  136.   
  137.         }  
  138.         else  
  139.         {  
  140.             obj = new Bean();  
  141.         }  
  142.         obj.setId(bean.attributeValue("id"));  
  143.         obj.setClazz(bean.attributeValue("class"));  
  144.         beans.add(obj);  
  145.     }  
  146.     /** 
  147.      * 解析单个property节点 
  148.      * @param property 
  149.      * @param config 
  150.      */  
  151.     private void parseProperty(Element property, ConfigurationImpl config)  
  152.     {  
  153.         if (property.attributeValue("name").equals("proxyInterfaces"))  
  154.         {  
  155.             for (Object value : property.element("list").elements("value"))  
  156.             {  
  157.                 Element v = (Element) value;  
  158.                 config.getProxyInterfaces().add(v.getText());  
  159.             }  
  160.         }  
  161.         else if (property.attributeValue("name").equals("interceptorNames"))  
  162.         {  
  163.             for (Object value : property.element("list").elements("value"))  
  164.             {  
  165.                 Element v = (Element) value;  
  166.                 config.getInterceptorNames().add(v.getText());  
  167.             }  
  168.         }  
  169.         else if (property.attributeValue("name").equals("target"))  
  170.         {  
  171.             /* 
  172.              * 先找local,如果local为null,则找bean 
  173.              */  
  174.             String bean = property.element("ref").attributeValue("local") == null ? property  
  175.                     .element("ref").attributeValue("bean") : property.element(  
  176.                     "ref").attributeValue("local");  
  177.             config.setTarget(bean);  
  178.         }  
  179.     }  
  180.   
  181. }  

测试实例:

beans.xml
[html]  view plain copy print ?
  1. <bean id="BreadSeller" class="com.iss.spring.advice.BreadSeller"></bean>  
  2. <bean id="MyAdvice" class="com.iss.spring.advice.MyAdvice"></bean>  
  3. <bean id="proxy" class="com.iss.spring.myadvice.ConfigurationImpl">  
  4.     <property name="proxyInterfaces">  
  5.         <list>  
  6.             <value>com.iss.spring.advice.Seller</value>  
  7.         </list>  
  8.     </property>  
  9.     <property name="interceptorNames">  
  10.         <list>  
  11.             <value>MyAdvice</value>  
  12.         </list>  
  13.     </property>  
  14.     <property name="target">  
  15.         <ref local="BreadSeller" />  
  16.     </property>  
  17. </bean>  

MyAdvice.java

[java]  view plain copy print ?
  1. public class MyAdvice implements Advice  
  2. {  
  3.   
  4.     @Override  
  5.     public void before(Object target, Method method, Object[] args)  
  6.     {  
  7.         System.out.println(target+" is say hello to "+args[0] + " and then "+method);  
  8.     }  
  9.   
  10. }  


main
[java]  view plain copy print ?
  1. public static void main(String[] args)  
  2. {  
  3.     try  
  4.     {  
  5.         XmlContext context = new XmlContext("beans.xml");  
  6.         Seller seller = (Seller)context.getBean("proxy");//Seller接口就定义个sell(String)方法  
  7.         seller.sell("Genke");  
  8.           
  9.         Configuration config = (Configuration)context.getBean("proxy");  
  10.         System.out.println(config.getTarget());  
  11.           
  12.   
  13.     }  
  14.     catch (Exception e)  
  15.     {  
  16.         e.printStackTrace();  
  17.     }  
  18. }  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值