当前位置: 首页 > article >正文

Spring更简单的存取Bean

在上一篇blog里边我介绍了spring项目的创建以及Bean对象的存储和读取。

存储:1.首先创建Bean对象 2.将Bean对象注册到Spring容器中【Bean标签】

读取:1.获取Spring上下文对象 2.获取指定的Bean对象 3.使用Bean对象

但是随着Bean对象的增多以及使用频率的提高,上述过程难免有点效率低,所以需要学习下边更为简单的存取Bean对象的方式——使用注解,这也是Spring中更为简单存取Bean对象的核心。

一、存储Bean对象

0.准备工作:配置扫描路径(关键)

只有正确配置存储对象的扫描包路径,对应包下的类才能被正确识别并保存。

配置方法:

在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.spring
framework.org/schema/context https://www.springframework.org/schema/contex
t/spring-context.xsd">
<content:component-scan base-package="com.bit.service"></content:component-scan>
</beans>

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RkYxdegH-1683543406415)(F:\typora插图\image-20230508151930542.png)]

1.添加注解存储Bean对象

将对象注册到Spring中,有两种注解类型可以实现。分别是

1)五大类注解:@Controller、@Service、@Respository、@Component、@Configuration

2)一个方法注解:@Bean

1.1类注解

1.1.1@Controller

@Controller其实也叫做控制器注解,它的作用是验证用户请求数据的正确性,类似于安保系统。当认定当前访问者为非法用户黑客,就把他们加入黑名单。

下边是利用此注解存储Bean的代码示例:

@Controller // 将对象存储到 Spring 中
public class UserController {
    public void sayHi(String name) {
    	System.out.println("do UserController sayHi()");
    }
}

将这个注解加到类上边,当需要用到这个类的对象时,spring就会自动获取。

这里为了演示效果,我们先使用之前获取Bean的方法读取上述对象:

public class Application {
public static void main(String[] args) {
        ApplicationContext context =
        new ClassPathXmlApplicationContext("spring-config.xml");
        UserController userController = (UserController) context.getBean("userController",UserController.class);
        userController.sayHi();
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3H59DUOR-1683543406416)(F:\typora插图\image-20230508153320756.png)]

这样我们就拿到了这个对象

1.1.2@Service

@Service注解其实也叫做服务注解。它的作用就是编排和调度具体执行方法,在服务层,相当于导医台/服务中心,完成引导工作,但是不执行业务。

下边是使用@Service注解存储对象的代码示例:

@Service
public class UserService {
    public void sayHi(String name) {
    System.out.println("do UserService sayHi()");
    }
}

读取Bean对象(老方法):

public class App {
    public static void main(String[] args) {
        ApplicationContext context=new ClassPathXmlApplicationContext("spring-config.xml");
//        UserController userController=context.getBean("userController",UserController.class);
//        userController.sayHi();
        UserService userService=context.getBean("userService",UserService.class);
        userService.sayHi();
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mv3uSjt1-1683543406416)(F:\typora插图\image-20230508154039705.png)]

1.1.3剩余的类注解

剩余的三个注解使用方法与之类似,这里一并演示

@Repository也叫做仓库存储,是在数据持久层即和数据库进行交互的一层,其中数据库在程序中就叫做仓库。

@Component其实也叫做组件存储,是操作数据的工具类。

@Configuration其实也叫做配置存储,是用来存项目中的一些配置的。

存对应Bean方法

@Configuration
public class UserConfiguration {
    public void sayHi(){
        System.out.println("do UserConfiguration sayHi()");
    }
}
@Repository
public class UserRepository {
    public void sayHi(){
        System.out.println("do UserRepository sayHi()");
    }
}
@Component
public class UserComponent {
    public void sayHi(){
        System.out.println("do UserComponent sayHi()");
    }
}

取对象方法:

public class App {
    public static void main(String[] args) {
        ApplicationContext context=new ClassPathXmlApplicationContext("spring-config.xml");
//        UserController userController=context.getBean("userController",UserController.class);
//        userController.sayHi();
//        UserService userService=context.getBean("userService",UserService.class);
//        userService.sayHi();
        UserRepository userRepository=context.getBean("userRepository",UserRepository.class);
        UserComponent userComponent=context.getBean("userComponent",UserComponent.class);
        UserConfiguration userConfiguration=context.getBean("userConfiguration",UserConfiguration.class);
        userComponent.sayHi();
        userConfiguration.sayHi();
        userRepository.sayHi();
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DRv6zgpf-1683543406417)(F:\typora插图\image-20230508155204399.png)]

1.2 五大类注解之间的关系

1.2.1.为什么需要这么多种类注解

为了让程序员看到类注解之后就能直接知道当前类的用途。

例如:@Controller:表示业务逻辑层

@Service:表示服务层

@Repository:表示持久层

@Configuration:表示配置层

了解程序的工程分层会对这个作用有进一步的认识:

JavaEE标准分层至少三个:控制层、服务层、数据持久层,一般公司会在此基础上进行扩展。

这里有配置层是因为有时候感知不到配置文件,所以通过注解告诉他。所以一个项目的配置可能在配置文件或者配置层中。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XEi5Eoj3-1683543406417)(F:\typora插图\image-20230508160107973.png)]

1.2.2 类注解之间的关系

通过ctrl+左键,我们可以看到五大类注解的源码是这样的:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jAwStRo0-1683543406418)(F:\typora插图\image-20230508161108986.png)]

通过观察,我们不难发现,@Controller / @Service / @Repository / @Configuration 这四个类注解都是基于@Component这个注解实现的。

1.3 方法注解

见名知义,类注解是加在类上的,方法注解是加在方法上的。

使用方法注解,其实就是把表示当前方法的返回值将被装配到容器中。

使用方法:在当前方法上加上@Bean 注解,在方法所属类上边加上对应的类注解。即方法注解必须结合类注解使用。

下边是代码示例:

数据准备:创建实体类User。其中@Setter和@Getter以及@ToString注解是lombok插件提供的自动生成对应类的get、set和toString方法的注解(可略)。

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Setter
@Getter
@ToString
public class User {
    private int id;
    private String name;
    public void sayHi(){
        System.out.println("do User sayHi()");
    }

}

实体类:数据库中有一张表,代码中就会有一个对应的实体类,有一个容器去承接对应的实体类,这个实体类中的属性就对应表中的字段。

对应的包名:entity/model

实体类命名规则:

1.基本对象对应数据库中的一张表:与表名一致

2.扩展对象:xxxinfo/xxxinfoVO

组件层的代码:

@Component
public class UserComponent {
    @Bean
    public User user1() {
        User user = new User();
        user.setId(1);
        user.setName("Java");
        return user;
    }
    public void sayHi(){
        System.out.println("do UserComponent sayHi()");
    }
}

启动类:

    public static void main(String[] args) {
        ApplicationContext context=new ClassPathXmlApplicationContext("spring-config.xml");
        User user = (User) context.getBean("user1");
        System.out.println(user.toString());
    }

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-a7KLt3ez-1683543406418)(F:\typora插图\image-20230508163704329.png)]

1.4 Bean命名规则

不难发现,通常来讲Bean,我们使用的都是标准的大驼峰命名,而使用getBean读取的时候则是使用首字母小写+类类型获取的:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lD6CGCkc-1683543406418)(F:\typora插图\image-20230508164119689.png)]

当我们将首字母也写成大写时就会发现有如下错误:NoSuchBeanDefinationException即没有找到Bean异常其实也就是注入失败
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z5Y9CSNa-1683543406419)(F:\typora插图\image-20230508164313626.png)]

因为我们是使用的注解的方式自动注入的,所以Bean的命名是由Spring决定的,根据报错我们不难想到,需要查看Spring关于bean存储生成的规则:

查看方法:双击shift出发全局搜索,输入BeanName,点击AnnotationbeanName…[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nd56qfBl-1683543406419)(F:\typora插图\image-20230508164914466.png)]

观察并分析:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HMlJm5er-1683543406420)(F:\typora插图\image-20230508165714654.png)]

结论:默认情况下是首字母小写,如果类名是首字母和次字母都是大写,那么Bean名称就是它的类型名。

二、获取Bean对象

获取Bean对象也叫做对象装配或者对象注入(从Spring容器中拿出来给当前类装配上/注入,相当于一个完成工作必须的部件)。

对象装配实现方法有三种:

  1. 属性注入
  2. 构造方法注入
  3. Setter注入

其中属性注入最简单,实际开发中使用的也最多。构造方法是官方推荐的注入方式,官方给予的支持也很大,稍后详细说一下。

1.属性注入

属性注入实现方法:在对象上添加@Autowired注解。其中autowired是自动连线的意思。

将Service类注入到Controller类中,其中Service连接着数据持久层操作数据库即将Repository类注入到Service类中。

示例:

数据准备:UserRepository类代码:

@Repository
public class UserRepository {
    @Autowired
    private User user=new User();
    @Bean
    public User getUser(){
        return user;
    }
    public void sayHi(){
        System.out.println("do UserRepository sayHi()");
    }
}

UserService类代码:

@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;
    public User getUser(Integer id) {
        return userRepository.getUser();
    }
//    public void sayHi(){
//        System.out.println("do UserService sayHi()-->"+userRepository.getUser());
//    }
}

UserController类代码:

@Controller
public class UserController {
    @Autowired
    private UserService userService;
    public User getUser(Integer id) {
        return userService.getUser(id);
    }
//    public void sayHi(){
//        System.out.println("do UserController sayHi()");
//    }
}

获取数据:

public static void main(String[] args) {
        ApplicationContext context=new ClassPathXmlApplicationContext("spring-config.xml");

        UserController userController = context.getBean(UserController.class);
        System.out.println(userController.getUser(1).toString());
    }

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8NqhBlJv-1683543406420)(F:\typora插图\image-20230508171810898.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q7D7wHj4-1683543406420)(F:\typora插图\image-20230508171944138.png)]

2.构造方法注入

实现方法:在类的构造方法中实现注入,@Autowired注解加载构造方法上

示例:

@Controller
public class UserController2 {
    private UserService userService;
    @Autowired
    public UserController2(UserService userService) {
    	this.userService = userService;
    }
    public User getUser(Integer id) {
    	return userService.getUser(id);
    }
}
public class App {
    public static void main(String[] args) {
        ApplicationContext context=new ClassPathXmlApplicationContext("spring-config.xml");

        UserController2 userController = context.getBean(UserController2.class);
        System.out.println(userController.getUser(1).toString());
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XlrVw9JK-1683543406421)(F:\typora插图\image-20230508173014786.png)]

注意:当只有一个构造方法时,@Autowired注解可以省略(官方开的绿灯)

当多个构造方式时,必须明确指定到底哪一个构造方法使用@Autowired,否则程序会报错。如下所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CA2J3aU9-1683543406421)(F:\typora插图\image-20230508173617824.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Wx2UmqRX-1683543406422)(F:\typora插图\image-20230508174554448.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TRTR1dhx-1683543406422)(F:\typora插图\image-20230508173925122.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vWrxSzj2-1683543406423)(F:\typora插图\image-20230508174326243.png)]

通过观察我们还可以发现:无参构造很重要,如果我们自己写了一个或多个有参构造方法,就必须要在其中一个上边加@Autowired注解,指定使用哪个进行实例化。

或者说如果我们没标,它回去找无参构造没有就报错注入失败;如果我们标了,就必须是唯一的。

3.Setter方法注入

实现方法:与属性的Setter方法类似, 需要在设置 set ⽅法的时候需要加上 @Autowired 注解。

@Controller
public class UserController3 {
    private UserService userService;
    @Autowired
    public void setUserService(UserService userService) {
    	this.userService = userService;
    }
    public User getUser(Integer id) {
    	return userService.getUser(id);
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cKZ0KL0U-1683543406423)(F:\typora插图\image-20230508175208776.png)]

思考:set ⽅法如果不加 @Autowired 注解能注⼊成功 吗?

答:不能。注意这里不是注入成功了得到空对象了,因为如果是得到空对象了,返回的就是NULL而不是报错。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nB1WKzNY-1683543406424)(F:\typora插图\image-20230508175827735.png)]

三种注入方法的对比

类别优点缺点
属性注入简洁,使用方便1.功能上,没办法实现final修饰变量注入 2.兼容性上,兼容性不好,只适用于IoC容器 3.风险性上,写法简单,违背单一设计原则概率更大
构造方法注入1.可注入一个不可变对象 2.注入的对象不会被改变,构造方法只执行一次 3.通用性更好,所有的框架都支持构造方法 4.构造方法保证注入对象完全被初始化当有多个注入时会比较臃肿
Setter注入符合单一设计原则,因为只会赋值给一个对象1.不可注入不可变对象 2.注入的对象可能会被修改重置

从上边可以看出,对象的注入都是基于@Autowired这个注解实现的

另一种注入关键字:@Resource

@Resource是由jdk提供的注入关键字,@Autowired是由spring提供的。

使用方法:与autowired类似,放在属性上或者方法上,但是只能放在autowired上,不能用于构造方法,会直接报错。

使用示例:

@Controller
public class UserController4 {
    private UserService userService;
    @Resource//Setter注入
    public void setUserService(UserService userService) {
        this.userService = userService;
    }
    public User getUser(Integer id) {
        return userService.getUser(id);
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jsKiNZVr-1683543406424)(F:\typora插图\image-20230508181058766.png)]

@Controller
public class UserController4 {
    @Resource//属性注入
    private UserService userService;
    public void setUserService(UserService userService) {
        this.userService = userService;
    }
    public User getUser(Integer id) {
        return userService.getUser(id);
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NYeGUXQY-1683543406425)(F:\typora插图\image-20230508181143601.png)]

@Autowired和@Resource区别

相同点功能上都只能完成对象装配的工作
不同点1.来源不同,@Autowired来自spring,@Resource来自 2.支持用法不同,@Autowired支持属性注入、构造方法注入和Setter注入三种方式,而@Resource只支持属性注入和Setter方法注入两种方式 3.参数设置上不同,@Resoure支持更多的参数设置

实际工作,大部分场景下,两个没有区别,除非特殊情景。

同一类型多个Bean报错

在同一个类中,出现多个Bean,返回同一对象类型时,或者不同类中向Spring中注入相同类型的Bean时,程序可能会报错NoUniqueBeanDefinitionException即非唯一的Bean对象。

解决办法:

方案1:使用@Resource(name=“xxx”)进行定义(即起别名)

方案2:使用@Qualifier注解定义名称

方案一代码示例:

@Controller
class UserController5 {
    @Resource(name = "user1")
    private User user;
    public User getUser() {
    	return user;
    }
}

方案二代码示例:

@Controller
public class UserController6 {
    @Autowired
    @Qualifier(value = "user2")
    private User user;
    public User getUser() {
        return user;
    }
}

@Autowired、@Resource以及Bean在重命名方面区别

通过看源代码很容易发现,这三个注解都可以通过属性的设置实现对Bean重命名的效果。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JZ5rJF7O-1683543406425)(F:\typora插图\image-20230508183205111.png)]

默认情况下,spring的标识符要么是默认的,要么是指定Bean的name属性定义的,要么是Qualifier使用value定义的,要么是Resource的name指定的。

注意:1.重命名后,原来的默认方法名就不可以用了

@Order注解的作用

@Order 是用来控制Bean的执行顺序/优先级的而并非加载顺序。

注解@Order或者接口Ordered的作用是定义Spring IOC容器中Bean的执行顺序的优先级,而不是定义Bean的加载顺序,Bean的加载顺序不受@Order或Ordered接口的影响;

扫描路径与注解读取相关问题

  1. bean标签能不能与component-scan(组件扫描和路径)一起使用

  2. 五大类注解能不能不再component-scan下
    不能,。

  3. 在component-scan下,但是如果没有加五大类注解,能不能把当前对象加到spring容器中的

    不能。

  4. 不同包下的同名类:

    不可以(存储/load时就不行)
    两种解决方法:

    1. 直接改名字

    2. 起别名value选项定义同名类名(默认时null,其他的也是可以设置的)

    取对象的时候导包也要改
    认情况下,spring的标识符要么是默认的,要么是指定Bean的name属性定义的,要么是Qualifier使用value定义的,要么是Resource的name指定的。

注意:1.重命名后,原来的默认方法名就不可以用了

@Order注解的作用

@Order 是用来控制Bean的执行顺序/优先级的而并非加载顺序。

注解@Order或者接口Ordered的作用是定义Spring IOC容器中Bean的执行顺序的优先级,而不是定义Bean的加载顺序,Bean的加载顺序不受@Order或Ordered接口的影响;

扫描路径与注解读取相关问题

  1. bean标签能不能与component-scan(组件扫描和路径)一起使用

  2. 五大类注解能不能不再component-scan下
    不能,。

  3. 在component-scan下,但是如果没有加五大类注解,能不能把当前对象加到spring容器中的

    不能。

  4. 不同包下的同名类:

    不可以(存储/load时就不行)
    两种解决方法:

    1. 直接改名字

    2. 起别名value选项定义同名类名(默认时null,其他的也是可以设置的)

    取对象的时候导包也要改
    建议:尽量避免起相同类名


http://www.kler.cn/a/17901.html

相关文章:

  • idea 弹窗 delete remote branch origin/develop-deploy
  • Prometheus面试内容整理-Exporters
  • Vue2:组件
  • vscode远程连接服务器并启用tmux挂载进程
  • 专题十八_动态规划_斐波那契数列模型_路径问题_算法专题详细总结
  • 【计算机网络】【传输层】【习题】
  • php 设置meta标签中的keywords | description | content-type | copyright的方法函数
  • 字符设备驱动
  • [架构之路-187]-《软考-系统分析师》-5-数据库系统 - 操作型数据库OLTP与分析型数据库OLAP比较
  • Pytorch, tensor存储机制
  • 多元统计分析-聚类分析的原理与应用
  • 大数据技术之SparkSQL——数据的读取和保存
  • springboot+jsp商务安全邮箱(源码+文档)
  • Python代码学习之给图片添加文字或图片水印
  • UPF learing3:TRANS-11
  • python:可以求解Ax=b的库
  • E. Sergey and Subway(思维 + dp)
  • 入门力扣自学笔记264 C++ (题目编号:2432)
  • 网页和原生程序的交互方案
  • 17组漫画卡通字体推荐给设计师
  • 深入理解Python中的生成器和迭代器
  • ipad有必要用手写笔吗?电容笔和Apple pencil区别
  • 智安网络|网络安全威胁越来越多,教你如何全方面应对
  • PMP|敏捷高分口诀,迅速码住!
  • 单例模式的介绍
  • Yolov1 源码讲解 loss.py