基于xml 的注入
application.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<bean id="getServiceC" class="com.hank.beans.ServiceImplC" ></bean>
</beans>
var context = new ClassPathXmlApplicationContext("application.xml");
基于注解的 ioc
注入类
@Configuration 和 @Bean 的配合注入,代替的 xml 文件的 注入,可以通过配置类的 class 或 包名查找
@Configuration 本身也是 @Component,意味着配置类本身也会注入,
接口及实现类
package com.hank.beans
public interface Service {
public void doSome();
}
public class ServiceImplA implements Service {
@Override
public void doSome() {
System.out.println(ServiceImplA.class.getSimpleName());
}
}
public class ServiceImplB implements Service {
@Override
public void doSome() {
System.out.println(ServiceImplB.class.getSimpleName());
}
}
如下的俩个配置类,就类似于俩个 xml 文件,编写 bean
package com.hank.beans.config;
import com.hank.beans.Service;
import com.hank.beans.ServiceImplA;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class getServicesConfigA {
// 默认名字就是方法名,也就是 id
@Bean
public Service getServiceA(){
return new ServiceImplA();
}
}
@Configuration
public class getServicesConfigB {
@Bean
public Service getServiceB(){
return new ServiceImplB();
}
}
类似于读取xml 配置文件 获取注入的 bean
package com.hank;
import com.hank.beans.ServiceImplA;
import com.hank.beans.ServiceImplB;
import com.hank.beans.config.getServicesConfigA;
import com.hank.beans.config.getServicesConfigB;
import lombok.Data;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
@Data
public class Programmer {
public static void main(String[] args) {
//传入配置类,配置的类就是告诉在哪里找bean
var context = new AnnotationConfigApplicationContext(getServicesConfigA.class,
getServicesConfigB.class);
//也可以传入配置类所在的包
var context = new AnnotationConfigApplicationContext(getServicesConfigA.class.getPackageName(),
getServicesConfigB.class.getPackageName());
//如果配置类的包在 Programmer 的包下面,可以传入父包
var context = new AnnotationConfigApplicationContext(Programmer.class.getPackageName());
var bean1 =(ServiceImplA) context.getBean("getServiceA");
var bean2 = context.getBean(ServiceImplB.class);
bean1.doSome();
bean2.doSome();
}
}
@ComponentScan 和 @Bean 和 @Configuration 的配合注入
@ComponentScan 指定扫描的包,在包下找 @Configuration 标记的配置类,注入标记 @Bean 的方法返回的类
在代码里能做 @ComponentScan 扫描 @Configuration ,在传统的 xml 文件里也可以. 通过 context:component-scan</context:component-scan> 使用,指定包名,相当于把代码里的注入合并到 xml 的注入里.
@ComponentScan({"com.hank"})
public class Programmer {
public static void main(String[] args) {
// 传入配置类
var context = new AnnotationConfigApplicationContext(Programmer.class);
var beanA= context.getBean(ServiceImplA.class);
var beanB = context.getBean(ServiceImplB.class);
beanA.doSome();
beanB.doSome();
}
}
import 注入,可以组合多个 @Configuration 注入
@Import注解提供了@Bean注解的功能,同时还有原来 Spring 基于 xml 配置文件里的<import>
标签组织多个分散的xml文件的功能,
当然在这里是组织多个分散的@Configuration的类,这些 @Configuration 是第三方的,起到整合的意义,
并且 @Import 的 @Configuration的类优先加载other 包下的配置 @Configuration
package other;
public class ServiceAImpl {
}
@Configuration
public class ConfigServiceA {
@Bean
public ServiceAImpl getServiceA(){
return new ServiceAImpl();
}
}
com.hank 子包 uti 下的 @Configuration
package com.hank.uti;
public class ServiceBImpl {
}
@Configuration
//组合其他包下的 @Configuration,其他包是表示当前的包扫描扫描不到的
@Import(ConfigServiceA.class)
public class ConfigServiceB {
@Bean
public ServiceBImpl getServiceB() {
return new ServiceBImpl();
}
}
//输出如下
/**
configServiceB
other.ConfigServiceA
getServiceA
getServiceB
*/
@ComponentScan(basePackages = {"com.hank"})
public class Programmer {
public static void main(String[] args) {
{
var context = new AnnotationConfigApplicationContext(Programmer.class);
System.out.println("--------------------");
Arrays.stream(context.getBeanDefinitionNames()).forEach(System.out::println);
}
}
}
一个不常用的列子, import 去注入其他包下的普通类,或其他包下的@Component,在整合第三方类的时候或许有意义
public interface ServiceInterface {
void doSome();
}
@Component
public class ServiceAImpl implements ServiceInterface {
@Override
public void doSome() {
System.out.println(this);
}
}
@Component
//引入一个普通的类
@Import(ServiceCImpl.class)
public class ServiceBImpl implements ServiceInterface {
@Override
public void doSome() {
System.out.println(this);
}
}
//普通类没有加注解
public class ServiceCImpl implements ServiceInterface {
@Override
public void doSome() {
System.out.println(this);
}
}
//@Import(ServiceCImpl.class) 引入的普通类注入的 bean 名称是全限定名
@ComponentScan(basePackages = {"com.hank"})
public class Programmer {
public static void main(String[] args) {
{
var context = new AnnotationConfigApplicationContext(Programmer.class);
System.out.println("--------------------");
Arrays.stream(context.getBeanDefinitionNames()).forEach(System.out::println);
}
}
}
//serviceAImpl
//serviceBImpl
//com.hank.uti.ServiceCImpl
实现 ImportSelector 用于个性化动态注入其他的 @Configuration
其他包下的bean ,不能扫描到
package other;
public class ServiceAImpl {
}
public class ServiceCImpl {
}
@Configuration
public class ConfigServiceA {
@Bean
public ServiceBImpl getServiceA(){
return new ServiceBImpl();
}
}
@Configuration
public class ConfigServiceC {
@Bean
public ServiceCImpl getServiceC(){
return new ServiceCImpl();
}
}
com.hank 包下的 bean,包扫描会扫描到
package com.hank.uti;
//引入其他包下的 bean 通过实现 ImportSelector,可以获取 bean 上的所有注解元素据
class ServiceImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
var list = new ArrayList<String>();
if(!importingClassMetadata.hasAnnotation(EnableOtherServices.class.getName()))
return list.toArray(new String[list.size()]);
var map = importingClassMetadata.getAllAnnotationAttributes(
EnableOtherServices.class.getName(),true);
var obj = map.get("value");
obj.forEach(item -> {
if(item instanceof String[]){
var arr = (String[])item;
Arrays.stream(arr).forEach(p->{
switch (p) {
case "configServiceA":
list.add(other.ConfigServiceA.class.getName());
break;
case "configServiceC":
list.add(other.ConfigServiceC.class.getName());
break;
default:
break;
}
});
}
});
return list.toArray(new String[list.size()]);
}
}
//自定义注解,选择引入其他包下的指定 @Configuration
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(ServiceImportSelector.class)
@interface EnableOtherServices {
//指定引入的 @Configuration
String[] value();
}
//使用 @EnableOtherServices({"configServiceA","configServiceC"}) 把其他包下
// @Configuration 注入当前包的 ioc 容器
//在这里可以有选择性的注入外部包下的 指定 @Configuration
@Configuration
@EnableOtherServices({"configServiceA","configServiceC"})
public class ConfigServiceB {
@Bean
public ServiceBImpl getServiceB() {
return new ServiceBImpl();
}
}
/**
configServiceB
other.ConfigServiceA
getServiceA
other.ConfigServiceC
getServiceC
//后加载 @EnableOtherServices 注解的标注的 bean
getServiceB
*/
@ComponentScan(basePackages = {"com.hank"})
public class Programmer {
public static void main(String[] args) {
{
var context = new AnnotationConfigApplicationContext(Programmer.class);
System.out.println("--------------------");
Arrays.stream(context.getBeanDefinitionNames()).forEach(System.out::println);
}
}
}
实现 DefferredServiceImportSelector 实现后加载
还可以实现 DeferredImportSelector 接口,这样 selectImports返回的类就都是最后加载的,而不是像 ImportSelector 注解那样,先加载外部的bean
实现 ImportBeanDefinitionRegistrar,实现个性化加载
与ImportSelector用法与用途类似,但是如果我们想重定义Bean,例如动态注入属性,改变Bean的类型和Scope等等,就需要通过指定实现ImportBeanDefinitionRegistrar的类实现。例如。
com.other 包下的
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface BeanIoc {
}
@BeanIoc
public class Service{
}
//主包
package com.hank.uti;
/**
* 定义包路径。(指定包下所有添加了BeanIoc注解的类作为bean)
* 注意这里 @Import(BeanIocScannerRegister.class) 的使用
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(BeanIocScannerRegister.class)
public @interface BeanIocScan {
String[] basePackages() default "";
}
// ImportBeanDefinitionRegistrar 实现其它包下自定义注解注入
public class BeanIocScannerRegister implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
var annoAttrs = AnnotationAttributes.fromMap(
importingClassMetadata.getAnnotationAttributes(BeanIocScan.class.getName()));
if (annoAttrs == null || annoAttrs.isEmpty()) {
return;
}
String[] basePackages = (String[]) annoAttrs.get("basePackages");
// 找到指定包路径下所有添加了BeanIoc注解的类,并且把这些类添加到IOC容器里面去
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry, false);
scanner.addIncludeFilter(new AnnotationTypeFilter(BeanIoc.class));
scanner.scan(basePackages);
}
}
@ComponentScan(basePackages = {"com.hank"})
@BeanIocScan(basePackages = {"com.other"})
public class Programmer {
public static void main(String[] args) {
var context = new AnnotationConfigApplicationContext(Programmer.class);
System.out.println("start---------------------------------------");
Arrays.stream(context.getBeanDefinitionNames()).forEach(p -> {
System.out.println(p);
});
}
}
@Component 注入,通过包名查找
通过 @bean 进行注入,非常繁琐,如果类是自己定义的,不是别人提供的,就没必要写一个方法进行注入,直接对自己写的类添加 @Component 就可以直接注入
@Component
public class ServiceImplA implements Service {
@Override
public void doSome() {
System.out.println(ServiceImplA.class.getSimpleName());
}
}
@Component
public class ServiceImplB implements Service {
@Override
public void doSome() {
System.out.println(ServiceImplB.class.getSimpleName());
}
}
//传递包名,会自动去扫描包下面的 带有 @Component 的类,并注入
var context = new AnnotationConfigApplicationContext(Programmer.class.getPackageName());
// 传递class 也是可以的,多此一举,通过包扫描更方便
var context = new AnnotationConfigApplicationContext(ServiceImplA.class,ServiceImplB.class);
@Component 和 @ComponentScan 配合更优雅的写法
@ComponentScan({"com.hank"})
public class Programmer {
public static void main(String[] args) {
// 传入配置类
var context = new AnnotationConfigApplicationContext(Programmer.class);
var beanA= context.getBean(ServiceImplA.class);
var beanB = context.getBean(ServiceImplB.class);
beanA.doSome();
beanB.doSome();
}
}
注入属性 @value
此功能是要和 @PropertySource 配合加载配置文件才有有意义
@PropertySource 可以加在任意类或接口上,把解析的内容赋给 Environment 的 propertySources 可以加载 xml 文件 或其他文件只要有对应的文件解析器
application.application.properties
obj.name=bar
obj.age=20
@PropertySource(value = {"classpath:application.properties"},name = "aaa")
public class Programmer {
}
通过 SpEL 表达式来解析 Environment 的属性,或直接获取 Environment取值
@Data
@ToString
@Component
public class ServiceImplA implements ServiceInterface {
String name;
int age;
public ServiceImplA(@Value("${obj.name}") String name, @Autowired Environment environment) {
var env= environment.getProperty("obj.age");
this.name = name;
this.age=Integer.parseInt(env);
}
@Override
public void doSome() {
System.out.println(this);
}
}
把一个类注入到另一个类的属性上
@Data
@ToString
@Component
//在 ServiceImplA 注入后,在注入 ServiceImplB
@ConditionalOnBean(ServiceImplA.class)
public class ServiceImplB implements ServiceInterface {
@Value("#{serviceImplA}")
ServiceInterface service;
@Override
public void doSome() {
service.doSome();
}
}
@ConfigurationProperties 方便直接绑定配置到类
@ConfigurationProperties 会在编译的时候在输出目录下的META-INF 下生成 spring-configuration-metadata.json 文件
@Data
@ToString
@Component
@ConfigurationProperties(prefix = "obj")
public class ServiceImplB implements ServiceInterface {
//直接把 obj.name 绑定到 name 属性
String name;
//直接把 obj.age 绑定到 age 属性
int age;
@Override
public void doSome() {
System.out.println(this);
}
}
注解驱动与xml驱动互通
xml 注入引入注解注入
@bean 注解注入类
@Configuration
public class getServicesConfigA {
// 默认名字就是方法名,也就是 id
@Bean
public ServiceInterface getServiceA(){
return new ServiceImplA();
}
}
组件扫描注入的类
@Component
public class ServiceImplB implements ServiceInterface {
@Override
public void doSome() {
System.out.println(ServiceImplB.class.getSimpleName());
}
}
application.xml xml 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!--xml 自己注入的类-->
<bean id="getServiceC" class="com.hank.beans.ServiceImplC" ></bean>
<!--引入配置类 注入 bean-->
<context:annotation-config/>
<bean class="com.hank.beans.config.getServicesConfigA"></bean>
<!--引入组件扫描注入的类-->
<context:component-scan base-package="com.hank"/>
</beans>
var context = new ClassPathXmlApplicationContext("application.xml");
var arr = context.getBeanDefinitionNames();
Arrays.stream(arr).forEach(System.out::println);
// getServiceC
// org.springframework.context.annotation.internalConfigurationAnnotationProcessor
// org.springframework.context.annotation.internalAutowiredAnnotationProcessor
// org.springframework.context.event.internalEventListenerProcessor
// org.springframework.context.event.internalEventListenerFactory
// com.hank.beans.config.getServicesConfigA#0
// serviceImplB
// getServicesConfigA
// getServiceA
注解注入通过 @ImportResource 引入 xml 注入
@bean 注解注入类
@Configuration
public class getServicesConfigA {
// 默认名字就是方法名,也就是 id
@Bean
public ServiceInterface getServiceA(){
return new ServiceImplA();
}
}
组件扫描注入的类
@Component
public class ServiceImplB implements ServiceInterface {
@Override
public void doSome() {
System.out.println(ServiceImplB.class.getSimpleName());
}
}
application.xml xml 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!--xml 自己注入的类-->
<bean id="getServiceC" class="com.hank.beans.ServiceImplC" ></bean>
</beans>
@ComponentScan({"com.hank"})
//引入xml 注入
@ImportResource({"application.xml"})
public class Programmer {
public static void main(String[] args) {
{
// 传入配置类
var context = new AnnotationConfigApplicationContext(Programmer.class);
var arr = context.getBeanDefinitionNames();
Arrays.stream(arr).forEach(System.out::println);
}
}
// org.springframework.context.annotation.internalConfigurationAnnotationProcessor
// org.springframework.context.annotation.internalAutowiredAnnotationProcessor
// org.springframework.context.event.internalEventListenerProcessor
// org.springframework.context.event.internalEventListenerFactory
// programmer --new AnnotationConfigApplicationContext(Programmer.class)
// serviceImplB
// getServicesConfigA
// getServiceA
// getServiceC
}
@Bean 为什么必要要和 @Configuration 搭配 而不是 @Component
ServiceImplA 和 ServiceImplB ,ServiceImplB 组合了 serviceImplA
public class ServiceImplA implements ServiceInterface {
@Override
public void doSome() {
System.out.println(ServiceImplA.class.getSimpleName());
}
}
@Data
public class ServiceImplB implements ServiceInterface {
ServiceImplA serviceImplA;
@Override
public void doSome() {
System.out.println(ServiceImplB.class.getSimpleName());
}
}
@Bean 和 @Component 搭配, @Bean 方法没有被代理,只是把 @Bean 方法产生的类注入到容器
@Configuration与@Bean的组合,当在@Configuration配置类里面用了@Bean,那么这个方法就不再是普通方法了,这个方法会被cglib所代理,代理后的情况就是,你在调用这个方法的时候,会先去从IOC容器中获取对应的Bean,如果获取不到再调用你写的方法创建Bean
@Data
@Component
public class getServicesConfig {
@Bean
public ServiceImplA getServiceA(){
System.out.println("getServicesConfigA 执行一次");
return new ServiceImplA();
}
public ServiceInterface getServiceB(){
System.out.println("getServicesConfigB 执行一次");
var tmp=new ServiceImplB();
tmp.setServiceImplA(getServiceA());
return new ServiceImplB();
}
}
// 传入配置类
var context = new AnnotationConfigApplicationContext(Programmer.class);
context.getBean(ServiceImplA.class);
context.getBean(ServiceImplA.class);
//getServicesConfigA 执行一次 ,说明在注入容器的时候执行了一次
var config = context.getBean(getServicesConfig.class);
//如下代码所示 getServiceA 这个方法没有被代理,每次都会生成 ServiceImplA 对象,
在调用getServiceA 方法的时候
config.getServiceB();
//getServicesConfigB 执行一次
//getServicesConfigA 执行一次 @Bean 是普通方法
config.getServiceB();
//getServicesConfigB 执行一次
//getServicesConfigA 执行一次 @Bean 是普通方法
回调注入
比较常用的几个回调接口
接口名 | 用途 |
---|---|
BeanFactoryAware | 回调注入 BeanFactory |
ApplicationContextAware | 回调注入 ApplicationContext(与上面不同,后续 IOC 高级讲解) |
EnvironmentAware | 回调注入 Environment(后续IOC高级讲解) |
ApplicationEventPublisherAware | 回调注入事件发布器 |
ResourceLoaderAware | 回调注入资源加载器(xml驱动可用) |
BeanClassLoaderAware | 回调注入加载当前 Bean 的 ClassLoader |
BeanNameAware | 回调注入当前 Bean 的名称 |
这里面大部分接口,其实在当下的 SpringFramework 5 版本中,借助 @Autowired 注解就可以实现注入了,根本不需要这些接口,只有最后面两个,是因 Bean 而异的, 还是需要 Aware 接口来帮忙注入。下面咱来演示两个接口的作用,剩余的接口小伙伴们可以自行尝试编写体会一下即可。
通过实现接口注入,叫回调注入
@Component
public class AwaredTestBean implements ApplicationContextAware {
private ApplicationContext ctx;
public void print() {
System.out.println(ctx);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.ctx = applicationContext;
}
}
完全可以用 @Autowired 代替回调注入
@Component
public class AwaredTestBean1 {
@Autowired
private ApplicationContext ctx;
public void print() {
System.out.println(ctx);
}
}
工厂注入 FactoryBean
用普通的 bean 注入不好注入,在这种背景下创建的 FactoryBean,FactoryBean 是一个特殊的bean,本质上还是一个bean,FactoryBean的特殊之处在于它可以向容器中注册两个Bean,一个是它本身,一个是FactoryBean.getObject()方法返回值所代表的Bean,一般用于框架中,在实际开发中用的少。
FactoryBean总结如下:
- 对于调用方只需要知道需要什么对象即可, 不需要管生产方式, 符合迪米特法则: 知道的越少越好, 减少耦合
- 通过FactoryBean创建的对象与其他正常对象 使用起来毫无差异, 不需要区别对待
- 将创建对象的复杂操作, 全部封装到了FactoryBean接口的实现类中, 这个类更具语义化, 让大家一看就知道它要做什么事情, 并与Spring的编码风格进行统一
- 实现 FactoryBean 的工厂先创建,工厂实际的 bean 对象在使用此 Bean 对象的使用才会创建
FactoryBean 工厂及需要工厂创建的对象 UserService
@Component
public class CustomerFactoryBean implements FactoryBean{
@Override
public Object getObject() throws Exception {
return new UserService();
}
@Override
public Class<?> getObjectType() {
return UserService.class;
}
}
//工厂要创建的对象
public class UserService {
public UserService() {
System.out.println("userService construct");
}
}
我们知道默认情况下,在我们没有自定义命名策略的情况下,我们自定义的类被Spring扫描进容器后,Bean在Spring容器中的beanName默认是类名的首字母小写,所以这本次demo中,CustomerFactoryBean类的单例对象在容器中的beanName是customerFactoryBean。所以这个时候我们调用方法getBean(beanName)通过beanName去获取Bean,这个时候理论上应该返回的是CustomerFactoryBean类的单例对象。然而,我们将结果打印出来,却发现,这个对象的hashCode竟然和userService对象的hashCode一模一样,这说明这两个对象是同一个对象
如果要获取的工厂本身需要用 context.getBean("&customerFactoryBean") 去获取
public static void main(String[] args) {
{
// 传入配置类
var context = new AnnotationConfigApplicationContext(Programmer.class);
System.out.println("--------------------");
var beanA = context.getBean("customerFactoryBean");
System.out.println(beanA);
//com.hank.uti.UserService@2254127a
//只需要知道要什么对象,甚至不需要知道工厂的存在
var beanB = context.getBean(UserService.class);
System.out.println(beanB);
//com.hank.uti.UserService@2254127a
// 如果需要获取工厂本身要通过在 bean name 前加& -&customerFactoryBean
var beanC = context.getBean("&customerFactoryBean");
System.out.println(beanC);
//com.hank.uti.CustomerFactoryBean@5a7fe64f
}
}
单独的使用对象实现FactoryBean接口往往收益不大, 常用的都是同时继承其他类,实现其他接口来协作使用
- 创建对象时需要依赖于 别的接口 监听数据,推送数据过来的接口 做一些数据处理时, 可以使用FactoryBean.
- 需要搭配其他接口协作, 依赖于Spring的某个生命周期内, 某个时间节点 来生成对象
bean 的作用域
SpringFramework 中默认所有的 Bean 都是单实例的,即:一个 IOC 容器中只有一个
作用域类型 | 概述 |
---|---|
singleton | 一个 IOC 容器中只有一个【默认值】 |
prototype | 每次获取创建一个 |
request | 一次请求创建一个(仅Web应用可用) |
session | 一个会话创建一个(仅Web应用可用) |
application | 一个 Web 应用创建一个(仅Web应用可用) |
websocket | 一个 WebSocket 会话创建一个(仅Web应用可用) |
Spring 里的事件
定义事件
public class ApplicationContextEvent extends ApplicationEvent {
@Getter
private String msg;
@Getter
private Clock clock;
public ApplicationContextEvent(Object source, Clock clock,String msg) {
super(source, clock);
this.msg=msg;
this.clock= clock;
System.out.println(source);
//com.hank.uti.event.EventService@74287ea3
}
}
事件处理器,事件处理器必须要注入容器才会被订阅到 @Order 可以定义多个事件订阅者的执行顺序
//方法1 通过注解实现监听 @EventListener
@Component
class EventListener1 {
//这里是监听单个事件
@EventListener
@Order(1)
public void onApplicationEvent(ApplicationContextEvent event) {
System.out.println(event.getMsg());
System.out.println(event.getClock());
}
}
//方法2 通过接口实现监听
@Component
@Order(2)
class EventListener2 implements ApplicationListener<ApplicationContextEvent> {
@Override
public void onApplicationEvent(ApplicationContextEvent event) {
System.out.println(event.getMsg());
System.out.println(event.getClock());
}
}
事件发布源
//方法1 实现 ApplicationContextAware 接口
@Component
public class EventService1 implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext=applicationContext;
}
public void excute(){
var zone = ZoneId.of("Asia/Shanghai");
var c = Clock.system(zone);
applicationContext.publishEvent(new ApplicationContextEvent(this, c,"oko"));
}
}
//方法2 实现 ApplicationEventPublisherAware 接口
@Component
class EventService2 implements ApplicationEventPublisherAware{
ApplicationEventPublisher publisher;
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.publisher = publisher;
}
public void excute(){
var zone = ZoneId.of("Asia/Shanghai");
var c = Clock.system(zone);
publisher.publishEvent(new ApplicationContextEvent(this, c,"oko"));
}
}
//方法3 注入 ApplicationEventPublisher
@Component
class EventService2 {
@Autowired
private ApplicationEventPublisher publisher;
public void excute(){
var zone = ZoneId.of("Asia/Shanghai");
var c = Clock.system(zone);
publisher.publishEvent(new ApplicationContextEvent(this, c,"oko"));
}
}
发布事件
var context = new AnnotationConfigApplicationContext(Programmer.class);
var far= context.getBean(EventService1.class);
far.excute();
事件的监听方法返回的结果作为新事件自动发布
监听方法处理后返回值如果是 ApplicationEvent 的实现类,Spring会将结果当做新事件来发布。支持单个或是集合形式,集合形式的返回值会拆分成单个依次发送
//订单完成事件
@Getter
public class OrderCreateEvent extends ApplicationEvent {
private final String msg;
public OrderCreateEvent(Object source, String msg) {
super(source);
this.msg = msg;
}
}
//发送邮件事件
@Getter
class SendEmailEvent extends ApplicationEvent{
private final String msg;
public SendEmailEvent(Object source, String msg) {
super(source);
this.msg=msg;
}
}
//处理订单完成事件
@Component
class HandlerOrderCreateEvent{
@EventListener
public SendEmailEvent handler(OrderCreateEvent event){
System.out.println("第一次收到的事件消息:"+event.getMsg()+", 在处理中");
//返回的事件会被自动在发布
return new SendEmailEvent(this,"订单完成事件已经被处理了");
}
}
//处理订单完成事件
@Component
class HandlerSendEmail{
@EventListener
public void handler(SendEmailEvent event){
System.out.println("第二次收到的事件消息:"+event.getMsg()+",发送了邮件");
}
}
//发布事件 OrderCreateEvent 事件
@Component
public class OrderService {
@Autowired
private ApplicationEventPublisher publisher;
//发布事件
public void doSome(String msg) {
publisher.publishEvent(new OrderCreateEvent(this, msg));
}
}
//发布订单完成事件
//start---------------------------------------
//第一次收到的事件消息:订单已经完成了, 在处理中
//第二次收到的事件消息:订单完成事件已经被处理了,发送了邮件
@ComponentScan(basePackages = {"com.hank"})
public class Programmer {
public static void main(String[] args) {
var context = new AnnotationConfigApplicationContext(Programmer.class);
System.out.println("start---------------------------------------");
var bean= context.getBean(OrderService.class);
bean.doSome("订单已经完成了");
}
}
监听事件指定的内容 SpEL 表达式
@EventListener 可以指定对监听的事件内的消息进行过滤
修改condition 的内容,以决定是否处理此事件
@EventListener(condition = "#event.msg==event.getMsg()")
//处理订单完成事件
@Component
class HandlerOrderCreateEvent{
@EventListener(condition = "#event.msg==event.getMsg()")
public SendEmailEvent handler(OrderCreateEvent event){
System.out.println("第一次收到的事件消息:"+event.getMsg()+", 在处理中");
//返回的事件会被自动在发布
return new SendEmailEvent(this,"订单完成事件已经被处理了");
}
}
异步事件
如上例事件发布和事件处理都是同步阻塞完成的。可以使用 @Async 来完成异步处理事件。
@Component
@EnableAsync
class HandlerOrderCreateEvent{
@EventListener
@Async
public void handler(OrderCreateEvent event){
// SimpleAsyncTaskExecutor-1
System.out.println(Thread.currentThread().getName());
System.out.println("第一次收到的事件消息:"+event.getMsg()+", 在处理中");
}
}
使用异步事件时请注意以下限制:
- 如果异步事件侦听器抛出异常,它不会传播给调用者。需要使用 AsyncUncaughtExceptionHandler 来处理异常。
- 异步事件侦听器方法不能通过返回值来发布后续事件。如果需要发布另一个事件作为处理的结果,请注入一个 ApplicationEventPublisher 以手动发布该事件。
事件可以不是继承ApplicationEvent
发布的事件不一定要继承 ApplicationEvent
@Getter
public class OrderCreateEvent {
private final String msg;
public OrderCreateEvent(String msg) {
this.msg = msg;
}
}
//处理订单完成事件,事件没有继承 ApplicationEvent,处理事件的时候要用 PayloadApplicationEvent 包装
@Component
@EnableAsync
class HandlerOrderCreateEvent{
@EventListener
@Async
public void handler(PayloadApplicationEvent<OrderCreateEvent> event){
// SimpleAsyncTaskExecutor-1
System.out.println(Thread.currentThread().getName());
System.out.println("第一次收到的事件消息:"+event.getPayload().getMsg()+", 在处理中");
}
}
自定义范型事件
//事件里的实体
public class Order {
}
//范型事件
public class EntityCreatedEvent<T> extends ApplicationEvent {
public EntityCreatedEvent(T source) {
super(source);
}
}
//具体的范型事件,每种类型的事件都要定义一个类型
public class OrderEntityCreatedEvent extends EntityCreatedEvent<Order>{
public OrderEntityCreatedEvent(Order source) {
super(source);
}
}
//范型事件处理器
@Component
public class EventHandle {
@EventListener(OrderEntityCreatedEvent.class)
public void onOrderCreated(EntityCreatedEvent<Order> event) {
//因为运行时的类型擦除,这里获取的实体是 object
var obj=event.getSource();
System.out.println(obj);
}
}
//发布事件
@ComponentScan(basePackages = {"com.hank"})
public class Programmer {
public static void main(String[] args) {
var context =new AnnotationConfigApplicationContext(Programmer.class);
context.publishEvent(new OrderEntityCreatedEvent(new Order()));
}
}
spring 提供的通用泛型事件
//通用泛型事件
public class ResolvableEntityCreatedEvent<T> extends ApplicationEvent implements
ResolvableTypeProvider {
public ResolvableEntityCreatedEvent(T source) {
super(source);
}
@Override
public ResolvableType getResolvableType() {
return ResolvableType.forClassWithGenerics(getClass(), ResolvableType.forInstance(getSource()));
}
}
//事件处理器
@Component
public class ResolvableEntityCreatedEventListener {
@EventListener
public void onOrderCreated(ResolvableEntityCreatedEvent<Order> event) {
var resolvableType=event.getResolvableType();
var obj= event.getSource();
System.out.println(obj);
}
}
//发布事件
@ComponentScan(basePackages = {"com.hank"})
public class Programmer {
public static void main(String[] args) {
var context =new AnnotationConfigApplicationContext(Programmer.class);
context.publishEvent(new ResolvableEntityCreatedEvent<>(new Order()));
}
}
包扫描的细节
此注解接口的定义
public @interface ComponentScan {
@AliasFor("basePackages")
String[] value() default {};
@AliasFor("value")
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
}
还有一个 @ComponentScans() 可以把 ComponentScan 组合在一起
如上注解的定义除过指定包路径,还可以使用类的 Class 字节码 可以传入一组 Class 进去,它代表的意思,是扫描传入的这些 Class 所在包及子包下的所有组件。
包扫描测过滤
在实际开发的场景中,我们用包扫描拿到的组件不一定全部都需要,也或者只有一部分需要,这个时候就需要用到包扫描的过滤了。 通过 includeFilters 和 excludeFilters 指定过滤规则
通过自定义注解过滤 FilterType.ANNOTATION
自定义的注解及要注入的 bean
package com.hank.uti;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE , ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface NeedServices {
}
package com.hank.uti;
import org.springframework.stereotype.Component;
// @Component 注入
@Component
public class ServiceImplA implements ServiceInterface {
@Override
public void doSome() {
System.out.println(this);
}
}
// 自定义注解注入
@NeedServices
public class ServiceImplB implements ServiceInterface {
@Override
public void doSome() {
System.out.println(this);
}
}
includeFilters
输出如下的 bean com.hank.uti.ServiceImplA@6404f418 com.hank.uti.ServiceImplB@3e11f9e9
按照字面意思过滤 com.hank 包下的带有自定义注解的 NeedServices.class 类,但是输出的 ServiceImplA 和 ServiceImplB 那是因为,@ComponentScan 注解中还有一个属性:useDefaultFilters ,它代表的是“是否启用默认的过滤规则”。默认规则就是扫那些以 @Component 等注解为基准的模式注解。 其实这个属性的文档注释也写的很明白了
Indicates whether automatic detection of classes annotated with @Component @Repository, @Service, or @Controller should be enabled. boolean useDefaultFilters() default true;
默认的扫描会起作用,自己的过滤规则也会起作用,互相不会影响
@ComponentScan(basePackages = {"com.hank"},
//包含的过滤规则
includeFilters =@ComponentScan.Filter(type = FilterType.ANNOTATION,value = NeedServices.class ))
public class Programmer {
public static void main(String[] args) {
{
var context = new AnnotationConfigApplicationContext(Programmer.class);
System.out.println("--------------------");
if (context.containsBean("serviceImplA")) {
System.out.println(context.getBean(ServiceImplA.class));
}
if (context.containsBean("serviceImplB")) {
System.out.println(context.getBean(ServiceImplB.class));
}
}
}
}
可以排除默认规则 useDefaultFilters = false
@ComponentScan(basePackages = {"com.hank"}, useDefaultFilters = false,
includeFilters =@ComponentScan.Filter(type = FilterType.ANNOTATION,value = NeedServices.class ))
public class Programmer{
}
excludeFilters
@Target({ElementType.TYPE , ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface NotNeedServices {
}
@Component
@NotNeedServices
public class ServiceImplA implements ServiceInterface {
@Override
public void doSome() {
System.out.println(this);
}
}
@NeedServices
public class ServiceImplB implements ServiceInterface {
@Override
public void doSome() {
System.out.println(this);
}
}
// com.hank.uti.ServiceImplB@194bcebf
@ComponentScan(basePackages = {"com.hank"},
includeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION,value = NeedServices.class),
//排除的过滤规则会把其他规则包含进来的 bean 排除掉
excludeFilters = @ComponentScan.Filter(type=FilterType.ANNOTATION ,value =NotNeedServices.class)
)
通过类型过滤 FilterType.ASSIGNABLE_TYPE
可以指定单独的类,或指定接口或父来注入子类
public interface Color {
}
class Green implements Color{
}
class Yellow implements Color{
}
//单独的类
public class Gray {
}
输出 gray green yellow
@ComponentScan(basePackages = {"com.hank"},
includeFilters = {
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,value = com.hank.uti.Gray.class ),
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,value = Color.class )
}
)
public class Programmer {
public static void main(String[] args) {
{
var context = new AnnotationConfigApplicationContext(Programmer.class);
Arrays.stream(context.getBeanDefinitionNames()).forEach(System.out::println);
}
}
}
正则表达式过滤 FilterType.REGEX
如下 ".*Service" 匹配以为Service 结尾的 bean 排除掉
@ComponentScan(basePackages = {"com.hank"},
excludeFilters = {@ComponentScan.Filter(type = FilterType.REGEX,pattern ={".*Service$"} )}
)
自定义过滤规则 FilterType.CUSTOM
要过滤的注入对象
public interface ServiceInterface {
void doSome();
}
@Component
public class FarService implements ServiceInterface {
@Override
public void doSome() {
System.out.println(this);
}
@Component
public class ServiceImpl implements ServiceInterface {
@Override
public void doSome() {
System.out.println(this);
}
}
/*
MetadataReader 中有一个 getClassMetadata 方法,可以拿到正在扫描的类的基本信息,
此过滤器的功能是对能注入组件进行横向拦截过滤
*/
public class CusFilter implements TypeFilter {
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
throws IOException {
var classMetadata= metadataReader.getClassMetadata();
//要过滤的是实现了 ServiceInterface 接口,而且实现类是以 Service 结尾的类型
var tmp= Arrays.stream(classMetadata.getInterfaceNames()).collect(Collectors.toList());
if(classMetadata.getClassName().endsWith("Service") && tmp.contains(ServiceInterface.class
.getName())){
return true;
}
return false;
}
}
//FarService 被自定义过滤器给过滤掉了
@ComponentScan(basePackages = {"com.hank"},
excludeFilters ={@ComponentScan.Filter(type = FilterType.CUSTOM,value ={CusFilter.class}) })
public class Programmer {
public static void main(String[] args) {
{
var context = new AnnotationConfigApplicationContext(Programmer.class);
System.out.println("--------------------");
//serviceImpl
Arrays.stream(context.getBeanDefinitionNames()).forEach(System.out::println);
}
}
}
编程式包扫描
在 SpringFramework 中,有专门的类路径下的包扫描器叫 ClassPathBeanDefinitionScanner ,它可以指定一个根包,来扫描路径下的所有指定匹配规则的类 上面注解式的包扫描,也是调用的编程式的包扫描 API
要处理的类
public interface MyInterface {
void setName(String name);
String getName();
}
@Data
public class Order implements MyInterface {
private String name;
}
public class Programmer {
public static void main(String[] args) {
System.out.println("start---------------------------------------");
var context = new AnnotationConfigApplicationContext();
context.refresh();
//ClassPathBeanDefinitionScanner 执行 scan 扫描后直接注册
{
//类路径扫描器,需要一个实现了 beanDefineRegistry 的实现类进行 beanDefine 注册
var beanDefineRegistry =(BeanDefinitionRegistry)context.getDefaultListableBeanFactory();
// 类路径扫描器 代替 @ComponentScan(basePackages = {"com.hank"})
//var scanner =new ClassPathBeanDefinitionScanner(context);
var scanner =new ClassPathBeanDefinitionScanner(beanDefineRegistry);
//在扫描前指定过滤规则
scanner.addIncludeFilter((metadataReader, metadataReaderFactory) -> {
var stream =Arrays.stream(metadataReader.getClassMetadata().getInterfaceNames());
var arr =stream .filter(p->p.equalsIgnoreCase(MyInterface.class.getName()));
return arr.collect(Collectors.toList()).size()>0;
});
//scanner 扫描动作开始执行,返回符合条件的注册的 beanDefine 个数
var result= scanner.scan("com.hank");
System.out.println(result);
// 扫描后就直接注册了
Arrays.stream(context.getBeanDefinitionNames()).forEach(System.out::println);
}
}
}
ClassPathBeanDefinitionScanner 执行 findCandidateComponents 只扫描,不注册 注册通过 registerBeanDefinition 注入
{
//类路径扫描器,需要一个实现了 beanDefineRegistry 的实现类进行 beanDefine 注册
var beanDefineRegistry = (BeanDefinitionRegistry) context.getDefaultListableBeanFactory();
// 类路径扫描器 代替 @ComponentScan(basePackages = {"com.hank"})
//var scanner =new ClassPathBeanDefinitionScanner(context);
var scanner = new ClassPathBeanDefinitionScanner(beanDefineRegistry);
//在扫描前指定过滤规则
scanner.addIncludeFilter((metadataReader, metadataReaderFactory) -> {
var stream = Arrays.stream(metadataReader.getClassMetadata().getInterfaceNames());
var arr = stream.filter(p -> p.equalsIgnoreCase(MyInterface.class.getName()));
return arr.collect(Collectors.toList()).size() > 0;
});
//只扫描生成 beanDefine ,不进行注册
//扫描后生成 beanDefines
var beanDefinitions = scanner
.findCandidateComponents("com.hank");
//
beanDefinitions.forEach(p -> {
try {
if (Arrays.stream(Class.forName(p.getBeanClassName()).getInterfaces()).
collect(Collectors.toList()).size() > 0) {
var pr = p.getPropertyValues();
pr.addPropertyValue("name", "新加的名字");
}
//注册 beanDefine
var beanName = Introspector.decapitalize(p.getBeanClassName().
substring(p.getBeanClassName().lastIndexOf(".")+1));
context.registerBeanDefinition(beanName, p);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
});
//获取注册的bean
System.out.println(context.getBean("order"));
}
模块装配
使用 @Configuration + @Bean 注解组合,或者 @Component + @ComponentScan 注解组合,可以实现编程式 / 声明式的手动装配。 如果使用这两种方式,如果要注册的 Bean 很多,要么一个一个的 @Bean 编程式写,要么就得选好包进行组件扫描,而且这种情况还得每个类都标注好 @Component 或者它的衍生注解才行。面对数量很多的 Bean ,这种装配方式很明显会比较麻烦,需要有一个新的解决方案,那就是模块装配
条件装配
自定义装配的条件要实现一个接口 Condition 的方法 matches
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
//装配的条件
@Conditional(OnBeanCondition.class)
//自定义的条件注解
public @interface ConditionalOnBean {
Class value () ;
}
public class OnBeanCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
//获取自定义注解传入的 value
var far =
(Class) metadata.getAnnotationAttributes(ConditionalOnBean.class.getName()).get("value");
try {
//判断自定义条件注解传入的 value 类型的对象在 ioc 里是否存在
var list= context.getBeanFactory().getBeansOfType(far);
return list.values().stream().count()>0;
} catch (Exception e) {
return false;
}
}
}
TestB 依赖于 TestA ,如果 TestA 没有装配好,就不能装配 TestB
public class TestA {
}
public class TestB {
}
@Configuration
public class ConfigTest {
@Bean
@Order(20)
//TestB 依赖于 TestA
@ConditionalOnBean(TestA.class)
public TestB getTestB() {
return new TestB();
}
@Bean
public TestA getTestA() {
return new TestA();
}
}
上面的例子中是将 @Conditional 注解添加到了方法上此时条件仅对当前方法生效,@Conditional注解也可以加在类上, 此时条件对整个类中的组件注册均生效
如果需要特别指定对方法还是类起作用用如下代码 继承 ConfigurationCondition接口
public class OnBeanCondition implements ConfigurationCondition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
var far =
(Class) metadata.getAnnotationAttributes(ConditionalOnBean.class.getName()).get("value");
try {
var list= context.getBeanFactory().getBeansOfType(far);
return list.values().stream().count()>0;
} catch (Exception e) {
return false;
}
}
@Override
public ConfigurationPhase getConfigurationPhase() {
// 影响方法,注解加在方法上才有用
//return ConfigurationPhase.REGISTER_BEAN;
// 影响类型,注解加载类上才有用
return ConfigurationPhase.PARSE_CONFIGURATION;
}
}
用 @Component 的注入影响 @Configuration 注入
@Component
public class Test {
}
public class TestA {
}
public class TestB {
}
@Configuration
@ConditionalOnBean(Test.class)
public class ConfigTest {
@Bean("testA")
public TestA getTestA() {
return new TestA();
}
@Bean("testB")
public TestB getTestB() {
return new TestB();
}
}
var context = new AnnotationConfigApplicationContext(Programmer.class);
System.out.println("--------------------");
if(context.containsBean("testB")){
System.out.println( context.getBean(TestB.class));
}
上面的例子同时也说明
在spring ioc的过程中,优先解析@Component,@Service,@Controller注解的类。 其次解析配置类,也就是@Configuration标注的类。最后开始解析配置类中定义的bean。 配置类内的解析是按照 @bean 的先后顺序。
如果无法保证 @Conditional 内部判断的类型是否被加载,可以用如下方法解决
以下两种方式:
- 项目中条件注解依赖的类,大多会交给spring容器管理,所以如果要在配置中Bean通过@ConditionalOnBean依赖配置中的Bean时,完全可以用@ConditionalOnClass(Bean2.class)来代替。
- 如果一定要区分两个配置类的先后顺序,可以将这两个类交与EnableAutoConfiguration管理和触发。也就是定义在META-INF\spring.factories中声明是配置类,然后通过 @AutoConfigureBefore、 AutoConfigureAfter AutoConfigureOrder控制先后顺序。之所以这么做是因为这三个注解只对自动配置类的先后顺序生效。
常用的条件注解
条件注解 | Condition处理类 | 实例 | 解释 |
---|---|---|---|
@ConditionalOnBean | OnBeanCondition | @ConditionalOnBean(DataSource.class) | Spring容器中不存在对应的实例生效 |
@ConditionalOnMissingBean | OnBeanCondition | @ConditionalOnMissingBean(name = "redisTemplate") | Spring容器中不存在对应的实例生效 |
@ConditionalOnSingleCandidate | OnBeanCondition | @ConditionalOnSingleCandidate(FilteringNotifier.class) | Spring容器中是否存在且只存在一个对应的实例,或者虽然有多个但 是指定首选的Bean生效 |
@ConditionalOnClass | OnClassCondition | @ConditionalOnClass(RedisOperations.class) | 判断类是否存在 classpath 中,存在则符合条件 |
@ConditionalOnMissingClass | OnClassCondition | @ConditionalOnMissingClass(RedisOperations.class) | 判断类是否存在 classpath 中,不存在则符合条件 |
@ConditionalOnExpression | OnExpressionCondition | @ConditionalOnExpression(“’${server.host}’==’localhost’”) | 判断SpEL 表达式成立生效 |
@ConditionalOnJava | OnJavaCondition | @ConditionalOnJava(JavaVersion.EIGHT) | 指定Java版本符合要求生效 |
@ConditionalOnProperty | OnPropertyCondition | @ConditionalOnProperty(prefix = “spring.aop”, name = “auto”, havingValue = “true”, matchIfMissing = true) | 应用环境中的属性满足条件生效 |
@ConditionalOnResource | OnResourceCondition | @ConditionalOnResource(resources=”mybatis.xml”) | 存在指定的资源文件生效 |
@ConditionalOnWebApplication | OnWebApplicationCondition | 当前应用是Web应用生效 | |
@ConditionalOnNotWebApplication | OnWebApplicationCondition | 当前应用不是Web应用 | |
@ConditionalOnMissingFilterBean | 表示当 Spring 容器中,没有指定的 filter 时,满足条件 |
上面的扩展注解我们可以简单的分为以下几类:
- Bean作为条件:@ConditionalOnBean、@ConditionalOnMissingBean、@ConditionalOnSingleCandidate @ConditionalOnSingleCandidate 表示 Spring 容器中只有一个指定的 Bean 时,满足条件,如果有多个需要指定首选 Bean
- 类作为条件:@ConditionalOnClass、@ConditionalOnMissingClass。
- SpEL表达式作为条件:@ConditionalOnExpression。
- JAVA版本作为条件: @ConditionalOnJava
- 配置属性作为条件:@ConditionalOnProperty。
- 资源文件作为条件:@ConditionalOnResource。
- 是否Web应用作为判断条件:@ConditionalOnWebApplication、@ConditionalOnNotWebApplication。 web条件可以是 SERVLET ,REACTIVE ,ANY 之一
SpEL 作为条件
application.properties
obj.name=bar
obj.age=20
obj.enable=true
@ConditionalOnExpression
@Configuration
public class ConfigTest {
@Bean("testA")
@ConditionalOnExpression("${obj.enable}")
public TestA getTestA() {
return new TestA();
}
@Bean("testB")
@ConditionalOnExpression("!${obj.enable}")
public TestB getTestB() {
return new TestB();
}
}
Property 属性匹配
application.properties
obj.name=bar
obj.age=20
obj.enable=true
@ConditionalOnProperty 判断 obj.name 的属性值是否等于 bar,matchIfMissing 表示如果没有匹配的属性是否也代表匹配
@Configuration
public class ConfigTest {
@ConditionalOnProperty(name="obj.name",havingValue = "bar", matchIfMissing = false)
@Bean("testA")
public TestA getTestA() {
return new TestA();
}
}
@ConditionalOnProperty(name="obj.name") 表示有属性就匹配,不关心属性的值
Resource 资源匹配
@ConditionalOnResource 表示 classpath 下面是否能匹配到指定的资源,匹配则生效。
@Data
@ToString
@Component
@ConditionalOnResource(resources = {"a.yaml"})
public class ServiceImplA implements ServiceInterface{
}
classpath 下是否存在包
@ConditionalOnClass 此注解的作用是当前包依赖其他包的时,可以用此方法避免第三方的包没有引入
Dependson 匹配
@Dependson注解是在另外一个实例创建之后才创建当前实例,也就是,最终两个实例都会创建,只是顺序不一样 @ConditionalOnBean注解是只有当另外一个实例存在时,才创建,否则不创建,也就是,最终有可能两个实例都创建了,有可能只创建了一个实例,也有可能一个实例都没创建
正常情况下是先注入 @Component 在注入 @bean 但是通过 @DependsOn 可以改变注入的顺序
//接口
public interface ServiceInterface {
void doSome();
}
// bean 实现
@Configuration
public class ConfigService {
@Bean("serviceImplB")
public ServiceInterface serviceImplB () {
return new ServiceInterface() {
@Override
public void doSome() {
System.out.println(this);
}
};
}
}
//@Component 实现,但是要在 serviceImplB bean 注入后,才能注入
@Component
@DependsOn(value={"serviceImplB"})
public class ServiceImplA implements ServiceInterface {
@Value("#{serviceImplB}")
ServiceInterface service;
@Override
public void doSome() {
service.doSome();
System.out.println(this);
}
}
@ComponentScan(basePackages = {"com.hank"})
public class Programmer {
public static void main(String[] args) {
{
var context = new AnnotationConfigApplicationContext(Programmer.class);
System.out.println("--------------------");
// com.hank.uti.ConfigService$1@19b89d4
// com.hank.uti.ServiceImplA@4bbf6d0e
context.getBean(ServiceImplA.class ).doSome();
// com.hank.uti.ConfigService$$EnhancerBySpringCGLIB$$9a85c1bb@30b6ffe0
// 可以看到配置类被代理了
System.out.println(context.getBean(ConfigService.class));
}
}
}
Bean与BeanDefinition 及动态注册 bean
如图所示作为 作为最为重要的 BeanDefinition 是用来在运行时生成 IOC 管理的 bean BeanDefinition 是用来描述要注入容器中的对象的元信息
AttributeAccessor 做为跟接口定义存取元素的方法
public interface AttributeAccessor {
void setAttribute(String name, @Nullable Object value);
@Nullable
Object getAttribute(String name);
default <T> T computeAttribute(String name, Function<String, T> computeFunction) {}
@Nullable
Object removeAttribute(String name);
boolean hasAttribute(String name);
String[] attributeNames();
}
BeanMetadataElement也是一个接口,功能是表示 class 对象的来源,因为元素据的信息是来自 class
public interface BeanMetadataElement {
@Nullable
Object getSource();
}
AttributeAccessorSupport 抽象方法实现了元素的存取方法到一个内部的链表字典 元数据不应该是保存在BeanDefinition的beanClass、scope、lazyInit这些字段里面吗?这个map不是多次一举吗 Spring是为了方便扩展,不然BeanDefinition有新的特性,就要新增字段,这是其一; 其二,如果程序员要扩展Spring,而BeanDefinition中定义的字段已经无法满足扩展了呢? 用户也可以给这个map 里增加数据,方便自己使用
public abstract class AttributeAccessorSupport implements AttributeAccessor, Serializable {
private final Map<String, Object> attributes = new LinkedHashMap();
//---省略
}
//注入的组件
@Component
public class ServiceImpl implements ServiceInterface {
@Override
public void doSome() {
System.out.println(this);
}
}
public static void main(String[] args) {
{
var context = new AnnotationConfigApplicationContext(Programmer.class);
System.out.println("--------------------");
var beanDefinition= context.getBeanDefinition("serviceImpl");
List.of(beanDefinition.attributeNames()).forEach(p->{
System.out.println(p+":");
System.out.println(context.getBeanDefinition("serviceImpl").getAttribute(p));
});
}
//org.springframework.context.annotation.ConfigurationClassPostProcessor.configurationClass:
//lite
}
BeanDefinition是一个庞大的接口
注入用到的信息都在这个接口了,IOC 容器要保存的就是它,能够反射出 bean 也是靠它
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
// BeanDefinition定义了很多方法,
// 比如 setBeanClassName、getBeanClassName、setScope、getScope、setLazyInit、isLazyInit,
//setParentName 等
//
}
BeanMetadataAttributeAccessor 里保存的 key , value 也保存到 AttributeAccessorSupport 里的链表
public class BeanMetadataAttributeAccessor extends AttributeAccessorSupport
implements BeanMetadataElement {
//对 bean 的元素据进行保存
public void addMetadataAttribute(BeanMetadataAttribute attribute) {
super.setAttribute(attribute.getName(), attribute);
}
}
原理的分析还是就此打住,开别的文章仔细研究
注册和实例化其实就是spring容器管理bean的一个过程,先有注册bean才有实例化bean。 那么spring什么时候实例化bean呢?这里可以分为3种情况
- 如果我们使用BeanFactory作为bean的工厂类,则所有的bean都是在第一次使用该Bean的时候实例化,也就是从容器get出来的时候。
- 如果我们使用ApplicationContext作为bean的工厂类,则又分为以下几种情况:
- 如果bean的scope是singleton的,也就是单例,并且lazy-init为false(默认是false,所以可以不用设置),则ApplicationContext启动的时候就实例Bean,并且将实例化后的Bean放在缓存中,下次再使用该Bean的时 候,直接从这个缓存中。
- 如果bean的scope是singleton的,而lazy-init为true,则该Bean的实例化是在第一次使用该Bean的时候进行实例化。
- 如果bean的scope是prototype的,则该Bean的实例化是在第一次使用该Bean的时候进行实例化。
- 如果beanDefine 通过手工代码动态注入的,如下例子,无论哪种工厂,Bean的实例话是在使用 Bean的时候进行实例化
动态注册
没有注入的普通类
public class ReceiveService {
}
@Data
public class UserService implements ServiceInterface{
ReceiveService receiver;
private String name;
@Override
public void doSome() {
System.out.println(this);
}
}
通过 api 注入 beanDefine ,本质上注解注入也是通过这些api 进行的 生成注入 beanDefine
@ComponentScan(basePackages = {"com.hank"})
public class Programmer {
public static void main(String[] args) {
{
var context = new AnnotationConfigApplicationContext(Programmer.class);
System.out.println("--------------------");
{
var beanDefinitionBuilder = BeanDefinitionBuilder.
genericBeanDefinition(ReceiveService.class);
context.registerBeanDefinition("receiveService",beanDefinitionBuilder.getBeanDefinition());
}
var beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(UserService.class);
//设置scope
beanDefinitionBuilder.setScope(ConfigurableBeanFactory.SCOPE_SINGLETON);
//设置属性
beanDefinitionBuilder.
addPropertyValue(Name.of(UserService.class,p->p.getName()),"name1");
beanDefinitionBuilder.
addPropertyReference(Name.of(UserService.class,p->p.getReceiver()),"receiveService");
var beanDefine= beanDefinitionBuilder.getBeanDefinition();
//可以增加自定义的元数据
beanDefine.setAttribute("key1","value");
//注册 beanDefine 到 ioc ,此时并没有把 beanDefine 生成对象
context.registerBeanDefinition("userService",beanDefine);
/**
--------------------
16:43:01.010 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory
- Creating shared instance of singleton bean 'userService'
16:43:01.034 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory
- Creating shared instance of singleton bean 'receiveService'
UserService(receiver=com.hank.uti.ReceiveService@7daa0fbd, name=name1)
*/
//这个时候才生成 bean 对象
System.out.println( context.getBean("userService"));
}
}
}
refresh 准备上下文环境
public class Order {
}
public class Programmer {
public static void main(String[] args) {
System.out.println("start---------------------------------------");
//没有传入配置类,默认不会执行 refresh 方法
var context = new AnnotationConfigApplicationContext();
var beanFactory = context.getDefaultListableBeanFactory();
//工厂已经准备好了
System.out.println("工厂:"+beanFactory);
var builder= BeanDefinitionBuilder.genericBeanDefinition(Order.class );
var beanDefine=builder.getBeanDefinition();
context.registerBeanDefinition("order",beanDefine);
//因为工厂已经准备好了 beanDefine 可以注册进去
Arrays.stream(beanFactory.getBeanDefinitionNames()).forEach(p->{
System.out.println("bean 定义:"+p);
});
//没有执行 refresh 方法,准备 bean 生命周期需要的对象,没有这些东西是无法实例化 bean 的
//在获取bean 前必须要执行 refresh
System.out.println(context.getBean("order"));
}
}
BeanDefinitionRegistry 接口用来定义如何存取 BeanDefinition
@ComponentScan(basePackages = {"com.hank"})
public class Programmer {
public static void main(String[] args) {
var context = new AnnotationConfigApplicationContext(Programmer.class);
System.out.println("start---------------------------------------");
//BeanDefinitionRegistry 的实现者是 DefaultListableBeanFactory
//BeanDefinitionRegistry 是维护 BeanDefinition 的注册中心
DefaultListableBeanFactory defaultListableBeanFactory= context.getDefaultListableBeanFactory();
}
}
BeanDefinition 合并
BeanDefinition 的合并正常情况是由 ioc 在 bean 的生命周期内合并的
// 父类
@Data
@ToString(callSuper = true)
public class Child extends Parent{
String address;
}
//子类
@Data
@ToString(callSuper = true)
public class Child extends Parent{
String address;
}
@ComponentScan(basePackages = {"com.hank"})
public class Programmer {
public static void main(String[] args) {
var context = new AnnotationConfigApplicationContext(Programmer.class);
System.out.println("start---------------------------------------");
//注册父 beanDefinition, 此 beanDefinition 不能实例化,因为关联的 beanClass 是抽象类 Parent
{
var beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(Parent.class);
beanDefinitionBuilder.addPropertyValue("id", 1);
beanDefinitionBuilder.addPropertyValue("name", "bar");
var beanDefine = beanDefinitionBuilder.getBeanDefinition();
context.registerBeanDefinition("parent", beanDefine);
}
//注册子 beanDefinition, 指定父 beanDefinition
{
var beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(Child.class);
beanDefinitionBuilder.addPropertyValue("address", "foo");
beanDefinitionBuilder.setParentName("parent");
var beanDefine = beanDefinitionBuilder.getBeanDefinition();
context.registerBeanDefinition("child", beanDefine);
}
{
//getBeanDefinition 不会合并父子 beanDefine 的属性
if (context.containsBeanDefinition("child")) {
var beanDefine1 = context.getDefaultListableBeanFactory().getBeanDefinition("child");
System.out.println(beanDefine1.getPropertyValues());
//PropertyValues: length=1; bean property 'address'
//getMergedBeanDefinition 获取合并后的 beanDefine
var beanDefine2 = context.getDefaultListableBeanFactory().getMergedBeanDefinition("child");
System.out.println(beanDefine2.getPropertyValues());
//PropertyValues: length=3; bean property 'id'; bean property 'name'; bean property 'address'
}
}
}
}
bean 的生命周期
生命周期完整的示意图
graph TD
C1-.->A2
B2-.->A
A -.->A3
E3-.->B
subgraph 容器扩展 BeanDefinitionRegistryPostProcessor,扩展自BeanFactoryPostProcessor
A1(实例化 BeanDefinitionRegistryPostProcessor 接口)
A1-->|此时 beanFactory 是空容器,可以编程注入新的beanDefine| B1
B1(执行 postProcessBeanDefinitionRegistry 方法)
B1-->|此时 beanFactory 已经完成了beanDefine注入,可以修改及删除beanDefine|C1
C1(执行 postProcessBeanFactory 方法)
end
subgraph 容器扩展 BeanFactoryPostProcessor
A2(实例化 BeanFactoryPostProcessor 接口)
A2-->|此时 beanFactory 已经完成了beanDefine注入,可以修改及删除beanDefine|B2
B2(执行 postProcessBeanFactory 方法)
end
A(实例化 容器扩展 BeanPostProcessor)
subgraph 容器扩展 InstantiationAwareBeanPostProcessor接口,扩展自BeanFactoryPostProcessor
A3(实例化 InstantiationAwareBeanPostProcessor接口)
B3(执行postProcessBeforeInstantiation)
C3(实例化bean 执行 bean 构造函数或beanFactory 生成 bean 实例)
D3(执行postProcessAfterInstantiation方法)
E3(执行postProcessProperties方法)
A3-->|在实例话Bean前执行|B3
B3-->|执行bean 对象的构造函数或beanFactory|C3
C3-->|在实例话Bean后执行|D3
D3-->|在填充属性前执行|E3
end
B(Spring属性填充过程,核心就是实现,对Value,Autowired,Resource等属性或者方法修饰注解的依赖进行注入或者说是对于依赖对象的查找和填充过程)
B-.->A4
B-.->B4
B-.->C4
subgraph bean内感感知-Aware spring ioc
A4(实现BeanNameAware的setBeanName方法获取设置的 bean id)
B4(实现BeanFactoryAware的setBeanFactory方法获取获取 beanFactory)
C4(实现ApplicationContextAware的setApplicationContext方法获取上下文)
end
A4-.->C
B4-.->C
C4-.->C
C(容器扩展 BeanPostProcessor 执行postProcessBeforeInitialization方法)
C-.->A5
subgraph bean 的初始化方法
A5(实现InitializingBean的afterPropertiesSet方法可以在 bean 对象内再次更改属性)
B5(PostConstruct注解的标记的方法,实现初始化方法)
A5-->B5
end
B5-.->D
D(容器扩展 BeanPostProcessor 执行 postProcessAfterInitialization)
D-.->E
E(bean 对象的生成过程结束)
E-.->A6
E-.->B6
subgraph bean 对象的销毁
A6(bean对象实现DisposableBean接口的 destroy 方法)
B6(bean对象增加 PreDestroy注解标记的方法)
end
在生命周期内的 Aware
Spring的依赖注入的最大亮点就是你所有的Bean对Spring容器的存在是没有意识的。即你可以将你的容器替换成别的容器,例如Goggle Guice,这时Bean之间的耦合度很低 但是在实际的项目中,我们不可避免的要用到Spring容器本身的功能资源,这时候Bean必须要意识到Spring容器的存在,才能调用Spring所提供的资源,这就是所谓的Spring Aware。其实Spring Aware本来就是Spring设计用来框架内部使用的,若使用了Spring Aware,你的Bean将会和Spring框架耦合。
Spring提供的Aware接口:
BeanNameAware | 获得到容器中Bean的名称 |
---|---|
BeanFactoryAware | 获得当前bean factory,这样可以调用容器的服务 |
ApplicationContextAware | 获得当前application context,这样可以调用容器的服务 |
MessageSourceAware | 获得message source这样可以获得文本信息 |
ApplicationEventPublisherAware | 应用事件发布器,可以发布事件 |
ResourceLoaderAware | 获得资源加载器,可以获得外部资源文件 |
EnvironmentAware | 获取已经加载所有配置信息 |
Spring Aware的目的是为了让Bean获得Spring容器的服务。因为ApplicationContext接口集成了MessageSource接口,ApplicationEventPublisherAware接口和ResourceLoaderAware接口,所以Bean继承ApplicationContextAware可以获得Spring容器的所有服务,但原则上我们还是用到什么接口就实现什么接口。
BeanNameAware
BeanNameAware 接口是为了让自身Bean能够感知到,获取到自身在Spring容器中的id或name属性。 Spring自动调用。并且会在Spring自身完成Bean配置之后,且在调用任何Bean生命周期回调(初始化或者销毁)方法之前就调用这个方法。换言之,在程序中使用BeanFactory.getBean(String beanName)之前,Bean的名字就已经设定好了。
@Component
public class Test implements BeanNameAware {
@Override
public void setBeanName(String name) {
System.out.println(LocalDateTime.now() +"----ioc 容器内此 bean 的 name 是"+ name);
}
}
BeanFactoryAware
获得创建当前bean 的 bean factory,这样可以调用容器的服务
@Component
public class Test implements BeanFactoryAware {
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println(beanFactory);
}
}
ResourceLoaderAware
获取创建当前bean 的 ResourceLoader ,可以用来装载资源
@Component
public class Test implements ResourceLoaderAware {
@SneakyThrows
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
var resource = resourceLoader.getResource("classpath:a.txt");
var bytes = resource.getInputStream().readAllBytes();
var str = new String(bytes, "UTF-8");
System.out.println(str);
}
}
EnvironmentAware
获取配置文件的内容
@Component
@PropertySource("application.properties")
public class Test implements EnvironmentAware {
@Override
public void setEnvironment(Environment environment) {
var str=environment.getProperty("obj.name");
System.out.println(str);
}
}
BeanPostProcessor 后置处理器,对所有 bean 而言
该接口我们也叫后置处理器,作用是在Bean对象在实例化和依赖注入完毕后,在显示调用初始化方法的前后添加我们自己的逻辑。注意是Bean实例化完毕后及依赖注入完成后触发的 比较特别的是 BeanPostProcessor的作用域是当前容器中的所有bean(不包括一些特殊的bean),可以有多个实现类,不同实现类方法的执行顺序,可以实现接口 Ordered 的 getOrder 方法的返回值进行定义顺序(返回值小的先执行)。
注意点
- 如下需要bean 被此接口的方法过滤到,那bean 要在此接口的实现类之前注入
- 此接口的实现类不能被 aop
public interface BeanPostProcessor {
//实例化、依赖注入完毕,在调用显示的初始化之前完成一些定制的初始化任务,Spring AOP 就是在这一阶段起作用的
//因为不知道 bean 的类型,所以这里是 Object 类型,此方法不能返回 null
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
//实例化、依赖注入、初始化完毕时执行
//因为不知道 bean 的类型,所以这里是 Object 类型,此方法不能返回 null
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
@Component
@Data
public class Order {
String name;
}
@Component
@Data
public class Person {
@Value("0")
int id;
@Value("n=bob")
String name;
}
@Component
public class Test implements BeanPostProcessor, Ordered {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)throws BeansException {
if(bean instanceof Person){
((Person) bean).setId(10);
}
if(bean instanceof Order){
((Order) bean).setName("rice");
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)throws BeansException {
if(bean instanceof Person){
((Person) bean).setId(20);
}
if(bean instanceof Order){
((Order) bean).setName("apple");
}
return bean;
}
@Override
public int getOrder() {
return 1;
}
}
定制的初始化方法及销毁方法 @PostConstruct,@PreDestroy 俩个单独的注解
对自定义组件使用
@Component
@Data
public class Order {
/**
* 初始化方法
*/
@PostConstruct
public void init(){
System.out.println("初始化方法");
}
/**
* 销毁方法
*/
@PreDestroy
public void destroy(){
System.out.println("销毁方法");
}
}
对 Configuration bean 使用
@Component
@Data
public class Order {
public void init(){
System.out.println("初始化方法");
}
public void destroy(){
System.out.println("销毁方法");
}
}
//initMethod 和 destroyMethod 里的方法要定义 Order 类里
//不能在 ConfigOrder 里定义
@Configuration
public class ConfigOrder{
@Bean(initMethod = "init",destroyMethod = "destroy")
public Order getOrder(){
return new Order();
}
}
InitializingBean 不同于 BeanPostProcessor 是对单独的 bean 进行自定义业务处理
目前不知道有什么实际用处
@Component
@Data
public class Person implements InitializingBean {
@Value("0")
int id;
@Value("n=bob")
String name;
@Override
public void afterPropertiesSet() throws Exception {
}
}
DisposableBean 销毁对象前调用
DisposableBean 接口的方法只是对 bean 的 scope 是 SINGLETON 有效果,因为 如果 scope 是 SCOPE_PROTOTYPE 类型的在获取此 bean 后 容器就不管理此bean 了,自然不会调用此 bean
@Component
@Data
@Scope(BeanDefinition.SCOPE_SINGLETON)
public class Person implements DisposableBean {
@Override
public void destroy() throws Exception {
System.out.println("Person 对象销毁了");
}
}
@ComponentScan(basePackages = {"com.hank"})
public class Programmer {
public static void main(String[] args) {
var context =new AnnotationConfigApplicationContext(Programmer.class);
System.out.println("start---------------------------------------");
System.out.println(context.getBean(Person.class));
context.removeBeanDefinition("person");
// Person 对象销毁了
}
}
InstantiationAwareBeanPostProcessor 扩展自 BeanPostProcessor
InstantiationAwareBeanPostProcessor 此接口有扩展了三个接口
public interface InstantiationAwareBeanPostProcessor extends BeanPostProcessor {
//在调用 bean的构造函数或工厂方法前执行
default Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName)
throws BeansException {
return null;
}
//执行完 bean 的构造函数或工厂方法后执行
default boolean postProcessAfterInstantiation(Object bean, String beanName)
throws BeansException {
return true;
}
//如果 postProcessAfterInstantiation 返回 true ,此方法返回的 PropertyValues 会覆盖默认外部注入的值
default PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName)
throws BeansException {return null;}
}
示列代码
@Component
@Data
public class Order {
@Value("bob")
private String name;
{
System.out.println("实例初始化方法执行了");
}
static {
System.out.println("静态初始化方法执行了");
}
public Order() {
System.out.println("构造函数执行");
}
}
//拦截代码
@Component
public class Test implements InstantiationAwareBeanPostProcessor {
//bean 执行构造函数前执行
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName)
throws BeansException {
System.out.println("在 bean 执行构造函数前执行");
return
InstantiationAwareBeanPostProcessor.super.postProcessBeforeInstantiation(beanClass, beanName);
}
//在bean 执行构造函数之后执行
@Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
if(bean instanceof Order){
var order=(Order)bean;
System.out.println("在 bean 执行构造函数后执行,属性 :"+order.getName());
}
//return InstantiationAwareBeanPostProcessor.super.postProcessAfterInstantiation(bean, beanName);
//返回 true 后面的 postProcessProperties 方法才会执行
return true;
}
//在bean 属性注入前执行,
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName)
throws BeansException {
if(bean instanceof Order){
var order=(Order)bean;
System.out.println("在 bean 属性注入前执行,属性 :"+order.getName());
//由于 PropertyValues 设计为接口且只暴露可读方法,
//此处选用实现类 MutablePropertyValues重新包装,就可以重新赋值了
var mutablePropertyValues=new MutablePropertyValues(pvs);
mutablePropertyValues.addPropertyValue("name", "lucy");
//会代替外部配置的属性
return mutablePropertyValues;
}
return InstantiationAwareBeanPostProcessor.super.postProcessProperties(pvs, bean, beanName);
}
//后面的俩个方法是在 bean 执行自定义初始化方法前后执行
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)throws BeansException {
if(bean instanceof Order){
var order=(Order)bean;
System.out.println("在 bean 执行自定义初始化方法前执行,属性 :"+order.getName());
}
return InstantiationAwareBeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("在 bean 执行自定义初始化方法后执行");
return InstantiationAwareBeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
}
}
/**
在 bean 执行构造函数前执行
在 bean 执行自定义初始化方法后执行
16:32:04.109 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory -
Creating shared instance of singleton bean 'order'
在 bean 执行构造函数前执行
静态初始化方法执行了
实例初始化方法执行了
构造函数执行
在 bean 执行构造函数后执行,属性 :null
在 bean 属性注入前执行,属性 :null
在 bean 执行自定义初始化方法前执行,属性 :lucy
在 bean 执行自定义初始化方法后执行
*/
beanFactoryPostProcessor --beanFactory 的后置处理器
是 beanFactory 的后置处理器,属于容器的扩展点。在创建 beanDefine 后进行后处理。是对所有的 beanDefine 进行拦截。 调用时机:在BeanFactory标准初始化之后调用,这时所有的bean定义已经保存加载到beanFactory,但是bean的实例还未创建,来定制和修改BeanFactory 内的 beanDefine 的内容,如覆盖或添加属性,但是不要提前实例化 bean 在此接口方法内 BeanFactoryPostProcessor 的多个实现了可以实现 order 接口定义执行的顺序
@FunctionalInterface
public interface BeanFactoryPostProcessor {
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
修改 beanDefine 的属性
@Component
public class Test implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException {
var beanDefine= beanFactory.getBeanDefinition("order");
var pv= beanDefine.getPropertyValues();
if(pv.contains("name"))
pv.addPropertyValue("name","lucy");
}
}
如果只是修改 beanDefine 的属性,BeanPostProcessor 及 InstantiationAwareBeanPostProcessor 都可以做** beanFactoryPostProcessor 的时机是在 beanDine 已经加载到了 beanFactory 里但是还没有进行实例化之前,可以做一些预处理 比如删除某些已经加载的 beanDefine 或者是增加 beanDefine**
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface NotUse {
}
@Component
@NotUse
public class Order {
}
//移除所有使用了 NotUse 注解的 beanDefine
@Component
public class Test1 implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException {
//DefaultListableBeanFactory 实现了 ConfigurableListableBeanFactory
//DefaultListableBeanFactory 实现了 BeanDefinitionRegistry 接口
var registry = (BeanDefinitionRegistry) beanFactory;
Arrays.stream(beanFactory.getBeanDefinitionNames()).forEach(p -> {
var beanDefine = beanFactory.getBeanDefinition(p);
var beanClassName=beanDefine.getBeanClassName();
NotUse annotation= null;
try {
annotation = Class.forName(beanClassName).getAnnotation(NotUse.class);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
if(Objects.nonNull(annotation)){
registry.removeBeanDefinition(p);
}
});
}
}
BeanDefinitionRegistryPostProcessor -- BeanDefinitionRegistry 的后处理器
BeanDefinitionRegistryPostProcessor 是容器的扩展点,它用于 IOC 容器的生命周期中,所有 BeanDefinition 都准备好,即将加载到 BeanFactory 时回调触发,用于给 BeanFactory 中添加新的 BeanDefinition,BeanFactory 此时的 BeanDefinition 内还没注册任何 beanDefine
BeanDefinitionRegistryPostProcessor 扩展了 BeanFactoryPostProcessor
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}
public class Order {
}
@Component
public class Test implements BeanDefinitionRegistryPostProcessor {
//注册 beanDefine到 beanFactory
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
throws BeansException {
var builder = BeanDefinitionBuilder.genericBeanDefinition(Order.class );
var beanDefine=builder.getBeanDefinition();
registry.registerBeanDefinition("order",beanDefine);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException {
Arrays.stream(beanFactory.getBeanDefinitionNames()).forEach(p->{
System.out.println(p);
});
/**
* org.springframework.context.annotation.internalConfigurationAnnotationProcessor
* org.springframework.context.annotation.internalAutowiredAnnotationProcessor
* org.springframework.context.annotation.internalCommonAnnotationProcessor
* org.springframework.context.event.internalEventListenerProcessor
* org.springframework.context.event.internalEventListenerFactory
* programmer
* test
* order
*/
}
}
测试例子
@Component
@Data
public class Order implements InitializingBean, DisposableBean {
String name;
public Order(){
System.out.println("构造方法执行");
}
@PostConstruct
public void init(){
System.out.println("自定义初始化方法执行");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("afterPropertiesSet 执行");
}
@PreDestroy
public void preDestroy(){
System.out.println("PreDestroy 方法执行");
}
@Override
public void destroy() throws Exception {
System.out.println("DisposableBean 执行");
}
}
//对所有组件其作用的后处理器
@Component
public class Test implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
if(bean instanceof Order){
((Order) bean).setName("rice");
}
System.out.println("BeanPostProcessor pre 执行");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
if(bean instanceof Order){
((Order) bean).setName("apple");
}
System.out.println("BeanPostProcessor after 执行");
return bean;
}
}
@ComponentScan(basePackages = {"com.hank"})
public class Programmer {
public static void main(String[] args) {
var context = new AnnotationConfigApplicationContext(Programmer.class);
System.out.println("start---------------------------------------");
context.close();
/*
BeanPostProcessor pre 执行
BeanPostProcessor after 执行
构造方法执行
BeanPostProcessor pre 执行
自定义初始化方法执行
afterPropertiesSet 执行
BeanPostProcessor after 执行
start---------------------------------------
PreDestroy 方法执行
DisposableBean 执行
*/
}
}
BeansException 异常
BeansException 是运行时异常
public abstract class BeansException extends NestedRuntimeException {
/**
* Create a new BeansException with the specified message.
* @param msg the detail message
*/
public BeansException(String msg) {
super(msg);
}
/**
* Create a new BeansException with the specified message
* and root cause.
* @param msg the detail message
* @param cause the root cause
*/
public BeansException(@Nullable String msg, @Nullable Throwable cause) {
super(msg, cause);
}
}
BeansException 子类型举例:
- NoSuchBeanDefinitionException ,BeanDefine 还没有被注册
- NoUniqueBeanDefinitionException,存在多个 bean 实例
- BeanCreationException,生成 bean 对象时有异常
- BeanIsAbstractException,bean 是抽象没=类无法实例化
- BeanInitializationException,bean 在初始化的时候抛出异常
- BeanDefinitionStoreException,BeanDefine 有错误
spring BeanFactory
BeanFactory 关注点在获取单个 bean 的方法上
BeanFactory 定义获取 bean 的方式,并不关心如何 bean 保存在哪里,是如何保存的
public interface BeanFactory {
String FACTORY_BEAN_PREFIX = "&";
Object getBean(String var1) throws BeansException;
<T> T getBean(String var1, Class<T> var2) throws BeansException;
Object getBean(String var1, Object... var2) throws BeansException;
<T> T getBean(Class<T> var1) throws BeansException;
<T> T getBean(Class<T> var1, Object... var2) throws BeansException;
<T> ObjectProvider<T> getBeanProvider(Class<T> var1);
<T> ObjectProvider<T> getBeanProvider(ResolvableType var1);
boolean containsBean(String var1);
boolean isSingleton(String var1) throws NoSuchBeanDefinitionException;
boolean isPrototype(String var1) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String var1, ResolvableType var2) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String var1, Class<?> var2) throws NoSuchBeanDefinitionException;
@Nullable
Class<?> getType(String var1) throws NoSuchBeanDefinitionException;
@Nullable
Class<?> getType(String var1, boolean var2) throws NoSuchBeanDefinitionException;
String[] getAliases(String var1);
}
ListableBeanFactory 关注点在获取批量的 bean 上
ListableBeanFactory 就是名字 Listable 可列举的,可以获取所有的,或者按照类型获取批量的 bean
public interface ListableBeanFactory extends BeanFactory {
boolean containsBeanDefinition(String var1);
int getBeanDefinitionCount();
String[] getBeanDefinitionNames();
<T> ObjectProvider<T> getBeanProvider(Class<T> var1, boolean var2);
<T> ObjectProvider<T> getBeanProvider(ResolvableType var1, boolean var2);
String[] getBeanNamesForType(ResolvableType var1);
String[] getBeanNamesForType(ResolvableType var1, boolean var2, boolean var3);
String[] getBeanNamesForType(@Nullable Class<?> var1);
String[] getBeanNamesForType(@Nullable Class<?> var1, boolean var2, boolean var3);
<T> Map<String, T> getBeansOfType(@Nullable Class<T> var1) throws BeansException;
<T> Map<String, T> getBeansOfType(@Nullable Class<T> var1, boolean var2, boolean var3)
throws BeansException;
String[] getBeanNamesForAnnotation(Class<? extends Annotation> var1);
Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> var1)
throws BeansException;
@Nullable
<A extends Annotation> A findAnnotationOnBean(String var1, Class<A> var2)
throws NoSuchBeanDefinitionException;
@Nullable
<A extends Annotation> A findAnnotationOnBean(String var1, Class<A> var2, boolean var3)
throws NoSuchBeanDefinitionException;
}
HierarchicalBeanFactory 定义有层次的 BeanFactory
public interface HierarchicalBeanFactory extends BeanFactory {
@Nullable
BeanFactory getParentBeanFactory();
//本地工厂是否包含指定名字的 bean
boolean containsLocalBean(String var1);
}
AutowireCapableBeanFactory 关注自动装配的能力
AutowireCapableBeanFactory 定义的方法都是根据输入的参数生成一个 bean
AutowireCapableBeanFactory定义了5种装配策略:
- 不自动注入:AUTOWIRE_NO
- 使用BeanName策略注入:AUTOWIRE_BY_NAME
- 使用类型装配策略:AUTOWIRE_BY_TYPE
- 使用构造器装配策略:AUTOWIRE_CONSTRUCTOR
- 自动装配策略:AUTOWIRE_AUTODETECT
public interface AutowireCapableBeanFactory extends BeanFactory {
int AUTOWIRE_NO = 0;
int AUTOWIRE_BY_NAME = 1;
int AUTOWIRE_BY_TYPE = 2;
int AUTOWIRE_CONSTRUCTOR = 3;
@Deprecated
int AUTOWIRE_AUTODETECT = 4;
String ORIGINAL_INSTANCE_SUFFIX = ".ORIGINAL";
<T> T createBean(Class<T> beanClass) throws BeansException;
void autowireBean(Object existingBean) throws BeansException;
Object configureBean(Object existingBean, String beanName) throws BeansException;
Object createBean(Class<?> beanClass, int autowireMode, boolean dependencyCheck)
throws BeansException;
Object autowire(Class<?> beanClass, int autowireMode, boolean dependencyCheck)
throws BeansException;
void autowireBeanProperties(Object existingBean, int autowireMode, boolean dependencyCheck)
throws BeansException;
void applyBeanPropertyValues(Object existingBean, String beanName) throws BeansException;
Object initializeBean(Object existingBean, String beanName) throws BeansException;
Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
throws BeansException;
Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException;
void destroyBean(Object existingBean);
<T> NamedBeanHolder<T> resolveNamedBean(Class<T> requiredType) throws BeansException;
Object resolveBeanByName(String name, DependencyDescriptor descriptor) throws BeansException;
@Nullable
Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName)
throws BeansException;
@Nullable
Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter)
throws BeansException;
}
ConfigurableBeanFactory 关注点在 bean 属性的配置上
public interface ConfigurableBeanFactory extends HierarchicalBeanFactory, SingletonBeanRegistry {
String SCOPE_SINGLETON = "singleton";
String SCOPE_PROTOTYPE = "prototype";
void setParentBeanFactory(BeanFactory parentBeanFactory) throws IllegalStateException;
void setBeanClassLoader(@Nullable ClassLoader beanClassLoader);
@Nullable
ClassLoader getBeanClassLoader();
void setTempClassLoader(@Nullable ClassLoader tempClassLoader);
@Nullable
ClassLoader getTempClassLoader();
void setCacheBeanMetadata(boolean cacheBeanMetadata);
boolean isCacheBeanMetadata();
void setBeanExpressionResolver(@Nullable BeanExpressionResolver resolver);
@Nullable
BeanExpressionResolver getBeanExpressionResolver();
void setConversionService(@Nullable ConversionService conversionService);
@Nullable
ConversionService getConversionService();
void addPropertyEditorRegistrar(PropertyEditorRegistrar registrar);
void registerCustomEditor(
Class<?> requiredType, Class<? extends PropertyEditor> propertyEditorClass);
void copyRegisteredEditorsTo(PropertyEditorRegistry registry);
void setTypeConverter(TypeConverter typeConverter);
TypeConverter getTypeConverter();
void addEmbeddedValueResolver(StringValueResolver valueResolver);
boolean hasEmbeddedValueResolver();
@Nullable
String resolveEmbeddedValue(String value);
void addBeanPostProcessor(BeanPostProcessor beanPostProcessor);
int getBeanPostProcessorCount();
void registerScope(String scopeName, Scope scope);
String[] getRegisteredScopeNames();
@Nullable
Scope getRegisteredScope(String scopeName);
void setApplicationStartup(ApplicationStartup applicationStartup);
ApplicationStartup getApplicationStartup();
AccessControlContext getAccessControlContext();
void copyConfigurationFrom(ConfigurableBeanFactory otherFactory);
void registerAlias(String beanName, String alias) throws BeanDefinitionStoreException;
void resolveAliases(StringValueResolver valueResolver);
BeanDefinition getMergedBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
boolean isFactoryBean(String name) throws NoSuchBeanDefinitionException;
void setCurrentlyInCreation(String beanName, boolean inCreation);
boolean isCurrentlyInCreation(String beanName);
void registerDependentBean(String beanName, String dependentBeanName);
String[] getDependentBeans(String beanName);
String[] getDependenciesForBean(String beanName);
void destroyBean(String beanName, Object beanInstance);
void destroyScopedBean(String beanName);
void destroySingletons();
}