5. 自动装配
5.1. @Autowired
&@Qualifier
&@Primary
在原来,我们就是使用@Autowired
的这个注解来进行自动装配;
现在有一个BookController 类
package com.hutu.controller;
import com.hutu.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class BookController {
@Autowired
private BookService bookService;
}
还有一个BookService:
package com.hutu.service;
import com.hutu.dao.BookDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class BookService {
@Autowired
private BookDao bookDao;
public void print() {
System.out.println(bookDao);
}
@Override
public String toString() {
return "BookService{" +
"bookDao=" + bookDao +
'}';
}
}
还有一个BookDao:为了在自动装配的是哪一个,我们给这个BookDao加上一个标识属性:lable ,如果是通过包扫描到IOC容器中,标识为1,如果是在配置类里面通过@Bean
装配的标识为2:
package com.hutu.dao;
import org.springframework.stereotype.Repository;
/**
* 在IOC容器里面默认就是类名的首字母小写
*/
@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 + '\'' +
'}';
}
}
配置类: MainConfigOfAutowired
package com.hutu.config;
import com.hutu.dao.BookDao;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* 自动装配:
* Spring利用依赖注入(DI)完成对IOC容器中各个组件的依赖关系赋值
* 1) @Autowired:自动注入
* (1)默认优先按照类型去容器中去找对应的组件:applicationContext.getBean(BookDao.class);如果找到了则进行赋值;
* public class BookService {
* @Autowired
* BookDao bookDao;
* }
*/
@Configuration
@ComponentScan({"com.hutu.controller","com.hutu.service","com.hutu.dao"})
public class MainConfigOfAutowired {
@Bean("bookDao2")
public BookDao bookDao() {
BookDao bookDao = new BookDao();
bookDao.setLable("2");
return bookDao;
}
}
再来写上一个测试类:为了区分是装配的是通过包扫描的方式还是通过在配置类里面进行装配的:
@Test
public void test02() {
ApplicationContext context = new AnnotationConfigApplicationContext(MainConfigOfAutowired.class);
BookService bookService = context.getBean(BookService.class);
System.out.println(bookService);
}
测试结果
BookService{bookDao=BookDao{lable='1'}}
自动装配: Spring利用依赖注入(DI)完成对IOC容器中各个组件的依赖关系赋值
@Autowired:自动注入
(1)默认优先按照类型去容器中去找对应的组件:applicationContext.getBean(BookDao.class);如果找到了则进行赋值;
(2)如果找到了多个相同类型的组件,再将属性的名称作为组件的id去容器中查找 applicationContext.getBean(“bookDao”)
(3)@Qualifier("bookDao"):使用 @Qualifier 指定需要装配的组件的id,而不是使用属性名;
(4)自动装配默认一定要将属性赋值好,没有就会报错,可以使用@Autowired(required = false);来设置为非必须的;
(5)可以利用@Primary:让Spring在进行自动装配的时候,默认使用首选的bean; 也可以继续使用@Qualifier("bookDao")来明确指定需要装配的bean的名字。
@Service
public class BookService {
@Autowired
private BookDao bookDao;
}
如果我们想要装配bookDao2:我们就把属性名改成bookDao2就可以了:
public class BookService {
@Autowired
BookDao bookDao2;
}
就可以这样来写
package com.hutu.service;
import com.hutu.dao.BookDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class BookService {
@Autowired
private BookDao bookDao2;
public void print() {
System.out.println(bookDao2);
}
@Override
public String toString() {
return "BookService{" +
"bookDao=" + bookDao2 +
'}';
}
}
测试结果
BookService{bookDao=BookDao{lable='2'}}
虽然,在属性名写了bookDao2,但是,我就想要装配bookDao;实际上也是可以的:我们可以使用 @Qualifier
这个注解 @Qualifier("bookDao")
:使用 @Qualifier
指定需要装配的组件的id,而不是使用属性名
package com.hutu.service;
import com.hutu.dao.BookDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Service
public class BookService {
@Qualifier("bookDao")
@Autowired
private BookDao bookDao2;
public void print() {
System.out.println(bookDao2);
}
@Override
public String toString() {
return "BookService{" +
"bookDao=" + bookDao2 +
'}';
}
}
再来测试:发现又装配了bookDao了
BookService{bookDao=BookDao{lable='1'}}
而当我们的容器里面没有一个对应的bean的时候,这个时候,就是会报一个错
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name ‘bookService’: Unsatisfied dependency expressed through field ‘bookDao2’; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type ‘com.ldc.dao.BookDao’ available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Qualifier(value=bookDao), @org.springframework.beans.factory.annotation.Autowired(required=true)}
那可不可以在使用自动装配的时候,这个bean不是必须的呢?如果容器里面没有对应的bean,我就不装配,实际上也是可以的:我们要@Autowired
注解里面添加required = false
这个属性@Autowired(required = false)
package com.hutu.service;
import com.hutu.dao.BookDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Service
public class BookService {
@Qualifier("bookDao")
@Autowired(required = false)
private BookDao bookDao2;
public void print() {
System.out.println(bookDao2);
}
@Override
public String toString() {
return "BookService{" +
"bookDao=" + bookDao2 +
'}';
}
}
再来运行测试方法,测试结果为
BookService{bookDao=BookDao{lable='1'}}
还可以利用一个注解来让Spring在自动装配的时候,首选装配哪个bean:@Primary
package com.hutu.config;
import com.hutu.dao.BookDao;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
/**
* 自动装配:
* Spring利用依赖注入(DI)完成对IOC容器中各个组件的依赖关系赋值
* 1) @Autowired:自动注入
* (1)默认优先按照类型去容器中去找对应的组件:applicationContext.getBean(BookDao.class);如果找到了则进行赋值;
* public class BookService {
* @Autowired
* BookDao bookDao;
* }
*/
@Configuration
@ComponentScan({"com.hutu.controller","com.hutu.service","com.hutu.dao"})
public class MainConfigOfAutowired {
@Primary
@Bean("bookDao2")
public BookDao bookDao() {
BookDao bookDao = new BookDao();
bookDao.setLable("2");
return bookDao;
}
}
当然明确指定的注解也是不能用了:@Qualifier("bookDao")
package com.hutu.service;
import com.hutu.dao.BookDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Service
public class BookService {
// @Qualifier("bookDao")
@Autowired(required = false)
private BookDao bookDao2;
public void print() {
System.out.println(bookDao2);
}
@Override
public String toString() {
return "BookService{" +
"bookDao=" + bookDao2 +
'}';
}
}
这个时候再来运行测试方法,测试结果如下:这个时候,Spring就首选装配了标注了@Primary
注解的bean:
BookService{bookDao=BookDao{lable='2'}}
把装配的时候的属性名也变一下
package com.hutu.service;
import com.hutu.dao.BookDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@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 +
'}';
}
}
再来看看测试结果:还是装配了标注了@Primary
注解的bean
BookService{bookDao=BookDao{lable='2'}}
如果是使用了@Qualifier("bookDao")
明确指定了的:那还是按照明确指定的bean来进行装配
package com.hutu.service;
import com.hutu.dao.BookDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@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='1'}}
5.2. @Resource
&@Inject
Spring还支持使用@Resource(JSR250)和@Inject(JSR330)
-
(1)@Resource:可以和@Autowired一样实现自动的装配,默认是按照组件的名称来进行装配,没有支持@Primary 也没有支持和@Autowired(required = false)一样的功能
-
(2)@Inject:需要导入javax.inject的包,和@Autowired的功能一样,没有支持和@Autowired(required = false)一样的功能
AutowiredAnnotationBeanPostProcessor是用来解析完成自动装配的功能的
@Autowired:是Spring定义的 @Resource 和 @Inject都是java的规范
-
@Resource
的使用
package com.hutu.service;
import com.hutu.dao.BookDao;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class BookService {
@Resource
private BookDao bookDao;
public void print() {
System.out.println(bookDao);
}
@Override
public String toString() {
return "BookService{" +
"bookDao=" + bookDao +
'}';
}
}
测试方法
@Test
public void test02() {
ApplicationContext context = new AnnotationConfigApplicationContext(MainConfigOfAutowired.class);
BookService bookService = context.getBean(BookService.class);
System.out.println(bookService);
}
BookService{bookDao=BookDao{lable='1'}}
BookService{bookDao=BookDao{lable='1'}}
package com.hutu.service;
import com.hutu.dao.BookDao;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class BookService {
@Resource(name = "bookDao2")
private BookDao bookDao;
public void print() {
System.out.println(bookDao);
}
@Override
public String toString() {
return "BookService{" +
"bookDao=" + bookDao +
'}';
}
}
BookService{bookDao=BookDao{lable='2'}}
@Inject
的使用
导入jar包:
<!-- https://mvnrepository.com/artifact/javax.inject/javax.inject -->
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
可以这样来使用
package com.hutu.service;
import com.hutu.dao.BookDao;
import org.springframework.stereotype.Service;
import javax.inject.Inject;
@Service
public class BookService {
@Inject
private BookDao bookDao;
public void print() {
System.out.println(bookDao);
}
@Override
public String toString() {
return "BookService{" +
"bookDao=" + bookDao +
'}';
}
}
进行测试:发现也是可以支持@Primary
的功能的
BookService{bookDao=BookDao{lable='2'}}
5.3. 方法、构造器位置的自动装配
从@Autowired
这个注解点进去看一下源码: 可以发现这个注解可以标注的位置有:构造器,参数,方法,属性;都是从容器中来获取参数组件的值
@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.
* <p>Defaults to {@code true}.
*/
boolean required() default true;
}
@Autowired
注解标注在方法上:
用的最多的方式就是在@Bean
注解标注的方法的参数,这个参数就是会从容器中获取,在这个方法的参数前面可以加上@Autowired
注解,也可以省略,默认是不写@Autowired
,都能自动装配
Car.java
package com.hutu.boot01.bean;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
public class Car2 implements InitializingBean, DisposableBean {
public Car2() {
System.out.println("car2 无参构造");
}
@Override
public void destroy() throws Exception {
System.out.println("car2...destroy...");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("car2...afterPropertiesSet...");
}
}
Boss.java
package com.hutu.boot01.bean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* 默认在ioc容器中的组件,容器启动会调用无参构造器创建对象,再进行初始化赋值等操作
*/
@Component
public class Boss {
private Car2 car2;
public Car2 getCar2() {
return car2;
}
/**
* 标注在方法上,Spring容器在创建当前对象的时候,就会调用当前方法完成赋值;
* 方法使用的参数,自定义类型的值从IOC容器里面进行获取
*
* @param car2
*/
@Autowired
public void setCar(Car2 car2) {
this.car2 = car2;
}
@Override
public String toString() {
return "Boss{" +
"car2=" + car2 +
'}';
}
}
package com.hutu.config;
import com.hutu.dao.BookDao;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
/**
* 自动装配:
* Spring利用依赖注入(DI)完成对IOC容器中各个组件的依赖关系赋值
* 1) @Autowired:自动注入
* (1)默认优先按照类型去容器中去找对应的组件:applicationContext.getBean(BookDao.class);如果找到了则进行赋值;
* public class BookService {
* @Autowired
* BookDao bookDao;
* }
*/
@Configuration
//@ComponentScan({"com.hutu.controller","com.hutu.service","com.hutu.dao","com.hutu.bean"})
@ComponentScan({"com.hutu.bean"})
public class MainConfigOfAutowired {
// @Primary
// @Bean("bookDao2")
// public BookDao bookDao() {
// BookDao bookDao = new BookDao();
// bookDao.setLable("2");
// return bookDao;
// }
}
测试
package com.hutu.boot01;
import com.hutu.boot01.bean.Boss;
import com.hutu.boot01.bean.Car2;
import com.hutu.boot01.config.MainConfigOfAutowired;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class IOCTest_Autowired {
@Test
public void test01() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfigOfAutowired.class);
Boss boss = context.getBean(Boss.class);
System.out.println(boss);
Car2 car2 = context.getBean(Car2.class);
System.out.println(car2);
}
}
结果
Boss{car2=com.hutu.boot01.bean.Car2@4ebea12c}
com.hutu.boot01.bean.Car2@4ebea12c
@Autowired
注解标注在构造器上,默认加在IOC容器中的组件,容器启动的时候会调用无参构造器创建对象,再进行初始化赋值操作,构造器要用的组件,也都是从容器中来获取: 注意:如果组件只有一个有参的构造器,这个有参的构造器的 @Autowired注解可以省略,参数位置的组件还是可以自动从容器中获取
Boss.java 添加有参构造器
package com.hutu.boot01.bean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* 默认在ioc容器中的组件,容器启动会调用无参构造器创建对象,再进行初始化赋值等操作
*/
@Component
public class Boss {
private Car2 car2;
@Autowired
public Boss(Car2 car2){
this.car2 = car2;
System.out.println("Boss的有参构造器"+car2);
}
public Car2 getCar2() {
return car2;
}
/**
* 标注在方法上,Spring容器在创建当前对象的时候,就会调用当前方法完成赋值;
* 方法使用的参数,自定义类型的值从IOC容器里面进行获取
*
* @param car2
*/
@Autowired
public void setCar(Car2 car2) {
this.car2 = car2;
}
@Override
public String toString() {
return "Boss{" +
"car2=" + car2 +
'}';
}
}
测试
@Test
public void test01() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfigOfAutowired.class);
Boss boss = context.getBean(Boss.class);
System.out.println(boss);
Car2 car2 = context.getBean(Car2.class);
System.out.println(car2);
}
结果
Boss的有参构造器com.hutu.boot01.bean.Car2@76a2ddf3
Boss{car2=com.hutu.boot01.bean.Car2@76a2ddf3}
com.hutu.boot01.bean.Car2@76a2ddf3
标在构造器上:如果组件只有一个有参构造器,这个有参构造器的@Autowired可以省略,参数位置的组件还说可以自动从容器中获取。
Color.java
package com.hutu.boot01.bean;
public class Color {
private Car2 car2;
public Car2 getCar2() {
return car2;
}
public void setCar2(Car2 car2) {
this.car2 = car2;
}
@Override
public String toString() {
return "Color{" +
"car2=" + car2 +
'}';
}
}
package com.hutu.boot01.config;
import com.hutu.boot01.bean.Car2;
import com.hutu.boot01.bean.Color;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan({"com.hutu"})
public class MainConfigOfAutowired {
/**
* @Bean 标注的方法创建对象的时候,方法参数的值从容器中获取
* @param car2
* @return
*/
@Bean
public Color color(Car2 car2){
Color color = new Color();
color.setCar2(car2);
return color;
}
}
测试
package com.hutu.boot01;
import com.hutu.boot01.bean.Boss;
import com.hutu.boot01.bean.Car2;
import com.hutu.boot01.bean.Color;
import com.hutu.boot01.config.MainConfigOfAutowired;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class IOCTest_Autowired {
@Test
public void test01() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfigOfAutowired.class);
Boss boss = context.getBean(Boss.class);
System.out.println(boss);
Car2 car2 = context.getBean(Car2.class);
System.out.println(car2);
System.out.println("===============");
Color color = context.getBean(Color.class);
System.out.println(color);
}
}
结果
Boss{car2=com.hutu.boot01.bean.Car2@545b995e}
com.hutu.boot01.bean.Car2@545b995e
===============
Color{car2=com.hutu.boot01.bean.Car2@545b995e}
总结:
@Autowired:构造器,参数,方法,属性,都是从容器中获取参数组件的值。
(1)【标注在方法位置】:@Bean+方法参数,参数从容器中获取。
5.4. Aware注入Spring底层组件&原理
自定义组件想要使用Spring容器底层的一些组件(ApplicationContext、BeanFactory…) 自定义组件实现xxxAware接口就可以实现,在创建对象的时候,会调用接口规定的方法注入相关的组件; 把Spring底层的一些组件注入到自定义的bean中;
xxxAware等这些都是利用后置处理器的机制,比如ApplicationContextAware 是通过ApplicationContextAwareProcessor来进行处理的;
例如之前写的这个,实现ApplicationContextAware 接口,里面有一个setApplicationContext方法
package com.hutu.boot01.bean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.stereotype.Component;
import org.springframework.util.StringValueResolver;
@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}");
System.out.println("解析的字符串:" + s);
}
}
结果
当前bean的名字:red
解析的字符串:你好Windows 10
传入的IOC:org.springframework.context.annotation.AnnotationConfigApplicationContext@2ef9b8bc, started on Fri Dec 16 10:02:43 CST 2022
5.5. @Profile环境搭建
mysql驱动
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.29</version>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<!--德鲁伊数据库连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.10</version>
</dependency>
完整的pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.hutu</groupId>
<artifactId>boot01</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>boot01</name>
<description>boot01</description>
<properties>
<java.version>1.8</java.version>
<!-- <mysql.version>5.1.43</mysql.version>-->
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- <dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.29</version>
</dependency>
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
MainConfigOfProfile.java
package com.hutu.boot01.config;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.util.StringValueResolver;
import javax.sql.DataSource;
import java.beans.PropertyVetoException;
/**
* Profile:
* Spring为我们提供的可以根据当前的环境,动态的激活和切换一系列组件的功能;
* 开发环境,测试环境,生产环境
* 我们以切换数据源为例:
* 数据源:开发环境中(用的是A数据库)、测试环境(用的是B数据库)、而生产环境(用的又是C数据库)
*/
@Configuration
@PropertySource("classpath:/dbconfig.properties")
public class MainConfigOfProfile implements EmbeddedValueResolverAware {
@Value("db.user")
private String user;
private StringValueResolver valueResolver;
private String driver;
@Bean("testDataSource")
public DataSource dataSourceTest(@Value("${db.password}") String pwd) throws PropertyVetoException {
ComboPooledDataSource source = new ComboPooledDataSource();
source.setUser(user);
source.setPassword(pwd);
source.setJdbcUrl("jdbc:mysql://localhost:3306/guigu-auth?characterEncoding=utf-8&useSSL=false");
source.setDriverClass(driver);
return source;
}
@Bean("devDataSource")
public DataSource dataSourceDev(@Value("${db.password}") String pwd) throws PropertyVetoException {
ComboPooledDataSource source = new ComboPooledDataSource();
source.setUser(user);
source.setPassword(pwd);
source.setJdbcUrl("jdbc:mysql://localhost:3306/guigu-auth?characterEncoding=utf-8&useSSL=false");
source.setDriverClass(driver);
return source;
}
@Bean("prodDataSource")
public DataSource dataSourceProd(@Value("${db.password}") String pwd) throws PropertyVetoException {
ComboPooledDataSource source = new ComboPooledDataSource();
source.setUser(user);
source.setPassword(pwd);
source.setJdbcUrl("jdbc:mysql://localhost:3306/guigu-auth?characterEncoding=utf-8&useSSL=false");
source.setDriverClass(driver);
return source;
}
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.valueResolver = resolver;
driver = valueResolver.resolveStringValue("${db.driverClass}");
}
}
src/resources/dbconfig.properties
db.user=root
db.password=root
db.driverClass=com.mysql.cj.jdbc.Driver
测试
@Test
public void test_db() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfigOfProfile.class);
System.out.println("===============");
String[] names = context.getBeanDefinitionNames();
for (String name : names) {
System.out.println(name);
}
System.out.println("===============");
context.close();
}
结果
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
mainConfigOfProfile
testDataSource
devDataSource
prodDataSource
总结: 三种获取配置文件中的值
直接通过属性上面加上
@Value("${db.user}")
在参数上面使用
@Value("${db.password}")
public DataSource dataSourceTest(@Value("${db.password}") String pwd)实现EmbeddedValueResolverAware接口,在setEmbeddedValueResolver(StringValueResolver resolver)方法里面进行获取:里面使用 resolver.resolveStringValue("${db.driverClass}")方法解析,返回赋值给成员属性private StringValueResolver resolver;
5.6. @Profile根据环境注册bean
@Profile: Spring为我们提供的可以根据当前的环境,动态的激活和切换一系列组件的功能; 开发环境,测试环境,生产环境
以切换数据源为例: 数据源:开发环境中(用的是A数据库)、测试环境(用的是B数据库)、而生产环境(用的又是C数据库)
@Profile: 指定组件在哪一个环境的情况下才能被注册到容器中,不指定任何环境都能被注册这个组件 1)加了环境标识的bean,只有这个环境被激活的时候才能注册到容器中,默认是default环境,如果指定了default,那么这个bean默认会被注册到容器中 2)@Profile 写在配置类上,只有是指定的环境,整个配置类里面的所有配置才能开始生效 3)没有标注环境标识的bean,在任何环境都是加载的
现在,我来用@Profile注解只激活哪一个数据源:
加了环境标识的bean,只有这个环境被激活的时候才能注册到容器中。
在每个方法上面添加了@Profile注解
package com.hutu.boot01.config;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.context.annotation.PropertySource;
import org.springframework.util.StringValueResolver;
import javax.sql.DataSource;
import java.beans.PropertyVetoException;
/**
* Profile:
* Spring为我们提供的可以根据当前的环境,动态的激活和切换一系列组件的功能;
* 开发环境,测试环境,生产环境
* 以切换数据源为例:
* 数据源:开发环境中(用的是A数据库)、测试环境(用的是B数据库)、而生产环境(用的又是C数据库)
*/
@Configuration
@PropertySource("classpath:/dbconfig.properties")
public class MainConfigOfProfile implements EmbeddedValueResolverAware {
@Value("db.user")
private String user;
private StringValueResolver valueResolver;
private String driver;
@Profile("test")
@Bean("testDataSource")
public DataSource dataSourceTest(@Value("${db.password}") String pwd) throws PropertyVetoException {
ComboPooledDataSource source = new ComboPooledDataSource();
source.setUser(user);
source.setPassword(pwd);
source.setJdbcUrl("jdbc:mysql://localhost:3306/guigu-auth?characterEncoding=utf-8&useSSL=false");
source.setDriverClass(driver);
return source;
}
@Profile("dev")
@Bean("devDataSource")
public DataSource dataSourceDev(@Value("${db.password}") String pwd) throws PropertyVetoException {
ComboPooledDataSource source = new ComboPooledDataSource();
source.setUser(user);
source.setPassword(pwd);
source.setJdbcUrl("jdbc:mysql://localhost:3306/guigu-auth?characterEncoding=utf-8&useSSL=false");
source.setDriverClass(driver);
return source;
}
@Profile("prod")
@Bean("prodDataSource")
public DataSource dataSourceProd(@Value("${db.password}") String pwd) throws PropertyVetoException {
ComboPooledDataSource source = new ComboPooledDataSource();
source.setUser(user);
source.setPassword(pwd);
source.setJdbcUrl("jdbc:mysql://localhost:3306/guigu-auth?characterEncoding=utf-8&useSSL=false");
source.setDriverClass(driver);
return source;
}
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.valueResolver = resolver;
driver = valueResolver.resolveStringValue("${db.driverClass}");
}
}
测试
@Test
public void test_db() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfigOfProfile.class);
System.out.println("===============");
String[] names = context.getBeanDefinitionNames();
for (String name : names) {
System.out.println(name);
}
System.out.println("===============");
context.close();
}
结果
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
mainConfigOfProfile
数据源一个都没有打印出来。在dataSourceTest方法上添加默认值@Profile("default")。
@Profile("default")
@Bean("testDataSource")
public DataSource dataSourceTest(@Value("${db.password}") String pwd) throws PropertyVetoException {
ComboPooledDataSource source = new ComboPooledDataSource();
source.setUser(user);
source.setPassword(pwd);
source.setJdbcUrl("jdbc:mysql://localhost:3306/guigu-auth?characterEncoding=utf-8&useSSL=false");
source.setDriverClass(driver);
return source;
}
结果
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
mainConfigOfProfile
testDataSource
使用命令行动态参数的方式:在虚拟机参数的位置加载-Dspring.profile.active=test