Spring5中文文档【7】IOC容器之基于注解的容器配置

前言

本系列基于最新5.3.10版本,大部分内容copy于官方文档…
官方文档地址
从 Spring 2.5 开始就可以使用注解来配置依赖注入代替XML配置。

基于注解依赖于字节码元数据(注解)来连接组件而不是尖括号声明。开发人员不使用 XML 来描述 bean 连接,而是通过在相关类、方法或字段声明上使用注解将配置移动到组件类本身中。

1. @Required

@Required注解主要用在 setter 方法上,它表示该 setter 方法的属性必须要在配置时注入值。否则就会报 BeanInitializationException 异常。

从Spring Framework 5.1 开始,@Required注解 和RequiredAnnotationBeanPostProcessor正式弃用

案例演示

  1. 首先我们在Bean对象中对两个属性添加@Required注解,可以看到显示已过时。
    在这里插入图片描述
  2. 然后在XML中注入该Bean,可以发现错误信息,必须注入该Bean被@Required注解标识了的属性。
    在这里插入图片描述

2. @Autowired

在本节包含的示例中,可以使用JSR 330 的注解@Inject代替 Spring 的@Autowired注解。

之前说过,spring中的DI(依赖注入)由其自动装配功能,管理多个Bean之间的依赖关系,负责属性赋值或注入。

@Autowired 注解可以对类成员变量、方法及构造函数进行标注,让 spring 完成 bean 自动装配的工作。

@Target({
    
    ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
    
    
    boolean required() default true;
}

首先,我们创建一个Serive Bean和一个Dao Bean,演示Serive层调用Dao层进行数据查询。

创建UserDao,模拟查询数据库。

public class UserDao {
    
    
    
    public void select(){
    
    
        System.out.println("查询数据");
    }
}

创建UserService ,调用UserDao 查询数据库,如果是没有Spring,这时需要new一个UserDao ,而在Spring中,则只需要将UserDao Bean对象注入到UserService 中,就可以了,由Spring来创建及管理这些Bean对象。

扫描二维码关注公众号,回复: 13371030 查看本文章
public class UserService {
    
    

}

2.1 应用于构造函数

可以将@Autowired注解应用于构造函数,如以下示例所示:

public class UserService {
    
    

    private UserDao userDao;

    public void selectUser(){
    
    
        userDao.select();
    }
    
    @Autowired
    public UserService(UserDao userDao) {
    
    
        this.userDao = userDao;
    }
}

执行结果如下:
在这里插入图片描述

2.2 应用于setter 方法

可以将@Autowired注解应用于传统的setter 方法,如以下示例所示:

    @Autowired
    public void setUserDao(UserDao userDao) {
    
    
        this.userDao = userDao;
    }

2.3 应用于任意名称方法

可以将@Autowired注解应用于具有任意名称和多个参数的方法,如以下示例所示:

@Autowired
    public void test(UserDao userDao) {
    
    
        this.userDao = userDao;
    }
}

2.4 应用于任意名称方法

可以将@Autowired应用于字段(一般都这么用),甚至可以将其与构造函数混合使用,如下例所示:

    @Autowired
    private UserDao userDao;

通过将@Autowired添加到需要该数组类型的字段或方法来指示 Spring 提供特定类型的所有 bean, 如下例所示:

public class MovieRecommender {
    
    

    @Autowired
    private MovieCatalog[] movieCatalogs;

    // ...
}

这同样适用于其他类型集合,如下例所示:

public class MovieRecommender {
    
    

    private Set<MovieCatalog> movieCatalogs;

    @Autowired
    public void setMovieCatalogs(Set<MovieCatalog> movieCatalogs) {
    
    
        this.movieCatalogs = movieCatalogs;
    }

    // ...
}

默认情况下,当给定注入点没有匹配的候选 bean 时,自动装配失败,会抛出NoSuchBeanDefinitionException异常。对于声明的数组、集合或映射,至少需要一个匹配元素。报错信息如下:

默认是将@Autowired注解的方法和字段为当前Bean所需的依赖项。设置@Autowired的required属性为false,标记为非必需,使框架能够跳过不可满足的注入点,但是如果使用了注入的Bean对象,还是会抛出空指针异常。

    @Autowired(required = false)
    private UserDao userDao;

    public void selectUser(){
    
    
        userDao.select();
    }

2.5 注入Spring 默认的Bean对象

BeanFactory,ApplicationContext,Environment,ResourceLoader, ApplicationEventPublisher和MessageSource这些接口及其扩展接口(例如ConfigurableApplicationContext或ResourcePatternResolver)也可以使用@Autowired自动装配,无需特殊设置。

以下示例自动装配一个ApplicationContext对象:

    @Autowired
    private ApplicationContext context;

    public void selectUser(){
    
    
        String applicationId = context.getId();
        System.out.println(applicationId);
        userDao.select();
    }

@Autowired,@Inject,@Value,和@Resource注解由Spring BeanPostProcessor处理实现。这意味着您不能在自己的BeanPostProcessor或BeanFactoryPostProcessor类型(如果有)中应用这些注解。这些类型必须使用 XML 或 @Bean方法显式“连接” 。

3. @Primary

由于按类型自动装配可能会导致多个候选对象,因此通常需要对选择过程进行更多控制。实现此目的的一种方法是使用 Spring 的 @Primary注解。

@Primary表示当多个 bean 是自动装配到单值依赖项的候选者时,应优先考虑特定 bean。如果候选中恰好存在一个@Primary标注的bean,则它成为自动装配的值。

比如我们将之前的UserDao定义一个接口,然后定义两个实现类。
在这里插入图片描述
在UserService中注入。

    @Autowired
    private UserDao userDao;

在没有使用 @Primary注解时,会报错,发现了两个匹配的Bean。
在这里插入图片描述
这时可以使用@Primary标记其中一个实现类,表示存在相同Bean时,优先使用个,

4. @Qualifier

@Qualifier@Autowired都位于spring-beans模块的org.springframework.beans.factory.annotation包下。

@Target({
    
    ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Qualifier {
    
    

	String value() default "";
}

在自动装配时,如果存在多个实例,使用@Primary可以标记当前Bean为主要候选对象,从而优先注入这个Bean。而 Spring 的@Qualifier注解可以进行更多控制,比如给Bean定义标识符,在注入时,使用@Qualifier注解,其value属性表示需要注入bean的标识符。

比如以下示例,表示注入带有main标识符的Bean:

public class MovieRecommender {
    
    

    @Autowired
    @Qualifier("main")
    private MovieCatalog movieCatalog;

    // ...
}

还可以将@Qualifier在单个构造函数参数或方法参数上指定,如以下示例所示:

@Service
public class UserService {
    
    

    // @Autowired(required = false)

    private UserDao userDao;

    public void selectUser(){
    
    
        userDao.select();
    }
    @Autowired
    public UserService( @Qualifier("userDaoImplOne") UserDao userDao) {
    
    
        this.userDao = userDao;
    }
}

以下示例显示了相应的 bean 定义。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config/>

    <bean class="example.SimpleMovieCatalog">
        <qualifier value="main"/> 

        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean class="example.SimpleMovieCatalog">
        <qualifier value="action"/> 

        <!-- inject any dependencies required by this bean -->
    </bean>

    <bean id="movieRecommender" class="example.MovieRecommender"/>

</beans>

对于回退匹配,bean 名称被视为默认限定符值。因此,您可以使用idofmain而不是嵌套的限定符元素来定义 bean ,从而得到相同的匹配结果。但是,尽管您可以使用此约定来按名称引用特定的 bean,但@Autowired基本上是关于带有可选语义限定符的类型驱动注入。这意味着限定符值,即使使用 bean 名称回退,在类型匹配集中始终具有缩小语义。它们不会在语义上表达对唯一 bean 的引用id。良好限定的值是main 或EMEA或persistent,表达独立于从所述豆的特定部件的特性id,在匿名 bean 定义的情况下可能会自动生成,例如前面示例中的定义。

限定符也适用于类型化集合,如前所述——例如,to Set。在这种情况下,根据声明的限定符,所有匹配的 bean 作为集合注入。这意味着限定符不必是唯一的。相反,它们构成过滤标准。例如,您可以定义多个MovieCatalog具有相同限定符值“action”的 bean,所有这些 bean 都被注入到Set带有@Qualifier(“action”).

让限定符值在类型匹配候选中针对目标 bean 名称进行选择,不需要@Qualifier在注入点进行注释。如果没有其他解析指示符(例如限定符或主标记),对于非唯一依赖的情况,Spring 将注入点名称(即字段名称或参数名称)与目标 bean 名称进行匹配并选择同名候选人,如果有的话。

也就是说,如果您打算按名称表达注解驱动的注入,请不要主要使用@Autowired,即使它能够在类型匹配候选者中按 bean 名称进行选择。相反,使用 JSR-250@Resource注释,该注释在语义上定义为通过其唯一名称标识特定目标组件,声明的类型与匹配过程无关。@Autowired具有相当不同的语义:按类型选择候选 bean 后,指定的String 限定符值仅在那些类型选择的候选中考虑(例如,将account限定符与标记有相同限定符标签的 bean 进行匹配)。

从 4.3 开始,@Autowired还考虑用于注入的自引用(即对当前注入的 bean 的引用)。请注意,自注入是一种回退。对其他组件的常规依赖始终具有优先级。从这个意义上说,自我推荐不参与常规的候选人选择,因此特别是从不主要。相反,它们总是以最低的优先级结束。在实践中,您应该仅将自引用用作最后的手段(例如,通过 bean 的事务代理调用同一实例上的其他方法)。在这种情况下,考虑将受影响的方法分解为单独的委托 bean。或者,您可以使用@Resource,它可以通过其唯一名称获取返回到当前 bean 的代理。

尝试从@Bean同一配置类上的方法中注入结果也是一种有效的自引用场景。要么在实际需要的方法签名中延迟解析此类引用(而不是配置类中的自动装配字段),要么将受影响的@Bean方法声明为static,将它们与包含的配置类实例及其生命周期分离。否则,仅在回退阶段考虑此类 bean,而将其他配置类上的匹配 bean 选为主要候选对象(如果可用)。

@Autowired适用于字段、构造函数和多参数方法,允许在参数级别通过限定符注释进行缩小。相比之下,@Resource 仅支持带有单个参数的字段和 bean 属性设置器方法。因此,如果您的注入目标是构造函数或多参数方法,您应该坚持使用限定符。

您可以创建自己的自定义限定符注释。为此,请定义一个注释并@Qualifier在您的定义中提供该注释,如以下示例所示:

爪哇科特林

@Target({
    
    ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Genre {
    
    

    String value();
}

然后,您可以在自动装配的字段和参数上提供自定义限定符,如以下示例所示:

public class MovieRecommender {
    
    

    @Autowired
    @Genre("Action")
    private MovieCatalog actionCatalog;

    private MovieCatalog comedyCatalog;

    @Autowired
    public void setComedyCatalog(@Genre("Comedy") MovieCatalog comedyCatalog) {
    
    
        this.comedyCatalog = comedyCatalog;
    }

    // ...
}

5. @Resource

@Resource注解位于JDK中javax.annotation包下,是JSR-250规范提供的注解。

@Target({
    
    TYPE, FIELD, METHOD})
@Retention(RUNTIME)
public @interface Resource {
    
    

    name() default "";

    String lookup() default "";

    Class<?> type() default java.lang.Object.class;

    enum AuthenticationType {
    
    
            CONTAINER,
            APPLICATION
    }

    AuthenticationType authenticationType() default AuthenticationType.CONTAINER;

    boolean shareable() default true;

    String mappedName() default "";

    String description() default "";
}

Spring中可以通过在字段、bean 属性 或者setter 方法上使用 @Resource注解来支持注入。

@Resource采用 name属性。默认情况下,Spring 将该值解释为要注入的 bean 名称。换句话说,它遵循按名称语义,如以下示例所示:

@Service
public class UserService {
    
    

    @Resource(name = "userDaoImplOne")
    private UserDao userDao;

    public void selectUser(){
    
    
        userDao.select();
    }
}

6. @Value

@Value 通常用于注入外化属性,使用spring boot时,可以直接在application.properties配置文件中添加属性,然后使用@Value注入。

只使用了spring IOC 框架时,可以在resources下定义一个配置文件application.properties

在这里插入图片描述

然后使用@PropertySources注解引入外部化配置,再使用 @Value注解,使用${}表达式引入即可。

@Service
@PropertySource("classpath:application.properties")
public class UserService {
    
    

    @Resource(name = "userDaoImplOne")
    private UserDao userDao;

    @Value("${name}")
    private String name;

    public void selectUser(){
    
    
        System.out.println(name);
        userDao.select();
    }
}

Spring 提供了一个默认的宽松嵌入值解析器。它将尝试解析属性值,如果无法解析,则属性名称(例如${name})将作为值注入。

如果不想使用默认配置,可以使用注入PropertySourcesPlaceholderConfigurer,来自定义解析规则,也可以使用方法一样 setPlaceholderPrefix,setPlaceholderSuffix或setValueSeparator自定义的占位符。

@Configuration
public class AppConfig {
    
    

    @Bean
    public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
    
    
        return new PropertySourcesPlaceholderConfigurer();
    }
}

7. @PostConstruct和@PreDestroy

@PostConstruct和@PreDestroy和@Resource一样,也是JSR-250提供的注解,在 Spring 2.5 中引入,对这些注解的支持初始化回调和销毁回调中描述的生命周期回调机制的替代方案 。

    @PostConstruct
    public void postConstruct(){
    
    
        System.out.println("初始化回调");
    }

    @PreDestroy
    public void preDestroy(){
    
    
        System.out.println("销毁回调");
    }

像@Resource一样,@PostConstruct和@PreDestroy类型是标准Java库的一部分从JDK 6至8。然而,整个javax.annotation 包得到了来自Java核心模块JDK 9分离,并最终除去JDK 11被删除。

8. 使用泛型作为自动装配限定符

除了@Qualifier注解之外,您还可以使用 Java 泛型类型作为限定的隐式形式。例如,假设您有以下配置:

@Configuration
public class MyConfiguration {
    
    

    @Bean
    public StringStore stringStore() {
    
    
        return new StringStore();
    }

    @Bean
    public IntegerStore integerStore() {
    
    
        return new IntegerStore();
    }
}

假设前面 Bean 实现了一个泛型接口,(即Store<String>Store<Integer>),@Autowire可以将Store接口和泛型用作限定符,如下例所示:

爪哇科特林

@Autowired
private Store<String> s1; // <String> qualifier, injects the stringStore bean
@Autowired
private Store<Integer> s2; // <Integer> qualifier, injects the integerStore bean

通用限定符也适用于自动装配List、Map实例和数组。以下示例自动装配一个List:

// Inject all Store beans as long as they have an <Integer> generic
// Store<String> beans will not appear in this list
@Autowired
private List<Store<Integer>> s;

9 .用CustomAutowireConfigurer

CustomAutowireConfigurer是一个BeanFactoryPostProcessor,允许注册自己的自定义限定符注解类型,即使它们没有使用 Spring 的@Qualifier注解。以下示例显示了如何使用CustomAutowireConfigurer:

<bean id="customAutowireConfigurer"
        class="org.springframework.beans.factory.annotation.CustomAutowireConfigurer">
    <property name="customQualifierTypes">
        <set>
            <value>example.CustomQualifier</value>
        </set>
    </property>
</bean>

AutowireCandidateResolver通过以下方式确定autowire候选者:

  • 每个Bean定义的autowire-candidate值
  • 元素上可用的任何default-autowire-candidates模式
  • @Qualifier注解的存在以及在CustomAutowireConfigurer中注册的任何自定义注解

当多个 bean 有资格作为自动装配候选时,“主要”的确定如下:如果候选中的一个 bean 定义的primary 属性设置为true,则选择它。

猜你喜欢

转载自blog.csdn.net/qq_43437874/article/details/120670043
今日推荐