spring框架学习 - spring IoC 之 基于注解的容器配置补充

spring框架学习 - spring IoC 之 基于注解的容器配置补充,第1张

spring框架学习 - spring IoC 之 基于注解的容器配置补充

接上一篇博客:https://blog.csdn.net/qq_43605444/article/details/121967321?spm=1001.2014.3001.5502

10.3 使用@Primary 微调基于注解的自动装配

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

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

考虑以下将 firstMovieCatalog 定义为主要 MovieCatalog 的配置:

@Configuration
public class MovieConfiguration {

    @Bean
    @Primary
    public MovieCatalog firstMovieCatalog() { ... }

    @Bean
    public MovieCatalog secondMovieCatalog() { ... }

    // ...
}

使用上述配置,以下 MovieRecommender 自动装配到 firstMovieCatalog:

public class MovieRecommender {

    @Autowired
    private MovieCatalog movieCatalog;

    // ...
}

相应的bean定义如下:




    

    
        
    

    
        
    

    


10.4 用限定符微调基于注解的自动装配

@Primary 是一种有效的方法,当可以确定一个主要候选对象时,可以通过多个实例按类型使用自动装配。

当您需要对选择过程进行更多控制时,可以使用 Spring 的 @Qualifier 注解。 您可以将限定符值与特定参数相关联,缩小类型匹配的范围,以便为每个参数选择一个特定的 bean。 在最简单的情况下,这可以是一个简单的描述性值,如以下示例所示:

public class MovieRecommender {

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

    // ...
}

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

public class MovieRecommender {

    private MovieCatalog movieCatalog;

    private CustomerPreferenceDao customerPreferenceDao;

    @Autowired
    public void prepare(@Qualifier("main") MovieCatalog movieCatalog,
            CustomerPreferenceDao customerPreferenceDao) {
        this.movieCatalog = movieCatalog;
        this.customerPreferenceDao = customerPreferenceDao;
    }

    // ...
}

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




    

    
        
         

        
    

    
        
         

        
    

    


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

限定符也适用于类型化集合,如前所述 — 例如,适用于 Set。 在这种情况下,根据声明的限定符,所有匹配的 bean 作为集合注入。 这意味着限定符不必是唯一的。 相反,它们构成过滤标准。

例如,您可以定义多个具有相同限定符值“action”的 MovieCatalog bean,所有这些 bean 都被注入到一个用 @Qualifier("action") 注解的 Set 中。

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

也就是说,如果您打算按名称表达注解驱动的注入,请不要主要使用 @Autowired,即使它能够在类型匹配的候选中按 bean 名称进行选择。

相反,请使用 JSR-250 @Resource 注解,该注解在语义上定义为通过其唯一名称标识特定目标组件,声明的类型与匹配过程无关。

@Autowired 有相当不同的语义:在按类型选择候选 bean 后,指定的 String 限定符值仅在那些类型选择的候选中考虑(例如,将帐户限定符与标记有相同限定符标签的 bean 匹配)。

对于本身定义为集合、Map 或数组类型的 bean,@Resource 是一个很好的解决方案,通过唯一名称引用特定的集合或数组 bean。 也就是说,从 4.3 开始,您也可以通过 Spring 的 @Autowired 类型匹配算法匹配集合、Map 和数组类型,只要元素类型信息保留在 @Bean 返回类型签名或集合继承层次结构中即可。 在这种情况下,您可以使用限定符值在相同类型的集合中进行选择,如上一段所述。

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

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

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

您可以创建自己的自定义限定符注解。 为此,请定义注解并在定义中提供 @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;
    }

    // ...
}

接下来,您可以提供候选 bean 定义的信息。 您可以添加 标签作为 标签的子元素,然后指定类型和值以匹配您的自定义限定符注解。 该类型与注解的完全限定类名匹配。 或者,如果不存在名称冲突的风险,为了方便起见,您可以使用短类名称。 以下示例演示了这两种方法:




    

    
        
        
    

    
        
        
    

    


在类路径扫描和托管组件中,您可以看到一种基于注解的替代方法,以在 XML 中提供限定符元数据。 具体来说,请参阅提供带有注解的限定符元数据。

在某些情况下,使用没有值的注解就足够了。 当注解服务于更通用的目的并且可以应用于多种不同类型的依赖项时,这会很有用。 例如,您可以提供一个离线目录,当没有可用的 Internet 连接时可以搜索该目录。 首先定义简单的注解,如下例所示:

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

}

然后将注解添加到要自动装配的字段或属性中,如下例所示:

public class MovieRecommender {
	// 添加了@Offline 注解。
    @Autowired
    @Offline 
    private MovieCatalog offlineCatalog;

    // ...
}

现在 bean 定义只需要一个限定符类型,如下例所示:


    
     
    

除了简单值属性之外,您还可以定义自定义限定符注解,这些注解接受命名属性。 如果随后在要自动装配的字段或参数上指定了多个属性值,则 bean 定义必须匹配所有此类属性值才能被视为自动装配候选者。 例如,请考虑以下注解定义:

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

    String genre();

    Format format();
}

在这种情况下 Format 是一个枚举,定义如下:

public enum Format {
    VHS, DVD, BLURAY
}

要自动装配的字段使用自定义限定符进行注解,并包含两个属性的值:genre 和format,如以下示例所示:

public class MovieRecommender {

    @Autowired
    @MovieQualifier(format=Format.VHS, genre="Action")
    private MovieCatalog actionVhsCatalog;

    @Autowired
    @MovieQualifier(format=Format.VHS, genre="Comedy")
    private MovieCatalog comedyVhsCatalog;

    @Autowired
    @MovieQualifier(format=Format.DVD, genre="Action")
    private MovieCatalog actionDvdCatalog;

    @Autowired
    @MovieQualifier(format=Format.BLURAY, genre="Comedy")
    private MovieCatalog comedyBluRayCatalog;

    // ...
}

最后,bean 定义应该包含匹配的限定符值。 此示例还演示了您可以使用 bean 元属性代替 元素。 如果可用,则 元素及其属性优先,但如果不存在这样的限定符,自动装配机制将回退到 标签中提供的值,如以下示例中的最后两个 bean 定义 :




    

    
        
            
            
        
        
    

    
        
            
            
        
        
    

    
        
        
        
    

    
        
        
        
    


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

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

@Configuration
public class MyConfiguration {

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

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

假设前面的bean实现了一个泛型接口,(即Store和Store),你可以@Autowire Store接口,并使用泛型作为限定符,如下例所示:

@Autowired
private Store s1; //  qualifier, injects the stringStore bean

@Autowired
private Store s2; //  qualifier, injects the integerStore bean

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

// Inject all Store beans as long as they have an  generic
// Store beans will not appear in this list
@Autowired
private List> s;
10.6 使用 CustomAutowireConfigurer

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


    
        
            example.CustomQualifier
        
    

AutowireCandidateResolver 通过以下方式确定自动装配候选者:

  • 每个 bean 定义的自动装配候选值
  • 元素上可用的任何默认自动装配模式
  • @Qualifier 注解的存在以及向 CustomAutowireConfigurer 注册的任何自定义注解

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

10.7 使用@Resource 注入

Spring 还通过在字段或 bean 属性 setter 方法上使用 JSR-250 @Resource 注解 (javax.annotation.Resource) 来支持注入。 这是 Java EE 中的常见模式:例如,在 JSF 管理的 bean 和 JAX-WS 端点中。 对于 Spring 管理的对象,Spring 也支持这种模式。

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

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Resource(name="myMovieFinder") 
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
}

如果指定了 name 名称,没有找到对应的 bean 时会报错。

如果未明确指定名称,则默认名称是从字段名称或 setter 方法派生的。 如果是字段,则使用字段名称。 在 setter 方法的情况下,它采用 bean 属性名称。 下面的例子将把名为 movieFinder 的 bean 注入到它的 setter 方法中:

public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Resource
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
}

与注解一起提供的名称由 CommonAnnotationBeanPostProcessor 知道的 ApplicationContext 解析为 bean 名称。 如果您显式配置 Spring 的 SimpleJndiBeanFactory,则可以通过 JNDI 解析名称。 但是,我们建议您依赖默认行为并使用 Spring 的 JNDI 查找功能来保留间接级别。

在没有指定显式名称的 @Resource 用法的唯一情况下,与 @Autowired 类似,@Resource 查找主要类型匹配而不是特定命名的 bean 并解析众所周知的可解析依赖项:BeanFactory、ApplicationContext、ResourceLoader、ApplicationEventPublisher 和 MessageSource 接口。

因此,在以下示例中,customerPreferenceDao 字段首先查找名为“customerPreferenceDao”的 bean,然后回退到 CustomerPreferenceDao 类型的主要类型匹配:

public class MovieRecommender {

    @Resource
    private CustomerPreferenceDao customerPreferenceDao;

    // 根据已知的可解析依赖类型注入上下文字段:应用上下文。
    @Resource
    private ApplicationContext context; 

    public MovieRecommender() {
    }

    // ...
}
10.8 使用 @Value

@Value 通常用于注入外化属性:

@Component
public class MovieRecommender {

    private final String catalog;

    public MovieRecommender(@Value("${catalog.name}") String catalog) {
        this.catalog = catalog;
    }
}

使用以下配置:

@Configuration
@PropertySource("classpath:application.properties")
public class AppConfig { }

以及以下 application.properties 文件:

catalog.name=MovieCatalog

在这种情况下,catalog 参数和字段将等于 MovieCatalog 值。

Spring 提供了一个默认的宽松嵌入值解析器。 它将尝试解析属性值,如果无法解析,则属性名称(例如 ${catalog.name})将作为值注入。 如果你想对不存在的值保持严格的控制,你应该声明一个 PropertySourcesPlaceholderConfigurer bean,如以下示例所示:

@Configuration
public class AppConfig {

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

使用 JavaConfig 配置 PropertySourcesPlaceholderConfigurer 时,@Bean 方法必须是静态的。

如果无法解析任何 ${} 占位符,则使用上述配置可确保 Spring 初始化失败。 也可以使用 setPlaceholderPrefix、setPlaceholderSuffix 或 setValueSeparator 等方法来自定义占位符。

Spring Boot 默认配置一个 PropertySourcesPlaceholderConfigurer bean,它将从 application.properties 和 application.yml 文件中获取属性。

Spring 提供的内置转换器支持允许自动处理简单的类型转换(例如到 Integer 或 int)。 多个逗号分隔的值可以自动转换为字符串数组,无需额外的工作。

可以提供如下默认值:

@Component
public class MovieRecommender {

    private final String catalog;

    public MovieRecommender(@Value("${catalog.name:defaultCatalog}") String catalog) {
        this.catalog = catalog;
    }
}

Spring BeanPostProcessor 在后台使用 ConversionService 来处理将@Value 中的 String 值转换为目标类型的过程。 如果您想为您自己的自定义类型提供转换支持,您可以提供您自己的 ConversionService bean 实例,如下例所示:

@Configuration
public class AppConfig {

    @Bean
    public ConversionService conversionService() {
        DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();
        conversionService.addConverter(new MyCustomConverter());
        return conversionService;
    }
}

当@Value 包含 SpEL 表达式时,该值将在运行时动态计算,如下例所示:

@Component
public class MovieRecommender {

    private final String catalog;

    public MovieRecommender(@Value("#{systemProperties['user.catalog'] + 'Catalog' }") String catalog) {
        this.catalog = catalog;
    }
}

SpEL 还支持使用更复杂的数据结构:

@Component
public class MovieRecommender {

    private final Map countOfMoviesPerCatalog;

    public MovieRecommender(
            @Value("#{{'Thriller': 100, 'Comedy': 300}}") Map countOfMoviesPerCatalog) {
        this.countOfMoviesPerCatalog = countOfMoviesPerCatalog;
    }
}
10.9 使用 @PostConstruct 和 @PreDestroy

CommonAnnotationBeanPostProcessor不仅能识别@Resource 注解,还能识别JSR-250 生命周期注解:javax.annotation.PostConstruct 和javax.annotation.PreDestroy。

在 Spring 2.5 中引入,对这些注解的支持提供了初始化回调和销毁回调中描述的生命周期回调机制的替代方案。 假设 CommonAnnotationBeanPostProcessor 已在 Spring ApplicationContext 中注册,则在生命周期中与相应的 Spring 生命周期接口方法或显式声明的回调方法相同的点调用带有这些注解之一的方法。 在以下示例中,缓存在初始化时预填充并在销毁时清除:

public class CachingMovieLister {

    @PostConstruct
    public void populateMovieCache() {
        // populates the movie cache upon initialization...
    }

    @PreDestroy
    public void clearMovieCache() {
        // clears the movie cache upon destruction...
    }
}

各种生命周期机制组合的效果请参见生命周期机制组合。

与@Resource 一样,@PostConstruct 和 @PreDestroy注解类型是 JDK 6 到 8 的标准 Java 库的一部分。然而,整个 javax.annotation 包在 JDK 9 中与核心 Java 模块分离,最终在 JDK 11 中删除 。如果需要,现在需要通过 Maven Central 获取 javax.annotation-api 工件,只需像任何其他库一样将其添加到应用程序的类路径中即可。

文章参考:https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-autowired-annotation-qualifiers

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5671512.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-16
下一篇 2022-12-16

发表评论

登录后才能评论

评论列表(0条)

保存