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