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初探的更多相关文章

  1. spring源码学习之AOP(一)

    继续源码学习,看了spring中基础的容器和AOP感觉自己也没有什么长进,哈哈,我也不知道到底有用没有,这可能是培养自己的一种精神吧,不管那么多,继续学习!AOP中 AOP中几个重要的概念:(1)Ad ...

  2. spring源码学习之AOP(二)

    接着上一篇中的内容! 3.创建代理 在获取了所有的bean对应的增强器之后,便可以进行代理的创建了org.springframework.aop.framework.autoproxy包下的Abstr ...

  3. spring源码学习之路---深入AOP(终)

    作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. 上一章和各位一起看了一下sp ...

  4. spring源码学习之路---IOC初探(二)

    作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. 上一章当中我没有提及具体的搭 ...

  5. Spring 源码学习——Aop

    Spring 源码学习--Aop 什么是 AOP 以下是百度百科的解释:AOP 为 Aspect Oriented Programming 的缩写,意为:面向切面编程通过预编译的方式和运行期动态代理实 ...

  6. Spring 源码学习笔记10——Spring AOP

    Spring 源码学习笔记10--Spring AOP 参考书籍<Spring技术内幕>Spring AOP的实现章节 书有点老,但是里面一些概念还是总结比较到位 源码基于Spring-a ...

  7. Spring源码学习

    Spring源码学习--ClassPathXmlApplicationContext(一) spring源码学习--FileSystemXmlApplicationContext(二) spring源 ...

  8. Spring源码学习-容器BeanFactory(一) BeanDefinition的创建-解析资源文件

    写在前面 从大四实习至今已一年有余,作为一个程序员,一直没有用心去记录自己工作中遇到的问题,甚是惭愧,打算从今日起开始养成写博客的习惯.作为一名java开发人员,Spring是永远绕不过的话题,它的设 ...

  9. 【目录】Spring 源码学习

    [目录]Spring 源码学习 jwfy 关注 2018.01.31 19:57* 字数 896 阅读 152评论 0喜欢 9 用来记录自己学习spring源码的一些心得和体会以及相关功能的实现原理, ...

  10. Spring 源码学习笔记11——Spring事务

    Spring 源码学习笔记11--Spring事务 Spring事务是基于Spring Aop的扩展 AOP的知识参见<Spring 源码学习笔记10--Spring AOP> 图片参考了 ...

随机推荐

  1. 【转】禁用chrome firefox 的 WebRTC功能防止真实IP泄漏

    无论是使用VPN还是其它代理方式,很多时候我们不希望暴露自己的真实IP,且一直以来我们认为VPN是安全的,所有流量都会走VPN. 但最近暴露出一个WebRTC特性,会暴露我们的真实IP.适用浏览器:c ...

  2. minicom调试4G网卡

    [root@localhost toybrick]# minicom -D /dev/ttyUSB2 Welcome to minicom 2.7.1                          ...

  3. 产品经理 写SQL

    产品经理必备技能:写SQL - 云+社区 - 腾讯云https://cloud.tencent.com/developer/news/3177 产品经理学SQL(一)一个小时上手SQL | 人人都是产 ...

  4. ffmpeg x264安装

    fmpeg安装第三方编码器(encoder)库,ffmpeg编码h264(完) ffmpeg安装第三方编码器(encoder)库 关键词:ffmpeg.编码h264.第三方encoder 安装好了ff ...

  5. flutter的 图片组件基本使用

    import 'package:flutter/material.dart'; void main() { runApp(MyApp()); } class MyApp extends Statele ...

  6. Qt编写气体安全管理系统4-通信协议

    一.前言 通信协议解析是整个系统的核心灵魂,绝大部分人做软硬件通信开发,第一步估计就是写demo将协议解析好,然后再慢慢写整个界面和操作流程等,在工业控制领域,modbus协议应用还是非常广泛的,这个 ...

  7. python flask框架学习(三)——豆瓣微信小程序案例(二)整理封装block,模板的继承

    我们所要实现的效果: 点击电影的更多,跳转到更多的电影页面:点击电视剧的更多,跳转到更多的电视剧页面. 三个页面的风格相同,可以设置一个模板,三个页面都继承这个模板 1.在指定模板之前,把css放在一 ...

  8. 【Leetcode_easy】985. Sum of Even Numbers After Queries

    problem 985. Sum of Even Numbers After Queries class Solution { public: vector<int> sumEvenAft ...

  9. python的网络工具scapy

    文档 https://scapy.readthedocs.io/en/latest/api/scapy.sendrecv.html 阅读文档 https://blog.csdn.net/Al_xin/ ...

  10. CF1197D Yet Another Subarray Problem

    思路: 使用动态规划,在经典的最大子段和解法基础上进行扩展.dp[i][j]表示以第i个数为结尾,并且长度模m等于j的所有子段的最大cost. 实现: #include <bits/stdc++ ...