Spring常用的注解

100 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 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的运行参数。

X6fHJS.png

②作为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 {
    //...
}