Springframework-core
2021-04-07 22:36:17 1 举报
AI智能生成
https://docs.spring.io/spring-framework/docs/current/reference/html/core.html SpringFramework官方文档阅读笔记
作者其他创作
大纲/内容
The IoC Container
Container
接口
BeanFactory
提供配置框架和基础功能
ApplicationContext
提供企业级功能
ConfigurableApplicationContext
配置
支持多种格式的配置,XML、注解或Java代码等
初始化
使用Spring自带的ApplicationContext读取配置创建容器
使用
从容器中获取指定条件的Bean
Bean
接口
BeanDefinition
各种形式的Bean元数据配置最终都会表示为BeanDefinition
命名
id
容器内唯一
name
可设置多个别名
自动生成
默认为首字母小写的驼峰式
前两个都是大写字母,那么会保留原始名称
初始化
构造器
静态工厂
class: 包含静态工厂方法的类
factory-method:静态工厂方法
constructor-arg: 工厂方法参数
实例工厂
factory-bean: 包含实例工厂方法的bean
factory-method: 实例工厂方法
Dependencies
Dependency Injection
通过构造器注入
通过Setter方法注入
依赖解析流过程
1 根据配置创建和初始化容器
2 对于每个Bean,它的依赖来自于属性、构造器参数或静态工厂的参数。这些依赖会在该Bean实际创建的时候提供给它。
3 每个属性或构造器参数都是实际定义的值或者容器内其他Bean的引用
4 每个值类型的属性或构造器参数可以转换自于他们特定的格式,默认Spring能够将string格式的值转换为所有内建类型
环形依赖
如果注要采用构造器注入,那么就可能创建无法解决的环形依赖场景
可以通过setter注入来解决环形依赖问题
depends-on
控制Bean的加载、销毁顺序
在依赖加载之后加载,在依赖销毁之前销毁
lazy-init
Spring默认加载单例Bean也是为了尽早发现配置问题
启用懒加载,Bean被使用时才初始化
Autowire
no
默认
byName
通过属性名称装配
byType
通过属性类型装配,依赖类型只能有一个Bean,非必须
constructor
通过构造器参数类型装配,依赖类型的Bean必须存在
byType和constructor可以装配指定类型的数组、列表和Map,Map的key为Bean的名称
限制和缺点
精确的属性和构造参数的设置总会覆盖自动装配。不能够自动装配基本类型,字符串和Class对象或它们的数组
自动装配没有精确装配精确,bean之间的关系不能通过文档精确查看
装配信息不能通过工具自动生成文档
可能有多个Bean定义匹配setter方法或构造器参数的类型
控制
Bean: autowire-candidate
Bean定义设置为false可以避免该Bean作为byType自动装配的候选项
Beans: default-autowire-candidates
true/false
*Repository
只自动装配以Repository结尾的Bean
Method Injection
lookup-method
查找类型方法实现,查找方法的返回类型Bean,并构造一个默认的方法实现,返回该类型的对象
<public|protected> [abstract] <return-type> theMethodName(no-arguments);
replaced-method
替换方法实现
Bean Scopes
singleton
prototype
Spring不会管理原型Bean的完整生命周期。容器创建、配置或组装原型Bean,然后将其交给客户端,不再对其做其他记录
单例Bean可以有原型Bean依赖,但是这个依赖在运行期间不会改变,如果希望每次获取都是一个新的原型Bean,可以采用方法注入
web-aware
request
session
application
ServletContext
websocket
长期存在的Bean依赖短期存在的Bean时,应使用<aop:scoped-proxy/>,默认是CGLib代理
custom-scope
实现org.springframework.beans.factory.config.Scope
ConfigurableBeanFactory#registerScope(String scopeName, Scope scope);
Customizing the Nature of a Bean
Lifecycle
接口
Lifecycle
LifecycleProcessor
DefaultLifecycleProcessor
SmartLifecycle
Phased
getPhase返回值越小越先启动越后停止,未实现默认0
Callbacks
初始化之后
1 @PostConstruct
2 InitializingBean#afterPropertiesSet
3 init-method
销毁之前
1 @PreDestroy
2 DisposableBean#destroy
3 destroy-method
如果Bean配置了多个生命周期机制,如果方法名不同那么会按照上面的顺序执行;如果有同名方法,那么同名方法只会执行一次
Aware接口
Bean Definition Inheritance
创建Bean定义模板来继承和简化Bean定义
模板Bean定义需要设置abstract为true,子Bean需要指定parent
总是从子Bean定义中取这些配置:depends on, autowire mode, dependency check, singleton, and lazy init
Container Extension Points
自定义处理Bean:BeanPostProcessor
注册在BeanFactory中
AbstractBeanFactory
lazy-init无效
自定义配置元数据:BeanFactoryPostProcessor
注册在ApplicationContext中
AbstractApplicationContext
lazy-init无效
自定义实例化逻辑: FactoryBean
获取FactoryBean,Bean id 前面加&
注册FactoryBean时,指定的id为Bean的id
Annotation-based Container Configuration
@Autowire
构造器装配
只能有一个构造器被require=true的Autowire修饰
如果有多个构造器待选项,可以都给他们修饰Autowire,require=false
Spring会尽量匹配参数最多的构造器
如果都不匹配那么Spring会选择最基础的(默认)构造器
如果没有一个构造器使用@Autowired修饰那么Spring会选择最基础的(默认)构造器
如果只有一个构造器,那么一定会使用它,不论他是否被@Autowired修饰
以上提到的构造器不需要一定是公有的
setter装配
属性装配
required属性
构造器和工厂方法的参数,都默认必须。但是如果是多元素参数[数组,集合,Map],如果没有满足条件的依赖,就会给空实例
@Primary
@Autowire是根据Bean类型装配的,如果某个类型存在多个候选Bean,那么会选择primary=true的那个
@Qualifier
通过限定名称,来获取指定范围的Bean,name不要求全局唯一,相同的name可用于装配列表
创建自定义Qualifier注解,创建的注解用@Qualifier修饰
自定义自动装配配置
指定没有被@Qualifier修饰的自定义注解,做为自动装配候选项标识
CustomAutowireConfigurer
自动装配候选项解析
每个Bean定义的autowire-candidate值
Beans标签中的default-autowire-candidates
存在@Qualifier修饰,或者其他通过CustomAutowireConfigurer配置的注解
@Value
Spring内置
Spring内置的配置比较宽松,如果没有找到表达式相关配置,会将占位符表达式作为值注入
自定义PropertySourcesPlaceholderConfigurer
使用JavaConfig配置PropertySourcesPlaceholderConfigurer时,@Bean方法必须是static的
表达式
占位符
例:${xxx:default}
SpEL
例:#{systemProperties['user.catalog'] + 'Catalog' }
Classpath Scanning and Managed Components
@Component
元注解和组合注解
@ComponentScan
<context:component-scan>隐含开启了<context:annotation-config>
自动注册AutowiredAnnotationBeanPostProcessor,CommonAnnotationBeanPostProcessor
annotation-config=false可以禁用注册
组件扫描过滤器
指定 BeanNameGenerator
Bean名称生成器
指定 ScopeMetadataResolver
自定义Scope
实现必须提供无参构造器
指定 scopedProxy代理模式
no
interfaces
targetClass
@Bean
在non-@Configuration中定义@Bean方法
实例工厂
和修饰符无关
在@Configuration中定义@Bean方法
声明Bean
需要被CGLIB代理,所以必须可以被重写,不可以示private或final
调用@Configuration类中的非静态@Bean方法会被容器拦截(被代理了)
静态@Bean方法
声明需要提前加载的post-processor Bean:BeanFactoryPostProcessor或BeanPostProcessor
静态方法不会被容器拦截
在组件类上添加其他注解配置
@Scope
@Qualifier
编译器生成组件索引,替代自动扫描,提升加载效率
可以指定参数随时关闭该配置,spring.index.ignore=true
Java-based Container Configuration
@Bean
在@Configuration中定义
Bean定义
方法调用会被拦截
默认情况下,直接调用方法,返回的对象是单例的
在non-@Configuration中定义
工厂方法
方法调用不会被拦截
默认情况下,直接调用方法,返回的对象不是单例
默认注册公有的close 或 shutdown方法为destroy-method
@Configuration
修饰的类会被CGLIB代理
类似于XML配置中的每个XML
@Import
<import/>
@Conditional
为配置的加载提供条件
@Profile
混合使用XML和JavaConfig
基于JavaConfig
AnnotationConfigApplicationContext
@ImportResource("classpath:/com/acme/properties-config.xml")
基于XML
ClassPathXmlApplicationContext
至少开启<context:annotation-config/>,将config声明为Bean即可
Environment Abstraction
profiles
@Profile
对于重载的@Bean方法,必须要保证它们的@Profile配置相同,如果不同,只有第一个声明的有效
支持指定列表,关系为或
@Profile({"p1", "!p2"})
可以使用 & | ! 逻辑表达式,并且可以复合使用,复合使用需要加括号
例如:production & (us-east | eu-central)
@Profile("default")指代默认的Profile
Environment#setDefaultProfiles
spring.profiles.default
<beans profile="" >
properties
properties files,JVM system properties,system environment variables...
属性优先级,由上往下依次降低
自定义优先级
可以在源文件路径中使用占位符表达式
表达式中的属性必须是已经在其他PropertySource中注册过的
@PropertySource("classpath:/com/${my.placeholder:default/path}/app.properties")
<import resource="com/bank/service/${customer}-config.xml"/>
Registering a LoadTimeWeaver
@EnableLoadTimeWeaving
<context:load-time-weaver/>
Additional Capabilities of the ApplicationContext
MessageSource国际化支持
ResourceBundleMessageSource
ReloadableResourceBundleMessageSource
ApplicationContext本身扩展了MessageSource接口,可以类型转换直接使用
StaticMessageSource
消息监听支持
相关接口/类
ApplicationEventPublisher
用来发布事件
ApplicationEventMulticaster
控制监听器处理过程
ApplicationListener
监听器接口
ApplicationEvent
事件父类
内置事件
声明式监听@EventListener
监听指定事件
监听多种事件,方法不可以有参数
使用condition和EL表达式对事件进行过滤
condition属性中,EL表达式上下文
将另一种事件作为返回值,产生新的事件。此外,还可以返回事件的集合。这种特性不支持异步监听器。
同步监听和异步监听
同步监听,支持事务,监听器处理在publisher所在的事务中
异步监听,异常不会抛给调用者
AsyncUncaughtExceptionHandler
添加@Async注解,声明异步监听
使用@Order注解指定监听器调用顺序
结构化事件类
实现ResolvableTypeProvider接口,告诉Spring运行时环境提供的类型
ResourceLoader资源文件加载支持
ResourceLoader
ApplicationContext本身实现了ResourceLoader
ResourceLoaderAware
拿到的是ApplicationContext本身作为ResourceLoader
Resource
Application Startup tracking
应用启动轨迹
ApplicationStartupAware
收集自定义的启动步骤
Convenient ApplicationContext Instantiation for Web Applications
有层级的多上下文支持
HierarchicalBeanFactory
The BeanFactory
BeanFactory API提供了SpringIoC最基础的功能。特有的功能通常是集成了Spring的其他部分或相关的第三方框架。它的DefaultListableBeanFactory实现是高级容器GenericApplicationContext 委托的关键。
核心的BeanFactory API层以及它的DefaultListableBeanFactory实现不会考虑配置的格式或其他组件注解的使用。所有这些功能都是靠扩展(例如:XmlBeanDefinitionReader 和 AutowiredAnnotationBeanPostProcessor)和在共享的作为核心元数据表示的BeanDefinition对象上操作实现的
BeanFactory和ApplicationContext的区别
Resources
The Resource Interface
Resource接口中比较重要的几个方法,其他方法是用来获取资源表示的URL和File对象的(如果它们能够兼容地实现并且支持这些功能)
getInputStream 继承自InputStreamSource接口,每次调用都期望会返回一个全新的InputStream,关闭流是调用者的责任
exists 表明资源是否以物理形式真实存在
isOpen 表明这个流是否可以多次读取,为true只能读取一次。除了InputStreamResource这个实现,其他常用的实现都返回false
getDescription 返回该资源的描述信息,通常能指明资源的实际位置
资源的抽象在Spring中作为一个非常便利的工具类使用,即使代码不愿意和Spring耦合,也可以仅仅作为工具集使用
资源的抽象不能替代功能,它只是一个包装
WritableResource
支持写操作的资源
Built-in Resource Implementations
UrlResource
表示通过URL获取的资源,通常提供一个URL路径
ClassPathResource
表示从classpath中获取的资源,它使用线程上下文类加载器,一个给定的类加载器或者一个给定的类来加载资源。
classpath:前缀的路径
FileSystemResource
用来表示File句柄,它也支持Path句柄,用于Spring标准的基于String的路径转换,用Files API执行所有的操作
支持解析为File或URL
PathResource
用来表示Path句柄,用Path API执行所有的操作
支持解析为File或URL
实现了WritableResource
PathResource实际上是FileSystemResource的纯Path替代品,具有不同的createRelative行为
ServletContextResource
用来代表ServletContext资源,解释基于相关Web应用根目录的相对路径。
它总是支持流访问和URL访问,但仅在扩展Web应用程序档案且资源实际位于文件系统上时才允许java.io.File访问
它是在文件系统上扩展还是直接扩展,是否可以直接从JAR或其他类似数据库(可以想到的)中访问,实际上取决于Servlet容器
InputStreamResource
用来表示一个给定的InputStream,它应该只在没有其他Resource实现适用的情况下使用。通常更应该选择ByteArrayResource或尽可能选择其他基于文件的Resource实现
与其他资源实现不同,它表示了一个已经打开的资源,因此isOpen返回true,如果需要对资源进行多次读取就不要使用它
ByteArrayResource
用来表示一个给定字节数组,它用给定的字节数组创建一个ByteArrayInputStream
它在使用任意给定字节数字加载内容时非常有用,而且不像InputStreamResource只能使用一次
The ResourceLoader Interface
The ResourcePatternResolver Interface
容器默认委托实现PathMatchingResourcePatternResolver
Ant-path工具类org.springframework.util.AntPathMatcher
The ResourceLoaderAware Interface
实现ResourceLoaderAware获取ResourceLoader引用
@Autowire by Type ResourceLoader或ResourcePatternResolver
Resources as Dependencies
借助Spring自动注册的特殊Bean PropertyEditor将字符串解析为Resource,使用@Value将字面量注入进去,通过ResourceLoader或ResourcePatternResolver将他们解析为资源或资源列表
Application Contexts and Resource Paths
在没有任何前缀的条件下,ApplicationContext解析的资源是根据实现而定的
例如:ApplicationContext ctx = new ClassPathXmlApplicationContext("conf/appContext.xml"); 会默认从classpath下加载文件
例如:ApplicationContext ctx = new FileSystemXmlApplicationContext("conf/appContext.xml");则默认从文件系统路径下加载文件
使用特殊的classpath前缀或标准的URL前缀会覆盖默认创建的资源类型
例如:ApplicationContext ctx = new FileSystemXmlApplicationContext("classpath:conf/appContext.xml");会从classpath下加载文件
ant风格通配符的使用
不要使用classpath*前缀来构建一个实际的资源
隐含兼容性
如果指定的路径是一个file URL,那么通配符可以保证完全兼容
如果指定的路径是一个classpath,那么解析器必须获取最后一个不含统配符的路径,并调用Classloader.getResource方法。由于它只是路径中的一个节点,所以并不能完全知道它是哪一类URL。它通常是文件目录或者jar包,即时如此,此操作存在兼容性问题。在使用通配符遍历访问Jar包时,少数环境会失败。
classpath*前缀
通配符的classpath依赖ClassLoader.getResources方法,如今的很多服务器都有自己的ClassLoader实现,所以在使用时应该测试至少两个相同路径不同jar包的加载情况,如果返回了不当的结论,检查应用服务器的文档,设置可能会影响ClassLoader的行为。
带通配符的classpath前缀,查找多个路径时如果从根目录查找到多个classpath位置,不保证一定能够找到相关资源。它只会查找第一个classpath位置,没有找到便不会继续查找。这时候应该使用classpath*前缀,确保查找所有满足条件的classpath位置。
FileSystemResource注意事项
FileSystemApplicationContext中强制所有相关的FileSystemResource实例都按相对路径定位。如果需要按绝对路径定位,则需要添加file:// URL前缀
这两种写法等价,都是按当前工作目录的相对路径定位
添加file URL前缀,按照绝对路径定位
Validation, Data Binding, and Type Conversion
Validation by Using Spring’s Validator Interface
接口
Validator
Errors
Resolving Codes to Error Messages
MessageCodesResolver
不仅仅会将错误解析为传过来的错误码,还会解析成一些附加的错误码
DefaultMessageCodesResolver
rejectValue("age", "too.darn.old")会解析成三个错误码
too.darn.old
too.darn.old.age
too.darn.old.age.int
MessageSource
Bean Manipulation and the BeanWrapper
BeanWrapper支持设置和获取嵌套属性
Company中有name和managingDirector属性,Employee有name和salary属性
内置的PropertyEditor实现
Spring用PropertyEditor的概念来实现String和Object的转换
java.beans.PropertyEditor
PropertyEditorSupport
PropertyEditor抽象实现
java.beans.PropertyEditorManager
Spring使用java.beans.PropertyEditorManager设置可能需要的属性编辑器的搜索路径
JavaBeans自动发现PropertyEditor机制
JavaBeans自动发现BeanInfo机制间接关联
Spring内置的PropertyEditor
注册自定义的PropertyEditor
在同包下创建PropertyEditor,基于java自带的扫描机制
通过CustomEditorConfigurer注册
它是一个bean factory post-processor
factory post-processor
factory post-processor
bean factory post-processor
通过PropertyEditorRegistrar注册
PropertyEditorRegistrar也可以注入到CustomEditorConfigurer的propertyEditorRegistrars属性中
Spring Type Conversion
Converter
参数不能为null,返回值可能为null可能抛异常
DefaultConversionService中提供了注册Spring内部实现的常用转换器实现的方法
ConverterFactory
将转换逻辑集中管理
GenericConverter
支持复杂地转换器实现,比Converter更灵活,支持多中原类型和多种目标类型的转换。此外TypeDescriptor中还包含源域和目标域的上下文,用来获取域注解或域声明的泛型信息
ConditionalGenericConverter
用于先判断,满足条件再进行转换的场景
配置ConversionService
运行时用来统一执行转换逻辑的接口
配置一个默认的ConversionService。如果没有在Spring中配置ConversionService,那么将会使用原始的基于PropertyEditor的系统
使用ConversionService
注入便可以使用,默认DefaultConversionService自动注册了适用于大多数环境的转换器,单值类型的转换在列表转换过程中会复用,所以不必特别指定从一个列表到另一个列表的转换器
Spring Field Formatting
Formatter
用于本地化的转换
注解驱动的格式化
1. 注解支持的类型;2. 通过注解和域类型获取Printer和Parser
FormatterRegistry
FormattingConversionService是它的通用实现 , FormattingConversionServiceFactoryBean可以用来声明配置
FormatterRegistrar
配置全局的日期格式
Java Bean Validation
LocalValidatorFactoryBean
javax.validation.ValidatorFactory
javax.validation.Validator
org.springframework.validation.Validator
做了兼容处理,注册该FactoryBean之后,可以选择注入上面任何一个接口来调用验证方法
自定义验证约束
约束注解声明@Constraint
提供javax.validation.ConstraintValidator接口的实现
在运行时,ConstraintValidatorFactory会实例化这个引用的验证器。而LocalValidatorFactoryBean默认配置了SpringConstraintValidatorFactory,这代表让Spring来创建ConstraintValidator实例,因此你自定义的ConstraintValidators可以依赖注入其他bean
Spring驱动的方法校验
所有验证类必须要加上Spring的@Validated注解
依赖AOP代理
配置DataBinder
创建DataBinder并给他配置校验器,一旦配置了,可以通过调用binder.validate(),任何错误信息都
会加入到返回的BindingResult中
会加入到返回的BindingResult中
Spring Expression Language (SpEL)
SpEL是一个强大的表达式语言,支持在运行时查询或操作对象图。它不是直接绑定到Spring的,可以单独使用。
SpEL支持的功能
字面量表达式
访问属性,数组,列表和映射
内联列表
注意:是大括号,不是中括号
内联映射
数组创建
方法调用
操作符
关系运算符
任何 x > null 都返回true,任何 x < null 都返回false
instanceof和正则匹配
注意基本类型,他们会自动装箱, 1 instanceof T(int) 返回false, 1 instanceof T(Integer) 返回true
逻辑运算符
数学运算符
赋值
类表达式
StandardEvaluationContext 使用TypeLocator来查找类型,默认实现StandardTypeLocator中允许java.lang包下的不用写包名
调用构造器
除了基本类型和String不用写类全名,其余都需要写
变量
变量命名限制
#root
永远表示上下文的根对象
#this
表示当前解析的对象,上面的遍历查找的例子中它代表每一项
自定义函数
创建自定义函数
Bean引用
@打头代表bean引用
&打头代表获取它的FactoryBean引用
条件运算符
三元运算符
三元运算符特殊形式Elvis运算符
安全地导航属性
集合选择
此外:.^[selectionExpression]匹配第一个,.$[selectionExpression]匹配最后一个
集合投影
模板表达式
SpEL在Bean定义中的使用
@Value 属性值、setter方法、方法参数、构造器参数
Aspect Oriented Programming with Spring
AOP概念
AOP概念
Aspect:切面,横切多个类的关注点的结构
Join point: 连接点,在程序执行中的一个点,比如方法的执行或处理异常。在Spring中连接点总是表示方法的执行
Advice: 通知,在特定的连接点处切面执行的行为。不同的通知类型包括"around","before","after"。许多AOP框架,包括Spring,将通知模块化为一个拦截器并且维护一个拦截器链环绕连接点
Pointcut: 切点,用来匹配连接点的描述。通知会关联一个切点表达式,并且在每一个匹配的连接点上执行。切点表达式匹配连接点的概率是AOP的核心,Spring默认使用AspectJ切点表达式语言。
Introduction: 引介,在表示类中添加额外的方法或域。Spring AOP允许你向被增强的对象引入新的接口(和一个相关实现)。在AspectJ社区中,介绍被称为类型间声明
Target object: 被一个或多个切面通知的对象。Spring AOP用运行时的代理来实现,所以这个对象总是一个被代理的对象
AOP proxy: 被AOP框架创建用来实现切面规范的对象。在Spring框架中,AOP代理是JDK动态代理或者是CGLIB代理
Weaving: 织入,将切面与其他应用程序类型或对象链接以创建增强的对象。这个可以在编译时,加载时或者运行时。Spring AOP和其他纯Java AOP框架一样,是在运行时织入的。
Spring AOP通知类型
Spring AOP的功能和目标
Spring当前仅支持方法执行连接点,如果希望使用域访问或更新连接点可以选择AspectJ
AOP代理
AOP默认使用JDK动态代理来进行AOP代理,如果业务对象没有实现接口,那么Spring默认会采用CGLIB代理。
@AspectJ支持
@AspectJ提供了声明式切面的功能。Spring使用AspectJ提供的库解析和匹配切点来解释与AspectJ中相同的注解。AOP在运行时仍然是纯SpringAOP,并没有依赖AspectJ的编译器和织入器。
启用@AspectJ支持
启用支持意味着,当Spring确定一个Bean被一个或多个切面增强时,它将为那个Bean自动的生成一个代理来拦截方法的调用,确保增强如期执行。
声明切面
在Bean类上使用@Aspect,即将它声明为切面。切面不可以作为其他切面的目标
声明切点
切点决定了感兴趣的连接点并且让我们知道什么时候运行通知。SpringAOP只支持Bean方法执行连接点,所以可以基于匹配bean方法来考虑切点
由两部分组成:一个包含名称和任意参数的签名和一个用来确定在那些方法上执行增强的切点表达式
支持切点
Spring 还额外支持bean切点指示符 bean(idOrNameOfBean),可以使用*来作为通配符
bean(idOrNameOfBean)
bean(idOrNameOfBean)
支持复合的切点表达式
声明切点的例子
如何写好的切点
应尽可能的收窄切点表达式的范围。第二种类型的执行速度最快,应尽可能使用它。
建议将切点统一定义在一个公共的切面类(添加@Aspect防止其被作为切入对象)中,供其他的切面类引用
声明通知
Before
可以引用已经声明的切点
也可以直接指定切点表达式
AfterReturning
可以在注解中的returning属性设置返回值的参数名,将返回值作为通知方法的参数
AfterThrowing
可以在注解中的throwing属性设置异常的参数名,将异常作为通知方法的参数
After (Finally)
Around
JointPoint接口
所有的通知方法,都可以在第一个参数声明JointPoint对象,获取链接点方法的信息
向通知传递参数
不支持泛型集合参数的传递
可以按顺序指定参数名,否则,默认按顺序绑定参数名。JoinPoint, ProceedingJoinPoint, or JoinPoint.StaticPart类型在第一个位置可以不用指定参数名
带参数的执行
通知执行的顺序
切面实现org.springframework.core.Ordered或者标注@Order注解。值小的优先。
同一个切面中基于同一个切点的同一个通知类型应只定义一个,否则无法确认他们的顺序
同一个切面中基于同一个切点的通知优先级@Around, @Before, @After, @AfterReturning, @AfterThrowing。但是@After会在任意一个@AfterReturning或@AfterThrowing之后执行
设置了两个切面Aspect1和Aspect2,它们的
order分别为1和2。两个切面都在同一个切点
上声明了@Before,@After,@AfterReturning,
@Around四个通知。
order分别为1和2。两个切面都在同一个切点
上声明了@Before,@After,@AfterReturning,
@Around四个通知。
引介
+代表包含子类
多例切面定义
Spring支持perthis和pertarget两种,perthis对于每个匹配的SpringAOP代理对象创建切面,pertarget对于每个匹配的被代理的目标对象创建切面
案例:使用切面进行业务重试
新增标记注解,在指定的业务方法上增加重试逻辑
代理机制
如果目标对象实现了接口,那么Spring默认会使用JDK动态代理;否则,使用CGLIB动态代理。
强制使用CGLIB代理
自调用不会被AOP代理拦截
可以勉强使用这种方式调用代理的方法。但是不太好。
手动创建@AspectJ代理
在Spring应用中使用AspectJ
Load-Time Weaver
前面提到SpringAOP是使用运行时织入的,Spring可以使用AspectJ进行加载时织入。加载时织入指的是在应用类加载到JVM时将AspectJ切面织入到应用文件中。
1 定义AspectJ切面,里面使用的是AspectJ包中的注解。这里定义了一个方法调用计时器。
2 在META-INF/aop.xml中创建AspectJ配置
3 在Spring配置中启用加载时织入,或@EnableLoadTimeWeaver
4 测试案例
5 在运行JVM时,添加-javaagent运行环境参数
6 由于是加载时织入,类在加载时字节码已经被改变了,所以new出来的对象也包含切面中添加的功能
Spring加载时织入的原理
LoadTimeWeaver 接口的责任是添加一个或多个ClassFileTransformers到ClassLoader。真正对字节码进行操作的是ClassFileTransformers。而实际的ClassFileTransformer实现是ClassPreProcessorAgentAdapter。
DefaultContextLoadTimeWeaver会自动探测上面列表中的LoadTimeWeaver实现
也可以通过配置指定确定的实现
AspectJ加载时织入的属性配置
默认是autodetect
在使用这些web容器时,由于他们都提供了通用的ClassLoader,能够进行本地检测,
Spring的LT可能会利用这些ClassLoader实现来提供AspectJ切面织入。此时,就不再
需要在运行时指定参数-javaagent了。具体视服务器版本而定,可能需要额外的配置。
Spring的LT可能会利用这些ClassLoader实现来提供AspectJ切面织入。此时,就不再
需要在运行时指定参数-javaagent了。具体视服务器版本而定,可能需要额外的配置。
Spring AOP APIs
Pointcut
Spring的切点模型允许切点在多个通知类型间重用
相关接口
将切点接口分为两部分,便于分别重用这两部分和细粒度地组合操作
只有在isRuntime为true是才会调用三个参数的matches方法。大多数切点都是静态的,由参数可以看出,动态切点允许根据参数来判断是否满足条件。静态切点会缓存结果,效率比动态切点要高很多,应尽可能使用静态切点。
Spring支持切点的与或非操作
可以通过Pointcuts和ComposablePointcut实现,当然也可以通过切点表达式来实现
AspectJ表达式切点
org.springframework.aop.aspectj.AspectJExpressionPointcut 它是用AspectJ提供的库解析AspectJ切点表达式生成的
便捷的切点实现
静态切点
静态切点是基于目标方法和目标类的,不会考虑方法的参数。Spring只会在首次调用方法的时候计算静态切点一次
正则表达式切点
JdkRegexpMethodPointcut
RegexpMethodPointcutAdvisor
属性驱动切点
动态切点
动态切点比静态切点计算耗时更长。
控制流切点
从概念上讲Spring的控制流切点和AspectJ的cflow切点类似,但是没有后者强大。控制流切点通过当前的调用栈来匹配切点。
org.springframework.aop.support.ControlFlowPointcut
切点父类
StaticMethodMatcherPointcut
自定义切点
由于SpringAOP是java类而不是语言特性,所以可以定义任意复杂的自定义切点。但是还是推荐尽量使用AspectJ起点表达式语言
Advice
生命周期
每个通知都是一个Spring bean。通知可以在所有被增强的对象间共享,也可以每个对象一个,这取决于通知的策略。通常每个类一个通知实例用得最多,这种通知不依赖代理对象的状态。它们仅在方法和参数上操作。每个实例一个通知一般适用于引介,以支持混入。
相关接口
环绕通知 MethodInterceptor
前置通知 MethodBeforeAdvice
异常通知 ThrowsAdvice
正常返回通知 AfterReturningAdvice
引介
IntroductionInterceptor
DelegatingIntroductionInterceptor
IntroductionAdvisor
DefaultIntroductionAdvisor
在调用引介接口的方法时,引介拦截器要负责处理它。引介不会关联任何切点,它是基于类的而不是方法
Advisor
在Spring中,Advisor指的是只包含一个关联切点表达式通知对象的切面
DefaultPointcutAdvisor 关联切点和通知对象
使用ProxyFactoryBean来创建AOP代理
advisor的顺序很重要,决定了它们执行的顺序
使用inner bean避免target暴露出去,也保证了容器中只有一个类型为Person的Bean
支持前缀*,进行批量设置
简化代理的定义
使用抽象的父bean定义代理,然后在子bean定义中定义或覆盖特有的属性,来简化AOP代理的定义
使用ProxyFactory手动创建AOP代理
父类AdvisedSupport中有很多便捷的方法
不推荐手动创建
使用Advised接口操纵AOP代理对象
任何AOP代理都可以向上转型为Advised接口对象
这个能力使得可以在AOP代理对象创建后,对增强进行维护。唯一的限制是不能移除引介增强,可以通过工程生成新的代理来避免这个问题。
使用自动代理
有两种方式创建自动代理
通过使用在当前上下文中引用特定bean的自动代理创建器
会自动将当前上下问的Advisor应用到所有bean创建代理,如果Adviser指定了切点则需要匹配切点。DefaultAdvisorAutoProxyCreator提供过滤支持(通过使用命名约定,以便仅使用某些Advisor,从而允许在同一工厂中使用多个配置不同的AdvisorAutoProxyCreator)和排序。
基于源码级别的元数据属性创建自动代理
使用TargetSource实现
该接口负责返回实现连接点的“目标对象”,每当AOP代理处理方法调用时,都会向TargetSource实现请求目标实例
HotSwappableTargetSource
AbstractPoolingTargetSource
PrototypeTargetSource
ThreadLocalTargetSource
定义新的通知类型
使用org.springframework.aop.framework.adapter包中的SPI定义通知,需要实现标记接口Advice
收藏
收藏
0 条评论
下一页