spring源码学习(一)--AOP初探
LZ以前一直觉得,学习spring源码,起码要把人家的代码整体上通读一遍,现在想想这是很愚蠢的,spring作为一个应用平台,不是那么好研究透彻的,而且也不太可能有人把spring的源码全部清楚的过上一遍,哪怕是spring的缔造者。我们确实没有必要把源码全部过一遍,如果在看spring源码的过程中,能学习到东西是最关键的,说实话,下面的很多东西也是我边看源码,边看大神的解读写出来的,基本不属于原创,但是改变自己的思想,尽量从源码出发,不要一出现问题就只能寄所有希望于百度,如果能做到这点,最好不过了。
AOP中文翻译是面向切面编程,与面向对象,面向接口,面向服务等概念是相似的,所谓面向切面,即是使用切面与其他事物产生关系。面向对象强调一切皆对象,也可以说面向接口是一切皆接口,面向服务是一切皆服务,而面向切面也是一样,一切皆切面。
下面我们具体说说AOP,目前由AOP联盟给出了AOP的标准,AOP联盟的规范只是提供了几个接口定义,为了统一AOP的标准,下面来看看主要的几个接口的uml类图。

Advice接口:这是一个空接口,里面没有任何方法,只是用来标识一个通知。LZ自己的理解,这个接口定义了要通知什么内容。
Interceptor接口:Advice的子接口,这个接口和advice都不直接使用,一般是要扩展以后去实现特殊的拦截。
Joinpoint接口:代表了一个运行时的连接点。
Invocation接口:代表了程序中的一个调用,可以被拦截器Interceptor拦截。
下面还有几个接口,分别是Interceptor和Invocation的扩展接口,从下一层继承开始,interpretor的继承体系已经开始依赖于invocation。这从某种意义上来说,advice是依赖于joinpoint的。
以上就是AOP联盟规范里的的几个主要接口。下面我们看一下spring里的AOP的核心接口,这里叨唠一下看别人框架的一个小技巧,用抽象构建框架,用细节实现扩展。所以看别人代码,注意接口的关系和含义很关键。
Advice体系:Spring采用AOP联盟的Advice作为超级接口,扩展了很多子接口,比如BeforeAdvice,AfterAdvice等等,稍后以AfterReturningAdvice为例,讨论下spring的通知体系。
Pointcut接口:spring采用Pointcut作为切点的抽象,其中有一个方法返回一个MethodMatcher,作用很明显,就是说切点决定了要切入哪些方法。这里其实是定义了一个匹配规则。比如正则匹配,可以只匹配前缀为save的方法等等。
Advisor:通知器或者说通知者,我们从现实角度来说,通知者当然需要知道要通知什么。所以Advisor依赖于Advice,而Advisor旗下的子接口PointAdvisor还依赖于Pointcut,也就是说这个接口更确切的定位应该是包含了要通知谁和要通知什么,也就是说要能获得Advice和Pointcut。
下面我们用一个例子说明spring的AOP是如何工作的,在spring的bean中有一种特殊的bean,叫FactoryBean。这并不是一个普通的bean,而是用来产生bean的一个bean。这样说起来有点绕口,但这个接口就是用来做这个事的,它是为了实现一系列特殊加工过的bean而产生的接口。比如AOP中,我们其实就是要对一个bean进行增强,进行加工,让它在运行的过程中可以做一些特殊的事情。ProxyFactoryBean就是一个为了AOP实现的特殊的FactoryBean,它的作用很明显就是产生proxy的bean,也就是被我们增强过的bean,在这里给出它的源码。
public class ProxyFactoryBean extends ProxyCreatorSupport
implements FactoryBean<Object>, BeanClassLoaderAware, BeanFactoryAware { /**
* This suffix in a value in an interceptor list indicates to expand globals.
*/
public static final String GLOBAL_SUFFIX = "*"; protected final Log logger = LogFactory.getLog(getClass()); private String[] interceptorNames; private String targetName; private boolean autodetectInterfaces = true; private boolean singleton = true; private AdvisorAdapterRegistry advisorAdapterRegistry = GlobalAdvisorAdapterRegistry.getInstance(); private boolean freezeProxy = false; private transient ClassLoader proxyClassLoader = ClassUtils.getDefaultClassLoader(); private transient boolean classLoaderConfigured = false; private transient BeanFactory beanFactory; /** Whether the advisor chain has already been initialized */
private boolean advisorChainInitialized = false; /** If this is a singleton, the cached singleton proxy instance */
private Object singletonInstance; }
源码太长,这里只关心两个属性,interpretorNames和targetName,interpretorNames代表需要加强哪些东西以及需要怎样加强,也就是pointcut和advice。targetName代表的则是我们针对谁来做这些加强,即我们的目标对象。
首先interpretorNames是必须赋予的属性,这个属性指定了通知器或者是通知的名字。如果传入的是通知Advice,则会被自动包装为通知器。
targetName是我们要增强的目标对象,这个对象如果有实现的接口,则会采用JDK的动态代理实现,否则将需要第三方的jar包cglib。
下面我们测试一下spring AOP是不是真的可以给目标对象加额外功能。我们首先在spring的容器里添加ProxyFactoryBean,并声明目标对象和通知器。通知器里包含通知和切点。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"> <bean id="testAdvisor" class="com.springframework.aop.test.TestAdvisor"></bean>
<bean id="testTarget" class="com.springframework.aop.test.TestTarget"></bean>
<bean id="testAOP" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="targetName">
<value>testTarget</value>
</property>
<property name="interceptorNames">
<list>
<value>testAdvisor</value>
</list>
</property>
</bean> </beans>
下面是目标对象TestTarget。
public class TestTarget{
    public void test() {
        System.out.println("target.test()");
    }
    public void test2(){
        System.out.println("target.test2()");
    }
}
下面是通知器。
public class TestAdvisor implements PointcutAdvisor{
    public Advice getAdvice() {
        return new TestAfterAdvice();
    }
    public boolean isPerInstance() {
        return false;
    }
    public Pointcut getPointcut() {
        return new TestPointcut();
    }
}
通知器里自定义的通知(advice)和切点(pointcut),分别代表通知什么和在什么地方通知。首先是通知。
public class TestAfterAdvice implements AfterReturningAdvice{
    public void afterReturning(Object returnValue, Method method,
            Object[] args, Object target) throws Throwable {
        System.out.println("after " + target.getClass().getSimpleName() + "." + method.getName() + "()");
    }
}
下面是切点。
public class TestPointcut implements Pointcut{
    public ClassFilter getClassFilter() {
        return ClassFilter.TRUE;
    }
    public MethodMatcher getMethodMatcher() {
        return new MethodMatcher() {
            public boolean matches(Method method, Class<?> targetClass, Object[] args) {
                if (method.getName().equals("test")) {
                    return true;
                }
                return false;
            }
            public boolean matches(Method method, Class<?> targetClass) {
                if (method.getName().equals("test")) {
                    return true;
                }
                return false;
            }
            public boolean isRuntime() {
                return true;
            }
        };
    }
}
切点只在目标对象的test方法执行完后打印一下。代码就这些,我们来测试一下。
public class TestAOP {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new FileSystemXmlApplicationContext("classpath:beans.xml");
        TestTarget target = (TestTarget) applicationContext.getBean("testAOP");
        target.test();
        System.out.println("------无敌分割线-----");
        target.test2();
    }
}
打印结果。

我们增强的afterReturningAdvice已经起作用了。这里只是演示一下spring aop的实现,实际开发中不要这么用。
我相信很多人都能看懂以上代码逻辑,下一篇文章,我们顺着spring源码,看一下spring到底是如何实现的以上逻辑。
spring源码学习(一)--AOP初探的更多相关文章
- spring源码学习之AOP(一)
		继续源码学习,看了spring中基础的容器和AOP感觉自己也没有什么长进,哈哈,我也不知道到底有用没有,这可能是培养自己的一种精神吧,不管那么多,继续学习!AOP中 AOP中几个重要的概念:(1)Ad ... 
- spring源码学习之AOP(二)
		接着上一篇中的内容! 3.创建代理 在获取了所有的bean对应的增强器之后,便可以进行代理的创建了org.springframework.aop.framework.autoproxy包下的Abstr ... 
- spring源码学习之路---深入AOP(终)
		作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. 上一章和各位一起看了一下sp ... 
- spring源码学习之路---IOC初探(二)
		作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. 上一章当中我没有提及具体的搭 ... 
- Spring 源码学习——Aop
		Spring 源码学习--Aop 什么是 AOP 以下是百度百科的解释:AOP 为 Aspect Oriented Programming 的缩写,意为:面向切面编程通过预编译的方式和运行期动态代理实 ... 
- Spring 源码学习笔记10——Spring AOP
		Spring 源码学习笔记10--Spring AOP 参考书籍<Spring技术内幕>Spring AOP的实现章节 书有点老,但是里面一些概念还是总结比较到位 源码基于Spring-a ... 
- Spring源码学习
		Spring源码学习--ClassPathXmlApplicationContext(一) spring源码学习--FileSystemXmlApplicationContext(二) spring源 ... 
- Spring源码学习-容器BeanFactory(一) BeanDefinition的创建-解析资源文件
		写在前面 从大四实习至今已一年有余,作为一个程序员,一直没有用心去记录自己工作中遇到的问题,甚是惭愧,打算从今日起开始养成写博客的习惯.作为一名java开发人员,Spring是永远绕不过的话题,它的设 ... 
- 【目录】Spring 源码学习
		[目录]Spring 源码学习 jwfy 关注 2018.01.31 19:57* 字数 896 阅读 152评论 0喜欢 9 用来记录自己学习spring源码的一些心得和体会以及相关功能的实现原理, ... 
- Spring 源码学习笔记11——Spring事务
		Spring 源码学习笔记11--Spring事务 Spring事务是基于Spring Aop的扩展 AOP的知识参见<Spring 源码学习笔记10--Spring AOP> 图片参考了 ... 
随机推荐
- dump文件
			https://blog.csdn.net/icandoit_2014/article/details/78739962 可以看出,此种方法只适用于程序崩溃但没有立即自行退出的情况.倘若程序故障后自行 ... 
- chrome离线安装包下载
			Google Chrome 已经是许多人的默认浏览器,但由于“你懂的”原因,在线安装基本没有成功过,他自己的自动更新也多数一直在加载中,所以我们会到一些下载站下载安装包,但我的多次经历告诉我,下载回来 ... 
- No module named 'lsb_release'
			python3.7安装后提示 No module named 'lsb_release' 修改"/usr/bin/lsb_release" #!/usr/bin/python3.5 ... 
- Mybatis自定义控制台打印sql的日志工具
			调试mybatis源码时,想要更改日志的的实现工具,首先需要了解其原理. 源码包里有这部分的解释,翻译如下: Mybatis 的内置日志工厂提供日志功能,内置日志工厂将日志交给以下其中一种工具作代理: ... 
- 京东HBase平台进化与演进
			https://mp.weixin.qq.com/s/7_dxrqFWwIJxNtL9-xD6FA 
- Oracle 查询表注释以及字段注释
			Oracle 查询表注释以及字段注释 --表字段信息 select * from all_tab_columns a where a.TABLE_NAME='T_X27_USER'; --表注释信息 ... 
- Flutter AspectRatio、Card 卡片组件
			Flutter AspectRatio 组件 AspectRatio 的作用是根据设置调整子元素 child 的宽高比. AspectRatio 首先会在布局限制条件允许的范围内尽可能的扩展,widg ... 
- 使用localStorage写一个简单的备忘录
			使用html+js实现一个简单的备忘录,主要体会一下localStorage的用法. 先看看效果图: 在输入框中输入文字,点击保存按钮,文本内容会在下放展示出来, 然后刷新下浏览器,会发现文本内容不会 ... 
- IDEA子项目的相互依赖
			A项目依赖B项目 一.B项目打包,执行install 这一步其实就是把你的项目打包到本地仓库,你可以在本地仓库看到相应的jar包 二.在A项目的pom.xml引入依赖可以了 当B项目修改后,需要重新打 ... 
- osg坐标位置转换
			osg::Vec3f vec3f1 = hookNode->getBound().center(); osg::NodePathList nodePAthList1 = hookNode-> ... 
