spring5.0 之@Primary注解的应用
Posted Dreamer who
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring5.0 之@Primary注解的应用相关的知识,希望对你有一定的参考价值。
spring5.0 之@Primary注解的应用
在spring容器中,如果同一个类型有多个实例,但我们需要注入一个的时候,我们必须采取措施,不然spring容器 会报错:....required a single bean, but 2 were found:......... 有时候我们能保证同一个类型在spring容器中只有一个实例,有时候我们保证不了,此时不讨论by name注入。这 个时候@Primary注解就非常重要了。
org.springframework.context.annotation
Annotation Type Primary
@Target(value=TYPE,METHOD)
@Retention(value=RUNTIME)
@Inherited
@Documented
public @interface Primary
Indicates that a bean should be given preference when multiple candidates are qualified to autowire a single-valued dependency. If exactly one 'primary' bean exists among the candidates, it will be the autowired value.
This annotation is semantically equivalent to the <bean> element's primary attribute in Spring XML.
May be used on any class directly or indirectly annotated with @Component or on methods annotated with @Bean.
Example
@Component
public class FooService
private FooRepository fooRepository;
@Autowired
public FooService(FooRepository fooRepository)
this.fooRepository = fooRepository;
@Component
public class JdbcFooRepository
public JdbcFooService(DataSource dataSource)
// ...
@Primary
@Component
public class HibernateFooRepository
public HibernateFooService(SessionFactory sessionFactory)
// ...
Because HibernateFooRepository is marked with @Primary, it will be injected preferentially over the jdbc-based variant assuming both are present as beans within the same Spring application context, which is often the case when component-scanning is applied liberally.
Note that using @Primary at the class level has no effect unless component-scanning is being used. If a @Primary-annotated class is declared via XML, @Primary annotation metadata is ignored, and <bean primary="true|false"/> is respected instead.
Since:
3.0
Author:
Chris Beams
See Also:
Lazy, Bean, ComponentScan, Component
其实@Primary注解的实例优先于其他实例被注入。
下面看一个不得不使用@Primary注解的例子。
在spring security oauth2 资源服务器配置的时候,我们需要实例化一个ResourceServerTokenServices,该类主要从checkTokenEndpointUrl 地址校验用户登录的token。
spring boot 按照默认配置,会自动创建一个RemoteTokenServices,代码在ResourceServerTokenServicesConfiguration中的114行左右:
@Configuration
@Conditional(RemoteTokenCondition.class)
protected static class RemoteTokenServicesConfiguration
@Configuration
@Conditional(TokenInfoCondition.class)
protected static class TokenInfoServicesConfiguration
private final ResourceServerProperties resource;
protected TokenInfoServicesConfiguration(ResourceServerProperties resource)
this.resource = resource;
@Bean
public RemoteTokenServices remoteTokenServices()
RemoteTokenServices services = new RemoteTokenServices();
services.setCheckTokenEndpointUrl(this.resource.getTokenInfoUri());
services.setClientId(this.resource.getClientId());
services.setClientSecret(this.resource.getClientSecret());
return services;
我想自己创建一个自己的UserInfoTokenServices,如:
package com.sdcuike.spring.security;
import org.springframework.boot.autoconfigure.security.oauth2.resource.UserInfoTokenServices;
/**
* Created by beaver on 2017/6/12.
* <p>
* 获取用户信息-> oid
*
* @see https://stackoverflow.com/questions/35056169/how-to-get-custom-user-info-from-oauth2-authorization-server-user-endpoint/35092561
*/
public class RichUserInfoTokenServices extends UserInfoTokenServices
public RichUserInfoTokenServices(String userInfoEndpointUrl, String clientId)
super(userInfoEndpointUrl, clientId);
public RichUserInfoTokenServices(String userInfoEndpointUrl, String clientId, RichUserPrincipalExtractor richUserPrincipalExtractor)
super(userInfoEndpointUrl, clientId);
setPrincipalExtractor(richUserPrincipalExtractor);
按照spring boot java config方式创建给bean:
package com.sdcuike.practice.config.security;
import com.sdcuike.spring.security.RichUserDetails;
import com.sdcuike.spring.security.RichUserInfoTokenServices;
import com.sdcuike.spring.security.RichUserPrincipalExtractor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.security.oauth2.resource.PrincipalExtractor;
import org.springframework.boot.autoconfigure.security.oauth2.resource.ResourceServerProperties;
import org.springframework.boot.autoconfigure.security.oauth2.resource.UserInfoTokenServices;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
import java.util.Map;
/**
* Created by beaver on 2017/6/12.
* <p>
* 获取用户信息-> oid
*
* @see https://stackoverflow.com/questions/35056169/how-to-get-custom-user-info-from-oauth2-authorization-server-user-endpoint/35092561
*/
@Configuration
public class UserInfoTokenServicesConfig
@Value("$security.oauth2.resource.clientId")
private String clientId;
@Autowired
private ResourceServerProperties sso;
@Bean
// @Primary
public ResourceServerTokenServices richUserInfoTokenServices()
return new RichUserInfoTokenServices(sso.getUserInfoUri(), clientId, new RichUserPrincipalExtractor());
如果把@Primary注解注释掉,启动会报错:
***************************
APPLICATION FAILED TO START
***************************
Description:
Method springSecurityFilterChain in org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration required a single bean, but 2 were found:
- richUserInfoTokenServices: defined by method 'richUserInfoTokenServices' in class path resource [com/sdcuike/practice/config/security/UserInfoTokenServicesConfig.class]
- remoteTokenServices: defined by method 'remoteTokenServices' in class path resource [org/springframework/boot/autoconfigure/security/oauth2/resource/ResourceServerTokenServicesConfiguration$RemoteTokenServicesConfiguration$TokenInfoServicesConfiguration.class]
Action:
Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed
-
但实例化的remoteTokenServices我们无法控制(如果有,请通知俺),我们必须是使用@Primary注解,让我们控制哪个实例优先被注入。
博文相关代码见:https://github.com/sdcuike/spring-boot-oauth2-demo/blob/blog-2017-07-18/spring-boot-oauth-resource-server/src/main/java/com/sdcuike/practice/config/security/UserInfoTokenServicesConfig.java
以上是关于spring5.0 之@Primary注解的应用的主要内容,如果未能解决你的问题,请参考以下文章
JAVAEE框架整合技术之Spring02-AOP面向切面编程技术
JAVAEE框架整合技术之Spring02-AOP面向切面编程技术