Spring5
2024-01-14 19:06:58 0 举报
AI智能生成
Spring5官方文档笔记
作者其他创作
大纲/内容
容器
什么是容器?
org.springframework.context.ApplicationContext 接口代表Spring IoC容器,负责实例化、配置和组装bean。
容器的功能
负责Bean的生命周期,即创建、使用和销毁
元数据
什么是元数据?
元数据是关于数据的数据,是为了描述数据的相关信息的一组数据。
Spring元数据是描述Bean相关信息的一组数据
Spring元数据是描述Bean相关信息的一组数据
形式
注解
xml文件
Java配置
作用
定义Bean的,告诉Spring如何创建Bean
如何解析或提取需要的数据?
源码
Bean
什么是Bean?
在Spring中,构成应用程序主干并由Spring IoC容器管理的对象称为bean
Bean生命周期
组件扫描
注解:@Configuration 类中添加 @ComponentScan,其中 basePackages 属性是这两个类的共同父包。(或者,你可以指定一个用逗号或分号或空格分隔的列表,其中包括每个类的父包。)
xml:<context:component-scan base-package="com.acme"/>
xml:<context:component-scan base-package="com.acme"/>
扫描过滤Filter规则
默认情况下,用 @Component、@Repository、@Service、@Controller、 @Configuration 注解的类,或者本身用 @Component 注解的自定义注解是唯一被检测到的候选组件。
自定义Filter
@ComponentScan 注解的 includeFilters 或 excludeFilters 属性(或作为XML配置中 <context:component-scan> 元素的 <context:include-filter /> 或 <context:exclud-filter /> 子元素)
组件注册
组件类或包路径或xml文件路径直接传入容器构造参数
使用register方法手动注册组件类或scan方法扫描
注册BeanDefinition
Bean的实例化
构造方法
构造方法推断
如果目标Bean一开始就只定义了一个构造函数,那么在这样的构造函数上就不再需要 @Autowired 注解。然而,如果有几个构造函数,而且没有主要/默认构造函数,那么至少有一个构造函数必须用 @Autowired 注解,以便指示容器使用哪一个
工厂方法
普通工厂类
FactoryBean
在调用 ApplicationContext 的 getBean() 方法时,
在Bean的 id 前加上安培符号(&)会返回 FactoryBean 实例本身,
直接用id,返回会返回 FactoryBean 的产物,即getObject()返回值
在Bean的 id 前加上安培符号(&)会返回 FactoryBean 实例本身,
直接用id,返回会返回 FactoryBean 的产物,即getObject()返回值
依赖注入
什么是依赖?
Bean A引用Bean B,则B就是A的依赖
依赖注入
依赖注入(DI)是一个过程,对象仅通过构造参数、工厂方法的参数或在对象实例被构造或从工厂方法返回后在其上设置的属性来定义它们的依赖(即与它们一起工作的其它对象)。然后,容器在创建 bean 时注入这些依赖。这个过程从根本上说是Bean本身通过使用类的直接构造或服务定位模式来控制其依赖的实例化或位置的逆过程(因此被称为控制反转)
依赖表现形式?
Bean类的属性
构造方法参数
静态或非静态工厂方法参数
依赖注入的方式?
构造器注入
xml
</bean> 子元素</construct-arg>元素
autowired-mode = construct
注解
@Autowired
属性注入
xml
注解
@Autowired
工厂方法注入
xml
注解
方法注入
自动注入
类型的匹配
对于通过classpath扫描找到的XML定义的Bean或组件类,容器通常预先知道具体类型。然而,对于 @Bean 工厂方法,你需要确保声明的返回类型有足够的表现力。对于实现了多个接口的组件或可能被其实现类型引用的组件,考虑在你的工厂方法上声明最具体的返回类型(至少要与引用你的 bean 的注入点所要求的具体类型一样)。
可以是父类或者接口
@Autowired
匹配规则
先通过类型,在通过名称
默认必须有一个可注入的值(即有候选者,另外候选者如果无法确定其一,也会报错,而不是随机选一个或根据注册顺序选第一个),否者报错,可required 属性设置为 false,标记为非必须,会被设置为默认值
如果没有一个候选者可以被满足,那么将使用一个主要的/默认的构造函数(如果存在的话)
注意,被注解的构造函数不一定是公共的(public)。
如果有多个候选者,还会进一步筛选,同autowire=construct
@Primary和@Qualifier不能用于构造方法来帮助筛选
注意,被注解的构造函数不一定是公共的(public)。
如果有多个候选者,还会进一步筛选,同autowire=construct
@Primary和@Qualifier不能用于构造方法来帮助筛选
任何给定的Bean类中只有一个构造函数可以声明 @Autowired,并将 required 属性设置为 true,表示该构造函数在用作Spring Bean时要自动注入。因此,如果 required 属性的默认值为 true,则只有一个构造函数可以使用 @Autowired 注解。如果有多个构造函数声明该注解,它们都必须声明 required=false,才能被视为自动注入的候选者(类似于XML中的 autowire=constructor)。具有最大数量的依赖关系的构造函数将被选中,这些依赖关系可以通过Spring容器中的匹配Bean来满足。如果没有一个候选者可以被满足,那么将使用一个主要的/默认的构造函数(如果存在的话,未发现设置方式?)。同样地,如果一个类声明了多个构造函数,但没有一个是用 @Autowired 注解的,那么将使用一个主要/默认构造函数(如果存在的话)。如果一个类一开始只声明了一个构造函数,那么即使没有注解,它也会被使用。请注意,被注解的构造函数不一定是公共的(public)。
required只是对应一个候选者都没有的情况下,可以忽略或者说跳过(required=false)。但是如果有候选者,且无法确定获选者这种情况,无法跳过或忽略,而是报错
注入的构造函数和工厂方法参数是一种特殊情况,因为由于Spring的构造函数解析算法有可能处理多个构造函数,所以 @Autowired 中的 required 属性有一些不同的含义。构造函数和工厂方法参数实际上是默认需要的,但在单构造函数的情况下有一些特殊的规则,比如多元素注入点(数组、collection、map)如果没有匹配的Bean,则解析为空实例。这允许一种常见的实现模式,即所有的依赖关系都可以在一个独特的多参数构造函数中声明—例如,声明为一个没有 @Autowired 注解的单一公共构造函数。
子主题
Optional<T> 、@Nullable 注解、Kotlin内置的 null-safety 支持
@Autowired、@Inject、@Value 和 @Resource 注解是由Spring BeanPostProcessor 实现处理的。这意味着你不能在你自己的 BeanPostProcessor 或 BeanFactoryPostProcessor 类型(如果有的话)中应用这些注解。这些类型必须通过使用XML或Spring @Bean 方法明确地 "注入"
注入点类型
任何方法或字段,包括构造方法、set方法和其他任何方法
也可以混合同时使用
也可以混合同时使用
@Autowired注解放在方法上,不论什么方法都是找参数然后运行
放在任意名称的方法上时,会自动被调用执行
放在任意名称的方法上时,会自动被调用执行
如果有多个注入点对同一个属性操作赋值,那么最终结果,根据声明周期,最后使用set方法,故最终是set方法的赋值
如果注入的是数组或集合,就会把容器中所有指定元素类型的Bean都添加到数组或集合中,
Map类型的key必须是String类型方可注入
@Order 或标准的 @Priority 注解以及@Primary会影响在数组或列表中的顺序
Map类型的key必须是String类型方可注入
@Order 或标准的 @Priority 注解以及@Primary会影响在数组或列表中的顺序
微调自动注入
@Primary 主要
如果在候选者中正好有一个主要(primary)Bean存在,它就会优先成为自动注入的值。
@Qualifiers 限定符
一般结合@Autowired使用,帮助缩小范围
在通过类型选择候选Bean后,指定的 String qualifier 值只在这些类型选择的候选中被考虑
你可以将限定符的值与特定的参数联系起来,缩小类型匹配的范围,从而为每个参数选择一个特定的bean
在通过类型选择候选Bean后,指定的 String qualifier 值只在这些类型选择的候选中被考虑
你可以将限定符的值与特定的参数联系起来,缩小类型匹配的范围,从而为每个参数选择一个特定的bean
使用泛型作为自动注入 Qualifier,即泛型的实际类型相当于隐含添加@Qualifier注解
使用 CustomAutowireConfigurer即一个BeanFactoryPostProcessor,可以让你注册自己的自定义 qualifier 注解类型
使用
Qualifier 元数据是通过使用XML中 bean 元素的 qualifier 或 meta 子元素提供给候选Bean定义的,
或候选类上用类型级注解来提供 qualifier 元数据,或者@Bean方法上,
如果不指定名称元数据,则使用默认名称
或候选类上用类型级注解来提供 qualifier 元数据,或者@Bean方法上,
如果不指定名称元数据,则使用默认名称
按名称筛选,自动注入时配合使用@Qualifier筛选有指定Qualifier元数据的候选类
@Resource
注入点
字段和只有一个参数的bean属性setter方法
@Resource的注释:Resource注释标记应用程序所需的资源。此注释可以应用于应用程序组件类,也可以应用于组件类的字段或方法。当注释应用于字段或方法时,容器将在初始化应用程序组件时将所请求资源的实例注入该组件。如果注释应用于组件类,则注释声明应用程序将在运行时查找的资源
匹配规则
按照name属性值匹配,
如果没有明确指定名字,默认的名字来自于字段名或setter方法。
如果是一个字段,它采用字段名。如果是setter方法,则采用Bean的属性名(注意不是参数名或类型名)。
如果有多个候选者,则选择Primary主要的
如果没有明确指定名字,默认的名字来自于字段名或setter方法。
如果是一个字段,它采用字段名。如果是setter方法,则采用Bean的属性名(注意不是参数名或类型名)。
如果有多个候选者,则选择Primary主要的
在没有明确指定名称的 @Resource 使用的特殊情况下,与 @Autowired 类似,@Resource 找到一个主要的类型匹配,而不是一个特定的命名的 bean
@Value
注入点
字段或方法参数
匹配规则
注入外部properties文件中的属性,使用${}属性占位符或系统属性#{}
配合@PropertiesSource注解声明配置文件后使用
向Spring的 Environment 添加 PropertySource
向Spring的 Environment 添加 PropertySource
可以使用SpEL
Spring Boot默认配置了一个 PropertySourcesPlaceholderConfigurer Bean,它将从 application.properties 和 application.yml 文件中获取属性。
类型转换
Spring BeanPostProcessor 在幕后使用一个 ConversionService 来处理将 @Value 中的 String 值转换为目标类型的过程
自定义注解
自我注入
@Autowired 也考虑到了用于注入的自我引用(也就是对当前注入的Bean的引用)。请注意,自我注入是一种回退(fallback)。对其他组件的常规依赖总是具有优先权。在这个意义上,自我引用不参与常规的候选选择,因此特别是永远不会是主要的(primary)。相反,他们总是以最低的优先级结束。在实践中,你应该把自引用作为最后的手段(例如,通过Bean的事务代理调用同一实例上的其他方法)。在这种情况下,可以考虑将受影响的方法分解到一个单独的委托Bean中。另外,你也可以使用 @Resource,它可以通过唯一的名字获得一个回到当前Bean的代理。
试图在同一个配置类上注入 @Bean 方法的结果,实际上也是一种自我引用的情况。要么在实际需要的地方(相对于配置类中的自动注入字段)的方法签名中延迟地解析这种引用,要么将受影响的 @Bean 方法声明为 static,将它们与包含的配置类实例及其生命周期解耦。否则,这些Bean只在 fallback 阶段被考虑,其他配置类上的匹配Bean将被选为主要(primary)候选者(如果有的话)。
试图在同一个配置类上注入 @Bean 方法的结果,实际上也是一种自我引用的情况。要么在实际需要的地方(相对于配置类中的自动注入字段)的方法签名中延迟地解析这种引用,要么将受影响的 @Bean 方法声明为 static,将它们与包含的配置类实例及其生命周期解耦。否则,这些Bean只在 fallback 阶段被考虑,其他配置类上的匹配Bean将被选为主要(primary)候选者(如果有的话)。
基于Java的容器配置
@Bean
与<bean/>作用相同,定义Bean ,可在任何@Component使用,常常@configuration中使用
在@Configuration配置类中
完整模式
@configuration类中的@Bean方法可以调用其他@Bean方法来进行依赖注入
或作为参数,会被自动注入(可以跨配置类),也可用@Autowired 和 @Value 注入,
或作为参数,会被自动注入(可以跨配置类),也可用@Autowired 和 @Value 注入,
跨方法引用会被重定向到容器的生命周期管理,防止同一个 @Bean 方法被意外地通过普通的Java调用来调用
在普通@Component组件中
或@Configuration(proxyBeanMethods = false)
或@Configuration(proxyBeanMethods = false)
精简模式
@Bean 方法不能通过调用其他的 @Bean 方法声明bean间的依赖关系
@Bean方法只是一个普通的工厂方法
@Bean方法只是一个普通的工厂方法
类的设计方面没有任何限制,在运行时不需要应用CGLIB子类,可以是 final 的等等
属性
name
默认情况下,返回的Bean的名字和方法的名字是一样的
如果用数组指定多个,则第一个为name,剩下的为别名
如果用数组指定多个,则第一个为name,剩下的为别名
init-method 和 destroy-method 属性
可以使用接口default 默认方法来定义Bean,实现类会继承该Bean定义
@Bean方法返回值可以是实际类型的父类或实现的接口,
不过,提前类型预测的可见性限制在指定的接口类型,只有被实例化后,容器才会知道完整的类型,可能会影响匹配结果
每个这样的方法从字面上看只是一个特定的Bean引用的工厂方法,没有任何特殊的运行时语义
不过,提前类型预测的可见性限制在指定的接口类型,只有被实例化后,容器才会知道完整的类型,可能会影响匹配结果
每个这样的方法从字面上看只是一个特定的Bean引用的工厂方法,没有任何特殊的运行时语义
如果你一直通过声明的服务接口来引用你的类型,你的 @Bean 返回类型可以安全地加入这个设计决定。
然而,对于实现了多个接口的组件或可能被其实现类型引用的组件来说,声明最具体的返回类型是比较安全的
(至少要与引用你的Bean的注入点要求的具体类型一样)
然而,对于实现了多个接口的组件或可能被其实现类型引用的组件来说,声明最具体的返回类型是比较安全的
(至少要与引用你的Bean的注入点要求的具体类型一样)
定义scope使用@Scope("prototype")
添加描述@Description("Provides a basic example of a bean")
添加描述@Description("Provides a basic example of a bean")
@Bean
具有自动注入方法参数的能力
参数的注入方式和使用@Autowired相同都是先类型在名称,可以使用@Qualifier帮助筛选
普通Spring组件中的 @Bean 方法与Spring @Configuration 类中的对应方法的处理方式不同
在普通的 @Component 类中调用 @Bean 方法中的方法或字段具有标准的Java语义,没有特殊的CGLIB处理或其他约束条件适用
CGLIB代理是调用 @Configuration 类中 @Bean 方法中的方法或字段的方式,它创建了对协作对象的Bean元数据引用
在普通的 @Component 类中调用 @Bean 方法中的方法或字段具有标准的Java语义,没有特殊的CGLIB处理或其他约束条件适用
CGLIB代理是调用 @Configuration 类中 @Bean 方法中的方法或字段的方式,它创建了对协作对象的Bean元数据引用
@Bean 方法声明为 static 的,这样就可以在不创建其包含的配置类实例的情况下调用它们
CGLIB子类只能覆盖非静态方法。因此,对另一个 @Bean 方法的直接调用具有标准的Java语义,结果是直接从工厂方法本身返回一个独立实例
CGLIB子类只能覆盖非静态方法。因此,对另一个 @Bean 方法的直接调用具有标准的Java语义,结果是直接从工厂方法本身返回一个独立实例
@Bean 方法的Java语言可见性对Spring容器中产生的Bean定义没有直接影响
@Configuration 类中的常规 @Bean 方法需要是可重写的—也就是说,它们不能被声明为 private 或 final
@Configuration 类中的常规 @Bean 方法需要是可重写的—也就是说,它们不能被声明为 private 或 final
@Import或<import/>
@Import 注解允许从另一个配置类中加载 @Bean 定义,导入的可以是配置类,也可是组件
@Condition
根据一些任意的系统状态,有条件地启用或禁用一个完整的 @Configuration 类,甚至是单个的 @Bean 方法
@Profile
只有在Spring Environment 中启用了特定的配置文件时才激活
将Java和XML配置相结合
在XML方便或必要的情况下,你有一个选择:
要么通过使用例如 ClassPathXmlApplicationContext 以 "以XML为中心" 的方式实例化容器,
要么通过使用 AnnotationConfigApplicationContext 和 @ImportResource 注解来根据需要导入XML,以 "以Java为中心" 的方式实例化它。
要么通过使用例如 ClassPathXmlApplicationContext 以 "以XML为中心" 的方式实例化容器,
要么通过使用 AnnotationConfigApplicationContext 和 @ImportResource 注解来根据需要导入XML,以 "以Java为中心" 的方式实例化它。
基于xnl的容器配置
<constructor-arg
name
index
type
ref
<property
value
ref
name
自动注入
bean属性autowire
byName
通过set方法注入有同名bean的属性
byType
通过set方法注入有同类型(isAssignableFrom关系)bean的属性
如果有多个候选者,则报异常
如果有多个候选者,则报异常
这些类型直接跳过,不会注入:
Object,Boolean,boolean,Byte,byte,Character,char,Double,double,Float,float,Integer,int,Long,Short,shot,Enum,CharSequence,Number,Date,java.time.temporal.Temporal,java.net.URI,java.net.URI,java.util.Locale,java.lang.Class
Object,Boolean,boolean,Byte,byte,Character,char,Double,double,Float,float,Integer,int,Long,Short,shot,Enum,CharSequence,Number,Date,java.time.temporal.Temporal,java.net.URI,java.net.URI,java.util.Locale,java.lang.Class
construct
spring会找到x类中所有的构造方法(一个类可能有多个构造方法),然后将这些构造方法进行排序(先按修饰符进行排序,public的在前面,其他的在后面,如果修饰符一样的,会按照构造函数参数数量倒叙,也就是采用贪婪的模式进行匹配,spring容器会尽量多注入一些需要的对象)得到一个构造函数列表,会轮询这个构造器列表,判断当前构造器所有参数是否在容器中都可以找到匹配的bean对象,如果可以找到就使用这个构造器进行注入,如果不能找到,那么就会跳过这个构造器,继续采用同样的方式匹配下一个构造器,直到找到一个合适的为止。(还会比较参数类型和bean类型的匹配程度的打分)
no
不进行自动注入
default
按照父元素beans的default-autowire属性的值,bean的autowire可以覆盖父元素的全局设置
xml和注解混合使用
xml中bean定义和context-annotation + @Autowired等注解注入依赖
基础Bean定义也是在XML文件中明确定义的,而注解只驱动依赖性注入
xml中context-scan+ @Component@Autowired等声明bean或@Configuration
IOC或DI
什么是IOC控制反转?
为什么使用IOC思想?
如何实现IOC?
ID和IOC的关系?
Bean定义
容器在内部把元数据统一解析为BeanDefinition,再根据BeanDefinition创建Bean
另外,Bean定义也可以通过编码的方式直接创建,然后注册到容器中
Bean以及它们之间的依赖关系(即它们与之合作的其他对象)都反映在容器使用的配置元数据中
另外,Bean定义也可以通过编码的方式直接创建,然后注册到容器中
Bean以及它们之间的依赖关系(即它们与之合作的其他对象)都反映在容器使用的配置元数据中
元数据或BeanDefinition属性
作用域scope
取值
singleton(默认)
单例,getBean返回同一个实例
由容器管理完整的生命周期
与GoF singleton模式区别
GoF singleton模式对对象的范围进行了硬编码,
即每个ClassLoader创建一个且仅有一个特定类的实例。
Spring单例的范围最好被描述为从一个容器获取的都是同一Bean实例
即每个ClassLoader创建一个且仅有一个特定类的实例。
Spring单例的范围最好被描述为从一个容器获取的都是同一Bean实例
prototype
原型,getBean每次返回新实例
容器对prototype Bean进行实例化、配置和其他方面的组装,相当于new操作,然后将其移交给客户端管理,
配置的销毁生命周期回调不会被调用,
不过可自定义Bean后置处理器,来释放prototype Bean 持有的资源。
配置的销毁生命周期回调不会被调用,
不过可自定义Bean后置处理器,来释放prototype Bean 持有的资源。
request
每个Http Request各自持有一个Bean实例
session
Http Session
application
ServletContext
websocket
WebSocket
thread
默认不注册
自定义scope
步骤
实现Scope接口
调用容器registerScope方法注册
设置scope为自定义的scope
设置规则
应该对所有有状态的 bean 使用 prototype scope,
对无状态的 bean 使用 singleton scope。
对无状态的 bean 使用 singleton scope。
作为依赖的Scope Bean,寻找真正的目标Bean实例的方式
代理
<bean/>的<aop:scoped-proxy/>元素
或注解@ApplicationScope、@SessionScope和@RequestScope
或注解@ApplicationScope、@SessionScope和@RequestScope
方法注入
依赖注入点(也就是构造器或设置器参数或自动注入的字段)
声明ObjectFactory<MyTargetBean>或ObjectProvider<MyTargetBean>
声明ObjectFactory<MyTargetBean>或ObjectProvider<MyTargetBean>
设置方式
<bean/> 的scope属性
@Scope注解
scoped-proxy处理scope 依赖的便捷方式
懒加载开关lazy-init
默认true
父Bean定义parent
Bean定义的继承
一个子Bean定义从父定义继承配置数据。子定义可以覆盖一些值或根据需要添加其他值。
使用父Bean定义和子Bean定义可以节省大量的打字工作。有效地,这是一种模板化的形式。
子bean定义由 ChildBeanDefinition类表示
继承规则
如果没有指定,子Bean定义会使用父定义中的Bean类,但也可以覆盖它。
在后一种情况下,子Bean类必须与父类兼容(也就是说,它必须接受父类的属性值)。
在后一种情况下,子Bean类必须与父类兼容(也就是说,它必须接受父类的属性值)。
子Bean定义从父级继承scope、构造函数参数值、属性值和方法重写,并可以选择添加新的值。
你指定的任何scope、初始化方法、销毁(destroy)方法或 static 工厂方法设置都会覆盖相应的父类设置。
你指定的任何scope、初始化方法、销毁(destroy)方法或 static 工厂方法设置都会覆盖相应的父类设置。
其余的设置总是来自于子定义:依赖、自动注入模式、依赖检查、singleton和懒加载。
抽象abstract
当一个定义是 abstract 的,它只能作为一个纯模板Bean定义使用,作为子定义的父定义。
容器会忽略被定义为抽象的 bean 定义,也就不会去创建实例
被设置abstract="true"的Bean可以不设置class属性
如果你有一个(父)Bean定义,你打算只作为模板使用,并且这个定义指定了一个类,你必须确保将 abstract 属性设置为 true,否则应用上下文将实际(试图)预实化 abstract Bean。
容器会忽略被定义为抽象的 bean 定义,也就不会去创建实例
被设置abstract="true"的Bean可以不设置class属性
如果你有一个(父)Bean定义,你打算只作为模板使用,并且这个定义指定了一个类,你必须确保将 abstract 属性设置为 true,否则应用上下文将实际(试图)预实化 abstract Bean。
name
名字生成器
自定义Bean的性质
声明周期回调
由BeanPostProcessor执行所有回调方法
由BeanPostProcessor执行所有回调方法
初始化回调
Bean所有属性设置完成后执行
Bean所有属性设置完成后执行
@PostConstruct注解方法
实现InitializingBean接口afterPropertiesSet()
<bean/>或@Bean的init-method属性指定
<beans/> 元素属性中 default-init-method 属性
<beans/> 元素属性中 default-init-method 属性
销毁
容器销毁时执行 <bean/>或@Bean的 destroy-method属性指定
当值为inferred时,指示Spring自动检测特定bean类上的public close 或 shutdown 方法。
(任何实现了 java.lang.AutoCloseable 或 java.io.Closeable 的类都可以匹配)
或<beans/> 元素上的 default-destroy-method 属性
容器销毁时执行
@PreDestroy注解方法
实现DisposableBean接口destroy()
当值为inferred时,指示Spring自动检测特定bean类上的public close 或 shutdown 方法。
(任何实现了 java.lang.AutoCloseable 或 java.io.Closeable 的类都可以匹配)
或<beans/> 元素上的 default-destroy-method 属性
上述两种回调都可以同时存在多种方式,如果方法名不同,则按注解、接口、属性的顺序先后执行,同名则只会执行一次
启动和关闭的回调Lifecycle
在非Web应用中优雅地关闭Spring IoC容器
注册一个 shutdown hook,调用 registerShutdownHook() 方法
Aware接口或自动注入
使用这些接口会将你的代码与Spring API捆绑在一起,并且不遵循反转控制的风格。
因此,我们建议那些需要对容器进行编程访问的基础设施Bean使用这些接口
因此,我们建议那些需要对容器进行编程访问的基础设施Bean使用这些接口
Environment
配置文件peofiles
profile是一个命名的、逻辑上的bean定义组,只有在给定的profile处于活动状态时才会在容器中注册
即在不同的环境或情况中注册不同的bean
即在不同的环境或情况中注册不同的bean
Environment 对象在profile方面的作用是确定哪些profile(如果有的话)是当前活动(active)的,以及哪些profile(如果有的话)应该是默认活动的。
如果一个 @Configuration 类被标记为 @Profile,所有与该类相关的 @Bean 方法和 @Import 注解都会被绕过,除非一个或多个指定的 profiles 处于激活状态
同理被标记为 @Profile的@Bean方法
同理被标记为 @Profile的@Bean方法
使用
配置@Profile或<beans profile = "production" >
激活
编码:容器的setActiveProfiles方法,setDefaultProfiles()方法
属性:通过spring.profiles.active和spring.profiles.default属性声明性地激活profiles
其通过系统环境变量、JVM系统属性、web.xml 中的servlet上下文参数,甚至作为JNDI中的一个条目来指定
其通过系统环境变量、JVM系统属性、web.xml 中的servlet上下文参数,甚至作为JNDI中的一个条目来指定
可同时激活多个
属性properties
属性(Properties)在几乎所有的应用程序中都扮演着重要的角色,
它可能来自各种来源:properties 文件、JVM系统属性、系统环境变量、JNDI、Servlet上下文参数、特设的 Properties 对象、Map 对象等等
它可能来自各种来源:properties 文件、JVM系统属性、系统环境变量、JNDI、Servlet上下文参数、特设的 Properties 对象、Map 对象等等
与属性有关的 Environment 对象的作用是为用户提供一个方便的服务接口,用于配置属性源并从它们那里解析属以供查询。
PropertySource属性源
Spring的 StandardEnvironment 配置了两个 PropertySource 对象—一个代表JVM系统属性集合(System.getProperties()),一个代表系统环境变量集合(System.getenv())
StandardServletEnvironment 被填充了额外的默认属性源,包括servlet config、servlet context参数,以及 JndiPropertySource(如果JNDI可用)。
属性源搜索层次或优先级
- 自定义属性源
- ServletConfig 参数(如果适用 - 例如,在 DispatcherServlet 上下文的情况下)。
- ServletContext 参数(web.xml的context-param条目).
- JNDI环境变量(java:comp/env/ 条目)。
- JVM系统属性(-D 命令行参数)。
- JVM系统环境(操作系统环境变量)
@PropertySource("classpath:/com/myco/app.properties")或 声明式添加属性源
<import resource="com/bank/service/${customer}-config.xml"/>
<import resource="com/bank/service/${customer}-config.xml"/>
MessageSource 国际化
事件
步骤
自定义事件
注册服务Bean,注入发布器,方法发布事件
注册监听器Bean,监听到事件时,执行处理方法
注解方式
资源
一个 application context 是一个 ResourceLoader,它可以用来加载 Resource 对象。Resource 本质上是JDK java.net.URL 类的一个功能更丰富的版本。事实上,在适当的时候,Resource 的实现会包裹 java.net.URL 的实例。Resource 可以以透明的方式从几乎任何位置获取底层资源,包括从 classpath、文件系统位置、任何可以用标准URL描述的地方,以及其他一些变化
你可以配置部署在 application context 中的Bean,使其实现特殊的回调接口 ResourceLoaderAware,以便在初始化时自动回调,并将应用 application contex t本身作为 ResourceLoader 传递进来。你也可以公开 Resource 类型的属性,用于访问静态资源。它们像其他属性一样被注入其中。你可以将这些 Resource 属性指定为简单的 String 路径,并在Bean部署时依赖从这些文本字符串到实际 Resource 对象的自动转换。
提供给 ApplicationContext 构造函数的一个或多个位置路径实际上是资源字符串,以简单的形式,根据具体的上下文实现被适当地处理。例如,ClassPathXmlApplicationContext 将一个简单的位置路径视为 classpath 位置。你也可以使用带有特殊前缀的位置路径(资源字符串)来强制从 classpath 或URL加载定义,而不考虑实际的上下文类型。
提供给 ApplicationContext 构造函数的一个或多个位置路径实际上是资源字符串,以简单的形式,根据具体的上下文实现被适当地处理。例如,ClassPathXmlApplicationContext 将一个简单的位置路径视为 classpath 位置。你也可以使用带有特殊前缀的位置路径(资源字符串)来强制从 classpath 或URL加载定义,而不考虑实际的上下文类型。
应用程序启动跟踪
pplicationStartup 工具,用具体的指标来跟踪应用程序的启动步骤,可以帮助了解在启动阶段花费的时间
容器加载过程
容器扩展点
BeanPostProcessor
执行时机
对于容器创建的每个 bean 实例,后处理器在容器初始化方法(如 InitializingBean.afterPropertiesSet() 或任何已声明的 init 方法)被调用之前和任何 bean 初始化回调之后都会从容器获得一个回调
作用
后处理程序可以对Bean实例采取任何行动,包括完全忽略回调。bean类后处理器通常会检查回调接口,或者用代理来包装bean类。
一些Spring AOP基础设施类被实现为Bean后处理器,以提供代理封装逻辑。
一些Spring AOP基础设施类被实现为Bean后处理器,以提供代理封装逻辑。
只对所在容器中的Bean进行后处理
实现了 BeanPostProcessor 接口的类是特殊的,会被容器区别对待
实现了 BeanPostProcessor 接口的类是特殊的,会被容器区别对待
执行顺序
实现Ordered接口
使用
实现BeanPostProcessor接口
注册到容器
通过声明为Bean,
容器会自动检测在配置元数据中定义的实现 BeanPostProcessor 接口的任何Bean,并注册
容器会自动检测在配置元数据中定义的实现 BeanPostProcessor 接口的任何Bean,并注册
@Bean工厂方法返回值类型必须是实现类本身或直接接口类型,否则,ApplicationContext 无法在完全创建它之前按类型自动检测它。由于 BeanPostProcessor 需要尽早被实例化,以便应用于上下文中其他Bean的初始化,所以这种早期的类型检测是至关重要的。
通过addBeanPostProcessor方法编程方式注册
注册的顺序决定了执行的顺序,Ordered接口失效
编程方式注册的BeanPostProcessor总是先于自动注册之前执行
编程方式注册的BeanPostProcessor总是先于自动注册之前执行
标记为懒加载将被忽略
BeanFactoryPostProcessor
执行时机
容器实例化 BeanFactoryPostProcessor 实例以外的任何Bean之前
作用
读取配置元数据,并在对其进行潜在的修改。
只对所在容器中的Bean进行后处理
执行顺序
实现Ordered接口
使用
实现BeanFactoryPostProcessor接口
注册到容器
通过声明为Bean,
容器会自动检测在配置元数据中定义的实现 BeanFactoryPostProcessor 接口的任何Bean,并注册
容器会自动检测在配置元数据中定义的实现 BeanFactoryPostProcessor 接口的任何Bean,并注册
标记为懒加载将被忽略
AOP
什么是AOP?
OOP中模块化的关键单位是类,而AOP中模块化的单位是切面。切面使跨越多种类型和对象的关注点(如事务管理)模块化
AOP如何实现的?
使用代理实现方式中的的CGLib代理和JDK动态代理方式
如果Spring确定一个Bean被一个或多个切面所advice,它就会自动为该Bean生成一个代理来拦截方法调用,并确保 advice 在需要时被运行。
Spring AOP默认使用标准的JDK动态代理进行AOP代理
默认情况下,如果一个业务对象没有实现一个接口,就会使用CGLIB。由于对接口而不是类进行编程是很好的做法,业务类通常实现一个或多个业务接口。在那些(希望是罕见的)需要向没有在接口上声明的方法提供advice的情况下,或者在需要将代理对象作为具体类型传递给方法的情况下,可以 强制使用 CGLIB。
默认情况下,如果一个业务对象没有实现一个接口,就会使用CGLIB。由于对接口而不是类进行编程是很好的做法,业务类通常实现一个或多个业务接口。在那些(希望是罕见的)需要向没有在接口上声明的方法提供advice的情况下,或者在需要将代理对象作为具体类型传递给方法的情况下,可以 强制使用 CGLIB。
由于Spring的AOP框架是基于代理的,根据定义,目标对象内部的调用是不会被拦截的。对于JDK代理,只有代理上的public接口方法调用可以被拦截。使用CGLIB,代理上的public和protected的方法调用都可以被拦截(如果有必要,甚至可以拦截包中的可见方法)。然而,通过代理的普通交互应该始终通过public签名来设计
一旦调用最终到达目标对象,它可能对自身进行的任何方法调用,如 this.XX() ,都将被调用到 this 引用,而不是代理。这有很重要的意义。这意味着自我调用不会导致与方法调用相关的advice得到运行的机会。
最好的方法(这里的 "最好" 一词用得很宽泛)是重构你的代码,使自我调用不会发生。这确实需要你做一些工作,但这是最好的、最不具侵入性的方法
AopContext.currentProxy()这完全将你的代码与Spring AOP联系在一起,而且它使类本身意识到它是在AOP上下文中使用的,这与AOP背道而驰。它还需要在创建代理时进行一些额外的配置setExposeProxy(true)
最后,必须指出的是,AspectJ不存在这种自我调用的问题,因为它不是一个基于代理的AOP框架。
最好的方法(这里的 "最好" 一词用得很宽泛)是重构你的代码,使自我调用不会发生。这确实需要你做一些工作,但这是最好的、最不具侵入性的方法
AopContext.currentProxy()这完全将你的代码与Spring AOP联系在一起,而且它使类本身意识到它是在AOP上下文中使用的,这与AOP背道而驰。它还需要在创建代理时进行一些额外的配置setExposeProxy(true)
最后,必须指出的是,AspectJ不存在这种自我调用的问题,因为它不是一个基于代理的AOP框架。
Spring AOP与AspectJ AOP
Spring解释了与AspectJ 5相同的注解,使用AspectJ提供的库进行 pointcut 解析和匹配。不过,AOP运行时仍然是纯粹的Spring AOP,而且不依赖AspectJ编译器或织入器(weaver),使用AspectJ编译器和weaver可以使用完整的AspectJ语言
Spring AOP从未努力与AspectJ竞争,以提供一个全面的AOP解决方案。我们认为,Spring AOP等基于代理的框架和AspectJ等完整的框架都很有价值,它们是互补的,而不是竞争的。Spring AOP的AOP方法与其他大多数AOP框架不同。其目的不是提供最完整的AOP实现(尽管Spring AOP的能力很强)。Spring将Spring AOP和IoC与AspectJ无缝集成,以便在基于Spring的应用架构中实现AOP的所有用途。这种整合并不影响Spring AOP API或AOP联盟的API。Spring AOP仍然是向后兼容的。
Spring AOP为企业级Java应用中的大多数问题提供了一个很好的解决方案,这些问题是可以用AOP来解决的。
Spring AOP的AOP方法与其他大多数AOP框架不同。其目的不是提供最完整的AOP实现(尽管Spring AOP的能力很强)。相反,其目的是在AOP实现和Spring IoC之间提供一个紧密的整合,以帮助解决企业应用中的常见问题。
使用最简单的东西就可以了。Spring AOP比使用完整的AspectJ更简单,因为不需要在开发和构建过程中引入AspectJ编译器/织入器。如果你只需要advise在Spring Bean上执行操作,Spring AOP是正确的选择。如果你需要advise不由Spring容器管理的对象(比如典型的domain对象),你就需要使用AspectJ。如果你希望为简单的方法执行以外的连接点提供advise(例如,字段获取或设置连接点等),你也需要使用AspectJ。
Spring AOP是用纯Java实现的。不需要特殊的编译过程。Spring AOP不需要控制类加载器的层次结构,因此适合在Servlet容器或应用服务器中使用。
AOP概念
AOP的术语并不是特别直观
这些术语并不是针对Spring的
这些术语并不是针对Spring的
Aspect(切面): 一个跨越多个类的关注点的模块化。事务管理是企业级Java应用中横切关注点的一个很好的例子。在Spring AOP中,切面是通过使用常规类(基于 schema 的方法)或使用 @Aspect 注解的常规类(@AspectJ 风格)实现的。
Join point连接点: 程序执行过程中的一个点,例如一个方法的执行或一个异常的处理。在Spring AOP中,一个连接点总是代表一个方法的执行。
Advice通知: 一个切面在一个特定的连接点采取的行动。不同类型的advice包括 "around"、"before" 和 "after" 的advice(Advice 类型将在后面讨论)。许多AOP框架,包括Spring,都将advice建模为一个拦截器,并在连接点(Join point)周围维护一个拦截器链。
Pointcut切入点: 一个匹配连接点的谓词(predicate)。advice与一个切点表达式相关联,并在切点匹配的任何连接点上运行(例如,执行一个具有特定名称的方法)。由切点表达式匹配的连接点概念是AOP的核心,Spring默认使用AspectJ的切点表达式语言。
Introduction引介: 代表一个类型声明额外的方法或字段。Spring AOP允许你为任何 advice 的对象引入新的接口(以及相应的实现)。例如,你可以使用引入来使一个bean实现 IsModified 接口,以简化缓存。(介绍在AspectJ社区中被称为类型间声明)。可以通过使用 @DeclareParents 注解来做一个introduction。这个注解被用来声明匹配的类型有一个新的父类(因此而得名)
Target object目标对象: 被一个或多个切面所 advice 的对象。也被称为 "advised object"。由于Spring AOP是通过使用运行时代理来实现的,这个对象总是一个被代理的对象。
AOP proxy AOP代理: 一个由AOP框架创建的对象,以实现切面契约(advice 方法执行等)。在Spring框架中,AOP代理是一个JDK动态代理或CGLIB代理。
Weaving(织入): 将aspect与其他应用程序类型或对象连接起来,以创建一个 advice 对象。这可以在编译时(例如,使用AspectJ编译器)、加载时或运行时完成。Spring AOP和其他纯Java AOP框架一样,在运行时进行织入。
AOP如何使用?
基于注解
启用 @AspectJ 的支持,可以通过XML或Java风格的配置来启用
@Configuration 添加 @EnableAspectJAutoProxy 注解
aop命名空间+<aop:aspectj-autoproxy/>
依赖:aspectjweaver.jar
声明一个 Aspect
声明带有@Aspect 注解的Bean
Aspects(用 @Aspect 注解的类)可以有方法和字段,和其他类一样。它们也可以包含pointcut、advice和引入(间型的)声明
声明一个切点(Pointcut)
Spring AOP只支持Spring Bean的方法执行连接点,所以你可以把pointcut看作是对Spring Bean上的方法执行的匹配
一个切点声明有两个部分:一个由名称和任何参数组成的签名,以及一个切点表达式
作为pointcut签名的方法必须是一个 void 返回类型
作为pointcut签名的方法必须是一个 void 返回类型
切点表达式
Spring AOP支持以下AspectJ的切点指定器(PCD)
使用 &&、|| 和 ! 来组合 pointcut 表达式
共享命名切点的定义
声明一个通知或增强(Advice)
Advice类型
Before Advice
After Returning Advice
returning 属性获取返回值并传入方法参数,还能限制返回类型
After Throwing Advice
throwing 属性来限制匹配,并将抛出的异常绑定到 advice 参数上
After (Finally) Advice
Around Advice
应该声明 Object 为其返回类型,并且该方法的第一个参数必须是 ProceedingJoinPoint 类型。在 advice 方法的body中,你必须在 ProceedingJoinPoint 上调用 proceed(),以使底层方法运行
around advice 返回的值是方法的调用者看到的返回值,advice可以根据使用情况选择性地返回一个缓存的值、一个封装的值或一些其他的值
around advice 返回的值是方法的调用者看到的返回值,advice可以根据使用情况选择性地返回一个缓存的值、一个封装的值或一些其他的值
"围绕" 一个匹配的方法的执行而运行。它有机会在方法运行之前和之后进行工作,并决定何时、如何、甚至是否真正运行该方法。如果你需要以线程安全的方式分享方法执行前后的状态,例如启动和停止一个定时器,那么 Around advice 经常被使用。
Advice使用规则
始终使用符合你要求的最不强大的 advice 形式。例如,如果 before advice 足以满足你的需要,就不要使用 around advice。
Advice 参数
JoinPoint
args
Advice 顺序
当两个定义在不同切面的advice都需要在同一个连接点运行时,除非你另外指定,否则执行的顺序是不确定的。你可以通过指定优先级来控制执行的顺序。这可以通过在切面类中实现 org.springframework.core.Ordered 接口或用 @Order 注解来完成,这是正常的Spring方式。给定两个切面,从 Ordered.getOrder() 返回较低值的切面(或注解值)具有较高的优先权
正常
Around的proceed()方法之前部分
Before
proceed()执行目标方法
AfterReturning
After
Around的proceed()方法之后部分
方法返回最终结果
异常
Around的proceed()方法之前部分
Before
proceed()执行目标方法
AfterThrowing
After
Aspect 实例化模式
基于Schema的AOP支持
如果你喜欢基于XML的格式,Spring也提供了对使用 aop 命名空间标签定义切面的支持
@AspectJ或XML用于Spring AOP?
程序化编码方法创建@AspectJ代理
什么时候用AOP?
日志、权限等
验证、数据绑定和类型转换
验证
验证对象属性是否满足要求
Validator 接口
supports(Class)
validate(Object, org.springframework.validation.Errors)
ValidationUtils 工具类
数据绑定
BeanWrapper
工作方式:正如其名,它包装一个Bean来对该Bean进行操作,例如设置和检索属性
BeanWrapper 提供了设置和获取属性值(单独或批量)、获取属性描述符以及查询属性以确定它们是否可读或可写的功能,支持嵌套来操作子属性
BeanWrapper 通常不被应用程序代码直接使用,而是被 DataBinder 和 BeanFactory 使用。
内置的 PropertyEditor
实现 Object 和 String 之间的转换
例子
在XML文件中声明的某个Bean的属性的值,使用 ClassEditor 来尝试将该参数解析为一个 Class 对象
在Spring的MVC框架中,解析HTTP请求参数
可自定义和 覆盖内置的
类型转换
core.convert 包提供了一个通用的类型转换系统。该系统定义了一个用于实现类型转换逻辑的SPI和一个用于在运行时执行类型转换的API。在Spring容器中,你可以使用这个系统作为 PropertyEditor 实现的替代品,将外化的Bean属性值字符串转换为所需的属性类型。你也可以在你的应用程序中任何需要类型转换的地方使用public API
转换器(Converter) SPI
ConverterFactory集中整个类层次结构的转换逻辑
GenericConverter 具有比 Converter 更灵活但不那么强类型的签名,支持在多种源和目标类型之间进行转换。此外,GenericConverter 提供了可用的源和目标字段上下文,你可以在实现转换逻辑时使用。这样的上下文让类型转换由字段注解或字段签名上声明的通用信息驱动
ConditionalGenericConverter让一个 Converter 只在一个特定的条件成立的情况下运行
ConversionService API定义了一个统一的API,用于在运行时执行类型转换逻辑
Spring 字段格式化
在客户端环境中工作(如Web应用程序)并需要解析和打印本地化的字段值时,你可以使用 Formatter SPI
配置全局的 Date 和 Time 的格式
Java Bean 校验
Spring表达式语言SpEL
Spring表达式语言(简称 "SpEL")是一种强大的表达式语言,支持在运行时查询和操作对象图。该语言的语法与统一EL相似,但提供了额外的功能,最显著的是方法调用和基本的字符串模板功能。
不直接与 Spring 联系在一起,可以独立使用
需要创建一些引导性的基础设施类,如解析器。大多数Spring用户不需要处理这些基础结构,而是可以只编写表达式字符串进行评估
需要创建一些引导性的基础设施类,如解析器。大多数Spring用户不需要处理这些基础结构,而是可以只编写表达式字符串进行评估
表达式语言能表达的功能
评估(Evaluation)
所谓评估,就是指对SpEL表达式进行计算(评估),得到结果的过程
Bean定义中的表达式
使用SpEL表达式与基于XML或基于注解的配置元数据来定义 BeanDefinition 实例
Null安全(Null-safety)
@Nullable: 注解,表明一个特定的参数、返回值或字段可以是 null 的。
@NonNull: 注解表明特定的参数、返回值或字段不能为 null(在参数/返回值和字段上不需要,因为 @NonNullApi 和 @NonNullFields 分别适用)。
@NonNullApi: 包级的注解,声明非null为参数和返回值的默认语义。
@NonNullFields: 在包一级的注解,声明非null为字段的默认语义。
@NonNull: 注解表明特定的参数、返回值或字段不能为 null(在参数/返回值和字段上不需要,因为 @NonNullApi 和 @NonNullFields 分别适用)。
@NonNullApi: 包级的注解,声明非null为参数和返回值的默认语义。
@NonNullFields: 在包一级的注解,声明非null为字段的默认语义。
目前还不支持通用类型参数、数组变量和数组元素的无效性(nullability),但在即将发布的版本中应该会支持
Data Buffer 和编解码器
Java NIO提供了 ByteBuffer,但许多库在上面建立了自己的字节缓冲区API,特别是对于重复使用缓冲区和/或使用直接缓冲区对性能有利的网络操作。例如,Netty有 ByteBuf 层次结构,Undertow使用XNIO,Jetty使用带有回调的池化字节缓冲区来释放,等等。spring-core 模块提供了一组抽象,以便与各种字节缓冲区API协同工作,如下所示。
DataBufferFactory 对数据缓冲区的创建进行了抽象。
DataBuffer 代表一个字节缓冲区,它可以被 池化 (pooled)。
DataBufferUtils 为数据缓冲区提供实用方法。
编解码器(Codecs) 将数据缓冲流解码或编码为更高层次的对象。
DataBufferFactory 对数据缓冲区的创建进行了抽象。
DataBuffer 代表一个字节缓冲区,它可以被 池化 (pooled)。
DataBufferUtils 为数据缓冲区提供实用方法。
编解码器(Codecs) 将数据缓冲流解码或编码为更高层次的对象。
日志(Logging)
自Spring Framework 5.0以来,Spring在 spring-jcl 模块中实现了自己的Commons Logging bridge。该实现会检查classpath中是否存在Log4j 2.x API和SLF4J 1.7 API,并使用其中第一个发现的API作为日志实现,如果Log4j 2.x和SLF4J都不可用,则退回到Java平台的核心日志设施(也被称为JUL或 java.util.logging)。
Spring的Commons Logging变体只用于核心框架和扩展中的基础设施日志。
对于应用程序代码内的日志需求,最好直接使用Log4j 2.x、SLF4J或JUL
对于应用程序代码内的日志需求,最好直接使用Log4j 2.x、SLF4J或JUL
可以通过 org.apache.commons.logging.LogFactory 检索一个Log实现
AOT 编译优化
事务
源码解析
整合三方框架
MyBatis
SpringBoot
0 条评论
下一页