条件注解
条件注解
条件注解就是在满足某一个条件的情况下,生效的配置。
我们来看一下怎样使用套件注解。
我现在希望当系统运行在Windows 上时,自动打印出 Windows 上的目录展示命令(dir),Linux 运行时,则自动展示 Linux 上的目录展示命令(ls)。
首先定义一个显示文件夹目录的接口。
public interface ShowCmd {
String showCmd();
}
然后,分别实现 Windows 下的实例和 Linux 下的实例:
publi class WinShowCmd implements ShowCmd {
@Override
public String showCmd() {
return "dir";
}
}
publi class LinuxShowCmd implements ShowCmd {
@Override
public String showCmd() {
return "ls";
}
}
接下来,定义连个条件,一个是 Windows 下的条件,一个是 Linux 下的条件。
public class WindosCondition implements Condition {
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
return conditionContext.getEnvironment().getProperty("os.name").toLowerCase().contains("windows");
}
}
public class LinuxCondition implements Condition {
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
return conditionContext.getEnvironment().getProperty("os.name").toLowerCase().contains("linux");
}
}
有两个参数,conditionContext是本地环境信息,annotatedTypeMetadata是spring中的信息。
注意 Condition 要导入 spring 中的。
接下来,在定义 Bean 的时候,就可以配置条件注解了。
@Configuration
public class JavaConfig {
@Bean("showCmd)
@Conditional(WindosCondition.class)
ShowCmd winCmd() {
return new WinShowCmd();
}
@Bean("showCmd)
@Conditional(LinuxCondition.class)
ShowCmd winCmd() {
return new LinuxShowCmd();
}
}
@Conditional 它的作用是按照一定的条件进行判断,满足条件给容器注册bean。
同时,一定要给两个 Bean 取相同的名字,这样在调用时,才可以自动匹配。然后,给每一个 Bean 加上条件注解(@Conditional) 当条件中的 matches 方法返回 true 的时候,这个 Bean 的定义就会生效。
public class JavaMain {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(JavaConfig.class);
ShowCmd showCmd = (ShowCmd) context.getBean("showCmd);
System.out.println(showCmd.showCmd());
}
}
条件注解有一个非常典型的使用场景,就是多环境切换。
多环境切换
开发中,如何在 开发/生产/测试 环境之间进行快速切换?spring 中提供了 profile 来解决这个问题,profile 的底层就是条件注解。这个从 @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();
}
class ProfileCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
if (attrs != null) {
for (Object value : attrs.get("value")) {
if (context.getEnvironment().acceptsProfiles(Profiles.of((String[]) value))) {
return true;
}
}
return false;
}
return true;
}
}
我们定义一个 DataSource
public class DataSource {
private String url;
private String username;
private String password;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "DataSource{" +
"url='" + url + '\'' +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
然后,在配置 Bean 时,通过 @Profile 注解指定不同的环境:
@Configuration
public class JavaConfig {
@Bean("ds")
@Profile("dev")
DataSource devDataSource() {
DataSource dataSource = new DataSource();
dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/dev");
dataSource.setUsername("root");
dataSource.setPassword("111");
return dataSource;
}
@Bean("ds")
@Profile("prod")
DataSource prodDataSource() {
DataSource dataSource = new DataSource();
dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/prod");
dataSource.setUsername("root");
dataSource.setPassword("333");
return dataSource;
}
}
最后,在加载配置类,注意,
public class JavaMain {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles("dev");
ctx.register(JavaConfig.class);
ctx.refresh();
DataSource ds = (DataSource)ctx.getBean("ds");
System.out.println(ds);
}
}
此时,拿到的就是 dev 环境的数据源。
环境的切换除了上面使用注解,也可以在 XML 文件中配置。如下配置在 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="top.zxqs.DataSource" id="dataSource">
<property name="url" value="jdbc:mysql://127.0.0.1:3306/dev"/>
<property name="password" value="111"/>
<property name="username" value="root"/>
</bean>
</beans>
<beans profile="prod">
<bean class="top.zxqs.DataSource" id="dataSource">
<property name="url" value="jdbc:mysql://111.0.0.1:3306/prod"/>
<property name="password" value="dsfdasfdas"/>
<property name="username" value="root"/>
</bean>
</beans>
</beans>
启动类中设置当前环境并加载配置:
public class JavaMain {
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext();
ctx.getEnvironment().setActiveProfiles("prod");
ctx.setConfigLocation("applicationContext.xml");
ctx.refresh();
DataSource ds = (DataSource)ctx.getBean("dataSource");
System.out.println(ds);
}
}