《Spring 高手系列》(@Autowired)(@Resource)(@Qualifier)(@Primary)笔记

参考链接1

@Autowired

@Target({ElementType.CONSTRUCTOR, ElementType.METHOD,
 ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
	//是否必须,默认为true
	boolean required() default true;

}

修饰范围:构造器、方法、字段、方法参数、注解。

1:构造函数

    @Autowired
    public Service2(Service1 service1) {
        this.service1 = service1;
    }

2:普通方法

    @Autowired
    public void injectService1(Service1 service1) {
        this.service1 = service1;
    }

3:set方法

    @Autowired
    public void setService1(Service1 service1) {
        this.service1 = service1;
    }

4:方法参数

当方法级别有注解的时候默认所有字段都会注入,假设我们没有String类型的字段,可以在字段上将required 设为false,这样可以在容器没有String的时候不会报错。

    @Autowired
    public void injectService1(@Autowired Service1 service1, @Autowired(required = false) String name) { 
        this.service1 = service1;
    }

5:字段

@Component
public class Service3 {

    @Autowired
    private Service1 service1;

    @Autowired
    private Service2 service2;

    @Override
    public String toString() {
        return "Service3{" +
                "service1=" + service1 +
                ", service2=" + service2 +
                '}';
    }
}

小总结:

基本用法主要就是三种:字段、构造函数、set方法。普通方法和方法字段了解即可。

多候选者

当一个接口有多个类型的时候,使用名字作为区分,例如service1。

public interface IService {}

@Component
public class Service0 implements IService {}

@Component
public class Service1 implements IService {}
@Component
public class Service2 {

    @Autowired
    private IService service1;

    @Override
    public String toString() {
        return "Service2{" +
                "service1=" + service1 +
                '}';
    }
}

@Resource

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

    String 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 "";
}

案例1:将@Resource标注在字段上

IService

public interface IService {
}

Service0

@Component
public class Service0 implements IService {
}

Service1

@Component
public class Service1 implements IService {
}

Service2

@Component
public class Service2 {

    @Resource
    private IService service1;//@1

    @Override
    public String toString() {
        return "Service2{" +
                "service1=" + service1 +
                '}';
    }
}

Client

@ComponentScan
@ComponentScan
public class Client {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Client.class);
        for (String beanName : context.getBeanDefinitionNames()) {
            System.out.println(String.format("%s->%s", beanName, context.getBean(beanName)));
        }
    }
}
service0->com.example.lurenjia.spring.c20.al6.Service0@7f9fcf7f
service1->com.example.lurenjia.spring.c20.al6.Service1@2357d90a
service2->Service2{service1=com.example.lurenjia.spring.c20.al6.Service1@2357d90a}

修改部分代码,将service1改为service0

    @Resource
    private IService service0;
service0->com.example.lurenjia.spring.c20.al6.Service0@7f9fcf7f
service1->com.example.lurenjia.spring.c20.al6.Service1@2357d90a
service2->Service2{service0=com.example.lurenjia.spring.c20.al6.Service0@7f9fcf7f}

查找的过程中会根据候选类型的对象名字来匹配,修改前寻找IService类型Service1,修改后寻找Service0。

@Qualifier:限定符

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

	String value() default "";

}

基本信息:作用在字段、方法、方法参数、类型这几个地方,@Inherited 代表注解修饰类的子类同样有该注解。

官方示例

public class MovieRecommender {

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

    // ...
}
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;
    }

    // ...
}

案例1:用在类上

public interface IService {
}
@Component
@Qualifier("tag1") 
public class Service1 implements IService {
}
@Component
@Qualifier("tag1")
public class Service2 implements IService {
}
@Component
@Qualifier("tag2")//@1
public class Service3 implements IService {
}
@Component
public class InjectService {

    @Autowired
    @Qualifier("tag1")
    private java.util.Map<String, IService> serviceMap1;

    @Autowired
    @Qualifier("tag2")
    private java.util.Map<String, IService> serviceMap2;

    @Override
    public String toString() {
        return "InjectService{" +
                "serviceMap1=" + serviceMap1 +
                ", serviceMap2=" + serviceMap2 +
                '}';
    }
}
@ComponentScan
public class Client {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Client.class);
        for (String beanName : context.getBeanDefinitionNames()) {
            System.out.println(String.format("%s->%s", beanName, context.getBean(beanName)));
        }
    }
}
injectService->InjectService{serviceMap1={
service1=com.example.lurenjia.spring.c20.qulifier.d1.Service1@35083305, 
service2=com.example.lurenjia.spring.c20.qulifier.d1.Service2@8e0379d},
 serviceMap2={service3=com.example.lurenjia.spring.c20.qulifier.d1.Service3@341b80b2}}

案例2:@Autowired结合@Qulifier指定注入的bean

public interface IService {
}
@Component
public class Service1 implements IService {
}
@Component
public class Service2 implements IService {
}
@Component
public class InjectService {

    @Autowired
    @Qualifier("service2")
    private IService service;

    @Override
    public String toString() {
        return "InjectService{" +
                "service=" + service +
                '}';
    }
}
@ComponentScan
public class Client {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Client.class);
        for (String beanName : context.getBeanDefinitionNames()) {
            System.out.println(String.format("%s->%s", beanName, context.getBean(beanName)));
        }
    }
}
injectService->InjectService{service=com.example.lurenjia.spring.c20.qulifier.d2.Service2@4de5031f}

修改为 service1

    @Qualifier("service1")

输出

injectService->InjectService{service=com.example.lurenjia.spring.c20.qulifier.d2.Service1@4de5031f}

案例3:用在方法参数上

新增类 InjectService2 s1由 service2 注入而 s2 由 service1 注入

@Component
public class InjectService2 {
    private IService s1;
    private IService s2;

    @Autowired
    public void injectBean(@Qualifier("service2") IService s1, @Qualifier("service1") IService s2) { //@1
        this.s1 = s1;
        this.s2 = s2;
    }

    @Override
    public String toString() {
        return "InjectService2{" +
                "s1=" + s1 +
                ", s2=" + s2 +
                '}';
    }
}
injectService2->InjectService2{s1=com.example.lurenjia.spring.c20.qulifier.d2.Service2@14a2f921, 
s2=com.example.lurenjia.spring.c20.qulifier.d2.Service1@212bf671}

结果如预期所示,s1 = Service2 s2 = Service1

案例4:用在setter方法上

@Component
public class InjectService3 {

    private IService s1;
    private IService s2;

    @Autowired
    @Qualifier("service2")
    public void setS1(IService s1) {
        this.s1 = s1;
    }

    @Autowired
    @Qualifier("service2")
    public void setS2(IService s2) {
        this.s2 = s2;
    }

    @Override
    public String toString() {
        return "InjectService{" +
                "s1=" + s1 +
                ", s2=" + s2 +
                '}';
    }
}

输出

injectService3->InjectService{s1=com.example.lurenjia.spring.c20.qulifier.d2.Service2@17c386de, 
s2=com.example.lurenjia.spring.c20.qulifier.d2.Service2@17c386de}

@Primary:设置为主要候选者

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Primary {

}

作用在类型和方法上

官方示例

@Configuration
public class MovieConfiguration {

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

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

    // ...
}

优先注入@Primary修饰的bena

案例1:用在类上


public interface IService {
}
@Component
public class Service1 implements IService {
}
@Component
@Primary
public class Service2 implements IService {
}
@Component
public class InjectService {

    @Autowired
    private IService service1; //@1

    @Override
    public String toString() {
        return "InjectService{" +
                "service1=" + service1 +
                '}';
    }
}
@ComponentScan
public class Client {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Client.class);
        for (String beanName : context.getBeanDefinitionNames()) {
            System.out.println(String.format("%s->%s", beanName, context.getBean(beanName)));
        }
    }
}
injectService->InjectService{service1=com.example.lurenjia.spring.c20.primary.d1.Service2@75f9eccc}

案例2:用在方法上,结合@Bean使用

public interface IService {
}
public class Service1 implements IService {
}
public class Service2 implements IService {
}
public class InjectService {

    @Autowired
    private IService service1;//@1

    @Override
    public String toString() {
        return "InjectService{" +
                "service1=" + service1 +
                '}';
    }
}
@Configuration
public class MainConfig13 {

    @Bean
    public IService service1() {
        return new Service1();
    }

    @Bean
    @Primary
    public IService service2() {
        return new Service2();
    }

    @Bean
    public InjectService injectService() {
        return new InjectService();
    }
}
@ComponentScan
public class Client {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Client.class);
        for (String beanName : context.getBeanDefinitionNames()) {
            System.out.println(String.format("%s->%s", beanName, context.getBean(beanName)));
        }
    }
}
injectService->InjectService{service1=com.example.lurenjia.spring.c20.primary.d2.Service2@50a638b5}

由于MainConfig13中指定了 @Primary 所以优先注入 Service2

@Bean定义bean时注入依赖的几种方式

方式1:硬编码方式

public class Service1 {
}
public class Service2 {
}
public class Service3 {
    private Service1 service1;
    private Service2 service2;

    public Service1 getService1() {
        return service1;
    }

    public void setService1(Service1 service1) {
        this.service1 = service1;
    }

    public Service2 getService2() {
        return service2;
    }

    public void setService2(Service2 service2) {
        this.service2 = service2;
    }

    @Override
    public String toString() {
        return "Service3{" +
                "service1=" + service1 +
                ", service2=" + service2 +
                '}';
    }
}
@Configuration
public class MainConfig14 {
    @Bean
    public Service1 service1() {
        return new Service1();
    }

    @Bean
    public Service2 service2() {
        return new Service2();
    }

    @Bean
    public Service3 service3() {
        Service3 service3 = new Service3(); //@0
        service3.setService1(this.service1()); //@1
        service3.setService2(this.service2()); //@2
        return service3;
    }
}
@ComponentScan
public class Client {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Client.class);
        for (String beanName : context.getBeanDefinitionNames()) {
            System.out.println(String.format("%s->%s", beanName, context.getBean(beanName)));
        }
    }
}
service3->Service3{service1=com.example.lurenjia.spring.c20.bean.d1.Service1@10e41621, 
service2=com.example.lurenjia.spring.c20.bean.d1.Service2@353d0772}

方式2:@Autowired、@Resource的方式

方式3:@Bean标注的方法使用参数来进行注入

@Configuration
public class MainConfig15 {
    @Bean
    public Service1 service1() {
        return new Service1();
    }

    @Bean
    public Service2 service2() {
        return new Service2();
    }

    @Bean
    public Service3 service3(Service1 s1, Service2 s2) { //@0
        Service3 service3 = new Service3();
        service3.setService1(s1); //@1
        service3.setService2(s2); //@2
        return service3;
    }
}
service1->com.example.lurenjia.spring.c20.bean.d1.Service1@30ee2816
service2->com.example.lurenjia.spring.c20.bean.d1.Service2@31d7b7bf
service3->Service3{service1=com.example.lurenjia.spring.c20.bean.d1.Service1@30ee2816, service2=com.example.lurenjia.spring.c20.bean.d1.Service2@31d7b7bf}

@Bean标注的方法参数上使用@Autowired注解

    @Bean
    public Service3 service3_0(Service1 s1, @Autowired(required = false) Service2 s2) { //@0
        Service3 service3 = new Service3();
        service3.setService1(s1); //@1
        service3.setService2(s2); //@2
        return service3;
    }

@Bean结合@Qualifier

泛型注入

public interface IDao<T> {
}
public class BaseService<T> {
    @Autowired
    private IDao<T> dao; //@1

    public IDao<T> getDao() {
        return dao;
    }

    public void setDao(IDao<T> dao) {
        this.dao = dao;
    }
}
@ComponentScan
public class Client {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Client.class);
        for (String beanName : context.getBeanDefinitionNames()) {
            System.out.println(String.format("%s->%s", beanName, context.getBean(beanName)));
        }
    }
}
@Component
public class OrderService extends BaseService<OrderModel> {//@1
}
public class OrderModel {
}
public class UserModel {
}
@Component
public class UserService extends BaseService<UserModel> {//@1
}
@Component
public class OrderDao implements IDao<OrderModel> {//@1
}
@Component
public class UserDao implements IDao<UserModel> { //@1
}
userDao->com.example.lurenjia.spring.c20.type.UserDao@2145433b
userService->com.example.lurenjia.spring.c20.type.UserService@2890c451

常见面试问题

Autowired和Resource有什么区别

Autowired查找原理

Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException:
 No qualifying bean of type 'com.example.lurenjia.spring.c20.primary.d1.IService' available: expected single matching bean but found 2: service1,service2
	at org.springframework.beans.factory.config.DependencyDescriptor.resolveNotUnique(DependencyDescriptor.java:220)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1358)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1300)
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:640)
	... 14 more

定位到 AutowiredAnnotationBeanPostProcessor 这样一个类的 640行

面试难题:@Autowired 注解是如何实现的?

Resource查找原理

Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException:
 No qualifying bean of type 'com.example.lurenjia.spring.c20.primary.d1.IService' available: expected single matching bean but found 2: service1,service2
	at org.springframework.beans.factory.config.DependencyDescriptor.resolveNotUnique(DependencyDescriptor.java:220)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1358)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1300)
	at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.autowireResource(CommonAnnotationBeanPostProcessor.java:521)
	at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.getResource(CommonAnnotationBeanPostProcessor.java:497)
	at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor$ResourceElement.getResourceToInject(CommonAnnotationBeanPostProcessor.java:650)
	at org.springframework.beans.factory.annotation.InjectionMetadata$InjectedElement.inject(InjectionMetadata.java:228)
	at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119)
	at org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessProperties(CommonAnnotationBeanPostProcessor.java:318)
	... 12 more

定位到 CommonAnnotationBeanPostProcessor 这样一个类的 521行

@Qulifierd 作用

@Primary 作用

Spring为什么建议构造器注入

Spring为什么建议构造器注入?

在这里插入图片描述
在这里插入图片描述

Spring Team recommends "Always use constructor based dependency injection in your beans.
Always use assertions for mandatory dependencies".

Spring团队建议“在beans中始终使用基于构造函数的依赖注入。 总是对强制依赖使用断言。

我们去官网寻找答案:

https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-setter-injection


The Spring team generally advocates constructor injection, as it lets you implement application components as immutable objects and ensures that required dependencies are not null. Furthermore, constructor-injected components are always returned to the client (calling) code in a fully initialized state. As a side note, a large number of constructor arguments is a bad code smell, implying that the class likely has too many responsibilities and should be refactored to better address proper separation of concerns.

构造函数强调不可变。


Setter injection should primarily only be used for optional dependencies that can be assigned reasonable default values within the class. Otherwise, not-null checks must be performed everywhere the code uses the dependency. One benefit of setter injection is that setter methods make objects of that class amenable to reconfiguration or re-injection later. Management through JMX MBeans is therefore a compelling use case for setter injection.

set方式强调可选依赖。

文章里面说的单一职责原则其实不对,因为你的类注入太多依赖跟你使用字段还是构造器没有关系,跟开发者有关系。

总结:

  • use constructors for mandatory dependencies (强制依赖使用构造函数)
  • setter methods or configuration methods for optional dependencies(可选依赖项的setter方法或配置方法)
  • 字段注入时,不能声明为final,而构造函数可以声明为final。本质区别就是能否维护final字段。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值