# spring-annotation-example **Repository Path**: cjiangbo/spring-annotation-example ## Basic Information - **Project Name**: spring-annotation-example - **Description**: 学习。。。。。。。。。。 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2022-12-11 - **Last Updated**: 2023-12-28 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ### 1、@ComponentScan 通过使用 @ComponentScan,Spring 就会自动去扫描那些带有注解配置的类(@Controller、@Service、@Repository、@Component); Spring Boot 下如果你的其他包层次结构位于使用 @SpringBootApplication 标注主应用程序下方,则隐式组件扫描将自动涵盖,也就是说,不要明确标注 @ComponentScan,Spring Boot 会自动搜索当前应用主入口目录及其下方子目录; Spring Boot 下如果 @ComponentScan 和 @SpringBootApplication 同时存在,且 @ComponentScan 指定了非启动类所在的目录时,@SpringBootApplication 的默认扫描将失效,此时注意需要显示配置启动类扫描路径 基础配置示例: ```java @Configuration @ComponentScans( value = { // @ComponentScan(value = "com.onde.annotation") @ComponentScan(basePackages = "com.onde.annotation") } ) @ComponentScan(basePackageClasses = BeanConfig01.class) public class ComponentScanConfig { } ``` - @Configuration 告诉Spring这是一个配置类,配置类等效于配置文件(xml) - basePackages 或者 value 参数,两者是一样的,value 只不过是 basePackages 的另一种称呼,来直接指定我们所要扫描的包的名称; - 也可使用 basePackageClasses 来指向指定类; - Spring 下如果没有为 @ComponentScan 指定参数,那么 Spring 就只会扫描和 @ComponentScan 注解的类位于同一个包的带有 @Component 注解的其他类,然后将它们自动创建为一个 Bean; - 在 java 8 中,因为重复注解的特性,@ComponentScan 可多个配置指定多个规则,等效于@ComponentScans 其他配置: - excludeFilters = Filter[]:指定扫描的时候按照什么规则排除那些组件 - includeFilters = Filter[]:指定扫描的时候只需要包含哪些组件 - FilterType.ANNOTATION:按照注解 - FilterType.ASSIGNABLE_TYPE:按照给定的类型 - FilterType.ASPECTJ:使用ASPECTJ表达式 - FilterType.REGEX:使用正则指定 - FilterType.CUSTOM:使用自定义规则 includeFilters & FilterType.ANNOTATION 示例: 只注册@Controller注解的类; useDefaultFilters,是否自动检测带有 @Component 注解的类,默认为 true,像 @Repository、@Controller、@Service、@Configuration 内部都包含了 @Component,所以此示例需要配置 useDefaultFilters 为 false ```java @Configuration @ComponentScan(basePackages = "com.onde.annotation", includeFilters = { // 只注册@Controller注解的类 @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class}) }, useDefaultFilters = false) public class ComponentScanConfig02 { } ``` includeFilters & FilterType.ASSIGNABLE_TYPE 示例: 只注册AnnotationService类类型的类 ```java @Configuration @ComponentScan(basePackages = "com.onde.annotation", includeFilters = { // 只注册AnnotationService类类型的类 @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {AnnotationService.class}) }, useDefaultFilters = false) public class ComponentScanConfig03 { } ``` includeFilters & FilterType.CUSTOM示例: 只注册AnnotationDao类类型的类 ```java @Configuration @ComponentScan(basePackages = "com.onde.annotation", includeFilters = { // 自定义扫描规则 @ComponentScan.Filter(type = FilterType.CUSTOM, classes = {CustomTypeFilter.class}) }, useDefaultFilters = false) public class ComponentScanConfig04 { } ``` ```java public class CustomTypeFilter implements TypeFilter { /** * metadataReader:读取到当前正在扫描的类的信息 * metadataReaderFactory:可以获取到其他任何类信息的 */ @Override public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { // TODO Auto-generated method stub // 获取当前类注解的信息 AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata(); // 获取当前正在扫描的类的类信息 ClassMetadata classMetadata = metadataReader.getClassMetadata(); // 获取当前类资源(类的路径) Resource resource = metadataReader.getResource(); String className = classMetadata.getClassName(); System.out.println("--->" + className); // 只注册AnnotationDao类类型的类 if (className.equals(AnnotationDao.class.getName())) { return true; } return false; } } ``` excludeFilters 示例: 排除 @Service 注解的类 ```java @Configuration @ComponentScan(basePackages = "com.onde.annotation", excludeFilters = { // 去除其他测试干扰 @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {ComponentScanConfig01.class}), @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {ComponentScanConfig02.class}), @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {ComponentScanConfig03.class}), @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {ComponentScanConfig04.class}), // 排除 @Service 注解的类 @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Service.class}) }) public class ComponentScanConfig05 { } ``` ### 2、@Scope & @Lazy - @Bean 默认为单实例,即 @Scope("singleton") - @Scope 可配置 bean 为单实例或多实例 - singleton:单实例(默认值),ioc容器启动会调用方法创建对象放到ioc容器中 - prototype:多实例,ioc容器启动并不会去调用方法创建对象放在容器中,每次获取的时候才会调用方法创建对象 - request:同一次请求创建一个实例 - session:同一个session创建一个实例 - @Lazy 配置为懒加载,作用于单实例,单实例默认在容器启动的时候创建对象,懒加载容器启动不创建对象,在第一次使用(获取)bean时创建对象,并初始化 ### 3、@Conditional 按照一定的条件进行判断,满足条件给容器中注册bean,需自定义类实现 Condition 接口来设置自己的规则,可作用于方法或类上 示例: 自定义 Condition 规则,如果配置了环境变量condition.filter,且配置的值大于0则注册 ```java public class EnvironmentCondition implements Condition { /** * @param context 上下文信息 * @param metadata 注解信息 * @return */ @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { // 能获取到 ioc 使用的 beanFactory ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); // 能获取到类加载器 ClassLoader classLoader = context.getClassLoader(); // 能获取到当前环境信息 Environment environment = context.getEnvironment(); // 能获取到 bean 定义的注册类 BeanDefinitionRegistry registry = context.getRegistry(); // 获取环境参数 condition.filter String property = environment.getProperty("condition.filter"); // 能获取到容器中的 bean 注册情况,也可以给容器中注册bean boolean definition = registry.containsBeanDefinition("user"); if (StrUtil.isNotBlank(property) && Integer.valueOf(property) > 0) { return true; } return false; } } ``` ```java @Configuration public class ConditionalConfig01 { /** * 如果配置了环境变量condition.filter,且配置的值大于0则注册 * @Conditional 作用于方法上 * * @return */ @Conditional({EnvironmentCondition.class}) @Bean public User conditional_user_01() { User user = new User(); user.setId(30001); user.setName("conditional_user_01"); user.setAge(20); return user; } } ``` 自定义 Condition 规则,如果存在 bean conditionalConfig01 则注册,否则不注册 ```java public class BeanCondition implements Condition { /** * @param context 上下文信息 * @param metadata 注解信息 * @return */ @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { // 能获取到 bean 定义的注册类 BeanDefinitionRegistry registry = context.getRegistry(); // 能获取到容器中的 bean 注册情况,也可以给容器中注册bean boolean definition = registry.containsBeanDefinition("conditionalConfig01"); return definition; } } ``` ```java @Configuration @Conditional({BeanCondition.class}) public class ConditionalConfig02 { /** * 如果存在 bean conditionalConfig01 则注册 conditional_user_02,否则不注册 * @Conditional 作用于类上,满足当前条件,这个类中配置的所有 bean 注册才能生效 * * @return */ @Bean public User conditional_user_02() { User user = new User(); user.setId(30002); user.setName("conditional_user_02"); user.setAge(20); return user; } } ``` ### 4、IOC 中注册组件 #### 4.1、包扫描 + 组件标注注解 包扫描( @ComponentScan)+ 组件标注注解(@Controller、@Service、@Repository、@Component),通常用于注册自己项目内部的类 #### 4.2、@Bean 可用于注册自己项目内部的类或第三方包的类 示例: ```java @Configuration public class BeanRegisterConfig01 { /** * bean id 默认为方法名 * * @return */ @Bean public User bean_register_user01() { User user = new User(); user.setId(40001); user.setName("bean_register_user01"); user.setAge(20); return user; } /** * bean id 也可自己指定 * * @return */ @Bean("custom_bean_register_user02") public User bean_register_user02() { User user = new User(); user.setId(40002); user.setName("custom_bean_register_user02"); user.setAge(20); return user; } /** * 导入第三方类,非扫描包下的类 * * @return */ @Bean public ThirdPartyUser bean_register_third_party_user01() { ThirdPartyUser user = new ThirdPartyUser(); user.setId(40003); user.setName("bean_register_third_party_user01"); user.setPhone("18759178995"); return user; } } ``` #### 4.3、@Import @Import + @Configuration 可实现在 IOC 容器中自动注册组件,bean id默认是全类名,通过无参构造器创建类 示例: ```java @Configuration @Import({Animal.class}) public class BeanRegisterConfig02 { } ``` #### 4.4、ImportSelector 通过实现 ImportSelector 接口来返回需要注册到 IOC 容器中的组件 ```java public class DonaldDuckImportSelector implements ImportSelector { /** * @param annotationMetadata 可获取当前标注@Import注解的类的所有注解信息,和当前类的其他信息 * @return 返回值为导入到容器中的组件全类名 */ @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { return new String[]{Duck.class.getName()}; } } ``` 通过 ImportSelector 接口来实现 bean 的动态注册: ImportSelector 实现,根据 EnableAnimal 注解的 type 来决定要注入 Animal 的哪个实现类 Animal 父类,Duck、Mouse 均为 Animal 的子类 ```java public class AnimalImportSelector implements ImportSelector { /** * @param annotationMetadata 可获取当前标注@Import注解的类的所有注解信息,和当前类的其他信息 * @return 返回值为导入到容器中的组件全类名 */ @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { Map annotationAttributes = annotationMetadata.getAnnotationAttributes(EnableAnimal.class.getName()); if(annotationAttributes == null){ return new String[]{Animal.class.getName()}; } AnimalType type = (AnimalType) annotationAttributes.get("type"); switch (type) { case DUCK: return new String[]{Duck.class.getName()}; case MOUSE: return new String[]{Mouse.class.getName()}; default: return new String[]{Animal.class.getName()}; } } } ``` @EnableAnimal 注解实现 ```java @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented // 引入 AnimalImportSelector @Import(AnimalImportSelector.class) public @interface EnableAnimal { AnimalType type() default AnimalType.DUCK; } ``` 测试,@EnableAnimal 默认 type 为 AnimalType.DUCK,结果输出: ```java @EnableAnimal @SpringBootTest(classes = Application.class) @RunWith(SpringRunner.class) public class BeanRegisterTest02 { @Resource private Animal animal; @Test public void test01() { // 输出:Animal{name='Donald Duck', type='Duck'} System.out.println(animal); } } @EnableAnimal(type = AnimalType.MOUSE) @SpringBootTest(classes = Application.class) @RunWith(SpringRunner.class) public class BeanRegisterTest03 { @Resource private Animal animal; @Test public void test01() { // 输出:Animal{name='Jerry Mouse', type='Mouse'} System.out.println(animal); } } ``` #### 4.5、ImportBeanDefinitionRegistrar 配置类实现 ImportBeanDefinitionRegistrar 接口,可自定义往容器中注册想注入的 bean; 与 ImportSelector 的区别是,ImportSelector 接口返回的是需要注入的类,但不允许对这个类做其他额外的操作,ImportBeanDefinitionRegistrar 是可以注入 BeanDefinition,可以设置属性之类的行为 ```java public class DuckImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { /** * * @param annotationMetadata 可获取当前标注@Import注解的类的所有注解信息,和当前类的其他信息 * @param registry 注册类,registerBeanDefinition() 可以注册 bean */ @Override public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) { // 构建一个 BeanDefinition, Bean 的类型为 Duck // 这个 Bean 的属性 name 的值为 ImportBeanDefinitionRegistrar Duck AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Duck.class) .addPropertyValue("name", "ImportBeanDefinitionRegistrar Duck") .getBeanDefinition(); // Duck 注册到容器 registry.registerBeanDefinition("importBeanDefinitionRegistrarDuck", beanDefinition); } } ``` Mybatis 中 @MapperScan 就是用这种方式实现的,@MapperScan 指定 basePackages,扫描存在 @Mapper 的类注入到容器中; 示例: @ColorScan 标注需要扫描的包路径,@Color 标注需要注入的 bean,ColorImportBeanDefinitionRegistrar 通过 ImportBeanDefinitionRegistrar 接口实现 bean 的注册 ```java // 用于标注要注册的 bean @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented public @interface Color { } // 定义 Color 扫描类 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented @Import(ColorImportBeanDefinitionRegistrar.class) public @interface ColorScan { /** * 扫描路径 */ String[] basePackages() default {}; } // 通过 ColorScan 扫描注入 Color public class ColorImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware{ private ResourceLoader resourceLoader; /** * * @param annotationMetadata 可获取当前标注@Import注解的类的所有注解信息,和当前类的其他信息 * @param registry 注册类,registerBeanDefinition() 可以注册 bean */ @Override public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) { // 获取 ColorScan 注解的 basePackages 属性,即要扫描的类路径 AnnotationAttributes annotationAttributes = AnnotationAttributes.fromMap(annotationMetadata.getAnnotationAttributes(ColorScan.class.getName())); if (annotationAttributes == null || annotationAttributes.isEmpty()) { return; } String[] basePackages = (String[]) annotationAttributes.get("basePackages"); // ClassPathBeanDefinitionScanner 作用就是将指定包下的类通过一定规则过滤后 // 将 Class 信息包装成 BeanDefinition 的形式注册到IOC容器中 ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry, false); scanner.setResourceLoader(resourceLoader); //路径包含MapperBean的注解的bean scanner.addIncludeFilter(new AnnotationTypeFilter(Color.class)); //扫描包下路径 scanner.scan(basePackages); } @Override public void setResourceLoader(ResourceLoader resourceLoader) { this.resourceLoader = resourceLoader; } } @Color public class Red { private String color; public Red() { this.color = "Red"; } @Override public String toString() { return "Red{" + "color='" + color + '\'' + '}'; } } ``` #### 4.6、FactoryBean 1. 实现 FactoryBean 接口的 Bean,实际上对外暴露的是 getObject 方法返回的对象 2. FactoryBean 支持创建单例和多例,可以通过懒加载或容器启动时加载的方式创建 Bean 3. Spring 只会管理 FactoryBean 对象本身,通过 FactoryBean#getObject 创建出来的对象的生命周期不会由Spring管理,也就是说通过 getObject 返回的对象本身即使实现了生命周期接口,也不会被调用 4. 要获取工厂 Bean 本身,我们需要给 Bean Id 前面加一个&,如:&bean_register_factoryBean_user01 ```java public interface FactoryBean { String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType"; /** * 对外暴露的是 getObject 方法返回的对象 */ @Nullable T getObject() throws Exception; @Nullable Class getObjectType(); /** * 单例或多例 */ default boolean isSingleton() { return true; } } ``` 示例: ```java public class UserFactoryBean implements FactoryBean { private boolean isSingleton; public UserFactoryBean() { this.isSingleton = true; } public UserFactoryBean(boolean isSingleton) { this.isSingleton = isSingleton; } /** * 返回一个 User 对象,对象将会添加到 spring 容器中 * * @return */ @Override public User getObject() { User user = new User(); user.setId(40001); user.setName("bean_register_factoryBean_user01"); user.setAge(20); return user; } @Override public Class getObjectType() { return User.class; } /** * 是否单实例 * * @return */ @Override public boolean isSingleton() { return this.isSingleton; } } ``` 场景说明: 1. 如果已经明确要创建的 Bean 对象的 Class 的时候,直接使用 @Bean 即可,如果无法拿到 Class,则使用 FactoryBean 的方式 2. 如果不想暴露太多的 Bean 对象创建细节,如复杂的 Bean 创建过程,这种情况也可以使用 FactoryBean 场景示例: ​ 待补充 ### 5、Bean 生命周期 #### 5.1、init-method & destroy-method 通过 @Bean 指定 init-method 和 destroy-method 示例: ```java /** * 测试 bean */ @Data public class Person { private Integer id; private String name; public Person() { System.out.println("Life Cycle Demo: Create Person"); } public void init() { System.out.println("Life Cycle Demo: Init Person"); } public void destroy() { System.out.println("Life Cycle Demo: Destroy Person"); } public void setId(Integer id) { this.id = id; System.out.println("Life Cycle Demo: setId"); } public void setName(String name) { this.name = name; System.out.println("Life Cycle Demo: setName"); } } /** * 注入 IOC 容器,同时指定 initMethod 和 destroyMethod */ @Bean(initMethod = "init", destroyMethod = "destroy") public Person bean_life_cycle_person01() { Person person = new Person(); person.setId(50001); person.setName("bean_life_cycle_person01"); return person; } /** * 测试例子 */ @Test public void test01() { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(LifeCycleConfig01.class); // bean id 默认为方法名 Person person = (Person) applicationContext.getBean("bean_life_cycle_person01"); System.out.println(person); applicationContext.close(); } ``` 输出如下: ``` Life Cycle Demo: Create Person Life Cycle Demo: setId Life Cycle Demo: setName Life Cycle Demo: Init Person Person(id=50001, name=bean_life_cycle_person01) Life Cycle Demo: Destroy Person ``` 执行顺序: 1. 调用构造方法创建对象 2. 调用 set 方法设置属性值 3. 执行 initMethod 方法 4. 容器关闭后执行 destroyMethod 方法 #### 5.2、InitializingBean & DisposableBean - 通过 Bean 实现 InitializingBean 接口和 DisposableBean 接口 - InitializingBean#afterPropertiesSet():定义初始化逻辑 - DisposableBean#destroy():定义销毁逻辑 示例: ```java /** * 测试 bean */ @Data public class Student implements InitializingBean, DisposableBean { private Integer id; private String name; public Student() { System.out.println("Life Cycle Demo: Create Student"); } public void setId(Integer id) { this.id = id; System.out.println("Life Cycle Demo: setId"); } public void setName(String name) { this.name = name; System.out.println("Life Cycle Demo: setName"); } /** * DisposableBean#destroy():定义销毁逻辑 */ @Override public void destroy() { System.out.println("Life Cycle Demo: Destroy Student"); } /** * InitializingBean#afterPropertiesSet():定义初始化逻辑 */ @Override public void afterPropertiesSet() { System.out.println("Life Cycle Demo: Init Student"); } } /** * 注入 IOC 容器 */ @Bean public Student bean_life_cycle_student01() { Student student = new Student(); student.setId(50002); student.setName("bean_life_cycle_student01"); return student; } /** * 测试例子 */ @Test public void test02() { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(LifeCycleConfig02.class); // bean id 默认为方法名 Student student = (Student) applicationContext.getBean("bean_life_cycle_student01"); System.out.println(student); applicationContext.close(); } ``` 输出如下: ``` Life Cycle Demo: Create Student Life Cycle Demo: setId Life Cycle Demo: setName Life Cycle Demo: Init Student Student(id=50002, name=bean_life_cycle_student01) Life Cycle Demo: Destroy Student ``` 执行顺序: 1. 调用构造方法创建对象 2. 调用 set 方法设置属性值 3. 执行 InitializingBean#afterPropertiesSet() 方法 4. 容器关闭后执行 DisposableBean#destroy()方法 #### 5.3、@PostConstruct & @PreDestroy 使用 JSR250 规范中的注解,在初始化方法上加 @PostConstruct,在销毁方法上加 @PreDestroy,当容器在 Bean 执行到对应生命周期时调用对应方法 示例: ```java /** * 测试 bean */ @Data public class Teacher { private Integer id; private String name; public Teacher() { System.out.println("Life Cycle Demo: Create Teacher"); } @PostConstruct public void init() { System.out.println("Life Cycle Demo: Init Teacher"); } @PreDestroy public void destroy() { System.out.println("Life Cycle Demo: Destroy Teacher"); } public void setId(Integer id) { this.id = id; System.out.println("Life Cycle Demo: setId"); } public void setName(String name) { this.name = name; System.out.println("Life Cycle Demo: setName"); } } /** * 注入 IOC 容器 */ @Bean public Teacher bean_life_cycle_teacher01() { Teacher teacher = new Teacher(); teacher.setId(50003); teacher.setName("bean_life_cycle_teacher01"); return teacher; } /** * 测试例子 */ @Test public void test03() { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(LifeCycleConfig03.class); // bean id 默认为方法名 Teacher teacher = (Teacher) applicationContext.getBean("bean_life_cycle_teacher01"); System.out.println(teacher); applicationContext.close(); } ``` 输出如下: ``` Life Cycle Demo: Create Teacher Life Cycle Demo: setId Life Cycle Demo: setName Life Cycle Demo: Init Teacher Teacher(id=50003, name=bean_life_cycle_teacher01) Life Cycle Demo: Destroy Teacher ``` 执行顺序: 1. 调用构造方法创建对象 2. 调用 set 方法设置属性值 3. 执行 @PostConstruct 注解的 init() 方法 4. 容器关闭后执行 @PreDestroy 注解的 destroy() 方法 #### 5.4、BeanPostProcessor 容器中有 BeanPostProcessor 接口的实现类,容器所管理的 Bean 会在初始化前后进行一些处理工作,在初始化之前会调用接口中的 postProcessBeforeInitialization 方法,在初始化之后会调用接口中的 postProcessAfterInitialization 方法 示例: BeanPostProcessor 配置类: ```java @Component public class MyBeanPostProcessor implements BeanPostProcessor { /** * 初始化之前执行(@Bean init-method、@PostConstruct、InitializingBean 之前) * * @param bean 当前 bean * @param beanName bean 名称(bean id) * @return * @throws BeansException */ @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { // 只打印跟 Person 相关的信息 if(bean.getClass() == Person.class) { System.out.println("Life Cycle Demo postProcessBeforeInitialization bean: " + bean); System.out.println("Life Cycle Demo postProcessBeforeInitialization bean: " + beanName); } return bean; } /** * 初始化之后执行(@Bean init-method、@PostConstruct、InitializingBean 之后) * * @param bean 当前 bean * @param beanName bean 名称(bean id) * @return * @throws BeansException */ @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { // 只打印跟 Person 相关的信息 if(bean.getClass() == Person.class) { System.out.println("Life Cycle Demo postProcessAfterInitialization bean: " + bean); System.out.println("Life Cycle Demo postProcessAfterInitialization bean: " + beanName); } return bean; } } ``` 注入测试 Bean: ```java @ComponentScan(basePackages = "com.onde.annotation.config.lifeCycle", includeFilters = { @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {MyBeanPostProcessor.class}), }, useDefaultFilters = false) public class BeanPostProcessorConfig { /** * BeanPostProcessor 测试 * * @return */ @Bean public Person bean_life_cycle_person02() { Person person = new Person(); person.setId(50004); person.setName("bean_life_cycle_person02"); return person; } } ``` 测试方法: ```java @Test public void test04() { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanPostProcessorConfig.class); // bean id 默认为方法名 Person person = (Person) applicationContext.getBean("bean_life_cycle_person02"); System.out.println(person); applicationContext.close(); } ``` 输出如下: ``` Life Cycle Demo: Create Person Life Cycle Demo: setId Life Cycle Demo: setName Life Cycle Demo postProcessBeforeInitialization bean: Person(id=50004, name=bean_life_cycle_person02) Life Cycle Demo postProcessBeforeInitialization bean: bean_life_cycle_person02 Life Cycle Demo postProcessAfterInitialization bean: Person(id=50004, name=bean_life_cycle_person02) Life Cycle Demo postProcessAfterInitialization bean: bean_life_cycle_person02 Person(id=50004, name=bean_life_cycle_person02) ``` 执行顺序: 1. 调用构造方法创建对象 2. 调用 set 方法设置属性值 3. 执行 BeanPostProcessor#postProcessBeforeInitialization 方法 4. 执行 BeanPostProcessor#postProcessAfterInitialization 方法 #### 5.5、综合示例 Bean 定义: ```java @Data public class Police implements InitializingBean, DisposableBean { private Integer id; private String name; public Police() { System.out.println("Life Cycle Constructor: Create Police"); } @PostConstruct public void postConstruct() { System.out.println("Life Cycle @PostConstruct: Init Police"); } @PreDestroy public void preDestroy() { System.out.println("Life Cycle @PreDestroy: Destroy Police"); } /** * DisposableBean#destroy():定义销毁逻辑 */ @Override public void destroy() { System.out.println("Life Cycle DisposableBean: Destroy Police"); } /** * InitializingBean#afterPropertiesSet():定义初始化逻辑 */ @Override public void afterPropertiesSet() { System.out.println("Life Cycle InitializingBean: Init Police"); } public void initMethod() { System.out.println("Life Cycle initMethod: Init Police"); } public void destroyMethod() { System.out.println("Life Cycle destroyMethod: Destroy Police"); } public void setId(Integer id) { this.id = id; System.out.println("Life Cycle set: setId"); } public void setName(String name) { this.name = name; System.out.println("Life Cycle set: setName"); } } ``` BeanPostProcessor 定义: ```java @Component public class MyBeanPostProcessor implements BeanPostProcessor { /** * 初始化之前执行(@Bean init-method、@PostConstruct、InitializingBean 之前) * * @param bean 当前 bean * @param beanName bean 名称(bean id) * @return * @throws BeansException */ @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { // 打印跟 Person or Police 相关的信息 if(bean.getClass() == Person.class || bean.getClass() == Police.class) { System.out.println("Life Cycle Demo postProcessBeforeInitialization bean: " + bean + " beanName: " + beanName); } return bean; } /** * 初始化之后执行(@Bean init-method、@PostConstruct、InitializingBean 之后) * * @param bean 当前 bean * @param beanName bean 名称(bean id) * @return * @throws BeansException */ @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { // 打印跟 Person or Police 相关的信息 if(bean.getClass() == Person.class || bean.getClass() == Police.class) { System.out.println("Life Cycle Demo postProcessAfterInitialization bean: " + bean + " beanName: " + beanName); } return bean; } } ``` 测试方法: ```java @ComponentScan(basePackages = "com.onde.annotation.config.lifeCycle", includeFilters = { @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {MyBeanPostProcessor.class}), }, useDefaultFilters = false) public class LifeCycleConfig { /** * 综合测试 * * @return */ @Bean(initMethod = "initMethod", destroyMethod = "destroyMethod") public Police bean_life_cycle_police01() { Police police = new Police(); police.setId(50005); police.setName("bean_life_cycle_police01"); return police; } } @Test public void test05() { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(LifeCycleConfig.class); // bean id 默认为方法名 Police police = (Police) applicationContext.getBean("bean_life_cycle_police01"); System.out.println(police); applicationContext.close(); } ``` 输出如下: ``` Life Cycle Constructor: Create Police Life Cycle set: setId Life Cycle set: setName Life Cycle Demo postProcessBeforeInitialization bean: Police(id=50005, name=bean_life_cycle_police01) beanName: bean_life_cycle_police01 Life Cycle @PostConstruct: Init Police Life Cycle InitializingBean: Init Police Life Cycle initMethod: Init Police Life Cycle Demo postProcessAfterInitialization bean: Police(id=50005, name=bean_life_cycle_police01) beanName: bean_life_cycle_police01 Police(id=50005, name=bean_life_cycle_police01) Life Cycle @PreDestroy: Destroy Police Life Cycle DisposableBean: Destroy Police Life Cycle destroyMethod: Destroy Police ``` 执行顺序: 1. 调用构造方法创建对象 2. 调用 set 方法设置属性值 3. 执行 BeanPostProcessor#postProcessBeforeInitialization 方法 4. 执行 JSR250 规范 @PostConstruct 注解的 init() 方法 5. 执行 InitializingBean#afterPropertiesSet() 方法 6. 执行 @Bean 指定的 initMethod 方法 7. 执行 BeanPostProcessor#postProcessAfterInitialization 方法 8. 容器关闭后执行 JSR250 规范 @PreDestroy 注解的 destroy() 方法 9. 容器关闭后执行 DisposableBean#destroy()方法 10. 容器关闭后执行 @Bean 指定的 destroyMethod 方法 ### 6、属性赋值 #### 6.1、@PropertySource @PropertySource 注解用于指定资源文件读取的位置,能够读取 properties 文件、xml文件,通过 yaml 解析器配合自定义PropertySourceFactory 也可实现解析YAML文件 ```java // 只能作用于类上 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Repeatable(PropertySources.class) public @interface PropertySource { /** * 指示此属性源的名称,为空将基于基础资源生成名称 */ String name() default ""; /** * 指定资源路径 * classpath:/xxx/xxxx * file:/xxx/xxx/xx */ String[] value(); /** * 是否忽略资源不存在的情况,如果不忽略,当资源不存在时就报错,默认不忽略(spring 4.0 之后) */ boolean ignoreResourceNotFound() default false; /** * 指定资源文件的编码格式,果不指定就使用文件默认的(spring 4.3之后) */ String encoding() default ""; /** * 指定资源工厂,如果不指定,就使用默认的资源工厂,即 DefaultPropertySourceFactory * spring 4.3 之后,4.3 之前需要先手动注入资源文件解析器(PropertySourcesPlaceholderConfigurer) */ Class factory() default PropertySourceFactory.class; } ``` 示例: @PropertySource 和 Environment 配合使用获取外部属性配置 ```java @Configuration // 从 properties 文件中获取属性配置 @PropertySource(value = "classpath:db.properties") // 从 xml 文件中获取属性配置 // @PropertySource(value = "classpath:db.xml") public class PropertyValueConfig01 { @Resource Environment environment; @PostConstruct public void init() { System.out.println(environment.getProperty("DB_DRIVER_CLASS")); System.out.println(environment.getProperty("DB_URL")); System.out.println(environment.getProperty("DB_USERNAME")); System.out.println(environment.getProperty("DB_PASSWORD")); } } ``` #### 6.2、@Value @Value 用于属性赋值 - 基本数据赋值 - 可使用 SpEL 表达式进行赋值(#{1+1}) - 可通过 ${} 获取运行环境中的变量值(配置文件) ```java // 可用于属性、方法、参数 @Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Value { /** * 属性表达式 */ String value(); } ``` 示例: ```properties DB_DRIVER_CLASS=com.mysql.jdbc.Driver DB_URL=jdbc:mysql://localhost:3306/test DB_USERNAME=root DB_PASSWORD=123456 ``` ```java @Data @Component // 指定配置文件 @PropertySource(value = "classpath:db.properties") public class DbInfo { /** * SpEL 表达式赋值 */ @Value("#{10 * 2}") private Integer maxActive; /** * 基本数据赋值 */ @Value("5") private Integer minIdle; /** * 配置文件赋值,并设定空配置时的默认值 */ @Value("${MAX_IDLE:20}") private Integer maxIdle; /** * 配置文件赋值 */ @Value("${DB_DRIVER_CLASS}") private String dbDriverClass; @Value("${DB_URL}") private String dbUrl; @Value("${DB_USERNAME)") private String dbUsername; @Value("${DB_PASSWORD)") private String dbPassword; } ``` 结果: ``` DbInfo(maxActive=20, minIdle=5, maxIdle=20, dbDriverClass=com.mysql.jdbc.Driver, dbUrl=jdbc:mysql://localhost:3306/test, dbUsername=${DB_USERNAME), dbPassword=${DB_PASSWORD)) ``` ### 7、自动装配 示例配置 AutowiredConfig01: ```java // Bean 配置 @Configuration // 配置只扫描 AutowiredConfig01.class @ComponentScan(basePackages = "com.onde.annotation.config.autowired", includeFilters = { @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {AutowiredConfig01.class}) }, useDefaultFilters = false) public class AutowiredConfig01 { @Bean public User autowired_user01() { User user = new User(); user.setId(60001); user.setName("autowired_user01"); user.setAge(20); return user; } @Bean public User autowired_user02() { User user = new User(); user.setId(60001); user.setName("autowired_user02"); user.setAge(20); return user; } } ``` 示例配置 AutowiredConfig02: ```java // Bean 配置 @Configuration // 配置只扫描 AutowiredConfig02.class @ComponentScan(basePackages = "com.onde.annotation.config.autowired", includeFilters = { @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {AutowiredConfig02.class}) }, useDefaultFilters = false) public class AutowiredConfig02 { // 配置 autowired_user01 为首选装配对象 @Bean @Primary public User autowired_user01() { User user = new User(); user.setId(60001); user.setName("autowired_user01"); user.setAge(20); return user; } @Bean public User autowired_user02() { User user = new User(); user.setId(60001); user.setName("autowired_user02"); user.setAge(20); return user; } } ``` #### 7.1、@Autowired @Autowired 可用于组件装配: - 默认按照类型(class)去容器中找对应的组件 - 如果存在多个相同类型的组件,则需要将属性的名称作为组件的 ID 再去容器中查找 - 可通过 @Qualifier("id") 指定需要装配组件的 ID,而不是使用类型装配 - 或者通过 @Primary 注解指定自动装配时的首选 Bean - @Autowired 默认必须注入属性,找不到对应组件将抛异常,也可通过属性设置非必须 @Autowired(required=false) - @Autowired 可作用于构造器、方法、方法参数、属性 - @Autowired 标注在构造器上,则参数列表会进行自动装配,如果组件只有一个有参构造器,这个有参构造器的 @Autowired 可以省略 - @Autowired 标注在方法参数上,结合 @Bean 使用,用于自动装配容器中的对象,@Autowired 可以省略 - @Autowired 标注在方法上,该对象在注册时对应方法的参数列表会进行自动装配 - @Autowired 标注在属性上 常规用法 ```java // 可作用于构造器、方法、方法参数、属性 @Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Autowired { /** * Declares whether the annotated dependency is required. *

Defaults to {@code true}. */ boolean required() default true; } ``` 示例: ```java // 测试类 1 @SpringBootTest(classes = Application03.class) @RunWith(SpringRunner.class) // 引用配置 AutowiredConfig01 @Import(AutowiredConfig01.class) public class AutowiredTest01 { /** * 容器中存在类型为 User.class 的组件有两个:autowired_user01、autowired_user01 * 默认按类型查找,存在多个相同类型的组件,查找到多个 * 抛异常:No qualifying bean of type 'com.onde.annotation.bean.User' available: expected single matching bean but found 2: autowired_user01,autowired_user02 */ // @Autowired private User user01; /** * 设定组件装配为非必须,但容器中确实存在类型为 User.class 的组件:autowired_user01、autowired_user01 * 此时依旧存在异常:No qualifying bean of type 'com.onde.annotation.bean.User' available: expected single matching bean but found 2: autowired_user01,autowired_user02 */ // @Autowired(required = false) private User user02; /** * 若通过 @Qualifier("id") 指定需要装配组件的 ID,而不是使用类型装配,组件正常装配,注入组件:autowired_user01 */ @Autowired @Qualifier("autowired_user01") private User user03; /** * 注入一个不在扫描规则内的组件(容器中不存在该组件) * 此时将产生异常:No qualifying bean of type 'com.onde.annotation.service.AnnotationService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)} */ // @Autowired private AnnotationService annotationService01; /** * 注入一个不在扫描规则内的组件(容器中不存在该组件),且设置该组件为非必须 * 此时无异常,但装配的是 null * */ @Autowired(required = false) private AnnotationService annotationService02; @Test public void test01() { // 异常 System.out.println(user01); } @Test public void test02() { // 异常 System.out.println(user02); } @Test public void test03() { // 输出:User{id=60001, name='autowired_user01', age=20} System.out.println(user03); } @Test public void test04() { // 异常 System.out.println(annotationService01); } @Test public void test05() { // 无异常,但装配的是 null System.out.println(annotationService02); } } ``` ```java // 测试类 2 @SpringBootTest(classes = Application03.class) @RunWith(SpringRunner.class) // 引用配置 AutowiredConfig02 @Import(AutowiredConfig02.class) public class AutowiredTest02 { /** * 容器中存在类型为 User.class 的组件有两个:autowired_user01、autowired_user01 * autowired_user01 组件中存在 @Primary 注解,为首选装配对象 * 此时无异常,装配的组件为:autowired_user01 */ @Autowired private User user; @Test public void test01() { // 输出:User{id=60001, name='autowired_user01', age=20} System.out.println(user); } } ``` 示例配置AutowiredConfig03: ```java // Bean 配置 @Configuration // 配置只扫描 AutowiredConfig03.class @ComponentScan(basePackages = "com.onde.annotation.config.autowired", includeFilters = { @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {AutowiredConfig03.class}) }, useDefaultFilters = false) public class AutowiredConfig03 { @Bean public User autowired_user03() { User user = new User(); user.setId(60003); user.setName("autowired_user03"); user.setAge(20); return user; } @Bean public User autowired_user04() { User user = new User(); user.setId(60004); user.setName("autowired_user04"); user.setAge(20); return user; } /** * 注册 LoginInfo 对象 * @Autowired 标注在方法参数上,结合 @Bean 使用,用于自动装配容器中的对象,此处自动装配 autowired_user03 对象 */ @Bean public LoginInfo loginInfo(@Qualifier("autowired_user03") @Autowired User user) { LoginInfo loginInfo = new LoginInfo(); loginInfo.setUser(user); return loginInfo; } } ``` ```java @Service public class UserService { private User user01; private User user02; public UserService(@Qualifier("autowired_user03") User user01, @Qualifier("autowired_user04") User user02) { this.user01 = user01; this.user02 = user02; } /** * @Autowired 标注在构造器上,则参数列表会进行自动装配 * 如果组件只有一个有参构造器,这个有参构造器的 @Autowired 可以省略,此处有参构造器有两个所以不可省略 */ @Autowired public UserService(@Qualifier("autowired_user03") User user01) { this.user01 = user01; this.user02 = null; } public User getUser01() { return user01; } public User getUser02() { return user02; } /** * user02 通过构造器赋空值 * @Autowired 标注在方法上,参数列表的 User 对象将自动装配为 autowired_user04 */ @Qualifier("autowired_user04") @Autowired public void setUser02(User user02) { System.out.println("user02: " + this.user02); this.user02 = user02; } } ``` ```java // 测试类 3 @SpringBootTest(classes = Application03.class) @RunWith(SpringRunner.class) // 引用配置 AutowiredConfig03 和 UserService @Import({AutowiredConfig03.class, UserService.class}) public class AutowiredTest06 { /** * @Autowired 标注在属性上 */ @Autowired private UserService userService; /** * @Autowired 标注在属性上 */ @Autowired private LoginInfo loginInfo; @Test public void test01() { // 输出: // User{id=60003, name='autowired_user03', age=20} // User{id=60004, name='autowired_user04', age=20} // LoginInfo(user=User{id=60003, name='autowired_user03', age=20}) System.out.println(userService.getUser01()); System.out.println(userService.getUser02()); System.out.println(loginInfo); } } ``` #### 7.2、@Resource java JSR250 规范,@Resource 注解同样也可用于组件装配: - @Resource 存在两个属性 name 和 type,可指定按类型装配(Bean Class)或者按名称装配(Bean ID) - 不指定 name 或者 type 默认按名称装配(Bean ID) - 不支持 @Primary 注解 - 组件必须在容器中存在,否则抛异常 示例: ```java // 测试类 1 @SpringBootTest(classes = Application03.class) @RunWith(SpringRunner.class) // 引用配置 AutowiredConfig01,注入 AnnotationService @Import({AutowiredConfig01.class, AnnotationService.class}) public class AutowiredTest03 { /** * 容器中存在类型为 User.class 的组件有两个:autowired_user01、autowired_user01 * 默认按 ID 查找,通过 user01 查找不到 * 抛异常:No qualifying bean of type 'com.onde.annotation.bean.User' available: expected single matching bean but found 2: autowired_user01,autowired_user02 */ // @Resource private User user01; /** * 容器中存在类型为 User.class 的组件有两个:autowired_user01、autowired_user01 * 指定 ID 查找,通过 autowired_user01 查找并注入,正常装配 */ @Resource(name = "autowired_user01") private User user02; /** * 容器中存在类型为 User.class 的组件有两个:autowired_user01、autowired_user01 * 默认按 ID 查找,通过 autowired_user02 查找并注入,正常装配 */ @Resource private User autowired_user02; /** * 容器中存在类型为 User.class 的组件有两个:autowired_user01、autowired_user01 * 指定类型查找,查找到多个组件 * 抛异常:No qualifying bean of type 'com.onde.annotation.bean.User' available: expected single matching bean but found 2: autowired_user01,autowired_user02 */ // @Resource(type = User.class) private User user04; /** * 容器中存在类型为 AnnotationService.class 的组件只有一个:com.onde.annotation.service.AnnotationService * 未指定 ID 或者 类型,默认先按 ID 查找,再按类型查找 * 指定 ID 查找,通过 annotationService_01 查找不到 * 抛异常:No bean named 'annotationService_01' available */ // @Resource(name = "annotationService_01") private AnnotationService annotationService_01; @Test public void test01() { // 异常 System.out.println(user01); } @Test public void test02() { // 输出:User{id=60001, name='autowired_user01', age=20} System.out.println(user02); } @Test public void test03() { // 输出:User{id=60001, name='autowired_user02', age=20} System.out.println(autowired_user02); } @Test public void test04() { // 异常 System.out.println(user04); } @Test public void test05() { // 异常 System.out.println(annotationService_01); } } ``` #### 7.3、@Inject java JSR330 规范,@Inject 与 @Autowired 类似,可以完全代替 @Autowired,但没有 required 属性要求 Bean 必须存在 - 默认按照类型(class)去容器中找对应的组件 - 如果存在多个相同类型的组件,则需要将属性的名称作为组件的 ID 再去容器中查找 - 可通过 @Qualifier("id") 指定需要装配组件的 ID,而不是使用类型装配 - 或者通过 @Primary 注解指定自动装配时的首选 Bean - 需引入 javax.inject 依赖 ```xml javax.inject javax.inject 1 ``` 示例: ```java // 测试类 1 @SpringBootTest(classes = Application03.class) @RunWith(SpringRunner.class) // 引用配置 AutowiredConfig01 @Import(AutowiredConfig01.class) public class AutowiredTest04 { /** * 容器中存在类型为 User.class 的组件有两个:autowired_user01、autowired_user01 * 默认按类型查找,存在多个相同类型的组件,查找到多个 * 抛异常:No qualifying bean of type 'com.onde.annotation.bean.User' available: expected single matching bean but found 2: autowired_user01,autowired_user02 */ // @Inject private User user01; /** * 若通过 @Qualifier("id") 指定需要装配组件的 ID,而不是使用类型装配,组件正常装配,注入组件:autowired_user01 */ @Inject @Qualifier("autowired_user01") private User user02; @Test public void test01() { // 异常 System.out.println(user01); } @Test public void test02() { // 输出:User{id=60001, name='autowired_user01', age=20} System.out.println(user02); } } ``` ```java // 测试类 2 @SpringBootTest(classes = Application03.class) @RunWith(SpringRunner.class) // 引用配置 AutowiredConfig02 @Import(AutowiredConfig02.class) public class AutowiredTest05 { /** * 容器中存在类型为 User.class 的组件有两个:autowired_user01、autowired_user01 * autowired_user01 组件中存在 @Primary 注解,为首选装配对象 * 此时无异常,装配的组件为:autowired_user01 */ @Inject private User user; @Test public void test01() { // 输出:User{id=60001, name='autowired_user01', age=20} System.out.println(user); } } ``` #### 7.4、Aware 接口 Spring 检测到 Bean 实现了 Aware 接口,则会为其注入相应的依赖,通过让 Bean 实现 Aware 接口能在 Bean 中获得相应的 Spring 容器资源,Aware 接口是回调,监听器和观察者设计模式的混合,它表示 Bean 有资格通过回调方式被 Spring 容器通知 - BeanNameAware:注入当前 Bean 对应 beanName,Spring 将 Bean 的 ID 传递给 setBeanName() 方法,通过 Bean 的引用获得Bean 的 ID - BeanClassLoaderAware:注入加载当前 Bean 的 ClassLoader - BeanFactoryAware:注入当前 BeanFactory 容器的引用 - EnvironmentAware:注入 Enviroment,用于获取配置属性 - EmbeddedValueResolverAware:注入 EmbeddedValueResolver(Spring EL解析器),一般用于参数解析 - ApplicationContextAware:注入 ApplicationContext,获取应用上下文对象 - ServletContextAware:MVC应用中注入 ServletContext,可获取 context 中的参数 - ServletConfigAware:MVC应用中注入 ServletConfig,可获取 config 中的参数 示例: ```java @Configuration @ComponentScan(basePackages = "com.onde.annotation.config.autowired", includeFilters = { @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {AutowiredConfig04.class}) }, useDefaultFilters = false) public class AutowiredConfig04 implements ApplicationContextAware, BeanNameAware, ServletContextAware { public ApplicationContext applicationContext; public String beanName; public ServletContext servletContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { // 获取应该上下文对象 this.applicationContext = applicationContext; } @Override public void setBeanName(String name) { // 获取 beanName this.beanName = name; } @Override public void setServletContext(ServletContext servletContext) { // 获取 servletConfig this.servletContext = servletContext; } } ``` ### 8、profile 特性 #### 8.1、@Profile @Profile 指定组件在哪个环境下注册到容器中 - 未使用 @Profile 标注的 Bean,任何环境下都能注册这个组件 - @Profile 可在配置类或方法上指定 Spring 程序中激活 profile 特性 - 通过 setActiveProfiles 设置 - 通过启动参数设置:-Dspring.profiles.active=dev (也可在程序配置文件 yml 或 properties 中配置) - spring.profiles.active 默认值为 default - spring.profiles.active 也可通过 spring.profiles.default 参数修改默认配置值 示例: ```java // 配置类 @Configuration public class ProfileConfig01 { /** * Duck 父类为 Animal */ @Bean @Profile("dev") public Duck duck() { return new Duck(); } /** * Mouse 父类为 Animal */ @Bean @Profile("prod") public Mouse mouse() { return new Mouse(); } /** * spring.profiles.active 默认为 default,可通过 spring.profiles.default 参数修改 */ @Bean @Profile("default") public User default_profile_user() { User user = new User(); user.setId(70001); user.setName("default_profile_user"); user.setAge(20); return user; } @Bean @Profile("dev") public User dev_profile_user() { User user = new User(); user.setId(70002); user.setName("dev_profile_user"); user.setAge(20); return user; } @Bean public User profile_user() { User user = new User(); user.setId(70003); user.setName("profile_user"); user.setAge(20); return user; } } ``` ```java // 测试类 public class ProfileTest01 { @Test public void test01() { // 通过 setActiveProfiles 设置 profile AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(); applicationContext.getEnvironment().setActiveProfiles("dev"); // applicationContext.getEnvironment().setActiveProfiles("prod"); applicationContext.register(ProfileConfig01.class); applicationContext.refresh(); // Animal 子类为 Duck、Mouse // setActiveProfiles("dev") 输出:Animal{name='Donald Duck', type='Duck'} // setActiveProfiles("prod") 输出:Animal{name='Jerry Mouse', type='Mouse'} Animal animal = applicationContext.getBean(Animal.class); System.out.println(animal); applicationContext.close(); } @Test public void test02() { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ProfileConfig01.class); // 此种方式需配置启动参数 // Animal 子类为 Duck、Mouse // -Dspring.profiles.active=dev 输出:Animal{name='Donald Duck', type='Duck'} // -Dspring.profiles.active=prod 输出:Animal{name='Jerry Mouse', type='Mouse'} Animal animal = applicationContext.getBean(Animal.class); System.out.println(animal); applicationContext.close(); } @Test public void test03() { // 不指定 spring.profiles.active 时默认为 default,此时输出为 // default_profile_user is found: true (@Profile("default")) // dev_profile_user is found: false (@Profile("dev")) // profile_user is found: true (未使用 @Profile 标注的 Bean,任何环境下都能注册这个组件) // 增加启动参数:-Dspring.profiles.default = dev,修改默认的 spring.profiles.active 为 dev 时,此时输出为 // default_profile_user is found: false (@Profile("default")) // dev_profile_user is found: true (@Profile("dev")) // profile_user is found: true (未使用 @Profile 标注的 Bean,任何环境下都能注册这个组件) AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(ProfileConfig01.class); System.out.println("default_profile_user is found: " + applicationContext.containsBean("default_profile_user")); System.out.println("dev_profile_user is found: " + applicationContext.containsBean("dev_profile_user")); System.out.println("profile_user is found: " + applicationContext.containsBean("profile_user")); applicationContext.close(); } } ``` #### 8.2、Spring Boot profile 特性 除了 @Profile 注解外,Spring Boot 在 yml 或 properties 配置文件中也可根据不同 profile 定义不同配置 yml 配置示例: ```yaml spring: application: name: spring-annotation-example # 配置激活的环境 profiles: active: prod # springboot 2.4 之前,当然 2.4 之后也支持,但此种方式已过时 --- # dev 环境下 demo.profile.value1 取值为 dev spring: profiles: dev demo: profile: value1: dev --- # prod 环境下 demo.profile.value1 取值为 prod spring: profiles: prod demo: profile: value1: prod # springboot 2.4 之后,为了提升对 Kubernetes 的原生支持而作的修改 --- # dev 环境下 demo.profile.value2 取值为 dev spring: config: activate: on-profile: dev demo: profile: value2: dev --- # prod 环境下 demo.profile.value2 取值为 prod spring: config: activate: on-profile: prod demo: profile: value2: prod ``` properties 配置示例: properties 需定义多个配置文件,根据文件的命名格式来区分不同的 profile 配置:application-${profile}.properties ```properties # application-dev.properties demo.profile.value3=dev ``` ```properties # application-prod.properties demo.profile.value3=prod ``` 测试示例: ```java @SpringBootTest(classes = Application03.class) @RunWith(SpringRunner.class) // 引入 ProfileConfig01 配置 @Import(ProfileConfig01.class) public class ProfileTest02 { /** * yml 配置测试 */ @Value("${demo.profile.value1}") private String value1; /** * yml 配置测试 */ @Value("${demo.profile.value2}") private String value2; /** * properties 配置测试 */ @Value("${demo.profile.value3}") private String value3; /** * @Profile 测试 */ @Autowired private Animal animal; @Test public void test01() { // spring.profiles.active = dev 输出: // value1: dev // value2: dev // value3: dev // Animal{name='Donald Duck', type='Duck'} // spring.profiles.active = prod 输出: // value1: prod // value2: prod // value3: prod // Animal{name='Jerry Mouse', type='Mouse'} System.out.println("value1: " + value1); System.out.println("value2: " + value2); System.out.println("value3: " + value3); System.out.println(animal); } } ```