在Spring中想要更简单的存储和读取对象的核心是使用注解
目录
一、存储Bean对象
Spring相当于hash表,key就是String(Bean对象的name(id)),value就是Object(Bean对象)
key要保证唯一,key相同的键值对有一个会被取代
1、配置扫描路径
- 在Spring配置文件(resource目录下)中,配置扫描包路径;Spring容器只会扫描这个包路径下的所有的类,遇到添加注解的类,就会添加到Spring容器中
- Spring中不会扫描其它的路径,所以其他路径中即使有添加注解的类,也不会被存储到Spring容器中
- 对于指定包路径下的 未添加注释的 类不会存储到Spring容器中
一个项目中的有太多太多的文件和类,如果Spring一个一个的扫描,效率极低且开销大,出于对性能的考虑,就配置一个扫描路径,将需要存储的类都放到这个路径下
<?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">
<!-- 配置一下:bean注解扫描的根路径(方面后面更简单存储对象到spring容器) 目的 提高效率-->
<content:component-scan base-package="com.bit"></content:component-scan>
</beans>
注意:第七行中的 base-package 代表的就是扫描路径
以下所有添加注解的类都是在 com.bit 这个包路径下的
2、添加注解存储Bean对象
(1)、类注解
- @Controller
@Controller = < bean id=“userController” class=“com.bit.UserController”>< /bean >
@Controller
public class UserController {
public void sayHello(){
System.out.println("hello, Spring!!!");
}
}
- @Service
@Service = < bean id=“userService” class=“com.bit.UserService”>< /bean >
@Service
public class UserService {
public void sayHello(){
System.out.println("hello, Spring!!!");
}
}
- @Repository
@Repository
public class UserRepository {
public void sayHello(){
System.out.println("hello, Spring!!!");
}
}
- @Configuration
@Configuration
public class UserConfiguration {
public void sayHello(){
System.out.println("hello, Spring!!!");
}
}
- @Component
@Component
public class UserComponent {
public void sayHello(){
System.out.println("hello, Spring!!!");
}
}
查看启动类 App(并没有固定的路径)
// 启动类
public class App {
public static void main(String[] args) {
// 获取 Spring 上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
// 取出 bean
UserController userController = context.getBean("userController", UserController.class);
userController.sayHello();
}
// 打印 hello, Spring!!!
}
注意Bean对象的name(id)
分析源码可知
- 如果 类名 的第一个和第二个字符都是大写的,那么Bean对象的name就是 类名
- 否则的话,Bean的name 就是将类名的第一个字符小写得到
关于类注解的关系
- 这五个类注解所起到的作用是相同的
- 使用这么多的类注解,就是让程序员看到类注解后,就能直接了解当前类的用途
- 查看源码,@Controller、@Service、@Repository、@Configuration这些注解里面都有一个注解 @Component,所以它们都是属于@Component的 “子类”
程序的分层调用流程
各个分层的功能
- Controller 业务逻辑层
相当于“门卫”,对前端传来的参数进行校验,判断参数的合法性- Service 服务层
相当于“领导”,负责数据的组装,管理 调用数据库的接口- Repository 数据持久层
相当于“打工人”,直接操作数据库,进行数据的CRUD- Configuration 配置层
设置当前项目中所有的配置信息- Component 实体类
(2)、方法注解
设置一个类,进行实验
package com.model;
public class UserInfo {
private int id;
private String name;
private String password;
public UserInfo(int id, String name, String password) {
this.id = id;
this.name = name;
this.password = password;
}
@Override
public String toString() {
return "UserInfo{" +
"id=" + id +
", name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
}
- 方法注解是放到某个方法上的,将当前方法返回的对象存储到Spring容器中
- 在初始化Spring上下文对象的时候,Spring自动调用有方法注解的方法,将返回对象存储
- 方法注解一定要搭配类注解使用,这个类的Bean对象也被存储到了Spring容器中
为了提高性能,如果不搭配类注解,需要扫描所有的方法;搭配类注解,就只需要扫描指定类中的方法
- 如果没有重命名Bean对象,那么它默认的名字(id)就是方法名(原汁原味的方法名, 千万不要和类注解时Bean的名字搞混)
@Component
public class UserBeans {
public void func(){
System.out.println("wc");
}
// 将当前方法返回的对象,存储到spring容器中
@Bean
public UserInfo getUserInfo(){
System.out.println("自动调用了get方法");
UserInfo userInfo = new UserInfo(1,"qust","2020");
return userInfo;
}
}
启动类测试
// 启动类
public class App {
public static void main(String[] args) {
// 获取 Spring 上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
// 设置一个间隔
System.out.println("yyds");
// 取出 bean
// 方法注解
UserInfo userInfo = context.getBean("getUserInfo", UserInfo.class);
System.out.println(userInfo);
UserBeans userBeans = context.getBean("userBeans", UserBeans.class);
userBeans.func();
}
}
- 如果重命名了Bean对象,就不能使用方法名获取Bean对象了
- 一个Bean对象,可以指定多个名字;但是通过这多个名字获取是同一个对象
- 一个Bean对象,多个名字,相当于多个键值对(key不同,value指向同一个对象)
name=
可以省略
@Component
public class UserBeans {
@Bean(name = {"userInfo", "user1"})
public UserInfo getUserInfo(){
UserInfo userInfo = new UserInfo(1,"qust","2020");
return userInfo;
}
}
启动类测试
// 启动类
public class App {
public static void main(String[] args) {
// 获取 Spring 上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
// 指定 bean 的名称
UserInfo userInfo = context.getBean("userInfo", UserInfo.class);
System.out.println(userInfo);
// 1,qust,2020
}
}
- 多个Bean对象,使用相同的名称,后来的不会存储到Spring容器中
package com.bit;
@Component
public class UserBeans {
@Bean(name = "userInfo")
public UserOutfo getUserInfo2(){
UserOutfo userOutfo = new UserOutfo("郑州路","青岛科技大学");
return userOutfo;
}
@Bean(name = "userInfo")
public UserInfo getUserInfo(){
UserInfo userInfo = new UserInfo(8848,"qust","123");
return userInfo;
}
}
启动类测试
// 启动类
public class App {
public static void main(String[] args) {
// 获取 Spring 上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
// 指定 bean 的名称
UserInfo userInfo = context.getBean("userInfo", UserInfo.class);
System.out.println(userInfo);
// 报错,Bean对象的类型不匹配
}
}
二、获取Bean对象
获取Bean对象,是把对象取出来放到某个类中,也叫作对象注入,对象装配
程序员将创建对象(new一个对象)的控制权交给了Spring容器,需要使用时,从Spring容器中就取出这个对象(对象注入)
对象注入有三种方法:
- 属性注入
- 构造方法注入
- setter注入
- 创建一个controller包,表示业务逻辑层
在这个包中,创建一个类ArtController,执行相应操作
package com.bit.controller;
// 业务逻辑层
@Controller
public class ArtController {
public void add(String title, String content){
//校验前端传来的参数
if(title != null && content != null && !title.equals("") && !content.equals("")){
// 执行 后面的业务逻辑
}else{
System.out.println("传入的参数错误!!!");
}
}
}
- 创建一个service包,表示服务层
在这个包中,创建一个类ArtService,实际开发中,应该在这个类中注入Repository层的接口,此处省略
package com.bit.service;
// 服务层
// 组织数据和管理、调用数据库接口
@Service
public class ArtService {
public void add(String title, String content){
// 执行后面的流程,调用操作数据库的接口
System.out.println("Service 执行流程: " + title + " | " + content);
}
}
根据程序分层的调用流程,在ArtController类中,注入Service层的ArtService类
1、属性注入
- 先根据类型(ArtService)查询,如果只有一个同类型的对象,返回;
- 没有找到同类型的对象,注入失败;
- 如果有两个同类型的对象,根据变量名(artService(自己随便命名))查询
2、构造方法注入
- 当类中只有一个构造方法的时候,@Autowired可以省略;有多个构造方法的时候,@Autowired不能省略;因为如果有多个构造方法时,Spring并不知道要调用哪一个
- Spring调用构造方法时,会自动注入这个参数的,从Spring容器中取ArtService的Bean对象
创建Spring上下文对象(Spring容器中)时,初始化ArtController,调用其构造方法;如果Spring中有ArtService的Bean对象,将其取出,作为参数;如果没有,先初始化ArtService,将它的Bean对象存储到Spring中,再将ArtService的Bean对象取出,作为参数
3、setter注入
设置set方法时,任何时候都要加上@Autowired注解
与构造方法注入类似,Spring初始化ArtController时,自动调用setArtService方法,如果Spring中有ArtService的Bean对象,取出,作为参数;如果没有,初始化ArtService,将它的Bean对象存到Spring中,再取出,作为参数
代码描述
此处为了方便,多次设置同一属性 ArtService artService,这在实际开发中是不允许的
package com.bit.controller;
// 业务逻辑层
@Controller
public class ArtController {
// 1. 属性注入
@Autowired
private ArtService artService;
// 2. 构造方法注入
private ArtService artService;
@Autowired
public ArtController(ArtService artService){
this.artService = artService;
}
// 3. 使用 setter 注入
private ArtService artService;
@Autowired
public void setArtService(ArtService artService){
this.artService = artService;
}
public void add(String title, String content){
if(title != null && content != null && !title.equals("") && !content.equals("")){
// 执行 后面的业务逻辑
artService.add(title, content);
}else{
System.out.println("传入的参数错误!!!");
}
}
}
启动类
// 启动类
public class App {
public static void main(String[] args) {
// 获取 Spring 上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
ArtController artController = context.getBean("artController", ArtController.class);
artController.add("总决赛", "雄鹿 VS 勇士");
}
}
4、三种注入方式的优缺点
- 属性注入
优点:简洁,使用方便;
缺点:只能用于IoC容器,非IoC容器不可用 - 构造方法注入**(官方推荐使用的类注入方法)**
优点:通用性
缺点:如果有多个注入(参数多),会显得十分臃肿;
官方声明,出现这种情况,你应该从自身找原因,考虑一下当前类是否符合程序单一职责的设计模式,这个锅,我不背! - setter注入(官方早期推荐使用的类注入方法)
优点:通用性,但是不如构造方法注入
5、@Autowired和@Resource
这两个关键字都用于 类注入 的时候
区别:
- 出身不同,@Autowired来自于Spring,@Resource来自于JDK的注解
- 设置的参数的不同,相比于@Autowired,@Resource支持更多的参数设置
- 出现的场景不同,@Autowired可以修饰属性注入、构造方法注入、setter注入;@Resource可以修饰属性注入和setter注入,不能用于构造方法注入
引入@Resource就是为了解决 Spring容器中有同一类型的多个Bean对象,注入时会报错的问题
设置一个UserOutfo对象
package com.model;
public class UserOutfo {
private String address;
private String university;
public UserOutfo(String address, String university) {
this.address = address;
this.university = university;
}
@Override
public String toString() {
return "UserOutfo{" +
"address='" + address + '\'' +
", university='" + university + '\'' +
'}';
}
}
Spring容器中存储同一类型的多个对象
package com.bit;
@Component
public class UserBeans {
@Bean
public UserOutfo getUser1(){
UserOutfo userOutfo = new UserOutfo("郑州路","青岛科技大学");
return userOutfo;
}
@Bean
public UserOutfo getUser2(){
UserOutfo userOutfo = new UserOutfo("柳园路","聊城大学");
return userOutfo;
}
}
在ArtController中注入UserOutfo的Bean对象
package com.bit.controller;
// 业务逻辑层
@Controller
public class ArtController {
// 1. 属性注入
@Autowire
private UserOutfo user;
public void work(){
System.out.println(user);
}
}
启动类
// 启动类
public class App {
public static void main(String[] args) {
// 获取 Spring 上下文对象
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
}
}
程序执行的结果:
- 使用@Resource(name = " ")
- 使用@Autowired搭配@Qualifier(value = " ")
注意:@Resource(name = " ") 和 @Qualifier(value = " ")中,直接写@Bean对象的名字
package com.bit.controller;
// 业务逻辑层
@Controller
public class ArtController {
// 1. 属性注入
@Resource(name = "getUser1")
private UserOutfo user;
public void work(){
System.out.println(user);
}
}
package com.bit.controller;
// 业务逻辑层
@Controller
public class ArtController {
// 1. 属性注入
@Autowired
@Qualifier(value = "getUser1")
private UserOutfo user;
public void work(){
System.out.println(user);
}
}
启动类
// 启动类
public class App {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
ArtController artController = context.getBean("artController", ArtController.class);
artController.work();
}
}
它们就是查找对应 Bean对象 的唯一标准(只要这个value/name(Bean的名字)对不上,直接报错)
package com.bit.controller;
import javax.annotation.Resource;
// 业务逻辑层
// 对前端传来的参数进行校验,如果验证成功就调用后面的流程
@Controller
public class ArtController {
// 1. 属性注入
@Resource(name = "user")
private UserOutfo getUser2;
// @Autowired
// @Qualifier(value = "user")
// private UserOutfo getUser2;
public void add(String title, String content){
System.out.println(getUser2);
}
}