1. Spring 通用注解
1.1 @Configuration
标识在配置类上, 告诉 Spring 这是一个配置类, 配置类 == 配置文件
@Configuration
public class MainConfig {
}
1.2 @Bean
给容器注册一个 Bean, 类型(class)是返回值类型, id 默认是用方法名作为类型,
-
name, value
指定 id, 两个属性用 @AliasFor 注解标识了, 互为别名, 若同时出现其值必须一样 -
initMethod, destroyMethod
指定 Bean 的初始化和销毁方法
@Bean(name = "person", initMethod = "init", destroyMethod = "destroy")
public Person person() {
return new Person("lisi", 20);
}
1.3 AnnotationConfigApplicationContext
- 使用 xml 文件时用 ClassPathXmlApplicationContext
- 使用配置类时用 AnnotationConfigApplicationContext
new AnnotationConfigApplicationContext(MainConfig.class);
1.4 @ComponentScan
标识在配置类上
-
basePackages
指定要扫描的包 -
excludeFilters
excludeFilters = Filter[]: 指定扫描的时候按照什么规则排除哪些组件 -
includeFilter
includeFilters = Filter[]: 指定扫描的时候只需要包含哪些组件 -
useDefaultFilters
默认为 true,会扫描到 @Service @Reposity @Controller 等,所以当使用 includeFilter 时, 需要配置成 false,只扫描指定的注解
1.4.1 @ComponentScan.Filter
表示 Filter[] 数组中的一个 Filter
- type 属性
FilterType.ANNOTATION: 按照注解类型
FilterType.ASSIGNABLE_TYPE: 按照给定的类型
FilterType.ASPECTJ: 使用 ASPECTJ 表达式, 不常用
FilterType.REGEX: 使用正则指定
FilterType.CUSTOM: 使用自定义规则(实现 org.springframework.core.type.filter.TypeFilter 接口) - classes 属性
FilterType.ANNOTATION: 指定包含或不包含的注解的 class
FilterType.ASSIGNABLE_TYPE: 指定包含或不包含的类的 class, 包含子类和实现类
FilterType.CUSTOM: 指定自定义规则的实现类
1.5 @ComponentScans
可以多指定几个 @ComponentScan, 若只有一个 @ComponentScan, 则可以不写
- value
将多个 @ComponentScan 写在其中
@ComponentScans(value = {
@ComponentScan(basePackages = "com.tc",
// excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})}
includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})},
useDefaultFilters = false
)}
)
public class MainConfig {
}
1.6 @Scope
可以在实体类上标识, 也可以在标识了 @Bean 注解的方法上标识
- scopeName
prototype: 多实例的: IOC 容器启动并不会去调用方法创建对象放在容器中, 每次获取的时候才会调用方法创建对象
singleton: 单实例的(默认值): IOC 容器启动会调用方法创建对象放到 IOC 容器中, 以后每次获取就是直接从容器(map.get())中拿
request: 同一次请求创建一个实例
session: 同一个 session 创建一个实例
@Scope(scopeName = "prototype")
public Person person() {
}
1.7 @Lazy
懒加载
单实例 bean: 默认在容器启动的时候创建对象
懒加载: 容器启动不创建对象, 第一次使用(获取) Bean 创建对象, 并初始化
1.8 @Conditional
按照一定的条件进行判断, 满足条件才给容器中注册 bean
向注解中传入 实现了 Condition 的类的 Class 数组作为条件
/**
* 若系统为 Windows 系统, 则满足条件
*/
public class WindowsCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 获取当前环境信息(包括环境变量等系统信息)
Environment environment = context.getEnvironment();
String property = environment.getProperty("os.name");
return property != null && property.contains("Windows");
}
}
可以在方法上标识
@Bean(name = "bill")
@Conditional({WindowsCondition.class})
public Person person1() {
return new Person("Bill Gates", 62);
}
也可以在类上标识
// 类中组件统一设置, 满足当前条件, 这个类中配置的所有 bean 注册时才能生效
@Conditional(WindowsCondition.class)
public class MainConfig {
}
1.9 @Import
- 直接传入目标类的 Class 对象
public class Color {
}
// @Import 导入组件, id 默认是组件的全类名
@Import({Color.class})
public class MainConfig2 {
}
- 还可以传入实现了 org.springframework.context.annotation.ImportSelector 接口的实现类, 来实现导入多个组件
public class MyImportSelector implements ImportSelector {
/**
* @param importingClassMetadata 当前标注 @Import 注解的类的所有注解信息
* @return 导入到容器中的组件全类名
*/
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"全类名1", "全类名2", ...};
}
}
@Import({MyImportSelector.class})
public class MainConfig2 {
}
- 还可以传入实现了 org.springframework.context.annotation.ImportBeanDefinitionRegistrar 接口的实现类
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/**
* @param importingClassMetadata 当前类的注解信息
* @param registry BeanDefinition 注册类, 把所有需要添加到容器中的 bean,
* 调用 BeanDefinitionRegistry.registerBeanDefinition 手动注册进来
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
boolean definition = registry.containsBeanDefinition("Red");
boolean definition2 = registry.containsBeanDefinition("Blue");
if (definition && definition2) { // 如果容器中有 id 为 "Red" 和 "Blue" 的 Bean
// 向容器中添加一个 Color 类的对象, id 为 color
RootBeanDefinition beanDefinition = new RootBeanDefinition(Color.class);
registry.registerBeanDefinition("color", beanDefinition);
}
}
}
@Import({MyImportBeanDefinitionRegistrar.class})
public class MainConfig2 {
}
2. Spring 注解 – 生命周期
2.1 @Bean
@Bean 的 init-method 和 destroy-method 与生命周期相关, 上面讲过, 不多赘述
2.2 InitializingBean, DisposableBean
通过让 Bean 实现 InitializingBean(定义初始化逻辑), DisposableBean(定义销毁逻辑)
@Component
public class Cat implements InitializingBean, DisposableBean {
@Override
public void destroy() throws Exception {
System.out.println("Cat Destroy");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("Cat AfterPropertiesSet");
}
}
2.3 @PostConstruct, @PreDestroy
@Component
public class Dog {
// 对象创建并赋值之后调用
@PostConstruct
public void init() {
System.out.println("Dog @PostConstruct");
}
// 容器移除对象之前调用
@PreDestroy
public void destroy() {
System.out.println("Dog @PreDestroy");
}
}
2.4 BeanPostProcessor
实现 org.springframework.beans.factory.config.BeanPostProcessor 接口, 并注册到 IOC 容器中, 可以在所有 Bean 的初始化前后工作
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessBeforeInitialization -> " + beanName + " -> " + bean);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessAfterInitialization -> " + beanName + " -> " + bean);
return bean;
}
}
3. Spring 注解 – 属性赋值
3.1 @Value
使用 @Value 赋值
- 基本数值
- 可以写 SpEL #{}
- 可以写 ${}, 取出配置文件 [properties] 中的值(在运行环境变量里面的值), 与 @PropertySource 配合使用
// person.properties 内容
person.nickName=小张三
public class Person {
@Value("张三")
private String name;
@Value("#{20 - 2}")
private Integer age;
@Value("${person.nickName}")
private String nickName;
}
3.2 @PropertySource
读取外部配置文件中的 k/v 对, 保存到运行的环境变量中, 加载完外部的配置文件以后使用 ${} 取出配置文件的值
- value
为 String 数组, 可包含多个配置文件的路径
@Configuration
@PropertySource(value = {"classpath:/person.properties"})
public class MainConfigOfPropertyValues {
}
4. Spring 注解 – 自动注入
4.1 @Autowired
默认优先按照类型去容器中找对应的组件, 如果找到多个相同类型的组件, 再将属性的名称作为组件的 id 去容器中查找
- required
若在 IOC 容器中没有匹配的属性, 则可以不强制要求装配
@Autowired(required = false)
4.2 @Qualifier
指定需要装配的组件的 id, 而不是使用属性名
@Service
public class BookService {
private BookDao bookDao;
@Autowired
@Qualifier("bookDao2")
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
}
4.3 @Primary
指定 Spring 自动装配时首选的 bean, 也可以继续使用 @Qualifier 指定需要装配的 bean 的名字
/*
* 若无 @Qualifier 指定, 首先使用此 Bean
*/
@Bean(name = "bookDao2")
@Primary
public BookDao bookDao() {
return new BookDao();
}
4.4 @Resource
可以和 @Autowired 一样实现自动装配功能. 默认是按照组件名称进行装配的
不支持 @Primary, 不支持类似 @Autowired(required = false) 的功能
- name
指定自动注入的 Bean 的 name, 可以不写, 默认按照组件名称进行装配的
@Service
public class BookService {
private BookDao bookDao;
@Resource(name = "bookDao2")
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
}
4.5 @Inject
需要导入 javax.inject 的包, 和 @Autowired 的功能类似. 但是没有 required = false 的功能
@Service
public class BookService {
private BookDao bookDao;
@Inject
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
}
4.6 ApplicationContextAware, BeanNameAware, EmbeddedValueResolverAware
把 Spring 底层一些组件注入到自定义的 Bean 中
- ApplicationContextAware: 得到 ApplicationContext
- BeanNameAware: 获得当前 Bean 的 name
- EmbeddedValueResolverAware: 获得一个解析字符串的 Resolver 解析器
@Component
public class Red implements ApplicationContextAware, BeanNameAware, EmbeddedValueResolverAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("传入的 ApplicationContext: " + applicationContext);
this.applicationContext = applicationContext;
}
@Override
public void setBeanName(String name) {
System.out.println("当前 Bean 的名字: " + name); // 当前 Bean 的名字: red
}
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
String resolveStringValue = resolver.resolveStringValue("你好 ${os.name} 我是 #{20*18}");
System.out.println("解析的字符串: " + resolveStringValue); // 解析的字符串: 你好 Windows 10 我是 360
}
}
4.7 @Profile
指定组件在哪个环境的情况下才能被注册到容器中, 不指定, 则任何环境下都能注册这个组件
- 加了 @Profile 标识的 bean, 只有这个环境被激活的时候才能注册到容器中, 默认是 default 环境
- 写在配置类上, 只有是指定的环境的时候, 整个配置类里面的所有配置才能开始生效
- 没有 @Profile 标识, 没有标注环境标识的 bean, 在任何环境下都是加载的
@Configuration
@Profile("test")
public class MainConfigOfProfile {
/**
* 注册一个 DataSource, 要求必须在 test 环境下才能被创建
*/
@Bean(name = "testDataSource")
@Profile("test")
public DataSource dataSourceTest() throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser("user");
dataSource.setPassword("password");
dataSource.setDriverClass("driverClass");
dataSource.setJdbcUrl("jdbcUrl");
return dataSource;
}
}
激活指定的环境:
- 使用命令行动态参数: 在虚拟机参数位置加 -Dspring.profiles.active=test
- 代码的方式激活某种环境(要用 ApplicationContext 的无参构造器)
// 1. 创建一个applicationContext
ApplicationContext applicationContext = new AnnotationConfigApplicationContext();
// 2. 设置需要激活的环境, String... 可传入多个要激活的环境
applicationContext.getEnvironment().setActiveProfiles("dev");
// 3. 注册主配置类
applicationContext.register(MainConfigOfProfile.class);
// 4. 启动刷新容器
applicationContext.refresh();