@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行
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 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字段。