1. 概述
在 Spring Boot常用注解(一) - 声明Bean的注解 中学习了Spring Boot中声明Bean的注解
那Spring容器中的Bean是怎么实现自动装配(依赖)的呢?
这就是接下来学习的注入注解咯
注入Bean的注解:
- @Autowired
- @Inject
- @Resource
2. @Autowired注解
@Autowired注解源码:
package org.springframework.beans.factory.annotation;
/**
1. @since 2.5
2. @see AutowiredAnnotationBeanPostProcessor
3. @see Qualifier
4. @see Value
*/
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
boolean required() default true;
}
- @Autowired注解作用在构造函数、方法、方法参数、类字段以及注解上
- @Autowired注解可以实现Bean的自动注入
2.1 @Autowired注解原理
在Spring Boot应用启动时,Spring容器会自动装载一个org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor处理器,当容器扫扫描到@Autowired注解时,就会在IoC容器就会找相应类型的Bean,并且实现注入。
2.2 @Autowired注解使用
在Web MVC中控制层(Controller)访问的是业务层(Service),而业务层(Service)访问的是数据层(Dao),使用@Autowired注解实现注入
数据层:
package com.example.demo.chapter1.useannotation.autowired.dao;
/**
* 数据层接口,用于访问数据库,返回数据给业务层
* */
public interface IDao {
public String get();
}
/**
* 数据层接口实现类
*
* 1.数据层Bean用@Repository标注
* 2.当前Bean的名称是"autowiredUserDaoImpl"
* 3.设置当前Bean的为原型模式,即每次获取Bean时都创建一个新实例
* */
@Repository("autowiredUserDaoImpl")
@Scope("prototype")
public class UserDaoImpl implements IDao {
public String get() {
return "@Autowired注解实现自动装配";
}
}
业务层:
package com.example.demo.chapter1.useannotation.autowired.service;
/**
* 业务层接口
* 从数据层获取数据,处理结果返回给控制层
* */
public interface IService {
public String get();
}
/**
* 业务层接口实现
*
* 1.数据层Bean用@Service标注
* 2.当前Bean的名称是"autowiredUserServiceImpl"
* 3.设置当前Bean的为原型模式,即每次获取Bean时都创建一个新实例
* 4.业务层有一个数据层接口IDao,使用@Autowired注解标注
* */
@Service("autowiredUserServiceImpl")
@Scope("prototype")
public class UserServiceImpl implements IService {
/**
* @Autowired实现自动装配
* Spring IoC容器扫描到@Autowired注解会去查询IDao的实现类,并自动注入
* */
@Autowired
private IDao dao;
@Override
public String get() {
return dao.get();
}
}
- Bean通过注解@Service声明为一个Spring容器管理的Bean,Spring容器会扫描classpath路径下的所有类,找到带有@Service注解的UserServiceImpl类,并根据Spring注解对其进行初始化和增强,命名为autowiredUserServiceImpl
- Spring容器如果发现此类属性dao也有注解@Autowired,则会从Spring容器中查找一个已经初始化好的IDao的实现类,如果没有找到,则先初始化一个IDao实现类
控制层:
package com.example.demo.chapter1.useannotation.autowired.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.example.demo.chapter1.useannotation.autowired.service.IService;
/**
1. 控制层
2.
3. 1.控制层使用@RestController注解标注,返回json格式的字符串
4. 2.获取业务层返回的数据,输出到客户端
5. 3.请求:http:localhost:8080/autowiredController
6. */
@RestController
public class AutowiredController {
@Autowired
private IService service;
@RequestMapping("/autowiredController")
public String get () {
return service.get();
}
}
测试结果:
- 启动Spring Boot应用
- 浏览器输入:http:localhost:8080/autowiredController
-
返回结果:
2.3 @Qualifier注解
@Qualifier注解源码:
package org.springframework.beans.factory.annotation;
/**
* @since 2.5
* @see Autowired
*/
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Qualifier {
String value() default "";
}
- 如果Spring 容器中有两个数据层接口IDao的实现类
- 而业务层通过@Autowired注解标注的是IDao接口
- 这时就需要@Qualifier注解来指定需要自动装配的Bean的名称
- 否则会报如下的错:
Description:
Field dao in com.example.demo.chapter1.useannotation.autowired.service.UserServiceImpl required a single bean, but 2 were found:
- autowiredAdminDaoImpl: defined in file [D:\eclipse\workspace\boot\demo\target\classes\com\example\demo\chapter1\useannotation\autowired\dao\AdminDaoImpl.class]
- autowiredUserDaoImpl: defined in file [D:\eclipse\workspace\boot\demo\target\classes\com\example\demo\chapter1\useannotation\autowired\dao\UserDaoImpl.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
2.4 @Qualifier注解使用
数据层接口IDao:
public interface IDao {
public String get();
}
IDao实现类 - UserDaoImpl
@Repository("autowiredUserDaoImpl")
@Scope("prototype")
public class UserDaoImpl implements IDao {
public String get() {
return "@Autowired注解实现自动装配 - UserDaoImpl";
}
}
IDao实现类 - AdminDaoImpl
@Repository("autowiredAdminDaoImpl")
@Scope("prorotype")
public class AdminDaoImpl implements IDao {
@Override
public String get() {
return "@Autowired注解实现自动装配 - AdminDaoImpl";
}
}
@Qualifier注解使用:
@Service("autowiredUserServiceImpl")
@Scope("prototype")
public class UserServiceImpl implements IService {
/**
* @Autowired实现自动装配
* Spring IoC容器扫描到@Autowired注解会去查询IDao的实现类,并自动注入
* */
@Autowired
@Qualifier("autowiredUserDaoImpl")
private IDao dao;
@Override
public String get() {
return dao.get();
}
}
测试结果:
2.5 @Autowired注解的requird属性
在使用@Autowired注解时,首先在容器中查询对应类型的bean
- 如果查询结果Bean刚好为一个,自动注入
- 如果查询结果Bean不止一个,通过@Qualifier注解指定自动装配Bean的名称
- 如果没有查询到对应类型的Bean,由于默认@Autowired(required=true),会抛出异常,解决方法是使用@Autoiwired(quired=false)
- @Autowired(quired=true)意味着依赖是必须的
- @Autowired(quired=false)等于告诉 Spring:在找不到匹配 Bean 时也不报错
数据层接口:
package com.example.demo.chapter1.useannotation.autowired.dao;
/**
* 数据层接口
* */
public interface ITask {
public String get();
}
业务层接口:
package com.example.demo.chapter1.useannotation.autowired.service;
@Service("taskServiceImpl")
@Scope("prototype")
public class TaskServiceImpl implements IService {
@Autowired
private ITask task;
@Override
public String get() {
return task.get();
}
}
如果没有接口ITask的实现类,启动Spring Boot应用会报如下错:
没有找到ITask的实现类
Description:
Field task in com.example.demo.chapter1.useannotation.autowired.service.TaskServiceImpl required a bean of type 'com.example.demo.chapter1.useannotation.autowired.dao.ITask' that could not be found.
Action:
Consider defining a bean of type 'com.example.demo.chapter1.useannotation.autowired.dao.ITask' in your configuration.
解决方法 - 添加required=false
@Service("autowiredTaskServiceImpl")
@Scope("prototype")
public class TaskServiceImpl implements IService {
/**
* 接口ITask没有实现类
* 添加required=false不报错
* */
@Autowired(required=false)
private ITask task;
@Override
public String get() {
return task.get();
}
}
>推荐一下我的公众号: 【geekjc】,一起学习交流编程知识,分享经验,各种有趣的事,更多精彩内容,扫码进入小程序。