spring4.0源码分析━━━(AOP实现)

  • AOP的概念

AOP为Aspect Oriented Programming的缩写,意为:面向切面编程(也叫面向方面)。这就让一些问题很简单化了,例如:开始我们实现了一些逻辑并上线了,现在客户又来了一个新的需求。要在每次交易之前统计下,或者记录下他们的交易简单资料。而你发现你其他模块可能正好有这部分的功能。那AOP就可以用得上了,使用AOP就可以在不修改源代码的情况下新增这些功能。就是在交易前这个切面,新装你的一些功能。这有点像拦截器和Filter。其实都是一个原理。前面说了解析xml和Bean的实例化。

而AOP的实现的话都是在我前面两篇spring3.0源码分析的基础上实现。其实AOP算是对自己本身IOC的实例,你学好弄懂AOP。基本IOC的也是懂了。但是这里的AOP也用到了一些新的东西,像Aspect,还有JVM的反射和动态代理。还有CGLIB这里也是有用到的。但是默认的实现是用JVM的动态代理。

  • AOP的实例

AOP用起来还是很简单的。就把xml配置好就算完工了。有Advisor和aspect两种方式来完成。如果是用Advisor的话需要实现AfterReturningAdvice,MethodBeforeAdvice,ThrowsAdvice等接口。而如果用aspect的话则不用继承或者实现其他的类,一个普通的类即可。

Java代码

  1. public class LogAop {
  2. private final static Log log = LogFactory.getLog(LogAop.class);
  3. public void addLog(){
  4. log.info("add log ========================");
  5. }
  6. }

Java代码

  1. public class BeforeAdvisor implements MethodBeforeAdvice {
  2. private final static Log log =LogFactory.getLog(BeforeAdvisor.class);
  3. private int testSEL;
  4. @Override
  5. public void before(Method method, Object[] args, Object target)
  6. throws Throwable {
  7. log.info("in before advice and method="+method.getName()+ " args "+args.length);
  8. }
  9. ..........

Java代码

  1. public class AfterAdvisor implements AfterReturningAdvice {
  2. private final static Log log =LogFactory.getLog(AfterAdvisor.class);
  3. ........
  4. @Override
  5. public void afterReturning(Object returnValue, Method method,
  6. Object[] args, Object target) throws Throwable {
  7. // TODO Auto-generated method stub
  8. log.info("test sel the testSEL "+testSEL);
  9. log.info("after return advice");
  10. }
  11. }

而配置文件如下:

Java代码

  1. <bean id="LogAop" class="com.zzx.study.aop.LogAop" />
  2. <bean id="beforeAdvisor" class="com.zzx.study.aop.BeforeAdvisor" >
  3. <property name="testSEL" value="11"/>
  4. </bean>
  5. <bean id="beforeAdvisor2" class="com.zzx.study.aop.BeforeAdvisor2" >
  6. <property name="testSEL" value="23"/>
  7. </bean>
  8. <bean id="afterAdvisor" class="com.zzx.study.aop.AfterAdvisor" >
  9. <property name="testSEL" value="11"/>
  10. </bean>
  11. <aop:config>
  12. <aop:pointcut expression="execution(* com.zzx.study.di.BankSecurityDaoImpl.add(..))" id="target" />
  13. <aop:pointcut expression="execution(* com.zzx.study.di.Ban*.ad*(..))" id="nonePointCutTest" />
  14. <aop:advisor id="bid" pointcut-ref="target" advice-ref="beforeAdvisor" />
  15. <aop:advisor id="noAdvisor" pointcut-ref="nonePointCutTest" advice-ref="beforeAdvisor2" />
  16. <aop:advisor id="aid" pointcut-ref="target" advice-ref="afterAdvisor" />
  17. <aop:aspect ref="LogAop" >
  18. <aop:after method="addLog"  pointcut-ref="target"/>
  19. </aop:aspect>
  20. </aop:config>

向上面实现后则如果执行BankSecurityDaoImpl的add方法前就会执行BeforeAdvisor的before方法,然后执行add方法,最后是LogAop的addLog方法,AfterAdvisor的afterReturning方法。这里还可以让Advisor实现Order定义这些执行的前后。

Java代码  

  1. public class BeforeAdvisor implements MethodBeforeAdvice,Ordered {
  2. ............
  3. @Override
  4. public int getOrder() {
  5. // TODO Auto-generated method stub
  6. return 2;
  7. }
  8. }

这里的有一个getOrder方法,返回int值,越小则越先执行。

  • AOP在spring中的实现

在解析xml的时候说过,如果发现不是bean标签,则会是不同的类来解析。解析aop的为http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler。也就是AopNamespaceHandler类。进去到AopNamespaceHandler类中parse方法,实际是调用其父类的NamespaceHandlerSupport的parse方法。

Java代码

  1. private BeanDefinitionParser findParserForElement(Element element, ParserContext parserContext) {
  2. String localName = parserContext.getDelegate().getLocalName(element);
  3. BeanDefinitionParser parser = this.parsers.get(localName);
  4. if (parser == null) {
  5. parserContext.getReaderContext().fatal(
  6. "Cannot locate BeanDefinitionParser for element [" + localName + "]", element);
  7. }
  8. return parser;
  9. }

这里的parses是在解析xml的时候初始化。

Java代码

  1. public void init() {
  2. // In 2.0 XSD as well as in 2.1 XSD.
  3. registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
  4. registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
  5. registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
  6. // Only in 2.0 XSD: moved to context namespace as of 2.1
  7. registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
  8. }

如果是config标签,则是用ConfigBeanDefinitionParser类了,如果是aspectj-autoproxy标签则是用AspectJAutoProxyBeanDefinitionParser类来解析了。其实这两个的解析最终都是向DefaultListableBeanFactory中注册class为AspectJAwareAdvisorAutoProxyCreator的BeanDefinition。而AspectJAwareAdvisorAutoProxyCreator是有实现BeanPostProcessor、BeanFactoryAware接口的。而实例化一个bean的时候会经过这些BeanPostProcessor的处理。也就是这些BeanPostProcessor最终实现了把目标对象代理掉,而用代理前后spring就会调用到配置的这些Advisor来处理一些业务逻辑了。其中的BeanFactoryAware接口是把BeanFactory注入到AspectJAwareAdvisorAutoProxyCreator这个类中,而最终执行方法前通过这里注入的BeanFactory得到这些Advisor类并调用。

这里讲使用java动态代理的实现。在JdkDynamicAopProxy类中。使用到了责任链的模式,会先得到一个Advisor的list,然后在list中用链的方式执行下去。如果chain不是空的则会到ReflectiveMethodInvocation类中执行processed方法。

Java代码

  1. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  2. MethodInvocation invocation;
  3. Object oldProxy = null;
  4. boolean setProxyContext = false;
  5. TargetSource targetSource = this.advised.targetSource;
  6. Class targetClass = null;
  7. Object target = null;
  8. try {
  9. if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
  10. // The target does not implement the equals(Object) method itself.
  11. return equals(args[0]);
  12. }
  13. if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
  14. // The target does not implement the hashCode() method itself.
  15. return hashCode();
  16. }
  17. if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
  18. method.getDeclaringClass().isAssignableFrom(Advised.class)) {
  19. // Service invocations on ProxyConfig with the proxy config...
  20. return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
  21. }
  22. Object retVal;
  23. if (this.advised.exposeProxy) {
  24. // Make invocation available if necessary.
  25. oldProxy = AopContext.setCurrentProxy(proxy);
  26. setProxyContext = true;
  27. }
  28. // May be null. Get as late as possible to minimize the time we "own" the target,
  29. // in case it comes from a pool.
  30. target = targetSource.getTarget();
  31. if (target != null) {
  32. targetClass = target.getClass();
  33. }
  34. // Get the interception chain for this method.
  35. List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
  36. // Check whether we have any advice. If we don‘t, we can fallback on direct
  37. // reflective invocation of the target, and avoid creating a MethodInvocation.
  38. if (chain.isEmpty()) {
  39. // We can skip creating a MethodInvocation: just invoke the target directly
  40. // Note that the final invoker must be an InvokerInterceptor so we know it does
  41. // nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
  42. retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
  43. }
  44. else {
  45. // We need to create a method invocation...
  46. invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
  47. // Proceed to the joinpoint through the interceptor chain.
  48. retVal = invocation.proceed();
  49. }
  50. // Massage return value if necessary.
  51. if (retVal != null && retVal == target && method.getReturnType().isInstance(proxy) &&
  52. !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
  53. // Special case: it returned "this" and the return type of the method
  54. // is type-compatible. Note that we can‘t help if the target sets
  55. // a reference to itself in another returned object.
  56. retVal = proxy;
  57. }
  58. return retVal;
  59. }
  60. finally {
  61. if (target != null && !targetSource.isStatic()) {
  62. // Must have come from TargetSource.
  63. targetSource.releaseTarget(target);
  64. }
  65. if (setProxyContext) {
  66. // Restore old proxy.
  67. AopContext.setCurrentProxy(oldProxy);
  68. }
  69. }
  70. }

Java代码

  1. public Object proceed() throws Throwable {
  2. //  We start with an index of -1 and increment early.
  3. if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
  4. return invokeJoinpoint();
  5. }
  6. Object interceptorOrInterceptionAdvice =
  7. this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
  8. if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
  9. // Evaluate dynamic method matcher here: static part will already have
  10. // been evaluated and found to match.
  11. InterceptorAndDynamicMethodMatcher dm =
  12. (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
  13. if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
  14. return dm.interceptor.invoke(this);
  15. }
  16. else {
  17. // Dynamic matching failed.
  18. // Skip this interceptor and invoke the next in the chain.
  19. return proceed();
  20. }
  21. }
  22. else {
  23. // It‘s an interceptor, so we just invoke it: The pointcut will have
  24. // been evaluated statically before this object was constructed.
  25. return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
  26. }
  27. }

这里的interceptorOrInterceptionAdvice都是MethodInterceptor类型。因为解析xml的时候,会把<aop:advisor>最终生成class为DefaultBeanFactoryPointcutAdvisor类的BeanDefinition类,而<aop:aspect>会生成class为AspectJPointcutAdvisor的BeanDefinition。又通过DefaultAdvisorAdapterRegistry类把Advisor转换为MethodInterceptor类。

Java代码

  1. public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry, Serializable {
  2. ...........................
  3. public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
  4. List<MethodInterceptor> interceptors = new ArrayList<MethodInterceptor>(3);
  5. Advice advice = advisor.getAdvice();
  6. if (advice instanceof MethodInterceptor) {
  7. interceptors.add((MethodInterceptor) advice);
  8. }
  9. for (AdvisorAdapter adapter : this.adapters) {
  10. if (adapter.supportsAdvice(advice)) {
  11. interceptors.add(adapter.getInterceptor(advisor));
  12. }
  13. }
  14. if (interceptors.isEmpty()) {
  15. throw new UnknownAdviceTypeException(advisor.getAdvice());
  16. }
  17. return interceptors.toArray(new MethodInterceptor[interceptors.size()]);
  18. }
  19. public void registerAdvisorAdapter(AdvisorAdapter adapter) {
  20. this.adapters.add(adapter);
  21. }
  22. }

像beforeAdvisor则转换为MethodBeforeAdviceInterceptor这样的拦截器了。在这个拦截器中的invoke方法中会发现会先调用自己advice的before方法,也就是你自己实现的业务类。接着又会调用processed,也就是连接链的方式,一直调用下去,知道chain中所有都执行完成。

Java代码

  1. public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {
  2. private MethodBeforeAdvice advice;
  3. /**
  4. * Create a new MethodBeforeAdviceInterceptor for the given advice.
  5. * @param advice the MethodBeforeAdvice to wrap
  6. */
  7. public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
  8. Assert.notNull(advice, "Advice must not be null");
  9. this.advice = advice;
  10. }
  11. public Object invoke(MethodInvocation mi) throws Throwable {
  12. this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
  13. return mi.proceed();
  14. }
  15. }

这里的advice

时间: 2024-10-21 21:53:37

spring4.0源码分析━━━(AOP实现)的相关文章

Solr4.8.0源码分析(22)之 SolrCloud的Recovery策略(三)

Solr4.8.0源码分析(22)之 SolrCloud的Recovery策略(三) 本文是SolrCloud的Recovery策略系列的第三篇文章,前面两篇主要介绍了Recovery的总体流程,以及PeerSync策略.本文以及后续的文章将重点介绍Replication策略.Replication策略不但可以在SolrCloud中起到leader到replica的数据同步,也可以在用多个单独的Solr来实现主从同步.本文先介绍在SolrCloud的leader到replica的数据同步,下一篇

HBase1.0.0源码分析之请求处理流程分析以Put操作为例(二)

HBase1.0.0源码分析之请求处理流程分析以Put操作为例(二) 1.通过mutate(put)操作,将单个put操作添加到缓冲操作中,这些缓冲操作其实就是Put的父类的一个List的集合.如下: private List<Row> writeAsyncBuffer = new LinkedList<>(); writeAsyncBuffer.add(m); 当writeAsyncBuffer满了之后或者是人为的调用backgroundFlushCommits操作促使缓冲池中的

Solr4.8.0源码分析(10)之Lucene的索引文件(3)

Solr4.8.0源码分析(10)之Lucene的索引文件(3) 1. .si文件 .si文件存储了段的元数据,主要涉及SegmentInfoFormat.java和Segmentinfo.java这两个文件.由于本文介绍的Solr4.8.0,所以对应的是SegmentInfoFormat的子类Lucene46SegmentInfoFormat. 首先来看下.si文件的格式 头部(header) 版本(SegVersion) doc个数(SegSize) 是否符合文档格式(IsCompoundF

Solr4.8.0源码分析(4)之Eclipse Solr调试环境搭建

Solr4.8.0源码分析(4)之Eclipse Solr调试环境搭建 由于公司里的Solr调试都是用远程jpda进行的,但是家里只有一台电脑所以不能jpda进行调试,这是因为jpda的端口冲突.所以只能在Eclipse 搭建Solr的环境,折腾了一小时终于完成了. 1. JDPA远程调试 搭建换完成Solr环境后,对${TOMCAT_HOME}/bin/startup.sh 最后一行进行修改,如下所示: 1 set JPDA_ADDRESS=7070 2 exec "$PRGDIR"

Solr4.8.0源码分析(25)之SolrCloud的Split流程

Solr4.8.0源码分析(25)之SolrCloud的Split流程(一) 题记:昨天有位网友问我SolrCloud的split的机制是如何的,这个还真不知道,所以今天抽空去看了Split的原理,大致也了解split的原理了,所以也就有了这篇文章.本系列有两篇文章,第一篇为core split,第二篇为collection split. 1. 简介 这里首先需要介绍一个比较容易混淆的概念,其实Solr的HTTP API 和 SolrCloud的HTTP API是不一样,如果接受到的是Solr的

Solr4.8.0源码分析(24)之SolrCloud的Recovery策略(五)

Solr4.8.0源码分析(24)之SolrCloud的Recovery策略(五) 题记:关于SolrCloud的Recovery策略已经写了四篇了,这篇应该是系统介绍Recovery策略的最后一篇了.本文主要介绍Solr的主从同步复制.它与前文<Solr4.8.0源码分析(22)之SolrCloud的Recovery策略(三)>略有不同,前文讲到的是SolrCloud的leader与replica之间的同步,不需要通过配置solrconfig.xml来实现.而本文主要介绍单机模式下,利用so

Solr4.8.0源码分析(19)之缓存机制(二)

Solr4.8.0源码分析(19)之缓存机制(二) 前文<Solr4.8.0源码分析(18)之缓存机制(一)>介绍了Solr缓存的生命周期,重点介绍了Solr缓存的warn过程.本节将更深入的来介绍下Solr的四种缓存类型,以及两种SolrCache接口实现类. 1.SolrCache接口实现类 前文已经提到SolrCache有两种接口实现类:solr.search.LRUCache 和 solr.search.LRUCache. 那么两者具体有啥区别呢? 1.1 solr.search.LR

Solr4.8.0源码分析(17)之SolrCloud索引深入(4)

Solr4.8.0源码分析(17)之SolrCloud索引深入(4) 前面几节以add为例已经介绍了solrcloud索引链建索引的三步过程,delete以及deletebyquery跟add过程大同小异,这里暂时就不介绍了.由于commit流程较为特殊,那么本节主要简要介绍下commit的流程. 1. SolrCloud的commit流程 SolrCloud的commit流程同样分为三步,本节主要简单介绍下三步过程. 1.1 LogUpdateProcessor LogUpdateProces

Solr4.8.0源码分析(14)之SolrCloud索引深入(1)

Solr4.8.0源码分析(14) 之 SolrCloud索引深入(1) 上一章节<Solr In Action 笔记(4) 之 SolrCloud分布式索引基础>简要学习了SolrCloud的索引过程,本节开始将通过阅读源码来深入学习下SolrCloud的索引过程. 1. SolrCloud的索引过程流程图 这里借用下<solrCloud Update Request Handling 更新索引流程>流程图: 由上图可以看出,SolrCloud的索引过程主要通过一个索引链过程来实