一. 存储Bean对象
1. 配置扫描路径
配置扫描路径是使用注解之前的前置工作,是非常重要的,是必须的操作项.只有被配置的包下的所有类,添加了注解才能被正确的识别并保存到 Spring
中.
首先创建一个Spring项目.创建好后,第一步就是配置扫描路径:在resources
目录中创建一个spring-config.xml
文件.然后在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=""></content:component-scan>
</beans>
<content:component-scan base-package=""></content:component-scan>
中base-package=""
的值设置为需要扫描对象的根路径.注意:这个根路径是从java目录开始的.
2. 添加注解存储 Bean 对象
想要将对象存储在spring中,有两种注解类型可以实现:
使用类注解(五大类注解):
-
@Component
:@Component是一个通用的注解,表示一个类被标记为可被Spring容器扫描和管理的组件。使用@Component注解可以将一个普通的Java类注册为一个SpringBean。 -
@Controller
:@Controller注解用于标识一个类是Spring MVC中的控制器。控制器负责处理用户请求,并返回相应的视图或数据。通过@Controller注解标记的类会被Spring自动检测并注册为一个控制器Bean。 -
@Service
:@Service注解用于标识一个类是业务逻辑层的组件。通常,我们使用@Service注解将一个服务类标记为Spring管理的Bean,它负责处理业务逻辑的实现。 -
@Repository
:@Repository注解用于标识一个类是数据访问层的组件。通常,我们使用@Repository注解将一个DAO(数据访问对象)类标记为Spring管理的Bean,它负责封装与数据库的交互操作。 -
@Configuration
:用于标识一个类为配置类,通常用于定义Bean的创建、装配和其他配置信息。它用于告诉Spring容器该类包含了Bean的定义和依赖关系,可通过@Configuration注解的类创建一个Java-based配置来代替XML配置文件。
方法注解
-
@Bean
:@Bean注解用于标记一个方法是一个产生Bean实例的工厂方法。通常,我们将@Bean注解放置在@Configuration注解的类中的方法上。被@Bean注解标注的方法会被Spring容器调用,并将其返回的对象注册为一个Bean,可以通过名称或类型进行访问。
拓展注解:
-
@Autowired
:@Autowired是一个自动装配注解,用于实现依赖注入。当一个类需要依赖其他类的实例时,可以使用@Autowired注解自动将依赖注入到目标类中。Spring会根据类型进行自动查找并装配对应的Bean。
2.1 使用五大类注解存储Bean
首先,我们来了解如何使用五大类注解来储存对象
-
@Controller
package com.spring.demo; import org.springframework.stereotype.Controller; @Controller public class UserController { public void sayHi() { System.out.println("UserController sayHi!"); } }
在扫描路径下创建该
UserController
类.并在类上加@Controller
注解,此时就将Bean
存储到容器中了.接下来就是从 Spring 中读取出我们的对象,这里还是先使用依赖查找的方式来获取 Bean,使用五大类注解,默认情况下,Bean 的名字就是原类名首字母小写(小驼峰).
import com.spring.demo.UserController; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class App { public static void main(String[] args) { //1.获取 srping 容器 ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml"); //2.获取 bean 对象 //获取对象时使用类名的小驼峰形式作为 name 参数 UserController userController = context.getBean("userController", UserController.class); //3.使用 bean userController.sayHi(); } }
运行结果:
注意:要将Bean存储到Spring中需要满足两个条件:- 使用五大类注解创建的类
- 必须在配置的扫描路径下.(包括子包)
扫描路径也叫做根路径.两个条件缺一不可.
为什么要设置根路径?
设置根路径其实也是为了提高程序的性能,因为如果不设置根路径,Spring 就会扫描项目文件中所有的目录,但并不是所有类都需要储存到 Spring当中,这样性能就会比较低,设置了根路径,Spring 就只扫描该根路径下所有的目录就可以了,提高了程序的性能。下来我们演示一下没有配置扫描路径下的情况:
还需要知道的是使用注解存储的 Bean 和使用XML存储的的 Bean 是可以一同使用的,比如我们将刚刚有问题的Student
重新通过XML的方式进行存储.注意:默认情况下,使用原类名首字母小写就能读取到Bean对象.特例情况:原类名如果首字母和第二个字母都是大写的情况下,那么bean名称就是原类名.
-
@Service
启动类中代码://1.获取 srping 容器 ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml"); //2.获取 bean 对象 //获取对象时使用类名的小驼峰形式作为 name 参数 UserService userService = context.getBean("userService", UserService.class); //3.使用 bean userService.sayHi();
UserService
类:package com.spring.demo; import org.springframework.stereotype.Service; @Service public class UserService { public void sayHi(){ System.out.println("UserService sayHi!"); } }
运行结果:
其余三种使用方式相同,此处不再做介绍.
2.2 为什么要有五大类注解?
既然都五大类完成的是同样的工作,那为什么要有五大类注解呢?
其实五大类注解主要是为了规范 Java 项目的代码,Java 项目的标准分层如下:
- 控制层(Controller)
- 服务层(Service)
- 数据持久层(Dao)
而五大类注解便是对应着不同的层级别使用的,让程序猿看到某一个注解就可以明确这个了类是做什么的。
程序的⼯程分层,调⽤流程如下:
包括企业中也是按照这样的结构来将项目分层的,典型的比如阿里,它只是在标准分层在服务层(Service)做了一个扩展,划分的更加细致详细了.
五大类注解主要起到的是“见名知意”的作用,代码层面上来看,作用是类似的.查看五大类源码可知:
五大类的源码中除了 @Component
以外,其他四大类注解中都包含了 @Component 注解的功能,这四大类注解都是基于 @Component 实现的,是 @Component 拓展。
2.3 有关获取Bean参数的命名规则
上文中在使用依赖查找的方式获取Bean
时,我们讲到了getBean
方法的BeanName
是使用类名的小驼峰形式(即类名的首字母小写)以及第一个字母和第二个字母都大写情况下的特例.
注意:BeanName的规范命名规则并不是 Spring 独创的,而依照 Java 标准库的规则进行的。
BeanName的规范命名规则:
- 如果类名不存在或类名为空字符串,
BeanName
为原类名。 - 如果类名字长度大于1,且第一个与第二个字符为大写,
BeanName
为原类名。 - 其他情况,
BeanName
为原类名的小驼峰形式.
3. 使用方法注解储存 Bean 对象
3.1 方法注解储存对象的用法
类注解是添加到某个类上的,而方法注解是放到某个方法上的.在Spring框架的设计中,方法注解@Bean
要配合类注解才能将对象正常存储到Spring容器中.
举个🌰:我们有一个普通书本类
package com.spring.demo;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Controller;
@Controller
public class BookInfo {
private String bookName;
private String author;
private String style;
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getStyle() {
return style;
}
public void setStyle(String style) {
this.style = style;
}
@Override
@Bean
public String toString() {
return "com.spring.demo.BookInfo{" +
"bookName='" + bookName + '\'' +
", author='" + author + '\'' +
", style='" + style + '\'' +
'}';
}
}
下面演示使用@Bean方法注解储存对象:
package com.spring.demo;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Controller;
@Controller
public class Books {
@Bean
public BookInfo getBook(){
BookInfo bookInfo = new BookInfo();
bookInfo.setBookName("三体");
bookInfo.setAuthor("刘慈欣");
bookInfo.setStyle("文学科幻");
return bookInfo;
}
public void sayHi(){
System.out.println("Books sayHi!");
}
}
启动类:
获取方法注解储存的对象时,传入的BeanName
参数值默认值就是方法名,上面的代码中方法名为getBook,所以获取时,就使用getBook作为参数来进行获取。
import com.spring.demo.BookInfo;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App2 {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
BookInfo book = context.getBean("getBook", BookInfo.class);
System.out.println(book);
}
}
运行结果:
3.2 @Bean的重命名
获取方法注解储存的对象时,传入的BeanName参数值默值为方法名,但像上面那样返回对象的方法名称往往是getXXX这样式取名的,虽然在语法与实现上是没有问题的,但实际开发写出这样的代码,看起来还是比较别扭的。
实际上注解 @Bean 是可以加参数的,给储存的对象起别名,像下面这个样子。
package com.spring.demo;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Controller;
@Controller
public class Books {
@Bean(name = "book")
public BookInfo getBook(){
BookInfo bookInfo = new BookInfo();
bookInfo.setBookName("三体");
bookInfo.setAuthor("刘慈欣");
bookInfo.setStyle("文学科幻");
return bookInfo;
}
public void sayHi(){
System.out.println("Books sayHi!");
}
}
也可以给 Bean 设置多个别名,总结起来有如下几种方式:
//方式一(省略参数名的情况下默认是name)
@Bean("article1")
//方式二
@Bean(name = "article2")
//方式三
@Bean(value = "article3")
//起多个别名
@Bean(name = {"article4", "article5"})
@Bean(value = {"article6", "article7"})
@Bean({"article8", "article9", "article10"})
但是需要注意,当重新命名之后,就不能使用原来的方法名来获取对象了.
所以使用 @Bean 存储对象的beanName命名规则是,当没有设置name/value属性时,此时 Bean 的默认名字就是方法名,一旦添加了别名name/value属性后,就只能通过重命名的别名来获取 Bean 了,默认的使用方法名获取 Bean 对象就不能使用了。
@Bean 使用时,同一类如果多个 Bean 使用相同的名称,此时程序执行是不会报错的,他会根据类加载顺序和类中代码从上至下的的顺序,将第一个 Bean 存放到 Spring 中,但第一个之后的对象就不会被存放到容器中了,也就是只有在第一次创建 Bean 的时候会将对象和 Bean 名称关联起来,后续再有相同名称的Bean存储时候,容器会自动忽略。
还可以通过类注解 @Order 注解控制类加载顺序(值越小,优先级越高),进而影响 Bean 的存放的先后顺序.
3.3 同⼀类型多个 @Bean 报错
当出现以下多个 Bean,返回同⼀对象类型时程序会报错,如下代码所示:
运行结果如下:
报错的原因是,⾮唯⼀的 Bean 对象.
同⼀类型多个 Bean 报错处理:
解决同⼀个类型,多个 bean 的解决⽅案有以下两个:
- 使⽤
@Resource(name="user1")
定义。(@Resource下文有介绍)
- 使⽤
@Qualifier
注解定义名称。
二. 获取 Bean 对象(对象装配)
获取bean对象也叫做对象装配.是把bean对象取出来放到某个类中.有时候也叫对象注⼊(DI).
对象装配(对象注⼊)的实现⽅法以下 3 种:
- 属性注⼊
- 构造⽅法注⼊
- Setter 注⼊
接下来,我们分别来看。
下⾯我们按照实际开发中的模式,将Service
类注⼊到Controller
类中。
1. 属性注入
属性注⼊是使⽤ @Autowired
实现的,将 Service
类注⼊到 Controller
类中。User
代码:
package com.spring.demo;
public class User {
private Integer id;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
Service
代码:
package com.spring.demo;
import org.springframework.stereotype.Service;
@Service
public class UserService {
public void sayHi(){
System.out.println("UserService sayHi!");
}
public User getUser(Integer id){
User user = new User();
user.setId(id);
user.setName("xxxflower-"+id);
return user;
}
}
Controller
代码:
package com.spring.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class UserController {
@Autowired
private UserService userService;
public User getUser(Integer id){
return userService.getUser(id);
}
public void sayHi() {
System.out.println("UserController sayHi!");
}
}
运行结果:
优点:简单.
缺点:
- 没办法实现final修饰的变量注入.
- 兼容不好:只适用于Ioc容器.
- 风险:因为写法简单,所以违背单一设计原则的概率更大.
2. Setter注入
Setter 注⼊和属性的 Setter ⽅法实现类似,只不过在设置 set ⽅法
的时候需要加上 @Autowired
注解,如下代码所示:
package com.spring.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class UserController2 {
private UserService userService;
//2.使用Setter注入
@Autowired
public void setUserService(UserService userService) {
this.userService = userService;
}
public User getUser(Integer id){
return userService.getUser(id);
}
}
运行结果:
优点:符合单一设计原则(每个方法只传递一个对象)
缺点:
- 不能注入不可变对象
- 使用setter注入的对象可能会被修改.
3. 构造方法注入(Spring官方推荐)
构造⽅法注⼊是在类的构造⽅法中实现注⼊,如下代码所示:
package com.spring.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class UserController3 {
private UserService userService;
//3.构造方法注入
@Autowired
public UserController3(UserService userService){
this.userService = userService;
}
public User getUser(Integer id){
return userService.getUser(id);
}
}
运行结果:
特点:如果当前类中只有一个构造方法的话,那么@Autowired 注解可以省略.
优点:
- 可以注入一个不可变对象(使用fianl修饰的对象)
问题:为什么构造方法可以注入一个不可变对象,而属性注入和Setter注入却不行?
答:这是Java的规定,在java中,被final对象必须满足以下两个条件中的任意一个:
- final修饰的对象,是直接复制的.
- final修饰的对象,必须在构造方法中赋值.
- 注入的对象不会被改变(构造方法只能执行一次)
- 构造方法注入可以保证注入对象完全被初始化
- 通用性更好.
4. @Resource:另⼀种注入关键字
在进行类注⼊时,除了可以使⽤ @Autowired
关键字之外,我们还可以使⽤ @Resource
进⾏注⼊,如下代码所示:文章来源:https://www.toymoban.com/news/detail-658038.html
package com.spring.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import javax.annotation.Resource;
@Controller
public class UserController {
// @Autowired
@Resource
private UserService userService;
public User getUser(Integer id){
return userService.getUser(id);
}
public void sayHi() {
System.out.println("UserController sayHi!");
}
}
上述三种注入方式使用@Autowired
行,使用@Resource
也行,但是这两者也是有区别的:文章来源地址https://www.toymoban.com/news/detail-658038.html
- 出身不同:@Autowired 来自于
Spring
,而 @Resource 来⾃于JDK
的注解; - 使⽤时设置的参数不同:相比于
@Autowired
来说,@Resource
⽀持更多的参数设置,例如name
设置,根据名称获取 Bean。 -
@Autowired
可⽤于Setter
注⼊、构造函数注⼊和属性注⼊,⽽@Resource
只能⽤于Setter
注⼊和属性注⼊,不能⽤于构造函数注⼊。 -
@Autowired
先根据类型查找(byType),之后再根据名称查找(byName)。@Resource
先根据名称去查,之后再根据类型去查。
到了这里,关于【JavaEE进阶】Spring 更简单的读取和存储对象的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!