Spring 中用注解来存储和注入 Bean 对象
1.存储 Bean 对象
1.1 配置环境
在上一篇中👆介绍了如何创建一个Spring项目,并且涉及到如何通过<bean>
标签来存储Bean
对象,但是通过这种方式来存入Spring
是非常麻烦的,假如我有100
个对象需要存入Spring
中,那我岂不是要写100
个bean
标签?

然后再创建于一个启动类:

在包下面创建一个Bean
对象:

好,怎么用注解来存取Bean
对象呢?
第一步,需要在配置文件spring-config.xml
(这是自己创建的,详细的看上面的博客)中替换为下面的代码:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:content="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<content:component-scan base-package="com.spring.demo1"></content:component-scan>
</beans>
(如果你看了上一篇文章,你会发现只是多了这两行代码:
xmlns:content="http://www.springframework.org/schema/context……"
<content:component-scan base-package="com.spring.demo1"></content:component-scan>
)
这里的</content:component-scan>
标签就是用来指定需要扫描哪一路径的,也就是说,Spring
会扫描该路径下有哪些类是加了注解的,把加了注解的类存入Spring
容器中,这个路径是你自己定的。
1.2 添加注解存储 Bean 对象
想要将对象存储在Spring
中,有两种注解类型可以实现:
(1)类注解:@Controller
、@Service
、@Repository
、@Component
、@Configuration
。
(2)⽅法注解:@Bean
。
1.2.1 @Controller
它的格式如下:
@Controller //讲对象存储到 Spring 中
public class StudentController {
public void hi(){
System.out.println("你好,我是 StudentController 的 hi 方法!");
}
}

获取它:
public class Main {
public static void main(String[] args) {
//创建一个上下文(Spring容器)
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
//获取 Bean 对象
StudentController student = context.getBean(StudentController.class);
student.hi();
}
}

1.2.2 @Service
再创建一个Bean
对象,加上@Service
注解。
获取:
1.2.2 @Repository、@Component、@Configuration
@Repository
、@Component
、@Configuration
,这些注解与上面的用法是一样的,这里就不再赘述。
1.2.3 这 5 种注解有什么不同?
上面的5
种注解既然都能将Bean
对象存入容器中,那它们有什么区别呢?
当我们想要组装一个机器人时,需要使用不同的零件和工具,例如电机、齿轮、螺丝等。这些零件和工具各自有不同的作用,电机负责提供动力,齿轮用于传递动力,螺丝用于连接零件等。
类比到 Spring
中的注解,@Controller
、@Service
、@Repository
、@Component
、@Configuration
就像这些零件和工具一样,它们各自有不同的作用:
@Controller
:用于标识一个类是Spring MVC
框架中的控制器(Controller)
,处理HTTP请求并返回响应给客户端。@Service
:用于标识一个类是业务逻辑层(Service)
,包含了应用程序中的业务逻辑处理代码。@Repository
:用于标识一个类是数据访问层(DAO,Data Access Object)
,用于访问数据库和其他数据存储介质。@Component
:用于标识一个类是Spring
框架中的组件,一般用于定义那些没有明确角色的类。@Configuration
:用于标识一个类是Spring
的配置类,它可以包含多个bean
的定义。
它们就像一个标签,用来表示当前这个类是用来干嘛的,它们是一种规范、约定。

1.2.4 Bean 命名规则
我们已经了解了类注解的用法,那这里再来讲一讲关于取Bean对象
的一些知识。上面我们取Bean对象
是用了下面的方式:
//通过类型来接受
StudentService student1 = context.getBean(StudentService.class);
student1.hi();
还有其它方法,就是用类名的方式来取Bean对象
:
//方法一:用类的名字 + 类的类型
StudentService student1 = context.getBean("studentService",StudentService.class);
student1.hi();
//方法二:用类的名字
StudentService student1 = (StudentService)context.getBean("studentService");
student1.hi();
问题来了,这里的名字"studentService"
是正确的吗?在上一篇文章中,介绍的是用<bean id="" class="">
的方式来存Bean
对象的,这里的id
就是容器中对象的名称。但是在本篇中是用</content:component-scan>
标签来指定待存储对象的路径,并没有指定其名称,那怎么取对象呢?先不管,我们来试一试。
结果:
发现能运行成功,我们再改一下名字,这次改为大驼峰:
结果:
报错了,这就说明在取对象的时候,名字要用小驼峰,但事实真是这样的吗?我们再创建一个SController
类:
再来取对象:
唉?报错了,我们换回大驼峰试一试:
成功了。
总结:默认情况下,使用原类名首字母小写(小驼峰)可以读取到 Bean
对象;当原类名首字母与第二个字母为大写的情况下,就用原类名(大驼峰)来读取 Bean
对象。
当然,有一种办法可以指定类的名称:
5
个注解都可以这样指定,这时我们再来:
1.2.5 方法注解 @Bean
方法注解就是用来存方法返回的对象的,它是加在方法上的,并且这个方法必须得有返回值。
我们来创建一个实体类User
并完善get
、set
方法 和 一个操作实体类的类UserBeans
:
在类UserBeans
中有一个方法用来创建一个User
对象,并对它加上Bean
注解把返回值存入到Spring
容器中:
然后我们再到Main
中来看看它有没有存入成功。
结果:
发现报错了,为什么?造成这个错误的原因有两个:
@Bean
注解的命名规则与类注解
的命名规则不一样,这里的命名规则是加了注解的那个方法的方法名
:
//获取一个 User 对象,这里必须跟方法名相同: user1
User user = context.getBean("user1", User.class);
@Bean
注解必须搭配 5 大类注解,也就是上面的5个注解,Spring
只会在类注解
下扫描有没有@Bean
注解。这里随便写一个:
再来试一试:
1.2.6 @Bean 重命名
在获取 @Bean注解
存入的对象的时候,需要用方法名来获取,但是这样感觉不太方便,所以我们可以对它重命名:
@Bean(name = "user")//重命名,重命名后原来的名字不能用了。
public User user1(){
User user = new User();
user.setUid(1);
user.setUsername("张三");
user.setPassword("123456789");
user.setAge(20);
return user;
}
可以对它起多个名字:
1.2.7 多个 @Bean 的场景
当我有多个方法需要加@Bean
注解的时候:


(补充一下:方法如果有形参是不能存入容器中的,所以不能重载。)
如果是相同的名字呢?我们这里再创建一个类:
这时候有两个名字相同的方法,那么取对象会报错吗?



艾,怎么没有报错?为什么打印的是“老六”
?因为当@Bean重复
的时候,Spring
容器存储的过程是:如果遇到相同名称的对象,后一个替换前一个,就如Java中的Hash
一样。
当然,有一个注解可以改变它们的优先级:@Order



这时候就是打印张三
了,因为张三
最后被加载。
2. 注入 Bean 对象
对象注入就是把容器中的对象放入到另一个类中,有三种注入方式:
- 属性注入
- Setter注入
- 构造方法注入
2.1 属性注入
有如下代码
我想将StudentService
存到StudentController
中的studentService
属性上:用@Autowired
注解
@Controller //将对象存储到 Spring 中
public class StudentController {
@Autowired
public StudentService studentService;
public void hi(){
System.out.println("你好,我是 StudentController 的 hi 方法!");
//来验证是否注入成功
studentService.hi();
}
}

这里要注意属性的命名规范:studentService
,加了@Autowired
注解后,会先在容器中寻找是否有当前的类型(StudentService),如果只有一个,那么不管你的名称是什么,直接把这一个注入到属性中;要是有多个相同的类型,就会通过属性名称来寻找对象。
public class Main {
public static void main(String[] args) {
//创建一个上下文(Spring容器)
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
//获取 StudentController 对象
StudentController studentController = context.getBean("studentController",StudentController.class);
studentController.hi();
}
}
结果:

注入成功。
但是,它不能对实现final
修饰的变量进行注入:

这是因为final
对象要么直接赋值,要么在构造方法中赋值,这里违背了java语法的规范,所以不能赋值。
2.2 Setter 注入
在属性的set
方法上加一个@Autowired
注解:
@Controller //将对象存储到 Spring 中
public class StudentController {
private StudentService studentService;
//Setter注入,必须写一个set方法。
@Autowired
public void setStudentService(StudentService studentService) {
this.studentService = studentService;
}
public void hi(){
System.out.println("你好,我是 StudentController 的 hi 方法!");
//来验证是否注入成功
studentService.hi();
}
}
结果:
这种方法也是不能注入 final
属性的:

2.3 构造方法注入
@Controller //讲对象存储到 Spring 中
public class StudentController {
private StudentService studentService;
//构造方法注入
@Autowired // 当只有一个构造方法的时候,这个注解可以省略!
public StudentController(StudentService studentService){
this.studentService = studentService;
}
public void hi(){
System.out.println("你好,我是 StudentController 的 hi 方法!");
//来验证是否注入成功
studentService.hi();
}
}

成功。
这时候就可以对 final
属性进行注入了:
@Controller //讲对象存储到 Spring 中
public class StudentController {
// final 属性
private final StudentService studentService;
//构造方法注入
@Autowired
public StudentController(StudentService studentService){
this.studentService = studentService;
}
public void hi(){
System.out.println("你好,我是 StudentController 的 hi 方法!");
//来验证是否注入成功
studentService.hi();
}
}

2.4 @Resource 注解
@Resource注解
与@Autowired注解
是一模一样的用法,它们的区别是:
@Autowired
来⾃于Spring
,⽽@Resource
来⾃于JDK
的注解。@Resource
不能用于构造方法注入。@Resource
可以设置name
参数,根据名称来获取Bean
对象。
2.4.1 @Resource 的 name 属性
这里着重讲一下第三个点:
这里存入了3个User
对象到Spring
容器中,我们分别用@Autowired
、@Resource
注解来注入对象:
@Controller
public class UserController {
@Autowired
private User user;
public void hi(){
System.out.println("你好,我是 StudentController 的 hi 方法!");
//来验证是否注入成功
System.out.println(user.toString());
}
}
public class Main {
public static void main(String[] args) {
//创建一个上下文(Spring容器)
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
UserController userController = context.getBean("userController",UserController.class);
userController.hi();
}
}
结果:
@Controller
public class UserController {
@Resource
private User user;
public void hi(){
System.out.println("你好,我是 StudentController 的 hi 方法!");
//来验证是否注入成功
System.out.println(user.toString());
}
}
结果:
都是会报错的,为什么?因为这里查询用的名称是user
(默认用属性名来查找),容器里没有这个对象,但是我又不想改名称怎么办?答案就是用@Resource
的name
属性:
@Controller
public class UserController {
@Resource(name = "user1")//指定名称来取对象
private User user;
public void hi(){
System.out.println("你好,我是 StudentController 的 hi 方法!");
//来验证是否注入成功
System.out.println(user.toString());
}
}
结果:
2.4.1 @Autowired + @Qualifier
@Autowired
注解也能解决上面的问题,但是要借助@Qualifier
:
@Controller
public class UserController {
@Autowired
@Qualifier(value = "user1")//指定获取某个对象
private User user;
public void hi(){
System.out.println("你好,我是 StudentController 的 hi 方法!");
//来验证是否注入成功
System.out.println(user.toString());
}
}
结果:
(如有错误,请在评论区指出,谢谢🌹)
转载自:https://juejin.cn/post/7227022405503008827