spring源码分析(二)Aop
创建日期:2016.08.19
修改日期:2016.08.20-2016.08.21
交流QQ:992591601
参考资料:《spring源码深度解析》、《spring技术内幕》、传值播客spring教学视频
http://www.cnblogs.com/xing901022/p/4264334.html
http://www.cnblogs.com/digdeep/p/4528353.html
一,动态代理、java InvocationHandler实现、Cglib实现
要了解动态代理,可阅读设计模式相关书籍,不难理解。这篇博文我简单解释,动态代理就是与静态代理相对应的,在程序运行过程中动态创建的代理类。代理类可以简单理解为一种对目标类的增强,之后我们要使用目标类的话,只需用代理类就可以了。
实现动态代理有两种方式,其一是java自带的动态代理功能,另外就是使用Cglib库实现。前者的使用有一个必须的条件,那就是目标类必须实现接口。而后者的使用则是对前者的一种补充。
假设我们有这样两个bean,其一为AlienFishServiceBean,不实现任何接口。
其一为FishServiceImpl,实现FishService接口。
对于前者,我们需要使用Cglib来实现动态代理功能(示例代码,实现功能:当bean的fishName字段不为空时,才能调用该bean的方法):
- package cn;
- import java.lang.reflect.Method;
- import cn.aop.service.AlienFishServiceBean;
- import net.sf.cglib.proxy.Enhancer;
- import net.sf.cglib.proxy.MethodInterceptor;
- import net.sf.cglib.proxy.MethodProxy;
- /**
- * @ClassName: CGlibProxyFactory
- * @Description: CGlibProxyFactory
- * @author 无名
- * @date 2016-8-14 17:31:48
- * @version 1.0
- */
- public class CGlibProxyFactory implements MethodInterceptor {
- private Object targetObject;
- public Object createProxyIntance(Object targetObject) {
- this.targetObject = targetObject;
- Enhancer enhancer = new Enhancer();
- enhancer.setSuperclass(this.targetObject.getClass());
- enhancer.setCallback(this);
- return enhancer.create();
- }
- public Object intercept(Object proxy, Method method, Object[] args,
- MethodProxy methodProxy) throws Throwable {
- AlienFishServiceBean bean = (AlienFishServiceBean) this.targetObject;
- Object result = null;
- if(bean.getFishName()!=null) {
- result = methodProxy.invoke(targetObject, args);
- }
- return result;
- }
- }
对于后者,我们可以使用java自带的动态代理功能(示例代码,实现功能:当bean的fishName字段不为空时,才能调用该bean的方法):
- package cn;
- import java.lang.reflect.InvocationHandler;
- import java.lang.reflect.Method;
- import java.lang.reflect.Proxy;
- import cn.aop.service.FishService;
- import cn.aop.service.FishServiceImpl;
- /**
- * @ClassName: JDKProxyFactory
- * @Description: JDKProxyFactory
- * @author 无名
- * @date 2016-8-13 下午11:55:31
- * @version 1.0
- */
- public class JDKProxyFactory implements InvocationHandler {
- private Object targetObject;
- public Object createInstance(Object targetObject) {
- this.targetObject = targetObject;
- return Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(),
- this.targetObject.getClass().getInterfaces(), this);
- }
- @Override
- public Object invoke(Object proxy, Method method, Object[] args)
- throws Throwable {
- FishService fs = (FishServiceImpl)targetObject;
- Object result = null;
- if(fs.getFishName() != null) {
- result = method.invoke(targetObject, args);
- }
- return result;
- }
- }
上述两个代理方式使用时,先用代理类工厂创建对应代理类,然后使用代理类即可(此代理类是对目标类的代理,‘增强了’目标类的一些方面)。
二,Spring Aop
spring aop需要的jar包:
org.springframework.aop-xxx.jar(spring),aopalliance-1.0.jar,aspectjrt.jar, aspectjweaver.jar,cglib.jar
spring aop是在动态代理这种设计模式的基础之上的。
这里我说下aop的几个基本概念,都是基于我自己的理解,简单粗暴:
aspect:面,可以理解为一个事务,对该事务做对应处理。
pointcut:切入点,对应于面具体的切入的地方。
advice:spring定义了四个advice, BeforeAdvice, AfterAdvice, ThrowAdvice和DynamicIntroductionAdvice。在触及面和点的时候,根据配置,执行对应通知。
spring aop的实现由两种,其一是配置文件方式,另外是注解方式。
我们首先,用配置文件方式:
目标类,接口和实现类:
- package cn.service;
- public interface FishService {
- public void say00();
- public void say01();
- }
- package cn.service.impl;
- import cn.service.FishService;
- public class FishServiceBean implements FishService {
- public void say00() {
- System.out.println("I am fish 00");
- }
- public void say01() {
- System.out.println("I am fish 01");
- }
- }
下面的类提供对应的advice
- package cn.service;
- import org.aspectj.lang.ProceedingJoinPoint;
- /**
- * 切面
- *
- */
- public class MyInterceptor {
- public void doBefore() {
- System.out.println("before");
- }
- public void doAfter() {
- System.out.println("after");
- }
- public void doFinal() {
- System.out.println("final");
- }
- public void doThrowing() {
- System.out.println("throwing");
- }
- public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
- System.out.println("进入方法");
- Object result = pjp.proceed();
- System.out.println("退出方法");
- return result;
- }
- }
配置文件(aop:config内设置aop,切面里设置切入点,及几种advice):
- <?xml version="1.0" encoding="UTF-8"?>
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:context="http://www.springframework.org/schema/context"
- xmlns:aop="http://www.springframework.org/schema/aop"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
- http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
- http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
- <aop:aspectj-autoproxy/>
- <bean id="fishService" class="cn.service.impl.FishServiceBean"></bean>
- <bean id="aspetbean" class="cn.service.MyInterceptor"/>
- <aop:config>
- <aop:aspect id="asp" ref="aspetbean">
- <aop:pointcut id="mycut" expression="execution(* cn.service..*.*(..))"/>
- <aop:before pointcut-ref="mycut" method="doBefore"/>
- <aop:after-returning pointcut-ref="mycut" method="doFinal"/>
- <aop:after-throwing pointcut-ref="mycut" method="doThrowing"/>
- <aop:after pointcut-ref="mycut" method="doAfter"/>
- <aop:around pointcut-ref="mycut" method="doBasicProfiling"/>
- </aop:aspect>
- </aop:config>
- </beans>
测试类:
- package junit.test;
- import org.junit.BeforeClass;
- import org.junit.Test;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
- import cn.service.FishService;
- public class SpringAOPTest {
- @BeforeClass
- public static void setUpBeforeClass() throws Exception {
- }
- @Test public void interceptorTest(){
- ApplicationContext cxt = new ClassPathXmlApplicationContext("beans.xml");
- FishService fishService = (FishService)cxt.getBean("fishService");
- fishService.say00();
- fishService.say01();
- }
- }
运行结果:
与配置文件方式相对应的便是注解的方式
注解方式只需在spring的xml文件里保留 <aop:aspectj-autoproxy/> 就可以了。
而上面对应的MyInterceptor类需要多写相对的注解(著名切入点、advice等)。
三,Spring源码分析
首先自己设想一下spring实现aop的思路,大概是:1,获取、寻找所有bean,如果为AspectJ注解的类,则进行对应处理。
2,对标记为AspectJ注解的类进行增强器的提取(前文提到动态代理是对目标类的一种增强)
3,创建动态代理。
首先看一个类AopNamespaceHandler,这里是aop注解对应的解析器:
- public class AopNamespaceHandler extends NamespaceHandlerSupport {
- public void init() {
- // In 2.0 XSD as well as in 2.1 XSD.
- registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
- registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
- registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
- // Only in 2.0 XSD: moved to context namespace as of 2.1
- registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
- }
- }
我们看registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());这一句,可知其内涵是向NamespaceHandlerSupport这一类的
private final Map<String, BeanDefinitionParser> parsers =
new HashMap<String, BeanDefinitionParser>(); 这一map数据结构中注册对应解析器。
此后 一旦遇到aspectj-autoproxy注解,便自然会从map中取到对应解析器,并使用解析器AspectJAutoProxyBeanDefinitionParser解析。AspectJAutoProxyBeanDefinitionParser是继承了BeanDefinitionParser接口的。
- public BeanDefinition parse(Element element, ParserContext parserContext) {
- AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
- extendBeanDefinition(element, parserContext);
- return null;
- }
- public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
- ParserContext parserContext, Element sourceElement) {
- BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
- parserContext.getRegistry(), parserContext.extractSource(sourceElement));
- useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
- registerComponentIfNecessary(beanDefinition, parserContext);
- }
首先看下进入该函数时参数的状态:
sourceElement的name为aop:aspectj-autoproxy。这个函数就是专门用于注册aop:aspectj注解的。
这个方法三句代码,各有其作用,分别是:1注册AnnotationAwareAspectJAutoProxyCreator(AOP的实现都靠这个),返回的BeanDefinition设置对应的AnnotationAwareAspectJAutoProxyCreator
2处理proxy-target-class(CgLib或jdk)以及expose-proxy属性
3注册组件并通知
从spring的 DefaultAopProxyFactory类入手,该类继承自AopProxyFactory接口。
- package org.springframework.aop.framework;
- /**
- * Interface to be implemented by factories that are able to create
- * AOP proxies based on {@link AdvisedSupport} configuration objects.
- *
- * <p>Proxies should observe the following contract:
- * <ul>
- * <li>They should implement all interfaces that the configuration
- * indicates should be proxied.
- * <li>They should implement the {@link Advised} interface.
- * <li>They should implement the equals method to compare proxied
- * interfaces, advice, and target.
- * <li>They should be serializable if all advisors and target
- * are serializable.
- * <li>They should be thread-safe if advisors and target
- * are thread-safe.
- * </ul>
- *
- * <p>Proxies may or may not allow advice changes to be made.
- * If they do not permit advice changes (for example, because
- * the configuration was frozen) a proxy should throw an
- * {@link AopConfigException} on an attempted advice change.
- *
- * @author Rod Johnson
- * @author Juergen Hoeller
- */
- public interface AopProxyFactory {
- /**
- * Create an {@link AopProxy} for the given AOP configuration.
- * @param config the AOP configuration in the form of an
- * AdvisedSupport object
- * @return the corresponding AOP proxy
- * @throws AopConfigException if the configuration is invalid
- */
- AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException;
- }
我们看最核心的createAopProxy方法:
- public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
- if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
- Class targetClass = config.getTargetClass();
- if (targetClass == null) {
- throw new AopConfigException("TargetSource cannot determine target class: " +
- "Either an interface or a target is required for proxy creation.");
- }
- if (targetClass.isInterface()) {
- return new JdkDynamicAopProxy(config);
- }
- if (!cglibAvailable) {
- throw new AopConfigException(
- "Cannot proxy target class because CGLIB2 is not available. " +
- "Add CGLIB to the class path or specify proxy interfaces.");
- }
- return CglibProxyFactory.createCglibProxy(config);
- }
- else {
- return new JdkDynamicAopProxy(config);
- }
- }
这段代码逻辑很清晰,判断如果目标类实现接口则使用jdk创建代理,否则使用cglib。
再仔细看这段代码,发现无论是jdk还是cglib,都要使用AdvisedSupport config这个参数。
设置断点,查看该变量,发现果然信息量很大,对应的AdvisedSupport类代码我就不贴了,看下面截图也可以想象个大概了。
具体看下advice的内容,我们配置文件里配置的内容都在里面了:
至于这个config是怎样生成的,其实很好想象,就是前面讲的,对应注解的解析,注册。
new JdkDynamicAopProxy(config)
- /**
- * Construct a new JdkDynamicAopProxy for the given AOP configuration.
- * @param config the AOP configuration as AdvisedSupport object
- * @throws AopConfigException if the config is invalid. We try to throw an informative
- * exception in this case, rather than let a mysterious failure happen later.
- */
- public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {
- Assert.notNull(config, "AdvisedSupport must not be null");
- if (config.getAdvisors().length == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
- throw new AopConfigException("No advisors and no TargetSource specified");
- }
- this.advised = config;
- }
CglibProxyFactory.createCglibProxy(config)
- /**
- * Create a new Cglib2AopProxy for the given AOP configuration.
- * @param config the AOP configuration as AdvisedSupport object
- * @throws AopConfigException if the config is invalid. We try to throw an informative
- * exception in this case, rather than let a mysterious failure happen later.
- */
- public Cglib2AopProxy(AdvisedSupport config) throws AopConfigException {
- Assert.notNull(config, "AdvisedSupport must not be null");
- if (config.getAdvisors().length == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
- throw new AopConfigException("No advisors and no TargetSource specified");
- }
- this.advised = config;
- this.advisedDispatcher = new AdvisedDispatcher(this.advised);
- }
都是用config设置对应的advise属性。
小小总结:最后我又单步调试一通,结合上一章节内容《spring源码分析(一)IoC、DI》,发现AOP代理类的创建时CreateBean这一阶段开始的,其实这时候就是对应目标类用代理类取代了。
这是断点运行时,观察createBean返回的结果,可见创建的fishService bean实质上是JdkDynamicAopProxy代理类。在那之后我们使用fishService实际上就是使用对应的JdkDynamicAopProxy代理类了,之前注册的那些advice也就生效了:
(在这一过程中值得一提的还有AbstractAutoProxyCreator的postProcessAfterInitialization方法,正如注释所说,作用是Create a proxy with the configured interceptors if the bean is identified as one to proxy by the subclass.
AnnotationAwareAspectJAutoProxyCreator的findCandidateAdvisors方法,作用是获取增强器)
spring源码分析(二)Aop的更多相关文章
- Spring源码分析之AOP从解析到调用
正文: 在上一篇,我们对IOC核心部分流程已经分析完毕,相信小伙伴们有所收获,从这一篇开始,我们将会踏上新的旅程,即Spring的另一核心:AOP! 首先,为了让大家能更有效的理解AOP,先带大家过一 ...
- 【Spring源码分析】AOP源码解析(上篇)
前言 前面写了六篇文章详细地分析了Spring Bean加载流程,这部分完了之后就要进入一个比较困难的部分了,就是AOP的实现原理分析.为了探究AOP实现原理,首先定义几个类,一个Dao接口: pub ...
- Spring源码分析之AOP
1.AOP简介 AOP即面向切面编程(Aspect Oriented Programming),通过预编译方式及运行期动态代理实现程序功能的统一维护的一种技术.使用aop对业务逻辑的各个部分进行隔离, ...
- 【Spring源码分析】AOP源码解析(下篇)
AspectJAwareAdvisorAutoProxyCreator及为Bean生成代理时机分析 上篇文章说了,org.springframework.aop.aspectj.autoproxy.A ...
- Spring源码分析笔记--AOP
核心类&方法 BeanDefinition Bean的定义信息,封装bean的基本信息,从中可以获取类名.是否是单例.是否被注入到其他bean中.是否懒加载.bean依赖的bean的名称等. ...
- Spring源码分析之Bean的创建过程详解
前文传送门: Spring源码分析之预启动流程 Spring源码分析之BeanFactory体系结构 Spring源码分析之BeanFactoryPostProcessor调用过程详解 本文内容: 在 ...
- Spring源码分析之`BeanFactoryPostProcessor`调用过程
前文传送门: Spring源码分析之预启动流程 Spring源码分析之BeanFactory体系结构 本文内容: AbstractApplicationContext#refresh前部分的一点小内容 ...
- Spring源码分析之循环依赖及解决方案
Spring源码分析之循环依赖及解决方案 往期文章: Spring源码分析之预启动流程 Spring源码分析之BeanFactory体系结构 Spring源码分析之BeanFactoryPostPro ...
- Spring源码分析——BeanFactory体系之抽象类、类分析(二)
上一篇分析了BeanFactory体系的2个类,SimpleAliasRegistry和DefaultSingletonBeanRegistry——Spring源码分析——BeanFactory体系之 ...
随机推荐
- using 语句中使用的类型必须可隐式转换为“System.IDisposable
在使用 EF 出现 using 语句中使用的类型必须可隐式转换为“System.IDisposable 今天写在这里分享给大家 出现这样的问题,是因为没有引用 EntityFramework 这个程 ...
- C#实现按键精灵的'找图' '找色' '找字'的功能
背景:游戏辅助功能通常使用按键精灵编写脚本,按键精灵的最大卖点就是能够找到画面中字,图,色,这对于模拟用户鼠标操作至关重要,这能找到道具,找到血量,实现自动打怪,自动补血,自动买卖道具,博主闲来无聊, ...
- 详解在Visual Studio中使用git版本系统[转]
这篇教程的预期,是希望没有任何版本使用基础的新手也可以掌握,所以细节较多,不当之处,欢迎指正. 一 .安装 git 开发工具 如果要使用 git 进行版本管理,其实使用 git 命令行工具就完全足够了 ...
- 通过硬编码获取dubbo服务对象
运维进行监控dubbo服务的时候可能会调用dubbo服务对象,并且定期去执行,这时候如果需要添加新的服务,可能需要修改监控dubbo服务的配置,即dubbo-producer.xml或是dubbo-c ...
- CodeForces 743A Vladik and flights (水题)
题意:sb要从a到b,然后要乘坐飞机,只有两家有飞机,如果乘坐同一家的,就免费,如果不是就收到abs(j-i) 的费用,问你最少花费是多少. 析:直接考虑a和b是不是同一家的,如果是,花费为0,如果不 ...
- Java Environment Setting
As a non-Java developer, I am quit stuck in Java environment setting because I am not familiar with ...
- SqlIO优化
1SqlIO优化 set statistics io on--sqlset statistics io off 2Sql占用CPU时间 select c.total_worker_time, c.la ...
- c++中的指针
指针用起来是一把利器,但用得不好的童鞋 无异于 火上浇油 ,下面笔者将自己学习 的一点小小心得,与君共享 指针在类中 1.对象指针 初始化 Point a(4,5); Point *p1 = & ...
- 定时自动关闭messagebox
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; usin ...
- (转)Silverlight控件关系理解
原文地址http://www.cnblogs.com/Joetao/articles/1899664.html 本篇学习了Silverlight中的控件继承关系,了解控件的继承关系对应我们操作控件,使 ...