前面的文章详细的介绍了 Spring 对象的创建,以及对 Bean 对象的存取操作,但是通过配置文件注册 Bean 对象以及使用 ApplicationContext
或 BeanFactory
的方式获取 Bean 对象的操作就显得格外的复杂。因此,本文主要就是详细介绍了一种更加简单的方式来实现对 Bean 对象更加简单的储存和读取操作。
在 Spring 中,要想更加简单的实现对 Bean 对象的储存和使用,其核心就是使用注解
,本文主要就是演示如何使用注解实现对 Bean 对象的存取操作。
在之前储存 Bean 对象的时候,还需在 spring-congig
文件中添加一行
内容才行,而且,每需要新增一个 Bean 对象到 Spring 容器中就需要新增一行,这样的操作就显得非常麻烦了。
而现在只需要使用一个注解
就能代替这一行
内容,此时就变得非常方便。想要通过注解的方式将对象储存到 Spring 容器中,主要有两种注解类型可以实现:
使用类注解(五大类注解):
@Controller
(控制储存):验证用户请求的数据合法性,相当于安保系统;@Service
(服务储存):用于编排和调度具体的执行方法;@Repository
(仓库储存):持久层,与数据库进行交换;@Component
(组件储存):相当于工具类;@Configuration
(配置储存):项目中的一些配置。使用方法注解:
@Bean
:作用在方法上,需要配合上述的类注解使用。但在此之前还需要配置一下扫描路径
。
在 spring-config.xml
文件中添加如下一行记录:
其含义是,指定一个 base package
,即所有需要添加到 Spring 容器中的 Bean 对象都在 base package
所指定包或者其子包下。这里我知道的包是com.spring.demo
,那么就意味着,如果不是此包下的 Bean 对象,即使加上了注解,也不会被添加到 Spring 容器中。
使用 @Controller
注解储存 Bean 对象:
@Controller
public class StudentController1 {
public void sayHi(){
System.out.println("do studentController1 sayHi().");
}
}
使用 ApplicationContext
的方式获取 Bean 对象:
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
StudentController1 studentController1 =
context.getBean("studentController1", StudentController1.class);
studentController1.sayHi();
}
关于 Bean 对象的命名规则可见后文。
使用 @Service
注解储存 Bean 对象:
@Service
public class StudentController2 {
public void sayHi(){
System.out.println("do studentController2 sayHi().");
}
}
获取 Bean 对象:
StudentController2 studentController2 =
context.getBean("studentController2", StudentController2.class);
使用 @Repository
注解储存 Bean 对象:
@Repository
public class StudentController3 {
public void sayHi(){
System.out.println("do studentController3 sayHi().");
}
}
获取 Bean 对象:
StudentController3 studentController3 =
context.getBean("studentController3", StudentController3.class);
使用 @Component
注解储存 Bean 对象:
@Component
public class StudentController4 {
public void sayHi(){
System.out.println("do studentController4 sayHi().");
}
}
获取 Bean 对象:
StudentController4 studentController4 =
context.getBean("studentController4", StudentController4.class);
使用 @Configuration
注解储存 Bean 对象:
@Configuration
public class StudentController5 {
public void sayHi(){
System.out.println("do studentController5 sayHi().");
}
}
获取 Bean 对象:
StudentController5 studentController5 =
context.getBean("studentController5", StudentController5.class);
通过上述代码可以发现,在创建 Bean 对象的时候,都是使用的标准 “大驼峰” 的命名方式,而读取的时候 Bean 的名称则是其类名称的首字母小写,即小驼峰。
但是,此时创建一个 SController
类,并使用注解将其添加到 Spring 容器中,那么此时它的 Bean 对象的名称是什么呢?根据上面代码的规律,难道还是SController
吗?
SController sController = context.getBean("sController", SController.class);
当运行程序的时候,发现报错了:
其意思是不存在名称为 sController
这样 Bean 对象。此时如果将其改成 SController
,会是正确的吗?
关于 Bean 的名称生成的源码:
4. 找到了源码,即Introspector
类下的decapitalize
方法
该方法通过检查字符串的首字母是否为大写,并且第二个字符也是大写的情况下,直接返回原字符串,不做小写化处理。这样做是为了避免一些特殊情况下,例如缩写或首字母缩写词,不被误处理。
在Spring框架中,五大类常用的注解,分别是:@Component、@Controller、@Service、@Repository和@Configuration。
@Component
: 通用的组件注解,表示类是一个Spring管理的组件(Bean)。
@Controller
: 用于标识控制器类,通常用于Spring MVC中,处理HTTP请求和视图渲染。
@Service
: 用于标识服务类,表示该类提供一些业务逻辑处理。
@Repository
: 用于标识仓库类,表示该类用于数据访问,通常与数据库交互。
@Configuration
: 用于标识配置类,表示该类包含Spring配置信息,通常与@Bean
一起使用,用于定义 Bean。
通过上面代码的演示,发现这些注解的功能都是一样的,既然都是一样的为什么还需要有这么多不同的注解呢?
Spring 之所以提供这么多的注解,是为了更好的组织和管理应用程序的组件和依赖关系。因为每个注解都有自己特定的用途,让开发人员在应用程序中能够更方便地标识和区分不同类型的类。同时也提现了程序的工程分层:
@Controller
表示的是控制层,负责与用户进行交互,以及验证用户提交数据的合法性;@Service
表示的是服务层,用于编排和调度具体的执行方法,相当于车站中的服务台;@Repository
表示的是持久层,负责将数据持久化储存,通常需要与数据库进行交互。以上三个层次实现了程序的工程分层,同时也是 Java EE 标准分层的最核心分层。
查看 @Controller / @Service / @Repository / @Configuration
等注解的源码发现:
它们都是 @Component
子类注解,这意味着,被标注为 @Controller / @Service / @Repository / @Configuration
的类也被视为@Component
。
另外,@Configuration
注解是一个特殊的注解,它表明该类是 Spring 的配置类,用于定义 Bean 和配置应用程序的其他元素。配置类中的@Bean
注解用于定义 Bean。
首先创建一个 User 实体类:
package com.spring.demo.entity;
/**
* 普通的用户实体类
*/
public class User {
private Integer uid;
private String username;
private String password;
private Integer age;
public Integer getUid() {
return uid;
}
public void setUid(Integer uid) {
this.uid = uid;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"uid=" + uid +
", username='" + username + '\'' +
", password='" + password + '\'' +
", age=" + age +
'}';
}
}
创建一个组件类 UserBeans
,并使用方法注解 @Bean
将 User 类添加到 Spring 容器中:
@Controller
public class UserBeans {
@Bean
public User getUser(){
User user = new User();
user.setUid(1);
user.setUsername("王五");
user.setPassword("123456");
user.setAge(18);
return user;
}
}
注意,使用方法注解 @Bean
的时候需要搭配五大类注解才能生效。
获取 Bean 对象:
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
User user = context.getBean("getUser", User.class);
System.out.println(user);
}
注意,当使用方法注解@Bean
的时候,Bean 对象的默认名称就是其 添加到 Spring 容器中的方法名。
如果直接使用方法名作为 Bean 对象的名称,例如getUser
就显得非常的不合理,因此往往需要对 Bean 进行改名操作。但查看@Bean
源码的时候可以发现,其中的name
或 value
属性是一个数组,那么就意味着可以一个 Bean 对象取多个名称。
例如:
此时,可通过这两个名称,获取该 Bean 对象,发现它们是同一个 Bean:
另外需要注意的是,如果对 Bean 进行了重命名,则原来默认的方法名就失效了。
获取 Bean 对象也叫做对象装配
,即把对象取出来放到某个类当中,同时也叫做对象注入
。
对象注入的实现方式有以下三种:
属性注入:属性注入是通过在属性上使用注解实现的。常见的注解有@Autowired
和@Resource
。属性注入是在 Bean 对象的属性上直接进行注入,不需要提供setter
方法。
Setter
注入:Setter
注入是通过在 Bean 对象的 setter
方法上使用注解实现的。这种注入方式是在调用 Bean 的 setter
方法时,将依赖对象作为参数传入。
构造方法注入:构造方法注入是通过在 Bean 对象的构造方法上使用注解实现的。这种注入方式是在创建 Bean 对象的时候,通过构造方法参数传入依赖对象。
下⾯按照实际开发中的模式,将 Service 类注入到 Controller 类中,然后通过 main
方法获取 Controller 中的 Bean 对象。
首先创建一个 UserService
类和 UserController
类:
@Service
public class UserService {
public void sayHi(){
System.out.println("hi, userService.");
}
}
@Controller
public class UserController {
// 1. 属性注入
@Autowired
private UserService userService;
public void sayHi(){
System.out.println("do userController sayHi().");
userService.sayHi();
}
}
@Controller
public class UserController {
// 2. setter 注入
private UserService userService;
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
public void sayHi(){
System.out.println("do userController sayHi().");
userService.sayHi();
}
}
@Controller
public class UserController {
// 3. 构造方法注入
private UserService userService;
// @Autowired
public UserController(UserService userService) {
this.userService = userService;
}
public void sayHi(){
System.out.println("do userController sayHi().");
userService.sayHi();
}
}
注意,如果此时只有一个构造方法,则@Autowired
可以省略。
属性注入
finally
修饰的变量实现注入;Setter注入:
优点:符合单一设计原则,每个方法只能传递一个对象。
缺点:
finally
修饰的变量实现注入;构造方法注入:
优点:
finally
修饰的变量实现注入;缺点:构造方法参数较多时,代码显得冗长。
在进行类注入时,除了可以使用 @Autowired
关键字之外,我们还可以使用 @Resource
进行注入,如下代码所示:
属性注入:
public class UserController {
// 1. 属性注入
@Resource
private UserService userService;
public void sayHi(){
System.out.println("do userController sayHi().");
userService.sayHi();
}
}
Setter注入:
@Controller
public class UserController {
// 2. setter 注入
private UserService userService;
@Resource
public void setUserService(UserService userService) {
this.userService = userService;
}
public void sayHi(){
System.out.println("do userController sayHi().");
userService.sayHi();
}
}
@Autowired
是 Spring 框架提供的注解,而 @Resource
是JSR-250规范提供的注解,但是 Spring 也对其进行了支持。@Autowired
默认按照类型装配 Bean,如果多个类型匹配,可以配合 @Qualifier
注解指定具体的 Bean 名称。而 @Resource
默认按照属性名进行装配,可以通过 name 属性指定具体的 Bean 名称。@Autowired
是 Spring 的专有注解,更加灵活,功能更强大。@Resource
是标准的 Java 注解,适用于更通用的情况@Autowired
可用于 Setter
注入、构造函数注入和属性注入,而 @Resource
只能用于 Setter
注入和属性注入,不能用于构造函数注入。当存在多个类型相同的 Bean 对象,并且需要通过注解将其注入到其他 Bean 对象中时,如果没有明确指定注入哪个 Bean,就会导致报错。
例如,通过 Component 中的 UserBeans
将 User 注入到 Controller 中的 UserController
中。
首先,在UserBeans
使用@Bean
添加两个 User 对象到 Spring 容器中:
@Controller
public class UserBeans {
@Bean(name = {"user1", "u1"})
public User getUser1(){
User user = new User();
user.setUid(1);
user.setUsername("张三");
user.setPassword("123456");
user.setAge(18);
return user;
}
@Bean(name = "user2")
public User getUser2(){
User user = new User();
user.setUid(1);
user.setUsername("李四");
user.setPassword("123456");
user.setAge(18);
return user;
}
}
在 UserController
中分别使用 @Autowired
和 @Resource
注入获取 Bean 对象:
@Autowired
:
此时,由于存在两个相同类型的 Bean 对象,但是其名称不同,所以使用@Autowired
注解不知道获取哪个对象。
@Resource
使用@Resource
注解同样无法判断获取哪一个对象。
关于
@Autowired
和@Resource
查找 Bean 对象的顺序:
@Autowired
首先按照类型查找,然后再按照名称查找;@Resource
首先按照名称查找,然后再按照类型查找。
@Controller
public class UserController {
@Resource(name = "user1")
private User user;
public void sayHi(){
System.out.println("do userController sayHi().");
}
}
@Controller
public class UserController {
@Autowired
@Qualifier(value = "user1")
private User user;
public void sayHi() {
System.out.println("do userController sayHi().");
}
}