🎈博客主页:🌈我的主页🌈
🎈欢迎点赞 👍 收藏 🌟留言 📝 欢迎讨论!👏
🎈本文由 【泠青沼~】 原创,首发于 CSDN🚩🚩🚩
🎈由于博主是在学小白一枚,难免会有错误,有任何问题欢迎评论区留言指出,感激不尽!🌠个人主页
目录
- 🌟 一、Java配置
- 🌟 二、XML配置
- 🌟 三、原理分析
- 🌟 三、自定义Profile
🌟 一、Java配置
首先创建一个 DataSource 类:
java">public class DataSource {
private String username;
private String password;
private String url;
@Override
public String toString() {
return "DataSource{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
", url='" + url + '\'' +
'}';
}
}
向 Spring 容器中注册多个 DataSource,并且在注册的时候设置 Profile,只有当条件满足的时候,才向 Spring 容器中注册相应的 Bean,否则不注册
java">public class JavaConfig {
@Bean
@Profile("dev")
DataSource devDataSource(){
DataSource dev = new DataSource();
dev.setUsername("dev");
dev.setPassword("dev");
dev.setUrl("jdbc:mysql:3306/dev");
return dev;
}
@Bean
@Profile("prod")
DataSource prodDataSource(){
DataSource prod = new DataSource();
prod.setUsername("prod");
prod.setPassword("prod");
prod.setUrl("jdbc:mysql:3306/prod");
return prod;
}
}
这里到底向 Spring 容器中注册多少个 DataSource,主要还是看条件是否满足,如果有多个条件满足,那么就注册多个 DataSource 实例到 Spring 容器中,否则就不注册。
最后,启动容器:
java"> public static void main(String[] args) {
//这里注意,不能添加配置类,如果添加了配置类,则 refresh 方法会被调用,而 Spring 容器的初始化则正是从这里开始的
//此时,我们还没设置当前系统环境
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
//这里注意,不能添加配置类,如果添加了配置类,则 refresh 方法会被调用,而 Spring 容器的初始化则正是从这里开始的
//此时,我们还没设置当前系统环境
ConfigurableEnvironment evn = ctx.getEnvironment();
evn.setActiveProfiles("prod");
//此时再去设置配置类
ctx.register(JavaConfig.class);
//开始初始化容器
ctx.refresh();
DataSource dataSource = ctx.getBean(DataSource.class);
System.out.println(dataSource);
}
启动容器的时候,一开始不要传配置类进去,如果传了,则 refresh 方法会被自动调用,而此时我们还没设置容器的环境!所以,使用无参构造方法创建 AnnotationConfigApplicationContext 对象,然后设置系统环境,再去设置配置类,最后再去初始化容器
🌟 二、XML配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<beans profile="dev">
<bean class="com.dong.demo3.DataSource" >
<property name="username" value="dev"/>
<property name="password" value="dev"/>
<property name="url" value="jdbc:mysql:3306/dev"/>
</bean>
</beans>
<beans profile="prod">
<bean class="com.dong.demo3.DataSource" >
<property name="username" value="prod"/>
<property name="password" value="prod"/>
<property name="url" value="jdbc:mysql:3306/prod"/>
</bean>
</beans>
</beans>
这个是在 父标签 beans 中设置条件,满足对应的条件,父标签中的所有 Bean 才会被初始化
java">public class XMLDemo1 {
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext();
ctx.getEnvironment().addActiveProfile("prod");
ctx.setConfigLocation("beans1.xml");
ctx.refresh();
DataSource bean = ctx.getBean(DataSource.class);
System.out.println(bean);
}
}
🌟 三、原理分析
@Profile
注解的定义就是一个组合注解:
java">@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
// 从这里可以看到,本质上就是一个条件注解,条件则是 ProfileCondition
@Conditional(ProfileCondition.class)
public @interface Profile {
/**
* The set of profiles for which the annotated component should be registered.
*/
String[] value();
}
那我们来看下 ProfileCondition:
java">class ProfileCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 这句话实际上就是查询到 Profile 注解的所有属性
// 对于 Profile 注解来说,实际上就只有 value 属性
// value->dev value->prod
MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
if (attrs != null) {
// attrs.get("value") 表示读取属性中的 value 属性,value 属性的值是一个数组,所以这里需要遍历
for (Object value : attrs.get("value")) {
//判断当前系统环境中是否包含这个 value
if (context.getEnvironment().matchesProfiles((String[]) value)) {
return true;
}
}
return false;
}
return true;
}
}
🌟 三、自定义Profile
首先,先自定义注解:
java">@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Conditional(MyCondition.class)
public @interface MyProfile {
String[] value();
}
然后自定义条件,在自定义条件中解析注解:
java">public class MyCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(MyProfile.class.getName());
if(attrs != null){
List<Object> list = attrs.get("value");
for (Object value : list) {
boolean b = context.getEnvironment().acceptsProfiles(Profiles.of((String[]) value));
if(b){
return true;
}
}
}
return false;
}
}
最后使用自定义注解:
java">public class JavaConfig {
@Bean
@MyProfile("dev")
DataSource devDataSource(){
DataSource dev = new DataSource();
dev.setUsername("dev");
dev.setPassword("dev");
dev.setUrl("jdbc:mysql:3306/dev");
return dev;
}
@Bean
@MyProfile("prod")
DataSource prodDataSource(){
DataSource prod = new DataSource();
prod.setUsername("prod");
prod.setPassword("prod");
prod.setUrl("jdbc:mysql:3306/prod");
return prod;
}
}
启动容器:
java"> public static void main(String[] args) {
//这里注意,不能添加配置类,如果添加了配置类,则 refresh 方法会被调用,而 Spring 容器的初始化则正是从这里开始的
//此时,我们还没设置当前系统环境
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
//这里注意,不能添加配置类,如果添加了配置类,则 refresh 方法会被调用,而 Spring 容器的初始化则正是从这里开始的
//此时,我们还没设置当前系统环境
ConfigurableEnvironment evn = ctx.getEnvironment();
evn.setActiveProfiles("prod");
//此时再去设置配置类
ctx.register(JavaConfig.class);
//开始初始化容器
ctx.refresh();
DataSource dataSource = ctx.getBean(DataSource.class);
System.out.println(dataSource);
}