持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第7天,点击查看活动详情
@Autowired (@Inject)、@Conditional、@Qualifier、@Value、@Profile、@PropertySource、@Scope
@Autowired / @Inject
属性自动装配用的注解,可用于构造方法、setter方法以及属性上。@Inject 注解功能与其相似,区别在于@Autowired是Spring特有的注解,而@Inject是Java内的注解;
-
可注解的位置:属性、构造方法以及setter方法。
-
属性:required 默认为true,表示一定需要该bean依赖,若找不到该bean,则会抛出异常。
-
默认按类型匹配(byType),若想按名称匹配,则需要配合@Qualifier注解一起使用。
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:application-context.xml")
public class StudentMapperTest {
@Autowired(required = true)
private StudentMapper studentMapper;
@Test
public void getStudentById() {
Student student = studentMapper.getStudentById(1);
System.out.println(student);
}
}
@Autowired
与 @Resource
注解的差异:
-
相同点:
- @Autowired和@Resource都是可以用来装配bean,并都可以写在字段上或者setter方法上。
-
不同点:
-
@Autowired为Spring提供的注解,默认按类型匹配(即byType)。如果对所依赖的对象允许为null时,可设置required属性为false。如果想按照名称匹配,则需要与@Qualifier注解一起使用,即添加@Qualifier(name='xxx')
-
而@Resource注解为jdk1.6后提供的注解,默认按名称匹配(即byName)。如果找不到名称匹配的bean才会进行类型匹配。可以通过name属性指定名称。
-
需要注意的是,使用了@Resource注解并不是意味着与Spring进行了解耦,因为注解处理器(即对注解的解析)还是Spring提供的。
-
@Qualifier
该注解一般跟@Autowired 配合着使用,当bean对象不唯一时,可以用@Qualifier注解来表示需要注入的依赖是哪一个。例如当一个接口有多个实现时,利用该注解可以特定某一个实现。
可注解的位置:属性、构造方法以及setter方法。
@Autowired(required = true)
@Qualifier("studentMapper")
private StudentMapper studentMapper;
@PropertySource 与 @Value
@PropertySource 与 @Value 经常配合着一起使用,前者可以引入外部的文件,而后者通过SpringEl表达式解析文件中的变量,将值注入到被标记的属性中。
@Configuration
@PropertySource("classpath:src/main/resources/mqtt.properties")
public class Wx2MqttConfig {
@Value("${mqtt.host}")
private String host;
@Value("${mqtt.port}")
private String port;
@Value("${mqtt.username}")
private String username;
@Value("${mqtt.clientId}")
private String clientId;
@Value("${mqtt.password}")
private String password;
}
如果是SpringBoot项目,那么可以不需要用 @PropertySource注解,可以直接从默认的配置文件中读取变量值。
@Conditional
条件注解,Spring4出现的功能。当满足条件时才会向容器注册bean。
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
Class<? extends Condition>[] value();
}
只有一个属性,其值为继承Condition的实现类,是个数组。
@FunctionalInterface
public interface Condition {
/**
* @param ConditionContext
* @param AnnotatedTypeMetadata
**/
boolean matches(ConditionContext var1, AnnotatedTypeMetadata var2);
}
查看Condition接口可知该接口是函数式接口,实现类实现该接口,通过参数可获取应用上下文以及注解等情况,判定特定条件是否满足;当matches方法返回true时表示满足条件,允许自动注入该bean。
-
可注解的位置:方法、类。
-
属性
-
ConditionContext 接口:
public interface ConditionContext { BeanDefinitionRegistry getRegistry(); @Nullable ConfigurableListableBeanFactory getBeanFactory(); Environment getEnvironment(); ResourceLoader getResourceLoader(); @Nullable ClassLoader getClassLoader(); }
从接口定义中可以可以知道该属性提供以下几种功能:
- 通过getRegistry方法返回BeanDefinitionRegistry,可以检查bean的定义;
- getBeanFactory方法获取bean工厂,可以检查bean是否存在,甚至检查bean的属性;
- getEnvironment方法可以获取环境变量的值情况;
- getResourceLoader返回资源加载器;
- getClassLoader返回类的类加载器,可以检查类是否存在。
-
AnnotatedTypeMetadata接口:可以获得注解信息,比如:方法名称
-
在Spring中Condition的实现类只有ProfileCondition类,该条件类是为了检查不同环境下初始Bean的条件,需要配合@Profile注解一起使用。
// ProfileCondition#matches
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
if (attrs != null) {
Iterator var4 = ((List)attrs.get("value")).iterator();
Object value;
do {
if (!var4.hasNext()) {
return false;
}
value = var4.next();
} while(!context.getEnvironment().acceptsProfiles(Profiles.of((String[])((String[])value))));
return true;
} else {
return true;
}
}
从mathces方法中可以看出当有@Profile注解时,会判断当前环境是否支持注解上的环境,若不支持就会返回false,即此时不会去实例化该Bean。
值得注意的是,SpringBoot里提供了大量的Condition接口的实现类;当没有满足的实现类时,用户可自定义Condition的实现。
@Profile
用于区分不同环境下实例化不同bean的注解,原理即是用了Conditional注解,ProfileCondition实现了Condition接口,接口内matches方法匹配环境是否满足。
激活Profile有多种方式:
- 作为DispatcherServlet的初始化参数;
- 作为Web应用的上下文参数;
- 作为JNDI条目;
- 作为环境变量;
- 作为JVM的系统属性;
- 在集成测试环境下,使用@ActiveProfiles注解设置。
示例: ①作为JVM的系统属性;在启动tomcat前可以在配置中加上VM的运行参数。
②作为Web应用的上下文参数;在web.xml中配置上下文属性。
<context-param>
<param-name>spring.profiles.active</param-name>
<param-value>dev</param-value>
</context-param>
③在DispatcherServlet的初始化参数中配置;
<servlet>
<servlet-name>demo</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 配置servlet的属性 -->
<init-param>
<!-- 如果不指定contextConfigLocation,默认会去WEB-INF/[servlet-name]-servlet.xml 读取mvc配置 -->
<param-name>contextConfigLocation</param-name>
<!-- 这里指定路径为 springmvc.xml下 -->
<param-value>classpath:/springmvc.xml</param-value>
</init-param>
<init-param>
<param-name>spring.profiles.active</param-name>
<param-value>dev</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
④ 在集成测试环境下,使用@ActiveProfiles注解设置。
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:application-context.xml")
@ActiveProfiles("dev")
public class StudentMapperTest {
//...
}
@Scope
用于配置该Bean的作用域,取值为 singleton(默认)、prototype、request、session。
默认是单例,当需要每次获取的时候都重新实例化,则需要使用prototype。
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class Receiver {
//...
}