Spring条件装配注解详解
一、@Conditional注解基础
@Conditional是Spring Framework 4引入的条件注解,用于根据特定条件决定是否向容器注册Bean。该注解实现了运行时动态 Bean 装配的能力。
示例代码
定义一个服务接口:
package com.example.conditional;
/**
* 文件列表服务接口
*/
public interface FileListService {
String getListCommand();
}
Linux环境实现类:
package com.example.conditional;
/**
* Linux环境下的文件列表服务实现
*/
public class LinuxFileListService implements FileListService {
@Override
public String getListCommand() {
return "ls";
}
}
Windows环境实现类:
package com.example.conditional;
/**
* Windows环境下的文件列表服务实现
*/
public class WindowsFileListService implements FileListService {
@Override
public String getListCommand() {
return "dir";
}
}
Windows条件判断类:
package com.example.conditional;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
/**
* Windows操作系统条件判断
*/
public class WindowsOsCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String osName = context.getEnvironment().getProperty("os.name");
return osName != null && osName.contains("Windows");
}
}
Linux条件判断类:
package com.example.conditional;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
/**
* Linux操作系统条件判断
*/
public class LinuxOsCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String osName = context.getEnvironment().getProperty("os.name");
return osName != null && osName.contains("Linux");
}
}
配置类:
package com.example.conditional;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
/**
* Bean配置类
*/
@Configuration
public class ServiceConfiguration {
@Bean
@Conditional(WindowsOsCondition.class)
public FileListService windowsFileListService() {
return new WindowsFileListService();
}
@Bean
@Conditional(LinuxOsCondition.class)
public FileListService linuxFileListService() {
return new LinuxFileListService();
}
}
测试启动类:
package com.example.conditional;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
/**
* 应用启动类
*/
public class ApplicationStartup {
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(
ServiceConfiguration.class);
FileListService service = ctx.getBean(FileListService.class);
String osName = ctx.getEnvironment().getProperty("os.name");
System.out.println(osName + "系统下的列表命令为: " + service.getListCommand());
}
}
运行结果:
Windows 10系统下的列表命令为: dir
二、@ConditionalOnClass注解
@ConditionalOnClass注解用于判断类路径上是否存在指定的类,只有当目标类存在时才创建对应的Bean。
例如:@ConditionalOnClass(JdbcTemplate.class) 表示只有当classpath中存在JdbcTemplate类时,才会实例化该Bean。这在自动配置场景中非常常见,用于实现可选依赖的功能。
三、@ConditionalOnBean注解
@ConditionalOnBean用于检查容器中是否已存在指定的Bean,仅当目标Bean存在时才创建当前Bean。
示例代码
实体类定义:
package com.example.model;
import lombok.Data;
/**
* 父亲实体类
*/
@Data
public class Father {
private String name;
private int age;
private Child child;
}
package com.example.model;
import lombok.Data;
/**
* 孩子实体类
*/
@Data
public class Child {
private String name;
private int age;
}
配置类:
package com.example.conditional;
import com.example.model.Child;
import com.example.model.Father;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 条件配置类
*/
@Configuration
public class ConditionalConfiguration {
// 注释掉此Bean后,Father的注入将失败
/*@Bean
public Child childBean() {
Child child = new Child();
child.setAge(8);
child.setName("小华");
return child;
}*/
@Bean
@ConditionalOnBean(Child.class)
public Father fatherBean(Child child) {
Father father = new Father();
father.setAge(38);
father.setName("小王爸爸");
father.setChild(child);
return father;
}
}
控制器测试类:
package com.example.conditional;
import com.example.model.Father;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
/**
* 测试控制器
*/
@Controller
public class TestController {
@Autowired(required = false)
private Father father;
@ResponseBody
@GetMapping("/test")
public void testConditionalOnBean() {
System.out.println("Father bean: " + father);
}
}
测试结果分析:
场景一:当容器中不存在Child Bean时,如果直接使用@Autowired注入Father,启动时会抛出异常:
Field father in com.example.conditional.TestController required a bean of type 'com.example.model.Father' that could not be found.
解决方案是在@Autowired注解中设置required=false,允许依赖为null,此时启动正常但father为null。
场景二:取消Child Bean的注释后重新启动,控制台输出:
Father(name=小王爸爸, age=38, child=Child(name=小华, age=8))
四、其他条件注解
- @ConditionalOnClass:类路径上存在指定类时才创建Bean
- @ConditionalOnExpression:Spring EL表达式结果为true时才创建Bean
- @ConditionalOnMissingBean:容器中不存在指定Bean时才创建
- @ConditionalOnMissingClass:类路径上不存在指定类时才创建
- @ConditionalOnNotWebApplication:非Web应用时才创建Bean
五、@ConditionalOnProperty注解
@ConditionalOnProperty用于根据配置文件中的属性值来决定是否创建Bean。
语法示例:
@ConditionalOnProperty(prefix = "person", name = "super.enable", matchIfMissing = false, havingValue = "true")
参数说明:
- prefix:配置属性的前缀
- name:配置属性的名称(必填)
- havingValue:与配置值对比,相同则条件成立
- matchIfMissing:当配置属性不存在时的匹配行为,默认为false
示例:@ConditionalOnProperty(prefix = "aspectLog", name = "enable", havingValue = "true", matchIfMissing = true)
这表示当配置文件中存在aspectLog.enable=true时功能开启;如果配置文件未设置该属性,由于matchIfMissing=true,也会默认开启。