文章目录
了解 Bean
-
Spring 启动时读取应用程序提供的 Bean 配置信息,并在 Spring 容器中生成一份相应的 Bean 配置注册表,根据注册表实例化 Bean,装配好 Bean 之间的依赖关系。
-
Bean 配置信息是 Bean 的元数据信息,主要有4个方面:
- Bean 的实现类
- Bean 的属性信息,如数据源的连接数、用户名、密码等
- Bean 的依赖关系,Spring 根据依赖关系配置完成 Bean 之间的装配
- Bean 的行为配置,如生命周期范围、生命周期各过程的回调函数等
- Bean 元数据信息在 Spring 容器中由
org.springframework.beans.factory.config.BeanDefinition
接口形成的 Bean 注册表,Spring 支持多种形式的 Bean 的配置方式。基于XML配置、基于注解配置、基于 Java Config 类配置、基于Croovy 动态语言配置
基于注解的配置
了解注解
实现的前提
- 需要配置注解 Bean 的扫描器,如xml配置的扫描器
<context:component-scan base-package="com.learning.spring.ioc.bean">
注解了解
- @Component:标注一个Bean,可以设置名称,默认是类名首字母小写。
- @Service、@Repository、@Controller 三个内部组合了 @Component,为了区分不同 Bean 的功能
@Component(value = "annotationBean")
public class AnnotationBean {}
依赖注入
- 依赖注入的注解:标注在属性或者setXXX方法都生效
@javax.annotation.Resource
:默认按名字属性,如果属性为空,指定采用变量名或者方法名@org.springframework.beans.factory.annotation.Autowired
:默认按类型,如果要使用按名字,需要添加注解@Qualifier(value = "名字")
Autowired
标注集合:spring 会将容器中所有类型为实现了某个接口的 Bean 注入集合变量。默认加载的顺序是不固定的,可以使用 @Order 注解实现加载顺序(见下面例子)Autowired
支持Spring4X的泛型的注入,集合标注(list、map)等,功能比Resource
更强大
自动扫描器详解
XML(Schema) 配置:context:component-scan
- 标签的属性
属性与子标签 | 描述 |
---|---|
base-package: | 标注基本的需要扫描的路径,默认是该路径下全部的 |
resource-pattern: | 对base-package路径下进行过滤,默认是基类包中的所有类,其值为 “**\/*.class” |
use-default-filters: | 默认 true,表示默认会对标注了 @Component,@Service、@Repository、@Controller 的Bean 进行扫描。如需要使用 context:include-filter ,需要设置默认的这个属性为 false |
include-filter: | 子标签。要包含的目标类;可以设置多个 |
exclude-filter: | 子标签。要排除的目标类。可以设置多个 |
- include-filter、exclude-filter 的 type 属性
属性 | 描述 |
---|---|
annotation | 标注了 XXannotation 的类(最常用) |
assignable | 标记继承扩展类 |
aspectj | 采用 aspectj 表达式 |
regex | 正则表达式 |
custom | 自定义实现,必须实现 org.springframework.core.type.filter.TypeFilter 接口 |
注解配置:ComponentScan
- Java Config 的配置方法会大量使用该注解,如使用 SpringBoot
- 这个注解标注了 @Repeatable 元注解,可以多次配置使用,参考Java 进阶注解的相关博文
@ComponentScan(basePackages = {"com.learning.spring.ioc.bean"},
includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Controller.class)})
延迟加载、作用范围、生命过程方法
- @Lazy:对延迟依赖的支持。在容器启动的时候,对于 Bean 上面标注了 @Lazy、@Autowired 注解的属性,不会立即注入属性值,而是延迟到调用此属性的时候才会注入属性值。注意:@Lazy 必须标注在属性及目标 Bean 两个地方
- @Scope:作用范围
- @PostConstruct、@PreDestroy:生命过程方法,与 init-method、destroy-method 属性指定方法功能一致
代码示例
- Bean 实现类
@Component(value = "annotationBean")
@Getter
public class AnnotationBean {
@Autowired
@Qualifier(value = "injectionBean")
private AnnotationInjectionBean injectionBean;
void done() {
injectionBean.done();
}
/**
* spring 会将容器中所有类型为实现了 InjectionBean 接口的Bean 注入这个变量
* 默认加载的顺序是不固定的,可以使用 @Order 注解实现加载顺序
*/
@Autowired(required = false)
private List<InjectionBean> list;
/**
* spring 会将实现了 InjectionBean 接口的Bean 注入Map
* key 为 Bean 的名字
* value 为所有实现了 InjectionBean 的 Bean
*/
@Autowired(required = false)
private Map<String, InjectionBean> map;
// @Lazy 需要生效,属性这边需要标注,类那边也需要标注
@Autowired
@Lazy
private AnnotationInjectionBean3 annotationInjectionBean3;
}
interface InjectionBean {
void done();
}
@Component(value = "injectionBean")
@Order(value = 1)
class AnnotationInjectionBean implements InjectionBean {
public AnnotationInjectionBean() {
System.out.println("AnnotationInjectionBean 被实例化了");
}
@Override
public void done() {
System.out.println("调用了 AnnotationInjectionBean.done() 方法");
}
}
@Component(value = "injectionBean2")
@Order(value = 2)
class AnnotationInjectionBean2 implements InjectionBean {
public AnnotationInjectionBean2() {
System.out.println("AnnotationInjectionBean2 被实例化了");
}
@Override
public void done() {
System.out.println("调用了 AnnotationInjectionBean2.done() 方法");
}
}
// @Lazy 需要生效,类这边需要标注,属性那边也需要标注,
@Component(value = "injectionBean3")
@Order(value = 3)
@Lazy
class AnnotationInjectionBean3 implements InjectionBean {
public AnnotationInjectionBean3() {
System.out.println("AnnotationInjectionBean3 被实例化了");
}
@Override
public void done() {
System.out.println("调用了 AnnotationInjectionBean3.done() 方法");
}
/**
* 注解可以标注多个方法
*/
@PostConstruct
public void init() {
System.out.println("@PostConstruct 注解标注初始化方法");
}
@PreDestroy
public void destroy() {
System.out.println("@PreDestroy 注解标注销毁方法");
}
}
- xml 装配自动扫描
<!--扫描注解定义的Bean-->
<context:component-scan base-package="com.learning.spring.ioc.bean" resource-pattern="**/*.class" use-default-filters="true"/>
- 简单测试
ApplicationContext context = new ClassPathXmlApplicationContext("spring-context.xml");
AnnotationBean annotationBean = context.getBean("annotationBean", AnnotationBean.class);
System.out.println(annotationBean.getList());
System.out.println(annotationBean.getMap());
基于Java Config 的配置
提供 Bean 的定义
了解Java Config 的配置
- @Configuration:普通的类只需要标注 @Configuration 注解,就可以为 Spring 容器提供 Bean 定义的信息
- @Bean:每个标注了@Bean 的类方法都相当于提供一个 Bean 的定义信息。
- Bean 的类型为方法返回值类型
- Bean 的名字默认为方法名,可以通过 name 属性定义
- Spring 会被Bean 标注的方法进行 aop 增强。调用获取 @Bean 标注的方法,不是简单的运行方法的逻辑,而是从容器中获取 Bean
- 在 @Bean 标注的方法处,还可以标注 @Scope 控制 Bean 的生命周期
- 多个 @Configuration 配置类中的Bean,如要相互引用?
- @Configuration 组合了注解 @Component,本身也是一个Bean,可以直接注入,然后使用
- Java Config 与 Xml 配置相互引用
- Java Config 引用 Xml:
@ImportResource("classpath:bean-java-config.xml")
- Xml 引用 Java Config
<context:component-scan base-package="com.learning.spring.ioc.bean.jbased" resource-pattern="AppConfig.class"/>
- @Import 注解组装多个 @Configuration 标注的配置,启动容器的时候只需要存入组装后的。
代码示例
@Configuration
@Import(AppInjectionConfig.class)
@ImportResource("classpath:bean-java-config.xml")
public class AppConfig {
// @Configuration 组合了注解 @Component,本身也是一个Bean,可以直接注入,然后使用
@Autowired
private AppInjectionConfig appInjectionConfig;
/**
* Bean 的类型为返回值类型
* Bean 的名字默认为方法名,可以通过 name 属性定义
*
* @return
*/
@Bean
public ConfigBean configBean() {
ConfigBean configBean = new ConfigBean();
// Spring 会被Bean 标注的方法进行aop增强
// 调用 configInjectionBean() 不是简单的运行方法的逻辑,而是从容器中获取 Bean
configBean.setConfigInjectionBean(appInjectionConfig.configInjectionBean());
return configBean;
}
}
@Setter
class ConfigBean {
private ConfigInjectionBean configInjectionBean;
public void done() {
System.out.println("调用了 ConfigBean.done 方法");
}
public void injection() {
System.out.println("调用了 ConfigBean.injection 方法");
configInjectionBean.injection();
}
}
// 被注入的 @Configuration 标注配置
@Configuration
public class AppInjectionConfig {
@Bean
public ConfigInjectionBean configInjectionBean() {
return new ConfigInjectionBean();
}
}
class ConfigInjectionBean {
public void injection() {
System.out.println("调用了 ConfigInjectionBean.injection 方法");
}
}
启动 Spring 容器
- 使用 AnnotationConfigApplicationContext 启动 Spring 容器
// 也可以使用 @Import(AppInjectionConfig.class) 组装到一个配置类中
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class, AppInjectionConfig.class);
System.out.println("初始化 AnnotationConfigApplicationContext 完成, 同时实例化 bean");
ConfigBean configBean = context.getBean("configBean", ConfigBean.class);
configBean.done();
context.close();
/*
初始化 AnnotationConfigApplicationContext 完成, 同时实例化 bean
调用了 ConfigBean.done 方法
*/
通过编码方式动态添加
- 实现在Spring 容器启动阶段动态的注入自定义的 Bean,保证动态注入的 Bean 也可以被 AOP 所增强,需要实现 Bean 工厂后置处理器
- 编码实现
- DefaultListableBeanFactory 实现了 ConfigurableListableBeanFactory接口,提供了可扩展配置、循环枚举的功能,可以通过此类实现 Bean 的动态注入
- 可以使用
registerBeanDefinition
注册 Bean 定义,也可以使用registerSingleton
直接注册 Bean 实例
@Component
public class CodeBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// DefaultListableBeanFactory 实现了 ConfigurableListableBeanFactory接口
// 提供了可扩展配置、循环枚举的功能,可以通过此类实现 Bean 的动态注入
DefaultListableBeanFactory factory = (DefaultListableBeanFactory) beanFactory;
// 通过 BeanDefinitionBuilder 创建 Bean 定义
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(CodeBean.class);
// 注入。设置属性 codeReferenceBean,此属性引用已经定义的 Bean:codeReferenceBean
builder.addPropertyReference("codeReferenceBean", "codeReferenceBean");
// 注册 Bean 定义
factory.registerBeanDefinition("codeBean", builder.getRawBeanDefinition());
// 也直接注册一个 Bean 实例
factory.registerSingleton("codeBean2", new CodeBean());
}
}
class CodeBean {
public CodeBean() {
System.out.println("CodeBean 被实例化了~~");
}
}
@Component
class CodeReferenceBean {
public CodeReferenceBean() {
System.out.println("CodeReferenceBean 被实例化了~~");
}
}