相关课程:Spring入门篇——moocer
文章目录
第一章 概述
1-1 Spring入门课程简介
课程包含内容
- 什么是框架
- Spring简介
- IOC(配置、注解)
- Bean(配置、注解)
- AOP(配置、注解、AspectJ、API)
主要介绍SpringFramework基础的IOC、AOP
如何学习Spring
- 掌握用法
- 深入理解
- 不断实践
- 反复总结
- 再次深入理解与实践
资源
1-2 Spring概况
- Spring是一个开源框架,为了解决企业应用开发的复杂性而创建的,但现在已经不止应用于企业应用。
- 是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架
- 从大小与开销两方面而言Spring都是轻量级的
- 通过控制反转(IoC)的技术达到松耦合的目的
- 提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务进行内聚性的开发。
- 包含并管理应用对象的配置和生命周期,这个意义上是一种容器
- 将简单的组件配置、组合成为复杂的应用,这个意义上是一种框架
- Spring上开发应用简单、方便、快捷
Spring作用
- 容器
- 提供了对多种技术的支持
- JMS
- MQ支持
- UnitTest
- …
- AOP(事务管理、日志等)
- 提供了众多方便应用的辅助类(JDBC Template等)
- 对主流应用框架(Hibernate等)提供了良好的支持
适用范围
- 构建企业应用(Spring MVC + Spring + Hibernate/ibatis)
- 单独使用Bean容器(Bean管理)
- 单独使用AOP进行切面处理
- 其他的Spring功能,如:对消息的支持等
- 在互联网中的应用
1-3 Spring框架
什么是框架
- 维基百科:软件框架,通常指的是为了实现某个业界标准或完成特定基本任务的软件组件规范,也指为了实现某个软件组件规范时,提供规范所要求之基础功能的软件产品
- 框架就是制定一套规范或者规则(思想),大家(程序员)在该规范或者规则(思想)下工作。或者说就是使用别人搭好的舞台,你来做表演
- 框架的特点
- 半成品
- 封装了特定的处理流程和控制逻辑
- 成熟的、不断升级改进的软件
- 框架与类库的区别
- 框架一般是封装了逻辑、高内聚的,类库则是松散工具的组合
- 框架专注于某一领域,类库则是更通用的
为什么使用框架
- 软件系统日趋复杂
- 重用度高,开发效率和质量提高
- 软件设计人员更专注于对领域的了解,使需求分析更充分
- 易于上手、快速解决问题
第二章 Spring IoC容器
接口及面向接口编程
接口
- 用于沟通的中介物的抽象化
- 实体把自己提供给外界的一种抽象化说明,用以由内部操作分离出外部沟通方法,使其能被修改内部而不影响外界其他实体与其交互的方式
- 对应Java接口即声明,声明了哪些方法是对外提供的
- 在Java8中,接口可以拥有方法体
面向接口编程
- 结构设计中,分清层次及调用关系,每层只向外(上层)提供一组功能接口,各层间仅依赖接口而非实现类
- 接口实现的变动不影响各层间的调用,这一点在公共服务中尤为重要
- “面向接口编程”中的“接口”是用于隐藏具体实现和实现多态性的组件
什么是IoC
- IoC:控制反转,控制权的转移,应用程序本身不负责依赖对象的创建和维护,而是由外部容器负责创建和维护
- DI(依赖注入)是其一种实现方式,是由IoC容器在运行期间,动态地将某种依赖关系注入到对象之中
- 目的:创建对象并且组装对象之间的关系
- 在IoC容器中,所有的对象都被称之为Bean
Spring的Bean配置
- Spring对于Bean的管理由两种方式:基于xml文件的配置;注解
Bean的初始化
- 基础:两个包
- org.springframework.beans
- org.springframework.context
- BeanFactory提供配置结构和基本功能,加载并初始化Bean
- ApplicationContext保存了Bean对象并在Spring中被广泛使用
- 方式:ApplicationContext
- 本地文件
- Classpath
- Web应用中依赖Servlet或Listener
Spring注入
- Spring注入是指在启动Spring容器加载bean配置的时候,完成对变量的赋值行为
- 常用的两种注入方式
- 设值注入
- 构造注入
设值注入
在这里,injectoinDAO是injectionService的一个属性,自动的调用类中成员变量的set方法,进行注入,对属性进行赋值。
...
<bean id="injectionService" class="...">
<property name="injectionDAO" ref="injectionDAO"/>
</bean>
<bean id="injectionDAO" class="..."/>
...
构造注入
在IoC容器调用Service的class中类的构造方法去创建类的实例的时候,根据reference把DAO的class中类的实例赋值给injectionDAO这个属性。
...
<bean id="injectionService" class="...">
<constructor-arg name="injectionDAO" ref="injectionDAO"/>
</bean>
<bean id="injectionDAO" class="..."/>
...
第三章 Spring Bean装配(上)
- Id
- Class
- Scope
- Constructor arguments
- Properties
- Autowiring mode
- lazy-initialization mode
- Initialization/destruction method
Bean的作用域
- singleton:单例,指一个Bean容器中只存在一份
- prototype:每次请求(每次使用)创建新的实例,destroy方式不生效
- request:每次http请求创建一个实例且仅在当前request内有效
- session:同上,每次http 请求创建,当前session内有效
- global session:基于portlet的web中有效(portlet定义了global session),如果是在web中,同session
在Spring中通过设置scope,来定义其作用域
Bean的生命周期
- 定义
- 初始化
- 使用
- 销毁
初始化
两种方式:
- 实现
org.springframework.beans.factory.InitializingBean
接口,覆盖afterPropertiesSet
方法 - 配置init-method(单个Bean)/default-init-method(所有Bean统一配置)
销毁
有两种方式:
-
实现
org.springframework.beans.factory.DisposableBean
接口,覆盖destroy
方法 -
覆盖destroy-method/default-destroy-method
-
两种方法都存在时,接口方法要先于自己配制的方法(即方法一先于方法二执行)
-
当bean实现了自己的init和destroy时,默认的初始化和销毁方法会失效
Aware
- Spring中提供了一些以Aware结尾的接口,实现了Aware接口的bean在被初始化之后,可以获取相应资源
- 通过Aware接口,可以对Spring相应资源进行操作(一定要慎重)
- 为对Spring进行简单的扩展提供了方便的入口
Bean的自动装配(Autowiring)
- No:不做任何操作(默认选项)
- byname:根据属性名自动装配。此选项将检查容器并根据名字查找与属性完全一致的bean,并将其与属性自动装配。在IoC容器中,不允许两个bean拥有相同的id。
- byType:如果容器中存在一个与指定属性类型相同的bean,那么将与该属性自动装配;如果存在多个该类型bean,那么抛出异常,并指出不能使用byType方式进行自动装配;如果没有找到相匹配的bean,则什么事都不发生
- Constructor:与byType方式类似,不同之处在于它应用于构造器参数。如果容器中没有找到与构造器参数类型一致的bean,那么抛出异常
Resources
- 针对于资源文件的统一接口
- Resources
- UrlResource
- ClassPathResource
- FileSystemResources
- ServletContextResources
- InputStreamResources
- ByteArrayResources
ResourceLoader
所有的application context都实现了ResourceLoader接口,所有的application context都可以用来获取Resource的实例。
前缀
- classpath: Loaded from the classpath
- file: Loaded as a URL, from the filesystem
- http: Loaded as a URL
- (none): Depends on the underlying ApplicationContext
第四章 Spring Bean装配(下)
Bean管理的注册实现
- Classpath扫描与组建管理
- 类的自动检测与注册Bean
- context:annotaion-config/
- @Component, @Repository, @Service, @Controller
- @Required
- @Autowired
- @Qualifier
- @Resource
Classpath扫描与组建管理
从Spring3.0开始,Spring JavaConfig项目提供了很多特性,包括使用java而不是XML定义bean,即可以通过注解的方式去定义bean。
- @Component是一个通用注解,可用于任何bean
- @Repository, @Service, @Controller时更有针对性的注解
- @Repository通常用于注解DAO类,即持久层
- @Service通常用于注解Service类,即服务层
- @Controller通常用于Controller类,即控制层(MVC)
元注解
- 许多Spring提供的注解可以作为自己的代码,即“元数据注解”,元注解是一个简单的注解,可以应用到另一个注解
- 除了value(),元注解还可以有其他的属性,允许定制
@Component // Spring will see this and treat @Service in the same way as @Component
public @interface Service {
// ...
}
类的自动检测与Bean的注册
-
Spring可以自动检测类并注册Bean到ApplicationContext中
-
为了能够检测这些类并注册相应的Bean,需要下面内容,其中
base-package
表示扫描该包下的所有文件<beans xmlns=""...> <context:component-scan base-package="org.example"/> </beans>
<context:component-scan>
扫描基于类的注解,而<context:annotation-config>
只能在完成了bean的注册后,去处理bean中基于方法或成员变量的注解。通常在使用前者后,不用再使用后者。AutowiredAnnocationBeanPostProcessor
和CommonAnnotationBeanPostProcessor
也会被包含进来
context:annotaion-config/
- 通过在基于XML的Spring配置如下标签(请注意包含上下文命名空间)
- context:annotaion-config/仅会查找在同一个application context中的bean注解
使用过滤器进行自定义扫描
-
默认情况下,类被自动发现并注册bean的条件是:使用
@Component
,@Repository
,@Service
,@Controller
注解或者使用@Component
的自定义注解 -
可以使用
use-default-filters="false"
禁用自动发现与注册 -
可以通过过滤器修改上面的行为,如下例子的XML配置忽略所有的
@Repository
注解并用“Stub”代替<beans> <context:component-scan base-package="org.example"> <context:include-filter type="regex" expression=".*Stub.*Repository"/> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/> </context:component-scan> </beans>
-
Filter Type: annotation, assignable, aspectj, regex, custom
定义Bean
-
扫描过程中组件被自动检测,Bean名称是由
BeanNameGenerator
生成的(@Component
,@Repository
,@Service
,@Controller
都会有个name属性用于显式设置Bean Name) -
可自定义bean命名策略,实现
BeanNameGenerator
接口,并一定要包含一个无参数构造器<beans> <context:component-scan base-package="org.example" name-generator="org.example.MyNameGenerator" /> </beans>
作用域(Scope)
-
通常情况下自动查找Spring组件,其
scope
是singleton
,Spring2.5提供了一个标识scope的注解@Scope
-
也可以自定义scope策略,实现
ScopeMetadataResolver
接口并提供一个无参构造器<beans> <context:component-scan base-package="org.example" scope-resolver="org.example.MyScopeResolver" /> </beans>
代理方式
-
可以使用
scoped-proxy
属性指定代理,有三个可选:no
,interfaces
,targetClass
<beans> <context:component-scan base-package="org.example" scoped-proxy="interfaces" /> </beans>
Autowired 注解说明
@Required
- 适用于bean属性的setter方法
- 这个注解仅仅表示,受影响的bean属性必须在配置时被填充,通过在bean定义或通过自动装配一个明确的属性值
@Autowired
- 可以将
@Autowired
注解理解为“传统”的setter方法 - 可用于构造器或成员变量
在使用@Autowired
的过程中
-
默认情况下,如果因找不到合适的bean将会导致autowiring失败抛出异常,可以通过下面的方式避免
publci class SimpleMovieLister { private MovieFinder movieFinder; @Autowired(required=false) public void setMovieFinder (MovieFinder movieFinder) { this.movieFinder = movieFinder; } }
-
每个类只能有一个构造器被标记为
required=true
-
@Autowired
的必要属性,建议使用@Required
注解
可以使用@Autowired
注解那些众所周知的解析依赖性接口,比如:BeanFactory
, ApplicationContext
, Environment
, ResourceLoader
, ApplicationEventPublisher
, MessageSource
public class MovieRecommender {
@Autowired
private ApplicationContext context;
public MovieRecommender() {
}
}
- 可以通过添加注解给需要该类型的数组的字段或方法,以提供
ApplicationContext
中的所有特定类型的bean - 可以用于装配key为String的Map
- 如果希望数组有序,可以让bean实现
org.springframework.core.Ordered
接口或使用@Order
注解
@Autowired
是由Spring BeanPostProcessor处理的,所以不能再自己的BeanPostProcessor或BeanFactoryPostProcessor类型应用这些注解,这些类型必须通过XML或者Spring的@Bean
注解加载。
@Qualifier
- 按类型自动装配可能多个bean实例的情况,可以使用Spring的
@Qualifier
注解缩小范围(或指定唯一),也可以用于指定单独的构造器参数或方法参数 - 可用于注解结合类型变量
在Java文件中实现:
public class MovieRecommender {
// 在属性当中使用
@Autowired
@Qualifier("main")
private MovieCatalog movieCatalog;
// 或者是在方法参数当中使用
public void prepare(@Qualifier("main")MovieCatalog moviecatalog, CustomerPreferenceDao customerPreferenceDao) {
}
}
在XML文件中实现:
<bean class="Example.SimpleMovieCatalog">
<qualifier value="action"/>
</bean>
- 如果通过名字进行注解注入,主要是用的不是
@Autowored
(即使在技术上能够通过@Qualifier
指定bean的名字),替代方式是使用JSR-250的@Resource
,它是通过其独特的名字来定义来识别特定的目标(这是一个与所声明的类型是无关的匹配过程) - 因语义差异,集合或Map类型的bean无法通过
@Autowired
来注入,因为没有类型匹配到这样的bean,为这些bean使用@Resource
注解,通过唯一名称引用集合或Map的bean。 @Autowired
适用于fileds, constructors, multi-argument methods这些允许在参数级别使用@Qualifier
注解缩小范围的情况@Resource
适用于成员变量、只有一个参数的setter方法,所以在目标是构造器或一个多参数方法时,最好的方式是使用@Qualifier
定义自己的qualifier注解
Java
// ...
@Qualifier
public @interface Genre {
String value();
}
XML
<bean class="example.SimpleMovieCatalog">
<qualifier type="Genre" value="Action"/>
</bean>
基于Java的容器注解
@Bean
-
@Bean
标识一个用于配置和初始化一个由Spring IoC容器管理的新对象的方法,类似于XML配置文件的<bean/>
-
通常在Spring的
@Configuration
注解的类中使用@Bean
注解任何方法,@Component
注解也可以@Configuration public class AppConfig { @Bean public MyService myService() { return new MyServiceImpl(); } }
该方法等同于
<beans> <bean id="myService" class="com.acme.services.MyServiceImpl"/> </beans>
-
通过
@Bean(name="beanName")
,自定义Bean name,如果没有指定名称,则默认是方法的名称。
@ImportResource and @Value
从资源文件中加载资源文件的配置
配置文件:XML中的context:property-placeholder
进行资源的引用,通过${proName}
的形式进行资源中key-value的引用。
注解:通过@ImportResource
进行资源的引用,通过@Value
注解,从资源文件中取出key对应的value赋值给对应的变量。
@Bean and @Scope
- 默认
@Bean
作用域是单例,使用@Scope
,通过为value
赋值,来更改为prototype
等其他作用域。 - 默认
@Bean
的代理方式是默认,使用@Scope
,通过为proxyMode
赋值,来更改为interfalces
等其他代理方式。
基于范型的自动装配
@Autowired
@Configuration
public class MyConfiguration {
@Bean
public StringStore stringStore() {
return new StringStore();
}
@Bean
public IntegerStore integerStore() {
return new IntegerStore();
}
}
// ...
// <String> qualifier, injects the stringStore bean
@Autowired
private Store<String> s1;
// Inject all Store beans as long as they have an <Integer> generic
// Store<String> beans will not appear in this list
@Autowired
private<list<Store<Integer>> s;
自定义qualifier注解
-
CustomAutowireConfigurer是BeanFactoryPostProcessor的子类,通过它可以注解自己的qualifier注解类型(即使没有使用Spring的@Qualifier注解)
<bean id="customAutowireConfigurer" class="..."> <property name="customQualifierTypes"> <set> <value>example.CustomQualifier</value> </set> </property> </bean>
-
该AutowireCandidateResolver决定自动装配的候选者
- 每个bean定义的autowire-candidate值
- 任何中的default-autowire-candidates
- @Qualifier注解及使用CustomAutowireConfigurer的自定义类型
Spring对JSR的支持
@Resource
- Spring还支持使用JSR-250
@Resource
注解的变量或setter方法,这是一种在Java EE 5和6的通用模式,Spring管理的对象也支持这种模式 @Resource
有一个name属性,并且默认Spring解释该值作为被注入bean的名称- 如果没有显式地指定
@Resource
的name,默认的名称是从属性名或者setter方法得出 - 注解提供的名字被解析为以一个bean的名称,这是由ApplicationContext中的CommonAnnotationBeanPostProcessor发现并处理的
@PostConstruct and @PreDestroy
- CommonAnnotationBeanPostProcessor不仅能识别JSR-250中的生命周期注解
@Resource
,在Spring2.5中引入支持初始化回调和销毁回调,前提是CommonAnnotationBeanPostProcessor是在Spring的ApplicationContext中注册的
public class CachingMovieLister {
@PostConstruct
public void populateMovieCache() {
// populates the movie cache upon intialization
}
@PreDestroy
public void clearMovieCache() {
// clears the movie cache upon destruction
}
}
JSR330标准注解
- 从Spring3.0开始支持JSR330标准注解(依赖注入注解),其扫描方式与Spring注解一致
- 使用JSR330需要依赖javax.inject包
- 使用Maven引入方式
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
@Inject
@Inject
等效于@Autowired
,可以适用于类、属性、方法、构造器
@Named
- 如果想使用特定名称进行依赖注入,使用
@Named
@Named
于@Component
是等效的
第五章 Spring AOP基本概念
AOP基本概念及特点
AOP实现方式
- 预编译
- AspectJ
- 运行期动态代理(JDK动态代理、CGLib动态代理)
- Spring AOP、JbossAOP
AOP相关概念
- 切面Aspect:一个关注点的模块化,这个关注点可能会横切多个对象
- 连接点Joinpoint:程序执行过程中的某个特定的点
- 通知Advice:在切面的某个特定的连接点上执行的动作
- 切入点Pointcut:匹配连接点的断言,在AOP中通知和一个切入点表达式相关
- 引入Introduction:在不修改类代码的前提下,为类添加新的方法和属性
- 目标对象Target Object:被一个或者多个切面所通知的对象
- AOP代理AOP Proxy:AOP框架创建的对象,用来实现切面契约(aspect contract)(包括通知方法执行等功能)
- 织入Weaving:把切面连接到其他的应用程序类型或者对象上,并创建一个被通知的对象,分为:编译时织入、类加载时织入、执行时织入
Advice的类型
- 前置通知Before Advice:在某个连接点之前执行的通知,但不能阻止连接点前的执行(除非它抛出一个异常)
- 返回后通知After Returning Advice:在某连接点正常完成后执行的通知
- 抛出异常后通知After Throwing Advice:在方法跑出异常退出时执行的通知
- 后通知After(Finally) Advice:当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)
- 环绕通知Around Advice:包围一个连接点的通知
Spring框架中AOP的用途
- 提供了声明式的企业服务,特别是EJB的替代服务的声明
- 允许用户定制自己的方面,以完成OOP与AOP的互补使用
Spring的AOP实现
- 纯java实现,无需特殊的编译过程,不需要控制类加载器层次
- 目前只支持方法执行连接点(通知Spring Bean的方法执行)
- 不是为了提供最完整的AOP实现(尽管它非常强大),而是侧重于提供一种AOP实现和Spring IoC容器直接的整合,用于帮助解决企业应用中的常见问题
- Spring AOP不会与AspectJ竞争,从而提供综合全面的AOP解决方案
有接口和无接口的Spring AOP实现区别
- Spring AOP默认使用标准的JavaSE动态代理作为AOP代理,这使得任何接口(或者接口集)都可以被代理
- Spring AOP中也可以使用CGLIB代理(如果以一个业务对象并没有实现一个接口)
配置切面aspect
Schema —— based AOP
基于使用配置的AOP实现
- Spring所有的切面和通知器都必须放在一个
<aop:config>
内(可以配置包含多个<aop:config>
元素),每一个<aop:config>
可以包含pointcut、advisor和aspect元素(它们必须按照这个顺序进行声明) <aop:config>
风格的配置大量使用了Spring的自动代理机制
配置切入点Pointcut
在AOP中通知Advice和一个切入点表达式关联
pointcut说明(查找Spring官方文档)
- execution用于确定切入点
- execution(public * *(…)):切入点为执行所有public方法时
- execution(* set*(…)):切入点为执行所有set开始的方法时
- execution(* com.xyz.service.AccountService.*(…)):切入点为执行AccountService类中的所有方法时
- execution(* com.xyz.service…(…)):切入点为执行com.xyz.service包下的所有方法时
- execution(* com.xyz.service…(…)):切入点为执行com.xyz.service包及其子包下的所有方法时
- within用于匹配指定类型内的方法执行(only in Spring AOP)
- within(com.xyz.service.*)
- within(com.xyz.service…*)
- this用于匹配当前AOP代理对象类型的执行方法(only in Spring AOP)
- this(com.xyz.service.AccountService)
- target用于匹配当前目标对象类型的执行方法(only in Spring AOP)
- args用于匹配当前执行的方法传入的参数为指定类型的执行方法(only in Spring AOP)
Advice应用
- Before advice:在
<aop:aspect>
中配置<aop:before>
,在其中指定pointcut-ref或者pointcut,以及method - After returning advice:在
<aop:aspect>
中配置<aop:after-returning>
,在其中指定pointcut-ref或者pointcut,以及method - After throwing advice:
<aop:aspect>
中配置<aop:after-throwing>
,在其中指定pointcut-ref或者pointcut,以及method,并可以使用throwing属性来指定可被传递的异常的参数名称 - After(finally) advice:
<aop:aspect>
中配置<aop:after>
,在其中指定pointcut-ref或者pointcut,以及method - Around advice:
<aop:aspect>
中配置<aop:around>
,在其中指定pointcut-ref或者pointcut,以及method,通知方法的第一个参数必须是ProceedingJoinPoint类型 - Advice parameters:在
<aop:pointcut>
中的expression内,表达式中添加参数类型,并在表达式后添加and args(parameters)
字段,其中parameters为参数类型所对应的参数名
Introductions应用
- 简介允许一个切面声明一个实现指定接口的通知对象,并且提供了一个接口实现类来代表这些对象
- 由
<aop:ascpect>
中的<aop:declare-parents>
元素声明,该元素用于声明所匹配的类型拥有一个新的parent(因此得名)
Aspect instantiation models
- schema-defined aspects只支持singleton model
Advisors
- advisor就像一个小的自包含的方面,只有一个advice
- 切面自身通过bean表示,并且必须实现某个advice接口,同时,advisor也可以很好的利用AspectJ的切入点表达式
- Spring通过配置文件中
<aop:advisor>
元素支持advisor。实际使用中,大多数情况下它会和transactional advice配合使用 - 为了定义一个advisor的优先级以便让advice可以有序,可以使用order属性来定义advisor的顺序
第六章 Spring AOP的API介绍
Pointcut
- 实现之一:NameMatchMethodPointcut,根据方法名字进行匹配
- 成员变量:mappedNames,匹配的方法名集合
<bean id="pointcutBean" class="...">
<property name="mappedNames">
<list>
<value>sa*</value>
</list>
</property>
</bean>
Before advice
- 一个简单的通知类型
- 必须实现org.springframework.aop.BeforeAdvice接口
- 只是在进入方法之前被调用,不需要MethodInvocation对象
- 前置通知可以在连接点执行前插入自定义行为,但不能改变返回值
Throws advice
- 如果连接点抛出异常,throws advice在连接点返回后被调用
- 如果throws-advice的方法抛出异常,那么它将覆盖原有异常
- 接口org.springframework.aop.ThrowsAdvice不包含任何方法,仅仅是一个声明,实现类需要实现类似下面的方法
- void afterThrowing([Method, args, target], ThrowableSubclass);
After Returning advice
- 后置通知必须实现org.springframework.aop.AfterReturningAdvice接口
- 可以访问返回值(但不能修改)、被调用的方法、方法的参数和目标
- 如果抛出异常,将会抛出拦截器链,替代返回值
Interception around advice
- Spring的切入点模型使得切入点可以独立与advice重用,以针对不同的advice可以使用相同的切入点
Introduction advice
- Spring把引入通知作为一种特殊的拦截通知
- 需要IntroductionAdvisor和IntroductionInterceptor
- 仅适用于类,不能和任何切入点一起使用
Advisor API in Spring
- Advisor是仅包含一个切入点表达式相关的单个通知的方面
- 出了introductions,advisor可以用于任何通知
- org.springframework.aop.support.DefaultPointcutAdvisor是最常用的advisor类,它可以与MethodInterceptor,BeforeAdvice或者ThrowsAdvice一起使用
- 它可以混合在Spring同一个AOP代理的advisor和advice
ProxyFactoryBean
- 创建Spring AOP代理的基本方法是使用org.springframework.aop.framework.ProxyFactoryBean
- 这可以完全控制切入点和通知(advice)以及他们的顺序
使用ProxyFactoryBean或者其他IoC相关类来创建AOP代理的好处:
- 使用ProxyFactoryBean或者其他IoC相关类来创建AOP代理的最重要好处是通知和切入点也可以由IoC来管理
- 被代理类没有任何接口,使用CGLIB代理,否则JDK代理
- 通过设置proxyTargetClass为true,可以强制使用CGLIB
- 如果目标类实现了一个(或者多个)接口,那么创建代理的类型将依赖ProxyFactoryBean的配置
- 如果ProxyFactoryBean的proxyInterfaces属性被设置为一个或者多个全限定接口名,基于JDK的代理将被创建
- 如果ProxyFactoryBean的proxyInterfaces属性没有被设置,但是目标实现了一个(或者更多)接口,那么ProxyFactoryBean将自动检测到这个目标类已经实现了至少一个接口,创建一个基于JDK的代理
其他功能
- 使用匿名内部bean来隐藏目标和代理之间的区别
Proxying classes
- CGLIB代理的工作原理是在运行时生成目标类的子类,Spring配置这个生成的子类委托方法调用到原来的目标
- 子类是用来实现Decorator模式,织入通知
- CGLIB的代理对用户是透明的,需要注意
- final方法不能被通知,因为它们不能被覆盖
- 不用把CGLIB添加到classpath中,在Spring3.2中,CGLIB被重新包装并包含在Spring核心的JAR(即基于CGLIB的AOP就像JDK动态代理一样“开箱即用”)
- 用
*
做通配,匹配所有拦截器(Interceptor)加入通知链
简化proxy定义
- 使用父子bean定义,以及内部bean定义,可能会带来更清爽和简洁的代理定义(抽象属性标记父bean定义为抽象的这样它不能被实例化)
使用ProxyFactory
- 使用Spring AOP而不必依赖于Spring IoC
ProxyFactory factory = new ProxyFactory(myBussinessInterfaceImpl); factory.addAdvice(myMethodInterceptor); factory.addAdvisor(myAdvisor); MyBusinessInterface tb = (MyBussinessInterface) factory.getProxy();
- 大多数情况下最佳实践是用IoC容器创建AOP代理
- 虽然可以编码方式实现,但是Spring推荐使用配置或注解方式实现
使用auto-proxy
- Spring也允许使用“自动代理”的bean定义,它可以自动代理选定的bean,这是建立在Spring的"bean post processor"功能基础上的(在加载bean的时候就可以修改)
- BeanNameAutoProxyCreator
- DefaultAdvisorAutoProxyCreator,当前IoC容器中自动应用,不用显示声明引用advisor的bean定义
第七章 Spring对AspectJ的支持
AspectJ介绍及Pointcut注解应用
AspectJ
@AspectJ
的风格类似纯java注解的普通java类- Spring可以使用AspectJ来做切入点解析
- AOP的运行仍旧是纯的Spring AOP,对AspectJ的编译器或者织入无依赖性
@ApectJ
切面使用@Aspect
注解配置,拥有@Aspect
的任何bean将被Spring自动识别并应用- 用
@Apect
注解的类可以有方法和字段,他们也可能包括切入点(pointcut),通知(Advice)和引入(introduction)声明 @Aspect
注解是不能够通过类路径自动检测发现的,所以需要配合使用@Component
注释或者在xml配置bean- 一个类中的
@Aspect
注解表示它为一个切面,并且将自己从自动代理中排除
Spring中配置@AspectJ
- 对
@AspectJ
支持可以使用XML(<aop:aspectj-autoproxy/>
)或Java风格(@EnableAspectJAutoProxy
)的配置 - 确保AspectJ的aspectjweaver.jar库包含在应用程序(版本16.8或更高版本)的classpath中
Pointcut
- 一个切入点通过一个普通的方法定义来提供,并且切入点表达式使用
@Pointcut
注解,方法返回类型必须为void
Supported Pointcut Designators
- execution:匹配方法执行的连接点
- within:限定匹配特定类型的连接点
- this:匹配特定连接点的bean引用是制定类型的实例的限制
- target:限定匹配特定连接点的目标对象是指定类型的实例
- args:限定匹配特定连接点的参数是给定类型的实例
- @target:限定匹配特定连接点的类执行对象的具有给定类型的注解
- @args:限定匹配特定连接点实际传入参数的类型具有给定类型的注解
- @within:限定匹配到内具有给定的注释类型的连接点
- @annotation:限定匹配特定连接点的主体具有给定的注解
组合Pointcut
- 切入点表达式可以通过&&、||和!进行组合,也可以通过名字引用切入点表达式
- 通过组合,可以建立更加复杂的切入点表达式
定义良好的pointcuts
- AspectJ是编译期的AOP
- 检查代码并匹配连接点与切入点的代价是昂贵的
- 一个好的切入点应该包括以下几点
- 选择特定类型的连接点,如:execution, get, set, call, handler
- 确定连接点范围,如:within, withincode
- 匹配上下文信息,如:this, target, @annotation
Advice定义及实例
Before advice
@Before("...")
After returning advice
@AfterReturning("...")
- 有时候需要在通知体内得到返回的实际值,可以使用
@AfterReturning
绑定返回值的
After throwing advice
@AfterThrowing("...")
- 有时候需要在通知体内得到返回的实际值,可以使用
@AfterReturning
绑定返回值的形式
After(finally) advice
@After("...")
- 最终通知必须准备处理正常和异常两种返回情况,它通常用于释放资源
Around advice
@Around("...")
- 环绕通知使用
@Around
注解来声明,通知方法的第一个参数必须是ProceedingJoinPoint类型 - 在通知内部调用ProceedingJoinPoint的proceed()方法会导致执行真正的方法,传入一个Object[]对象,数组中的值将被作为参数传递给方法
Advice扩展
给advice传递参数
- 在Advice注解表达式后使用
&&
符号,添加参数,参数形式可以是args(params)
、@annotation(params)
等
Advice的参数及泛型
- Spring AOP可以处理泛型类的声明和使用方法的参数
Advice参数名称
- 通知和切入点注释有一个额外的
argNames
属性,它可以用来制定所注解的方法的参数名 - 如果第一参数是JoinPoint、ProceedingJoinPoint、JoinPoint.StaticPart,那么可以忽略
Introductions
- 允许一个切面声明一个通知对象实现指定接口,并且提供了一个接口实现类来代表这些对象
- introduction使用
@DeclareParents
进行注解,这个注解用来定义匹配的类型拥有一个新的parent
切面实例化模型
- "perthis"切面通过制定
@Aspect
注解perthis子句实现 - 每个独立的service对象执行时都会创建一个切面实例
- service对象的每个方法在第一次执行的时候创建切面实例,切面在service对象失效的同时失效