Spring讲解解析
Spring源码解析之BeanFactoryPostProcessor(一)

Spring源码解析之BeanFactoryPostProcessor(⼀)BeanFactoryPostProcessor在前⾯⼏个章节,笔者有介绍过BeanFactoryPostProcessor接⼝,在spring在解析BeanDefinition之后,根据BeanDefinition初始化bean之前,会回调我们编写的BeanFactoryPostProcessor实现类并调⽤postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)⽅法,spring会通过这个⽅法传⼊⼀个ConfigurableListableBeanFactory对象,我们可以对这个bean⼯⼚对象新增或修改BeanDefinition。
spring初始化bean⼀个典型的流程,就是根据我们标记在类上的@Component⽣成⼀个BeanDefinition,BeanDefinition中包含这个类的class对象,然后根据class对象⽣成实例。
如果我们编写两个Service:UserService和OrderService,并在类上标注@Component,再编写⼀个BeanFactoryPostProcessor接⼝,在接⼝中我们拿到UserService的BeanDefinition,并修改class为OrderService,那么我们从spring容器中获取userService这个bean,它的类型是UserService呢还是OrderService呢?来看下⾯的⽰例:package org.example.service;import ponent;@Componentpublic class OrderService {}package org.example.service;import ponent;@Componentpublic class UserService {}在Test1BeanFactoryPostProcessor类中,我们获取userService的BeanDefinition,并打印它的class对象,这⾥应该是UserService,然后我们再设置BeanDefinition的class为OrderServiceTest1BeanFactoryPostProcessor.javapackage org.example.service;import org.springframework.beans.BeansException;import org.springframework.beans.factory.config.BeanFactoryPostProcessor;import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;import org.springframework.context.annotation.ScannedGenericBeanDefinition;import ponent;@Componentpublic class Test1BeanFactoryPostProcessor implements BeanFactoryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {ScannedGenericBeanDefinition beanDefinition = (ScannedGenericBeanDefinition) beanFactory.getBeanDefinition("userService");System.out.println("UserService beanDefinition class:" + beanDefinition.getBeanClass());beanDefinition.setBeanClass(OrderService.class);}}MyConfig.javapackage org.example.config;import ponentScan;import org.springframework.context.annotation.Configuration;@Configuration@ComponentScan("org.example.service")public class MyConfig {}测试⽤例:@Testpublic void test01() {ApplicationContext ac = new AnnotationConfigApplicationContext(MyConfig.class);System.out.println("userService class:" + ac.getBean("userService").getClass());}运⾏结果:UserService beanDefinition class:class erServiceuserService class:class org.example.service.OrderService可以看到,spring容器会回调我们编写的bean⼯⼚后置处理器BeanFactoryPostProcessor实现类Test1BeanFactoryPostProcessor ,在回调⽅法中,我们可以从bean⼯⼚获取spring容器已经解析的UserService对应的BeanDefinition对象,打印这个BeanDefinition对象的class对象也确实是UserService的class对象,之后我们修改UserService对应的BeanDefinition的class对象为OrderService的class对象,之后我们从spring容器获取beanName为userService的bean可以看到bean的实现类已经被替换成OrderService对象。
SpringIoC公共注解详解

SpringIoC 公共注解详解前⾔本系列全部基于 Spring 5.2.2.BUILD-SNAPSHOT 版本。
因为 Spring 整个体系太过于庞⼤,所以只会进⾏关键部分的源码解析。
什么是公共注解?公共注解就是常见的Java 注解,特别是JSR-250中的注解。
例如:@Resource 、@PostConstructor 、@PreDestroy 等等,本⽂也就主要分析这三个注解在 Spring 中是如何处理的。
正⽂@Resource 注解的处理对 @Resource 注解的处理类是 CommonAnnotationBeanPostProcessor ,它通过实现 InstantiationAwareBeanPostProcessor 接⼝,重写postProcessProperties() ⽅法实现对标注了 @Resource 注解的字段或⽅法的⾃动注⼊。
InstantiationAwareBeanPostProcessor 接⼝的详细信息可以查看。
关于 CommonAnnotationBeanPostProcessor 这个后置处理器是怎么加⼊到 beanFactory 中的,我们在 ⼀⽂中介绍过主要是通过AnnotationConfigUtils#registerAnnotationConfigProcessors() 实现的。
BeanDefinition 合并后的后置处理CommonAnnotationBeanPostProcessor#postProcessMergedBeanDefinition上⾯代码中的 findAutowiringMetadata() ⽅法就是利⽤反射遍历类的所有字段和⽅法,找到标注了 @Resource 注解的,并缓存进injectionMetadataCache 中。
注意:静态字段和静态⽅法会过滤掉。
findAutowiringMetadata() ⽅法基本和 AutowiredAnnotationBeanPostProcessor 中的⼀致,只是处理的注解不同⽽已,可以查查看⼀⽂中该⽅法的详解。
Spring注解之@Lazy注解使用解析

Spring注解之@Lazy注解使⽤解析
@Lazy⽤于指定该Bean是否取消预初始化。
主要⽤于修饰Spring Bean类,⽤于指定该Bean的预初始化⾏为,
使⽤该Annotation时可以指定⼀个boolean型的value属性,该属性决定是否要预初始化该Bean
lazy代表延时加载,lazy=false,代表不延时,如果对象A中还有对象B的引⽤,会在A的xml映射⽂件中配置b的对象引⽤,多对⼀或⼀对多,不延时代表查询出对象A的时候,会把B对象也查询出来放到A对象的引⽤中,A对象中的B对象是有值的。
lazy=true代表延时,查询A对象时,不会把B对象也查询出来,只会在⽤到A对象中B对象时才会去查询,默认好像是false,你可以看看后台的sql语句的变化就明⽩了,⼀般需要优化效率的时候会⽤到
@Lazy(true)
@Component
public class Chinese implements Person{
//codes here
}
@DependsOn⽤于强制初始化其他Bean。
可以修饰Bean类或⽅法,使⽤该Annotation时可以指定⼀个字符串数组作为参数,每个数组元素对应于⼀个强制初始化的Bean
@DependsOn({"steelAxe","abc"})
@Component
public class Chinese implements Person{
//codes here
}
以上就是本⽂的全部内容,希望对⼤家的学习有所帮助,也希望⼤家多多⽀持。
Spring源码解析-AntPathMatcher

Spring源码解析-AntPathMatcher最近在看SpringMVC的源码,发现request分发时,路径匹配最后是委托给AntPathMatcher实现的.索性看看吧.⽂章摘要: 1. ant匹配规则 2. PathMatcher接⼝ 3. 通过测试⽤例看AntPathMatcher的使⽤ant匹配规则AntPathMatcher如名使⽤的ant 的匹配规则,我们先看看吧. 字符wildcard 描述 ? 匹配⼀个字符 * 匹配0个及以上字符 ** 匹配0个及以上⽬录directories看⼏个官⽅的例⼦吧: com/t?st.jsp - 匹配: com/test.jsp , com/tast.jsp , com/txst.jsp com/*.jsp - 匹配: com⽂件夹下的全部.jsp⽂件 com/**/test.jsp - 匹配: com⽂件夹和⼦⽂件夹下的全部.jsp⽂件, org/springframework/**/*.jsp - 匹配: org/springframework⽂件夹和⼦⽂件夹下的全部.jsp⽂件 org/**/servlet/bla.jsp - 匹配: org/springframework/servlet/bla.jsp , org/springframework/testing/servlet/bla.jsp , org/servlet/bla.jspPathMatcher接⼝主要是判断是否匹配pattern,并解析出path中的参数1package org.springframework.util;23public interface PathMatcher {45/**6 * 判断传⼊的path是否可以作为pattern使⽤7*/8boolean isPattern(String path);910/**11 * 使⽤pattern匹配path12*/13boolean match(String pattern, String path);1415/**16 * 如名,是否开始部分匹配17*/18boolean matchStart(String pattern, String path);1920/**21 * 提取path中匹配到的部分,如pattern(myroot/*.html),path(myroot/myfile.html),返回myfile.html22*/23 String extractPathWithinPattern(String pattern, String path);2425/**26 * 提取path中匹配到的部分,只是这边还需跟占位符配对为map,27 * 如pattern(/hotels/{hotel}),path(/hotels/1),解析出"hotel"->"1"28*/29 Map<String, String> extractUriTemplateVariables(String pattern, String path);3031/**32 * 提供⽐较器33*/34 Comparator<String> getPatternComparator(String path);3536/**37 * 合并pattern,pattern1然后pattern238*/39 String combine(String pattern1, String pattern2);4041 }通过测试⽤例看AntPathMatcher的使⽤⼀看测试⽤例,瞬间服了,⼈家开发真是规范.⼈家整这么规范,还是有空直接看源码好了.这边挑⼏个简单的例⼦看看就好1. match 跟 matchStart 的差异,这个我们在测试⽤例看下⾯的情况会⽐较明确 这边的代码,我截取了⼀⼩部分1package org.springframework.util;2public class AntPathMatcherTests {3 @Test4public void match() {5// ...6assertFalse(pathMatcher.match("/x/x/**/bla", "/x/x/x/"));7// ...8 }9 @Test10public void withMatchStart() {11// ...12assertTrue(pathMatcher.matchStart("/x/x/**/bla", "/x/x/x/"));13// ...14 }15 }2. extractPathWithinPattern,代码很清楚,不废话1package org.springframework.util;2public class AntPathMatcherTests {3 @Test4public void extractPathWithinPattern() throws Exception {5// ...6 assertEquals("", pathMatcher.extractPathWithinPattern("/docs/commit.html", "/docs/commit.html"));7 assertEquals("cvs/commit", pathMatcher.extractPathWithinPattern("/docs/*", "/docs/cvs/commit"));8 assertEquals("docs/cvs/commit", pathMatcher.extractPathWithinPattern("/d?cs/*", "/docs/cvs/commit")); 9// ...10 }11 }3. extractUriTemplateVariables1package org.springframework.util;2public class AntPathMatcherTests {3 @Test4public void extractUriTemplateVariables() throws Exception {5 Map<String, String> result = pathMatcher.extractUriTemplateVariables("/hotels/{hotel}", "/hotels/1");6 assertEquals(Collections.singletonMap("hotel", "1"), result);7// ...8 result = pathMatcher.extractUriTemplateVariables("/{page}.*", "/42.html");9 assertEquals(Collections.singletonMap("page", "42"), result);10// ...11 }12/**13 * SPR-778714*/15 @Test16public void extractUriTemplateVarsRegexQualifiers() {17 Map<String, String> result = pathMatcher.extractUriTemplateVariables(18 "{symbolicName:[\\p{L}\\.]+}-sources-{version:[\\p{N}\\.]+}.jar",19 "com.example-sources-1.0.0.jar");20 assertEquals("com.example", result.get("symbolicName"));21 assertEquals("1.0.0", result.get("version"));22// ...23 }24 }4. combine1package org.springframework.util;2public class AntPathMatcherTests {3 @Test4public void combine() {5// ...6 assertEquals("/hotels", bine("/hotels", null));7 assertEquals("/hotels/booking", bine("/hotels/*", "/booking"));8 assertEquals("/hotels/**/booking", bine("/hotels/**", "booking"));9 assertEquals("/hotels/**/booking", bine("/hotels/**", "/booking"));10 assertEquals("/hotels/booking", bine("/hotels", "/booking"));11 assertEquals("/hotels/{hotel}", bine("/hotels/*", "{hotel}"));12 assertEquals("/hotels/**/{hotel}", bine("/hotels/**", "{hotel}"));13 assertEquals("/hotels/*/booking/{booking}", bine("/hotels/*/booking", "{booking}"));14 }15 }。
Spring的xml文件详解

Spring的xml⽂件详解spring的xml配置⽂件头:<?xml version="1.0" encoding="UTF-8"?><beans xmlns="/schema/beans"xmlns:xsi="/2001/XMLSchema-instance"xmlns:context="/schema/context"xsi:schemaLocation="/schema/beans/schema/beans/spring-beans-3.0.xsd/schema/context/schema/context/spring-context-3.0.xsd">...<!--中间xml⽂件部分-->.....</beans>⼀直在复制黏贴,但是不知道作⽤是什么,不理解的话常出错。
xmlns和命名空间⾸先,介绍⼀下xmlns的作⽤,如下所⽰,⼀个 xml ⽂档中如果包含如下两种定义不同,但是名称相同的元素, xml 解析器是⽆法解析的,因为它不能确定当你调⽤document.getElementsByTagName("book") 时应该返回哪个元素。
<!-- 这⾥的 table 元素描述的是⼀个表格--><table><tr><td>Apples</td><td>Bananas</td></tr></table><!-- 这⾥的 table 元素描述的是⼀个家居桌⼦--><table><name>African Coffee Table</name><width>80</width><length>120</length></table>这时候可以通过在名称增加前缀解决这个问题<!-- 这⾥的 table 元素描述的是⼀个表格--><h:table> <!--添加了前缀 h --><h:tr><h:td>Apples</h:td><h:td>Bananas</h:td></h:tr></h:table><!-- 这⾥的 table 元素描述的是⼀个表格--><f:table> <!--添加了前缀 f --><f:name>African Coffee Table</f:name><f:width>80</f:width><f:length>120</f:length></f:table>由此,引⼊⼀个概念命名空间,通过增加前缀表⽰不同的那是不同命名空间下的table,从⽽解决了⽭盾,但是不同的⼈都有⾃⼰创建的不同的命名空间来描述同样的东西,不利于xml⽂件信息的解析,⽐如说,同样都是⽔果,可以从颜⾊和⾹味不同⾓度来定义成如下两种形式:<!--按照⽔果⾹味来定义--><perfume:fruit><name>....</name><perfume>.....</perfume></perfume:fruit><!--按照⽔果颜⾊来定义--><color:fruit><name>....</name><color>....</color></color:fruit>为此,w3c(万维⽹联盟)对于⼀些类型,定义了对应的命名空间和这些类型的标准,xml解释器碰到这些类型的时候就会通过这些标准去解析这类型的标签,为了确保命名空间的唯⼀,所以不同的命名空间的通常使⽤URL作为被识别的id,如下例⼦:xmlns:xsi="/2001/XMLSchema-instance"这句话的作⽤是当前引⼊了⼀个叫做xsi的命名空间,xsi可以在接下来要使⽤该命名空间时所使⽤的,如下:<xsi:schemaLocation="...... ......">⽽这个很长的字符串,则是xsi这个名称空间被xml解释器内部所识别的时候所真正使⽤的id,但也本⾝只是被当做⼀个字符串名字去处理,xml解释器根据这个id去获取它对应的标准,从⽽知道这个命名空间定义有什么样的标签(xml解释器⾃带有⼀些通⽤的命名空间的标准),这个字符串虽然看起来是URL,但是和对应的⽹页上的信息没有关系,只是⽤来提供命名空间唯⼀性的作⽤,⽹址有时可以被打开,上⾯会有关于该命名空间的信息。
spring源码解析(一)---占位符解析替换

spring源码解析(⼀)---占位符解析替换⼀、结构类图①、PropertyResolver : Environment的顶层接⼝,主要提供属性检索和解析带占位符的⽂本。
bean.xml配置中的所有占位符例如${}都由它解析②、ConfigurablePropertyResolver : 该接⼝定义了如何对组件本⾝进⾏配置。
如:刚刚提到获取value时可以指定任意类型,这依赖于ConversionService进⾏类型转换,当前接⼝就提供了对ConversionService的设置和获取。
另外,可以配置属性占位符的格式,包括:占位符前缀(默认为"${")、占位符后缀(默认为"}")、占位符值分隔符(默认为":",⽤于分隔propertyName和defaultValue)。
组件还可以设置哪些属性是必须存在的,还可以校验必须存在的属性是否真的存在(不存在的话会抛出异常)③、AbstractPropertyResolver : 实现了ConfigurablePropertyResolver接⼝的所有⽅法④、PropertySourcesPropertyResolver : 以PropertySources属性源集合(内部持有属性源列表List<PropertySource>)为属性值的来源,按序遍历每个PropertySource,获取到⼀个⾮null的属性值则返回⼆、demo⽰例public static void main(String[] args) {Properties properties = System.getProperties();properties.setProperty("prefixName", "read-code");ApplicationContext ac = new ClassPathXmlApplicationContext("classpath:${prefixName}-spring.xml");ReadCodeService readCodeService = (ReadCodeService) ac.getBean("readCodeService");readCodeService.say();}View Code三、源码剖析1、⼊⼝ :ClassPathXmlApplicationContext构造函数setConfigLocationspublic ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)throws BeansException {super(parent);setConfigLocations(configLocations);if (refresh) {refresh();}}2、AbstractRefreshableConfigApplicationContext①、ClassPathXmlApplicationContext构造函数调⽤它的基类AbstractRefreshableConfigApplicationContext.setConfigLocations/*** Set the config locations for this application context.* <p>If not set, the implementation may use a default as appropriate.*/public void setConfigLocations(String... locations) {if (locations != null) {Assert.noNullElements(locations, "Config locations must not be null");this.configLocations = new String[locations.length];for (int i = 0; i < locations.length; i++) {this.configLocations[i] = resolvePath(locations[i]).trim(); // 解析路劲}}else {this.configLocations = null;}}②、解析路劲/*** Resolve the given path, replacing placeholders with corresponding* environment property values if necessary. Applied to config locations.* @param path the original file path* @return the resolved file path* @see org.springframework.core.env.Environment#resolveRequiredPlaceholders(String)*/protected String resolvePath(String path) {return getEnvironment().resolveRequiredPlaceholders(path);}3、AbstractPropertyResolverpublic String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {if (this.strictHelper == null) {this.strictHelper = createPlaceholderHelper(false);}return doResolvePlaceholders(text, this.strictHelper);}上述⽅法主要做了两件事 :①、初始化占位符解析器createPlaceholderHelper : 主要是初始化占位符的常量,eg : 前缀 ${ 后缀} and so on②、调⽤私有⽅法---替换占位符具体值private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {return helper.replacePlaceholders(text, new PropertyPlaceholderHelper.PlaceholderResolver() {@Overridepublic String resolvePlaceholder(String placeholderName) {return getPropertyAsRawString(placeholderName);}});}4、占位符 key - > value ,实现PropertyPlaceholderHelper内部接⼝PlaceholderResolver⽅法resolvePlaceholder。
Spring源码分析基本介绍

Spring源码分析基本介绍摘要:本⽂结合《Spring源码深度解析》来分析Spring 5.0.6版本的源代码。
若有描述错误之处,欢迎指正。
前⾔作为⼀名开发⼈员,阅读源码是⼀个很好的学习⽅式。
本⽂将结合《Spring源码深度解析》来分析Spring 5.0.6版本的源代码,若有描述错误之处,欢迎指正。
Spring是2003年兴起的⼀个轻量级Java开源框架,旨在解决企业应⽤开发的复杂性。
Spring发展⾄今,衍⽣出⾮常丰富的模块,并应⽤在多种场景,⽐如:桌⾯应⽤,Web应⽤等。
Spring的模块化可以允许你只使⽤需要的模块,⽽不必全部引⼊。
⽬录⼀、整体架构1. 核⼼容器2. 数据访问/集成3. Web4. AOP5. Test⼆、设计理念三、使⽤场景1. 典型的Spring web应⽤程序2. Spring中间层使⽤第三⽅web框架3. 远程调⽤4. EJBs-包装现存POJOs⼀、整体架构Spring框架是⼀个分层架构,他包含⼀系列的功能要素,并被分为⼤约20个模块,如下图所⽰(很遗憾,并没有找到Spring5的架构图,下图是Spring4的,但结合Spring5的源码来看,该图还是能够体现Spring5的核⼼模块)这些模块被总结为以下⼏部分。
1. 核⼼容器Core Container(核⼼容器)包含有Core、Beans、Context和Expression Language模块。
Core和Beans模块是框架的基础部分,提供IoC(控制反转)和DI(依赖注⼊)特性。
这⾥的基础概念是BeanFactory,它提供对Factory模式的经典实现来消除对程序性单例模式的需要,并真正地允许你从程序逻辑中分离出依赖关系和配置。
Core模块主要包含Spring框架基本的核⼼⼯具类,Spring的其他组件都要使⽤到这个包⾥的类,Core模块是其他组件的基本核⼼。
当然你也可以在⾃⼰的应⽤系统中使⽤这些⼯具类。
Spring技术内幕:深入解析Spring架构与设计原理

Spring技术内幕深入解析Spring架构与设计原理(一)引子缘起已经很久没有写帖子了,现在总算是有点时间写些东西,也算是对自己的一个记录吧。
刚刚完成了一个软件产品,从概念到运营都弄了一下,正在推广当中,虽然还没有能够达到盈亏平衡,但是这个过程,对自己也算是一种历练。
先不管结果如何,好呆走过这么一遭了。
我打算用这个帖子,把自己在这个过程中的一些心得,特别是对Spring新的理解,记录下来。
使用这个帖子的标题,持续下来。
简单来说,自己的软件产品是一个基于互联网的SaaS协同软件平台,操作简单,支持流程定义,管理和多种客户端 -像短信,MSN,智能手机什么的(我这里就不多做什么广告了),也有一个企业版的版本,使用的技术框架是Hibernate + Spring + Wicket,下面是Linux和MySQL,还有云计算的平台的使用,以支持其扩展性,虽然现在还没有可扩展性的需求,但似乎不难从SaaS上,就会想到云计算, 其实,它们真的是天生的一对!关于云计算,自己对这个技术很感兴趣,觉得和开源软件的结合,是很有意思的,因为它们都有基于服务的基因,在云计算平台的使用上,也有一些初步的实践。
云计算是一个很有意思的话题,但在这里主要是想谈Spring,所以对云计算,这里就先不多说了,但非常欢迎有兴趣的朋友和一起另外找地方讨论!回到正题,在我自己的产品中,其中除了Wicket和云计算外,其他都是大家非常熟知的了,像Hibernate, Spring, MySQL什么的。
在这个过程中,发现自己对一些技术点也有了新的认识,最有体会的是Spring。
当然,在这个过程中,更大的收获是对产品开发整个过程的认识,在这点上,真是一言难尽........回到自己还算了解的Spring, 这次我使用的是3.0的代码,所以,有机会也把这些代码读了几遍,比原来的理解要加深了许多,也发现了不少和2.0代码不同的地方,以及自己一些对 Spring的新的理解,这些,就让我就用这个帖子系列,给自己总结一下,也算是对自己以前的那个代码分析的帖子做一个新的交代吧。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
正则表达式切入点示例
பைடு நூலகம்
1.
2.
3. 4. 5. 6. 7. 8. 9.
切入点的运算
Spring在切入点上支持以下运算:
或和与。或运算表示只需有一个切入点被匹配 就执行方法。与运算表示所有的切入点都匹配 的情况下才执行。通常或运算要更有用一些。 org.springframework.aop.support.Pointcuts类 中的静态方法来编写 或者使用同一个包内的ComposablePointcut类。 不过使用AspectJ切入点表达式通常会更简单一 些。
public abstract class LogWriter { abstract void writeLog(); }
比如一个医生的职责就是看病,至于照顾 病人完全可以交给护士去做,这样条理才 能清楚。 再者,Java只提供了单继承,因此具体访问 类只能继承这个父类,如果具体访问类还 要继承其它父类,比如另外一个如JFrame 的父类,将无法方便实现。
各种通知类型
Around通知:包围一个连接点的通知,如方法调用。这是 最强大的通知。Around通知在方法调用前后完成自定义的 行为,它们负责选择继续执行连接点或通过返回它们自己 的返回值或抛出异常来短路执行。 Before通知:在一个连接点之前执行的通知,但这个通知 不能阻止连接点前的执行(除非它抛出一个异常)。 Throws通知:在方法抛出异常时执行的通知。Spring提供 强制类型的Throws通知,因此你可以书写代码捕获感兴趣 的异常(和它的子类),不需要从Throwable或Exception 强制类型转换。 After returning通知:在连接点正常完成后执行的通知。 例如,一个方法正常返回,没有抛出异常。
切入点可以使用
实用切入点实现
Spring提供几个实用的切入点实现,一些可以直接使用, 另一些需要子类化来实现应用相关的切入点。
(1)静态切入点
静态切入点只基于方法和目标类,而不考虑方法的参数。静态切入点足 够满足大多数情况的使用。Spring可以只在方法第一次被调用的时候计算静 态切入点,不需要在每次方法调用的时候计算。 正则表达式切入点。 属性驱动的切入点:一类重要的静态切入点是元数据驱动的切入点。 它使用元数据属性的值,典型地,使用源代码级元数据。
例如,某一个操作在各个模块中都有涉及,这个操作就 可以看成“横切”存在于系统当中。 在许多情况下,这些操作与业务逻辑相关性不强或者不 属于逻辑操作的必须部分,而面向对象的方法很难对这 种情况做出处理。比如记录日志,按照传统的编程习惯, 可能要这样做:
1. 2. 3. 4.
然后,所有需要做日志记录的类继承这个抽象的父类 LogWriter,然后分别实现LogWriter抽象类中的 writeLog()方法。可是这不仅破坏了代码的完整性,也 给以后的维护工作带了困难。
如果能将“不可见的”、通用的日志代码 注入主程序中,那条理可就清楚多了。AOP 可以做到。 Spring framework是很有前途的AOP技术。 作为一种非侵略性的,轻型的 AOP framework,无需使用预编译器或其它 的元标签,便可以在Java程序中使用它。
AOP 相关概念
方面(Aspect):一个关注点的模块化,这个关注点实现可能另外横切多 个对象。事务管理是J2EE应用中一个很好的横切关注点例子。 连接点(Joinpoint):程序执行过程中明确的点,如方法的调用或特定 的异常被抛出等。 通知(Advice):在特定的连接点,AOP框架执行的动作。各种类型的 通知包括“around”、“before”和“throws”通知。 切入点(Pointcut):指定一个通知将被引发的一系列连接点的集合。 引入(Introduction):添加方法或字段到被通知的类。Spring允许引入新 的接口到任何被通知的对象。 目标对象(Target Object):包含连接点的对象,也被称为被通知或被代 理对象。 AOP代理(AOP Proxy):AOP框架创建的对象,包含通知。在Spring中, AOP代理可以是JDK动态代理或CGLIB代理。 编织(Weaving):组装方面来创建一个被通知的对象。可以在编译时完 成(例如使用AspectJ编译器),也可以在运行时完成。
第十六章
Spring AOP面向方面编程
课程内容
AOP基本原理 AOP概念 Spring 框架介绍 切入点 Spring的通知类型 创建AOP代理 AOP在Spring中的应用 使用Spring AOP面向方面编程
什么是AOP
AOP是OOP的延续,是Aspect Oriented Programming的缩写,意思是面向方面编程。 AOP实际是GOF四人组设计模式的一种扩展,设计 模式所追求的是降低代码之间的耦合度,增加程 序的灵活性和可重用性,AOP实际上就是设计模 式所追求目标的一种实现。 所谓的分离关注就是将某一通用的需求功能从不 相关的类中分离出来;同时,能够使得很多类共 享一个行为,一旦行为发生变化,不必修改很多 类,只要修改这个行为就可以。
切入点
Spring的切入点模型能够使切入点独立于通知类型被重用。 同样的切入点有可能接受不同的通知。Spring的通知有四种类型, 如下表所示:
类型 接口 说明 在目标方法被调用之 前调用
前置通知 org.springframework.aop.Method BeforAdvice
后置通知 org.springframework.aop.AfterRet 当目标方法被调用之 后调用 urningAdvice 环绕通知 org.aopalliance.intercept.MethodI nterceptor 异常通知 org.springframework.aop.Throws Advice 栏截对目标对象方法 的调用 当目标方法抛出异常 时调用