本文是尚硅谷雷丰阳老师《尚硅谷Spring注解驱动教程》课程的笔记
系列文章目录
1.Spring注解驱动-组件注册部分
2.Spring注解驱动-属性赋值和自动装配部分
文章目录
Spring注解驱动开发
属性赋值-@Value
使用
@Value
赋值
1.基本数值
2.可以写SpEL; #{}
3.可以写${};取出配置文件中的值(在运行环境变量里面的值),取出配置文件【properties】中的值(在运行环境变量里面的值)
新建
com/atguigu/config/MainConfigOfPropertyValues.java
@Configuration
public class MainConfigOfPropertyValues {
@Bean
public Person person(){
return new Person();
}
}
新建测试类
com/atguigu/test/IOCTest_PropertyValue.java
public class IOCTest_PropertyValue {
// 1.创建ioc容器
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfigOfPropertyValues.class);
@Test
@Test
public void test01() {
System.out.println("容器创建完成...");
printBeans(context);
System.out.println("===========");
Person person = (Person) context.getBean("person");
System.out.println(person);
// 关闭容器
context.close();
}
// 抽取公共方法输出容器里面所有组件
private void printBeans(AnnotationConfigApplicationContext context){
String[] beanDefinitionNames = context.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
}
}
结果
mainConfigOfPropertyValues
person
===========
Person{name='null', age=null}
1月 13, 2023 4:07:46 下午 org.springframework.context.annotation.AnnotationConfigApplicationContext doClose
信息: Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@7bc1a03d: startup date [Fri Jan 13 16:07:45 CST 2023]; root of context hierarchy
Process finished with exit code 0
我们发现,该person类并没有配置属性,如果要配置属性的话
传统xml配置文件
<bean id = "person" class="com.atguigu.bean.Person" scope="prototype">
<property name="age" value="18"></property>
<property name="name" value="zhansan"></property>
</bean>
注解方式
public class Person {
@Value("张三")
private String name;
@Value( "#{20-2}")
private Integer age;
}
结果
可发现对应的值都赋值上去了
mainConfigOfPropertyValues
person
===========
Person{name='张三', age=18}
属性赋值-@PropertySource加载外部配置文件
修改
Person
文件
public class Person {
@Value("张三")
private String name;
@Value( "#{20-2}")
private Integer age;
private String nickName;
...
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", nickName='" + nickName + '\'' +
'}';
}
...
}
新建配置文件
src/main/resources/person.properties
person.nickName=小张三
传统xml做法
<context:property-placeholder location="classpath:person.properties" />
<context:component-scan base-package="com.atguigu" use-default-filters="false"></context:component-scan>
<bean id = "person" class="com.atguigu.bean.Person" scope="prototype">
<property name="age" value="18"></property>
<property name="name" value="zhansan"></property>
<property name="nickName" value="${person.nickName}"></property>
</bean>
注解方法
修改
MainConfigOfPropertyValues
(com/atguigu/config/MainConfigOfPropertyValues.java
)
使用@PropertySource
读取外部配置文件中的K/V保存到运行的环境变量中;加载完外部的配置文件以后使用${}取出配置文件的值
@PropertySource(value = {"classpath:/person.properties"})
@Configuration
public class MainConfigOfPropertyValues {
@Bean
public Person person(){
return new Person();
}
}
查看@PropertySource()
注解源码
指定一个
String
数组,用于指定配置文件的路径classpath
由类路径开始或文件路径开始
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(PropertySources.class)
public @interface PropertySource {
name() default "";
String[] value();
boolean ignoreResourceNotFound() default false;
String encoding() default "";
Class<? extends PropertySourceFactory> factory() default PropertySourceFactory.class;
}
修改
person
@Value("${person.nickName}")
private String nickName;
结果发现乱码,修改
@PropertySource
@PropertySource(value = {"classpath:/person.properties"}, encoding = "utf-8")
结果
org.springframework.context.event.internalEventListenerFactory
mainConfigOfPropertyValues
person
===========
Person{name='张三', age=18, nickName='小张三'}
小测试
查看上下文中的配置
ConfigurableEnvironment environment = context.getEnvironment();
String property = environment.getProperty("person.nickName");
System.out.println(property);
结果
mainConfigOfPropertyValues
person
===========
Person{name='张三', age=18, nickName='小张三'}
小张三
自动装配-@Autowired&@Qualifier&@Primary
自动装配:Spring依赖注入(DI),完成对IOC容器中各个组件的依赖关系赋值;
1)@Autowired
,自动注入
- 默认优先按照类型去容器中找对应的组件:
applicationContext.gerBean(BookDao.class)
- 如果找到多个相同类型的组件,再将属性的名称作为组件的id去容器中查找
@Qualifier("bookDao")
:使用@Qualifier
指定需要装配的组件的id,(优先级更高)而不是使用属性名- 自动装配默认一定要将属性赋值好,没有找到就会报错;
- 可以使用
@Autowired(required = false)
,实现工厂中有就装配,没有就不进行装配@Primary
:让Spring进行自动装配的时候,默认使用首选的bean,也可以继续使用@Qualifier("bookDao")
指定需要装配的bean的名字// 名字默认是首字母小写 @Service public class BookService { @Autowired private BookDao bookDao; }
完善
BookService
和BookController
@Service
public class BookService {
@Autowired
private BookDao bookDao;
public void print() {
System.out.println(bookDao);
}
}
@Controller
public class BookController {
@Autowired
private BookService bookService;
}
创建
MainConfigOfAutowired
(com/atguigu/config/MainConfigOfAutowired.java
)
@Configuration
@ComponentScan({"com.atguigu.service", "com.atguigu.dao", "com.atguigu.controller"})
public class MainConfigOfAutowired {
}
创建
IOCTest_Autowired
(com/atguigu/test/IOCTest_Autowired.java
)
@Test
public void test01() {
// 1.创建ioc容器
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfigOfAutowired.class);
System.out.println("容器创建完成...");
// 关闭容器
System.out.println(context.getBean(BookService.class));
context.close();
}
结果,可以发现
BookService
容器创建完成...
com.atguigu.service.BookService@5456afaa
测试一下是否是容器中的DAO
public void test01() {
// 1.创建ioc容器
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfigOfAutowired.class);
System.out.println("容器创建完成...");
// 关闭容器
System.out.println(context.getBean(BookService.class));
System.out.println(context.getBean(BookDao.class));
context.close();
}
结果可以看出,‘BookService’中注入的
BookDao
确实是容器中的Dao
容器创建完成...
BookService{bookDao=com.atguigu.dao.BookDao@5456afaa}
com.atguigu.dao.BookDao@5456afaa
通过@Bean向容器注入另一个BookDao
@Configuration
@ComponentScan({"com.atguigu.service", "com.atguigu.dao", "com.atguigu.controller"})
public class MainConfigOfAutowired {
@Bean("bookDao2")
public BookDao bookDao(){
BookDao bookDao = new BookDao();
bookDao.setLable("2");
return bookDao;
}
}
改造BookDao
// 名字默认是类首名首字母小写
@Repository
public class BookDao {
private String lable = "1";
public String getLable() {
return lable;
}
public void setLable(String lable) {
this.lable = lable;
}
@Override
public String toString() {
return "BookDao{" +
"lable='" + lable + '\'' +
'}';
}
}
结果报错
容器创建完成...
BookService{bookDao=BookDao{lable='1'}}
...
at com.atguigu.test.IOCTest_Autowired.test01(IOCTest_Autowired.java:23)
解决方法一:修改属性名称,改用一样的bookDao2
修改BookService
@Service
public class BookService {
@Autowired
private BookDao bookDao2;
public void print() {
System.out.println(bookDao2);
}
@Override
public String toString() {
return "BookService{" +
"bookDao=" + bookDao2 +
'}';
}
}
结果发现装载的是BookDao2
容器创建完成...
BookService{bookDao=BookDao{lable='2'}}
解决方法二:使用@Qualifier()
修改BookService
@Qualifier("bookDao")
@Autowired
private BookDao bookDao2;
public void print() {
System.out.println(bookDao2);
}
结果
容器创建完成...
BookService{bookDao=BookDao{lable='1'}}
需要分析,工厂中有就装配,没有就不进行装配
查看
@Autowired
注解
public @interface Autowired {
/**
* Declares whether the annotated dependency is required.
* <p>Defaults to {@code true}.
*/
boolean required() default true;
}
通过设置required即可满足该需求
@Qualifier("bookDao")
@Autowired(required = false)
private BookDao bookDao2;
结果,没有的情况下该属性为null
容器创建完成...
BookService{bookDao=null}
使用@Primary
作用是工厂中有多个bean,但是按设置的首选进行装配
- 步骤一:设置@Primary
@Primary
@Bean("bookDao2")
public BookDao bookDao(){
BookDao bookDao = new BookDao();
bookDao.setLable("2");
return bookDao;
}
- 步骤二:去除
@Qualifier
@Service
public class BookService {
// @Qualifier("bookDao")
@Autowired(required = false)
private BookDao bookDao;
public void print() {
System.out.println(bookDao);
}
@Override
public String toString() {
return "BookService{" +
"bookDao=" + bookDao +
'}';
}
}
- 测试结果
容器创建完成...
BookService{bookDao=BookDao{lable='2'}}
BookDao{lable='2'}
注意,
@Qualifier
优先级比@Primary
高,在没有指定@Qualifier
时,用@Primary
指定的,用了@Qualifier
则用@Qualifier
指定的
自动装配-@Resource&@Inject
Spring支持使用
@Resource
(JSR250)和@Inject
(JSR330)【java规范的注解】
@Resource
:可以和@Autowired
一样实现自动装配功能,默认是按照组件名称进行装配的;没有能支持@Primary
功能以及没有支持@Autowired(required = false)
;@Inject
:需要导入javax.inject
的包,和Autowired
的功能一样,没有required = false的功能;
@Autowired
:Spring定义的;而@Resource
、@Inject
都是Java规范
AutowiredAnnotationBeanPostProcessor
:解析完成自动装配功能;
@Resource
- 改造
BookService
@Resource
private BookDao bookDao;
- 结果
容器创建完成...
BookService{bookDao=BookDao{lable='1'}}
可以看出装配的是label1的bean,这是因为默认是按照组件名称进行装配的
- 使用name指定装配的bean
@Resource(name = "bookDao2")
private BookDao bookDao;
- 结果
容器创建完成...
BookService{bookDao=BookDao{lable='2'}}
@Inject
使用这个注解需要先在maven中导入相应的依赖
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
- 使用@Inject
@Inject
private BookDao bookDao;
- 结果
首先,该注解和
@Resource
的功能一样;同时还具有@Primary
特性,默认装配;自动装配的特性也是可以调用的。
1月 20, 2023 3:52:35 下午 org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor <init>
信息: JSR-330 'javax.inject.Inject' annotation found and supported for autowiring
容器创建完成...
BookService{bookDao=BookDao{lable='2'}}
对比@Autowired
和 @Inject
,可以看出@Autowired
有一个属性而@Inject
没有
public @interface Autowired {
boolean required() default true;
}
public @interface Inject {
}
分析:为什么这些注解拥有自动注入的功能
我们可以看到上面的结果信息,看到AutowiredAnnotationBeanPostProcessor
,解析完成自动装配功能
自动装配-方法、构造器位置的自动装配
回顾:
@Component
默认加在ioc容器中的组件,容器启动会调用无参构造器创建对象,再进行初始化赋值操作。这个无参构造器是java默认给出的,但是当指定了有参构造器后,java就不会给出默认的无参构造器。
@Autowired
:构造器,参数,方法,属性;都是从容器中获取参数组件的值
- 【标注在方法位置】:
@Bean
+方法参数,参数从容器中获取。因此默认不写@Autowired
效果是一样的,都能自动装配- 【标在构造器上】:在单例模式的情况下,如果组件只有一个有参构造器,这个有参构造器参数的Autowired可以省略,参数位置的组件还是可以自动从容器中获取
- 【放在参数位置】
- 新建Boss类
src/main/java/com/atguigu/bean/Boss.java
@Component
public class Boss {
private Car car;
@Override
public String toString() {
return "Boss{" +
"car=" + car +
'}';
}
public Car getCar() {
return car;
}
public void setCar(Car car) {
this.car = car;
}
}
- 修改MainConfigOfAutowired,对其扫描路径加上对应的类
@Configuration
@ComponentScan({"com.atguigu.service", "com.atguigu.dao", "com.atguigu.controller", "com.atguigu.bean"})
public class MainConfigOfAutowired {
@Primary
@Bean("bookDao2")
public BookDao bookDao(){
BookDao bookDao = new BookDao();
bookDao.setLable("2");
return bookDao;
}
}
- 对
Car
加上@Component
@Component
public class Car {
public Car() {
System.out.println("car constructor...");
}
public void init() {
System.out.println("car...init...");
}
public void destroy() { System.out.println("car...destroy..."); }
}
@Autowired
标在方法上
标注在方法上,Spring容器创建当前对象,就会调用方法,完成赋值。方法使用的参数,自定义类型的值从ioc容器中获取
@Autowired
public void setCar(Car car) {
this.car = car;
}
测试查看容器中的Boss的Car,可看出是同一个car
容器创建完成...
com.atguigu.bean.Car@5456afaa
Boss{car=com.atguigu.bean.Car@5456afaa}
@Autowired
标在构造器上、参数位置上
- 修改
Boss
添加上有参构造器
方法一:
@Autowired
public Boss(Car car) {
this.car = car;
System.out.println("Boss...有参构造器");
}
方法二:
public Boss(@Autowired Car car) {
this.car = car;
System.out.println("Boss...有参构造器");
}
方法三:在方法二的基础上省略@Autowired
public Boss( Car car) {
this.car = car;
System.out.println("Boss...有参构造器");
}
构造器要用的组件,都是从容器中获取
方法三例子
- 修改
Color
类
public class Color {
private Car car;
public Car getCar() {
return car;
}
public void setCar(Car car) {
this.car = car;
}
}
- 修改
MainConfigOfAutowired
public class MainConfigOfAutowired {
@Primary
@Bean("bookDao2")
public BookDao bookDao(){
BookDao bookDao = new BookDao();
bookDao.setLable("2");
return bookDao;
}
@Bean
public Color color(@Autowired Car car){
Color color = new Color();
color.setCar(car);
return color;
}
}
注意,这里的
public Color color(Car car)
可以加@Autowired
亦可不加
@Bean
标注的方法创建对象的时候,方法参数的值从容器中获取
- 结果,可看出确实装载进去了
car constructor...
postProcessBeforeInitialization...car=>com.atguigu.bean.Car@4c012563
postProcessAfterInitialization...car=>com.atguigu.bean.Car@4c012563
Boss...有参构造器
postProcessBeforeInitialization...boss=>Boss{car=com.atguigu.bean.Car@4c012563}
postProcessAfterInitialization...boss=>Boss{car=com.atguigu.bean.Car@4c012563}
Boss{car=com.atguigu.bean.Car@4c012563}
自动装配-Aware注入Spring底层组件&原理
自定义组件想要使用Spring容器底层的一些组件(
ApplicationContext
、BeanFactory
,…)
自定义组件实现xxxAware
即可;在创建对象的时候,会调用接口规定的方法注入相关组件;Aware
把Spring底层一些组件注入到自定义的Bean中;
xxxAware
:功能使用xxxProcessor
;比如ApplicationContextAware
=====》ApplicationContextAwareProcessor
实际上这个功能是由xxxAwareProcessor
实现的, 而它则是实现BeanProcessor
接口,后置处理器机制
例子:
- 修改
red
@Component
public class Red implements ApplicationContextAware, BeanNameAware, EmbeddedValueResolverAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
System.out.println("传入的ioc:" + applicationContext);
this.applicationContext = applicationContext;
}
@Override
public void setBeanName(String name) {
System.out.println("当前bean的名字:" + name);
}
// 值解析器
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
String s = resolver.resolveStringValue("你好${os.name} 我是#{20*18}");
System.out.println("解析的字符串是:" + s);
}
}
结果
当前bean的名字:red
解析的字符串是:你好Windows 10 我是360
传入的ioc:org.springframework.context.annotation.AnnotationConfigApplicationContext@7ff2a664: startup date [Fri Jan 20 16:54:59 CST 2023]; root of context hierarchy
postProcessBeforeInitialization...red=>com.atguigu.bean.Red@42530531
postProcessAfterInitialization...red=>com.atguigu.bean.Red@42530531
postProcessBeforeInitialization...color=>com.atguigu.bean.Color@35645047
postProcessAfterInitialization...color=>com.atguigu.bean.Color@35645047
容器创建完成...
Debug相应的源码
- 首先查看后置处理器
ApplicationContextAwareProcessor
作为后置处理器,实际上,
postProcessBeforeInitialization
的bean
是Red
,然后判断是Red
是否实现了EnvironmentAware
…ApplicationContextAware
等接口。然后检查是否能调用方法AccessController.doPrivileged
,接着调用invokeAwareInterfaces(bean)
class ApplicationContextAwareProcessor implements BeanPostProcessor {
...
@Override
public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
AccessControlContext acc = null;
if (System.getSecurityManager() != null &&
(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) {
acc = this.applicationContext.getBeanFactory().getAccessControlContext();
}
if (acc != null) {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
invokeAwareInterfaces(bean);
return null;
}
}, acc);
}
else {
invokeAwareInterfaces(bean);
}
return bean;
}
}
...
接着查看
invokeAwareInterfaces(bean)
,此时的bean
仍然是Red
对象。然后判断Red
是否是Aware
接口旗下的,一个个判断,若是实现了EnvironmentAware
接口则将ioc容器中获取对应的对象并调用set方法赋值进去。
同时这个ApplicationContextAwareProcessor
已经把众多的xxxAware
接口实现了
private void invokeAwareInterfaces(Object bean) {
if (bean instanceof Aware) {
if (bean instanceof EnvironmentAware) {
((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
}
if (bean instanceof EmbeddedValueResolverAware) {
((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
}
if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
}
if (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
}
if (bean instanceof MessageSourceAware) {
((MessageSourceAware) bean).setMessageSource(this.applicationContext);
}
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}
}
}
自动装配-@Profile环境搭建
Profile:Spring为我们提供的可以根据当前环境,动态激活和切换一系列组件的功能
开发环境、测试环境、生成环境
数据源:(/A)(/A)(/A);
@Profile
- 使用Mavan引用数据源
<!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.44</version>
</dependency>
- 新建配置类
MainConfigOfProfile
代码分析:
首先设置了扫描的配置路径@PropertySource("classpath:/dbconfig.properties")
其次设置三种不同的方法用于获取配置文件中的值
- 方法一:直接使用
@Value("${db.user}")
- 方法二:在方法参数中调用获取
public DataSource dataSourceTest(@Value("${db.password}") String pwd) throws PropertyVetoException
- 方法三:使用值解析器。先实现
EmbeddedValueResolverAware
接口。重写setEmbeddedValueResolver
方法解析对应的参数this.resolveStringValue = valueResolver.resolveStringValue("${db.driverClass}");
//src/main/java/com/atguigu/config/MainConfigOfProfile.java
@PropertySource("classpath:/dbconfig.properties")
public class MainConfigOfProfile implements EmbeddedValueResolverAware {
@Value("${db.user}")
private String user;
private StringValueResolver valueResolver;
private String resolveStringValue;
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.valueResolver = resolver;
this.resolveStringValue = valueResolver.resolveStringValue("${db.driverClass}");
}
@Bean("testDataSource")
public DataSource dataSourceTest(@Value("${db.password}") String pwd) throws PropertyVetoException {
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
comboPooledDataSource.setUser(user);
comboPooledDataSource.setPassword(pwd);
comboPooledDataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
comboPooledDataSource.setDriverClass(resolveStringValue);
return null;
}
@Bean("testDevSource")
public DataSource dataSourceDev(@Value("${db.password}") String pwd) throws PropertyVetoException {
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
comboPooledDataSource.setUser(user);
comboPooledDataSource.setPassword(pwd);
comboPooledDataSource.setJdbcUrl("jdbc:mysql://localhost:3306/yygh_hosp");
comboPooledDataSource.setDriverClass(resolveStringValue);
return null;
}
@Bean("testProdSource")
public DataSource dataSourceProd(@Value("${db.password}") String pwd) throws PropertyVetoException {
ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();
comboPooledDataSource.setUser(user);
comboPooledDataSource.setPassword(pwd);
comboPooledDataSource.setJdbcUrl("jdbc:mysql://localhost:3306/school");
comboPooledDataSource.setDriverClass(resolveStringValue);
return null;
}
}
- 创建配置文件
# src/main/resources/dbconfig.properties
db.user = root
db.password = 123456
db.driverClass = com.mysql.jdbc.Driver
- 新建测试方法
// src/test/java/com/atguigu/test/IOCTest_Profile.java
public class IOCTest_Profile {
@Test
public void test01() {
// 1.创建ioc容器
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfigOfProfile.class);
System.out.println("容器创建完成...");
String[] beanNamesForType = context.getBeanNamesForType(DataSource.class);
for (String s : beanNamesForType) {
System.out.println(s);
}
context.close();
}
}
- 结果
容器创建完成...
testDataSource
testDevSource
testProdSource
自动装配-@Profile根据环境注册bean
@Profile
:指定组件在哪个环境的情况下次才能被注册到容器中,不指定,任何环境下都能注册这个组件
- 加入环境标识的bean,只有这个环境被激活时才能注册到容器中,默认是default环境
- 写在配置类上,只有是指定的环境的时候,整个配置类里面的所有配置才能开始生效
- 没有标注环境标识的bean下,任何环境下都是加载的。
- 查看
@Profile
注解,该注解中有唯一的一个属性,这个属性用于标识环境
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(ProfileCondition.class)
public @interface Profile {
/**
* The set of profiles for which the annotated component should be registered.
*/
String[] value();
}
- 修改
MainConfigOfProfile
@Profile("test")
@Bean("testDataSource")
public DataSource dataSourceTest(@Value("${db.password}") String pwd) throws PropertyVetoException {
...
}
@Profile("dev")
@Bean("testDevSource")
public DataSource dataSourceDev(@Value("${db.password}") String pwd) throws PropertyVetoException {
...
}
@Profile("prod")
@Bean("testProdSource")
public DataSource dataSourceProd(@Value("${db.password}") String pwd) throws PropertyVetoException {
...
}
此时运行会发现容器中一个bean都没了,这是因为每个环境都加了标识,若改成
@Profile("default)
则变成默认的环境
那么怎么才能按照对应的环境进行切换装载呢?
方法一:使用命令行动态参数
在虚拟机参数位置加载 -Dspring.profiles.active=test
- 修改vm变量
-ea -Dspring.profiles.active=test
- 结果:
容器创建完成...
testDataSource
方法二:代码的方式激活某种环境
首先查看AnnotationConfigApplicationContext
的构造方法
public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
this();
register(annotatedClasses);
refresh();
}
我们使用的是有参构造器,一启动就会进行加载对应的类并注入到Ioc容器中,这是不行的。因此需要使用无参构造器
对test01进行修改
- 创建一个ApplicationContext对象
- 设置需要激活的环境
- 注册主配置类
- 启动刷新容器
public class IOCTest_Profile {
@Test
public void test01() {
// 1.创建ioc容器
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
// 1. 创建一个ApplicationContext对象
// 2. 设置需要激活的环境
context.getEnvironment().setActiveProfiles("test", "dev");
// 3. 注册主配置类
context.register(MainConfigOfProfile.class);
// 4. 启动刷新容器
context.refresh();
System.out.println("容器创建完成...");
String[] beanNamesForType = context.getBeanNamesForType(DataSource.class);
for (String s : beanNamesForType) {
System.out.println(s);
}
context.close();
}
}
结果:(需要先将方法一的-Dspring.profiles.active=test
删除)
容器创建完成...
testDataSource
testDevSource
写在配置类上
@Profile("prod")
@PropertySource("classpath:/dbconfig.properties")
public class MainConfigOfProfile implements EmbeddedValueResolverAware {
...
}
结果:
容器创建完成...
分析,此时结果是什么都没有创建,这是因为标注了
@Profile("prod")
,此时前面仅配置了test
和dev
环境,并非prod
环境,因此导致MainConfigOfProfile
整个配置类都没被加载。
没有标识@Profile
- 修改MainConfigOfProfile
@PropertySource("classpath:/dbconfig.properties")
public class MainConfigOfProfile implements EmbeddedValueResolverAware {
...
@Bean
public Yellow yellow(){
return new Yellow();
}
@Profile("test")
@Bean("testDataSource")
public DataSource dataSourceTest(@Value("${db.password}") String pwd) throws PropertyVetoException { ... }
...
}
此时添加了一个@Bean的Yellow组件,同时删除了类上的@Profile(“prod”)。此时Yellow怎么都会被创建,而下面的配置类则按情况装载配置类
测试:
public class IOCTest_Profile {
@Test
public void test01() {
// 1.创建ioc容器
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
// 1. 创建一个ApplicationContext对象
// 2. 设置需要激活的环境
context.getEnvironment().setActiveProfiles("test", "dev");
// 3. 注册主配置类
context.register(MainConfigOfProfile.class);
// 4. 启动刷新容器
context.refresh();
System.out.println("容器创建完成...");
String[] beanNamesForType = context.getBeanNamesForType(DataSource.class);
for (String s : beanNamesForType) {
System.out.println(s);
}
String[] beanNamesForType1 = context.getBeanNamesForType(Yellow.class);
for (String s : beanNamesForType1) {
System.out.println(s);
}
context.close();
}
}
结果: 如分析一致
容器创建完成...
testDataSource
testDevSource
yellow