更简单的读取和存储对象

news2025/1/11 23:59:56

在上一篇文章中我们已经介绍在XML文件注册Bean的具体步骤,这一篇文章将会介绍使用更加简洁的方式(使用注解)来存储和读取Bean.这也是最常用的方法.

1. 创建并配置好Spring项目

和上一篇的步骤相同,下面就相当于复习如何创建Spring项目吧

  1. 创建一个 Maven 项目
  2. 为 Spring 项目添加依赖包(spring-beans/spring-context)
  3. 创建一个启动类
  4. 为 Spring 项目 创建配置文件(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">
</beans>

2. 存储 Bean

之前存储 Bean 时需要根据我们的需要,添加对应的 Bean 注册内容 才能完成注入, 而现在我们只需要一行内容就可以替代之前的写多行注册内容.那么为了完成这个简单的注入,实现要配置好扫描路径.

2.1 配置扫描路径

扫描路径: 决定 Spring 搜索的文件范围, 默认是从Java文件的根路径开始搜索

根路径: 在IDEA中可以修改文件的属性, 修改为不同类型文件的根目录, 对应的Java文件的根目录的颜色如图是浅蓝色的.
image.png

比如你在根目录下把需要注入到Spring中的对象创建到 "com.bean.controller"中,那么对应的扫描路径就应该为

<content:component-scan base-package="com.bean.controller"></content:component-scan>

注:

  • 只有在当前扫描包下的对象并且添加了注解的类才会被存储到Spring中
  • 同时使用注解注入和bean内容注入是不会发生冲突的

2.2 使用注解注入 Bean

通过两种注解方式使对象注入到Spring中

  1. 类注解: @Controller、@Service、@Repository、@Component、@Configuration
  2. 方法注解: @Bean

2.2.1 不同类注解的作用

相信大家想知道为什么有这么多类注解? 虽然最终完成的工作都是把对象注入,但是它们之间的关系就类似于不同地区的车牌号一样,不同地区的车牌号是不同的,这样就能够直观的辨识一辆车的归属地, 这里的类注解就是为了让程序员直观的了解当前类的用途,例如:

  • @Controller:业务逻辑层(验证前端传递的数据的合法性)
  • @Servie:服务层(服务调用的编排和汇总)
  • @Repository:持久层 (直接操控数据库)
  • @Configuration:配置层 (关于项目的使所有配置)
  • @Component: 组件(通用化的工具类)

程序之间的层次关系如下:

2.2.2 类注解之间的联系


通过查看五大注解的源码,可以发现它们都是@Component的子类.作用都是把 Bean 存储到Spring中

2.2.3 五大类注解使用示例

为了区分不同注解之间的关系,创建不同的包来验证其效果
image.png

那么当前项目的扫描路径就是从"com.bean"下的所有包

<content:component-scan base-package="com.bean"></content:component-scan>
  1. @Component 获取对象
package com.bean.component;

import org.springframework.stereotype.Component;

@Component
public class UserComponent {
    public void doComponent() {
        System.out.println("Do Component");
    }
}
  1. @Configuration 获取对象
package com.bean.config;

import org.springframework.context.annotation.Configuration;

@Configuration
public class UserConfiguration {
    public void doConfiguration() {
        System.out.println("Do Configuration");
    }
}
  1. @Controller 获取对象
package com.bean.controller;

import org.springframework.stereotype.Controller;

@Controller
public class UserController {
    public void doController() {
        System.out.println("Do Controller");
    }
}
  1. @Repository 获取对象
package com.bean.repository;

import org.springframework.stereotype.Repository;

@Repository
public class UserRepository {
    public void doRepository() {
        System.out.println("Do Repository");
    }
}
  1. @Service 获取对象
package com.bean.service;

import org.springframework.stereotype.Service;

@Service
public class UserService {
    public void doService() {
        System.out.println("Do Service");
    }
}
  1. 启动类
import com.bean.component.UserComponent;
import com.bean.config.UserConfiguration;
import com.bean.controller.UserController;
import com.bean.repository.UserRepository;
import com.bean.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {
    public static void main(String[] args) {
        // 1. 获取上下文对象
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        // 2. 使用 getBean 方法获取对象
        UserComponent userComponent = context.getBean("userComponent", UserComponent.class);
        UserConfiguration userConfiguration = context.getBean("userConfiguration", UserConfiguration.class);
        UserController userController = context.getBean("userController", UserController.class);
        UserRepository userRepository = context.getBean("userRepository", UserRepository.class);
        UserService userService = context.getBean("userService", UserService.class);
        // 3. 使用 Bean
        userComponent.doComponent();
        userConfiguration.doConfiguration();
        userController.doController();
        userRepository.doRepository();
        userService.doService();
    }
}

成功运行截图:
image.png

2.2.4 Bean 的命名规则

在Java中通常对类名的命名规则是大驼峰命名(例如: UserComponent, UserConfiguration等), 而在读取时使用的是首字母小写获取 Bean (例如:userComponent, userConfiguration等),但是这是普遍规则吗?
假如现在的类名的首字母和第二个字母都是大写时是否满足呢?

package com.bean.component;

import org.springframework.stereotype.Component;

@Component
public class UComponent {
    public void doComponent() {
        System.out.println("Do Component");
    }
}

image.png

使用首字母小写的方法结果是错误的,那么就需要知道 Bean 的命名规则

  1. 在 Idea中搜索 BeanName

image.png

  1. 点击进入查看, 找默认生成 Bean 名称方法

image.png

  1. 最后发现使用的名称生成器是JDK中 Introspector类中的方法

image.png

image.png

通过查看源代码得出结论:

  • 当类名的前两个字母都是大写的情况下, 那么 Bean 的名称默认为原类名
  • 其他情况都是首字母小写为 Bean 名称

知道问题的解决方案后,修改后就能够成功读取了
image.png

2.2.5 方法注解 @Bean

前面学习类注解是使用在方法上的,那么方法注解是不是使用在方法上呢?

package com.bean.model;

import org.springframework.context.annotation.Bean;

public class User {
    public String name;
    public int age;

    @Bean
    public User user1() {
        User user = new User();
        user.setAge(10);
        user.setName("李四");
        return user;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

我们发现尝试获取当前 Bean 时发生了异常
image.png
原来方法注解是需要搭配类注解共同完成注入的
image.png

image.png

同样的通过@Bean 注解 Bean 名称生成规则和前面一样,不同的是 @Bean 可以有多个名称,我们通过查看源码就可以发现,

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {
    @AliasFor("name")
    String[] value() default {};

    @AliasFor("value")
    String[] name() default {};

    /** @deprecated */
    @Deprecated
    Autowire autowire() default Autowire.NO;

    boolean autowireCandidate() default true;

    String initMethod() default "";

    String destroyMethod() default "(inferred)";
}

其 name是 一个String[]数组,也就意味着可以有0个或者多个名称.

    @Bean(name = {"u1", "u2"})
    public User user() {
        User user = new User();
        user.setAge(10);
        user.setName("李四");
        return user;
    }

并且name={}可以省略

    @Bean({"u1", "u2"})
    public User user() {
        User user = new User();
        user.setAge(10);
        user.setName("李四");
        return user;
    }

注: 对 Bean 重命名后就不能使用方法名获取 对象 了

2.2.6 注解方式的对比

相同点:

  • 都是把通过注解的方式把对象存储到Spring容器中

不同点:

  • @Component :通用的注解,可标注任意类为 Spring 的组件。如果一个 Bean 不知道属于哪个层,可以使用 @Component 注解标注
  • @Configuration :声明该类为一个配置类,可以在此类中声明一个或多个 @Bean 方法。
  • @Controller :对应 Spring MVC 控制层,主要用来接受用户请求并调用 Service 层返回数据给前端页面
  • @Service :对应服务层,主要设计一些复杂的逻辑,需要用到 Dao 层
  • @Repository :对应持久层即 Dao 层,主要用于数据库相关操作。
  • 类注解是通过路径扫描来自动侦测以及自动装配到 Spring 容器中,@Bean 注解通常是我们在标有该注解的方法中定义产生这个 bean,@Bean 告诉了 Spring 这是某个类的实例并且产生这个 Bean 对象的方法Spring 只会调用一次,随后Spring 就会把该对象存储在自己的IoC容器中.
  • @Component , @Repository , @ Controller , @Service 这些注解只局限于自己编写的类,而@Bean注解能把第三方库中的类实例加入IOC容器中并交给Spring管理

3. 获取 Bean

获取 Bean 也叫做 对象装配, 是把对象取出来放入某个类中, 有时候也叫做 对象注入, 其实现方式有下面三种

  1. 属性注入 (Field Injection)
  2. 构造方法注入 (Constructor Injection)
  3. Setter注入 (Setter Injection)

下面采用的是将 Service 类注入到Controller类中

3.1 属性注入

package com.bean.controller;

import com.bean.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class UserController {

    // 1. 属性注入
    @Autowired
    private UserService userService;
    
    public void doController() {
        System.out.println("Do Controller");
        userService.doService();
    }
}

运行成功截图:
image.png

3.1.1 优点

实现简单,只需要把需要的属性上加入@Autowired 注解即可

3.1.2 缺点

  1. 功能性问题

使用属性注入无法注入一个不可变对象(被final修饰对象)
image.png

原因: 在 Java中的 被 final 修饰的变量 要么直接初始化,要么使用构造方法初始化,当使用属性注入时都没有满足上面任一条件当然会出错

  1. 通用性问题

使用属性注入只适用于IoC框架, 如果在其他非IoC容器中使用就无法使用了

  1. 设计原则问题

因为属性注入的简易,所有可能会导致开发者在一个类中注入多个类,但是对于这些类是否需要呢? 所以有很大可能会违反单一设计原则.

3.2 Setter 注入

@Controller
public class UserController {
    private UserService userService;
    // 2. Setter 注入
    @Autowired
    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    public void doController() {
        System.out.println("Do Controller");
        userService.doService();
    }
}

3.2.1 优点

因为一个Setter方法只会针对一个对象,所以它的优点很明显: 符合单一设计原则

3.2.2 缺点

  1. 功能性问题

使用属性注入无法注入一个不可变对象(被final修饰对象)
image.png

  1. 注入对象可以被修改

对于当前类setXXX()方法是可见的,所以你可以在某处调用setXXX()方法从而改变注入对象

3.3 构造方法注入

package com.bean.controller;

import com.bean.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class UserController {
    private UserService userService;

    // 3. 构造方法注入
    @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }

    public void doController() {
        System.out.println("Do Controller");
        userService.doService();
    }
}

注:

  • 如果当前类只有一个构造方法那么 @Autowired 可以省略
  • 如果存在多个属性需要注入,那么只有被@Autowired 注解的构造方法可以成功注入,并且有且仅有一个构造方法可以被@Autowired注解

image.png

image.png

  • 可以在一个构造方法中注入多个对象

image.png

  • 对于注入的类,要确保不为空,否则会报错

image.png

对于List集合类含有默认的构造器不为空,但是Integer类的默认构造器需要参数,所以报错
在Spring 4.2 之前官方推荐使用注入方法是Setter注入,因为Setter更符合单一设计原则; 但在Spring 4.2 后官方推荐使用构造方法注入的方式, 因为构造方法有以下优点:

  1. 可以注入不可变对象

原因是通过构造方法注入就符合Java设计规范

  1. 注入对象不会被改变
    1. 因为构造方法只会执行一次
  2. 完全初始化
    1. 因为构造方法是在对象创建前优先调用的,所以注入对象在使用前一定被完全初始化
  3. 通用性更好
    1. 因为构造方法是Java最底层的框架, 所以在不同的框架下都能使用

3.4 @Resource 另一种注入关键字

@Resource 有两种注入方式:

  1. 属性注入
package com.bean.controller;

import com.bean.service.UserService;
import org.springframework.stereotype.Controller;

import javax.annotation.Resource;

@Controller
public class UserController2 {
    // 1. 属性注入
    @Resource
    private UserService userService;
    
    public void doController() {
        System.out.println("Do Controller 2.0");
        userService.doService();
    }
}

image.png

  1. Setter()注入
package com.bean.controller;

import com.bean.service.UserService;
import org.springframework.stereotype.Controller;

import javax.annotation.Resource;

@Controller
public class UserController2 {
    //2. Setter()注入
    private UserService userService;
    @Resource
    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    public void doController() {
        System.out.println("Do Controller 2.0");
        userService.doService();
    }
}

image.png

3.5 @Resource 和 @Autowired的异同点

  1. 相同点

@Autowired 和 @Resource都可以用来装配bean,都可以用于属性注入或setter()注入

  1. 不同点
  1. 来源不同: @Autowired 是Spring 提供的, @Resource是JDK的注解
  2. @Autowire 默认按类型装配,默认情况下必须要求依赖对象必须存在,如果要允许 null 值,可以设置它的 required 属性为 false
  3. @Resource 默认按名称装配,当找不到与名称匹配的 bean 时才按照类型进行装配。名称可以通过 name 属性指定,如果没有指定 name 属性,当注解写在字段上时,默认取字段名,当注解写在 setter 方法上时,默认取属性名进行装配.
  4. 装配顺序不同.
Autowired的装配顺序只根据type进行注入,不会去匹配name
Resource 的装配顺序
1. 如果同时指定 name 和 type,则从容器中查找唯一匹配的 bean 装配,找不到则抛出异常;
2. 如果指定 name 属性,则从容器中查找名称匹配的 bean 装配,找不到则抛出异常;
3. 如果指定 type 属性,则从容器中查找类型唯一匹配的 bean 装配,找不到或者找到多个抛出异常;
4. 如果不指定,则自动按照 byName 方式装配,如果没有匹配,则回退一个原始类型进行匹配,如果匹配则自动装配

注:

可以使用@Qualifier 指定名称,下面的两种写法效果是相同的

@Autowired(required = false) @Qualifier("userService")
private UserService userService;

@Resource(name = "userService")
private UserService userService;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/19787.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

微信小程序 | 酷炫时钟样式整理【附源码】

&#x1f4cc;个人主页&#xff1a;个人主页 ​&#x1f9c0; 推荐专栏&#xff1a;小程序开发成神之路 --【这是一个为想要入门和进阶小程序开发专门开启的精品专栏&#xff01;从个人到商业的全套开发教程&#xff0c;实打实的干货分享&#xff0c;确定不来看看&#xff1f; …

对给定的数组进行重新排列numpy.random.shuffle()和numpy.random.permutation()

【小白从小学Python、C、Java】 【计算机等级考试500强双证书】 【Python-数据分析】 对给定的数组进行重新排列 numpy.random.shuffle()和 numpy.random.permutation() [太阳]选择题 请问对以下Python代码说法错误的是&#xff1f; import numpy as np anp.arange(6) print(【…

零基础带你基于vue2架构搭建qiankun父子项目微前端架构

这里建议大家用 14版本左右的node版本 我们先创建一个目录 就叫qiankun 然后在终端打开 qiankun 目录 在终端输入指令 vue create vue-qiankun-base创建一个叫 vue-qiankun-base的vue项目 版本大家先选择vue2 vue-qiankun-base项目将作为我们的基座 然后在终端输入 vue …

SpringBoot SpringBoot 开发实用篇 5 整合第三方技术 5.3 手机验证码案例 - 生成验证码

SpringBoot 【黑马程序员SpringBoot2全套视频教程&#xff0c;springboot零基础到项目实战&#xff08;spring boot2完整版&#xff09;】 SpringBoot 开发实用篇 文章目录SpringBootSpringBoot 开发实用篇5 整合第三方技术5.3 手机验证码案例 - 生成验证码5.3.1 SpringBoot …

众焱公司网络平台建设-传输网的规划与设计

目 录 摘 要 I Abstract II 第一章 项目概述 1 1.1 项目目标 1 1.1.1 总体目标 1 1.1.2 阶段目标 1 1.2 设计原则 2 1.3总体拓扑图设计 3 第二章 应用分析 4 2.1 应用分类 4 2.1.1 应用系统总体框架 4 2.1.2 业务系统应用分类 5 2.1.3 信息管理系统应用分类 6 2.2 数据中心及分…

数据结构:栈和队列

栈是一种特殊的线性结构&#xff0c;只允许在栈顶进行进行插入和删除操作。 进行数据插入和删除操作的一端称为栈顶&#xff0c;另一端称为栈底。栈中的数据元素遵守后进先出&#xff08;先进后出&#xff09;LIFO&#xff08;Last In First Out&#xff09;的原则。 类比成将子…

小学生python游戏编程arcade----爆炸粒子类

小学生python游戏编程arcade----爆炸粒子类前言1.1 参数设置粒子加速下降的速度。如果不需要&#xff0c;则为0粒子退出的速度粒子移动的速度。范围为2.5<-->5&#xff0c;设置为2.5和2.5。每次爆炸有多少粒子粒子直径多大粒子颜色列表我们有可能将纹理翻转为白色&#x…

芒果改进YOLOv7系列:首发改进特征融合网络BiFPN结构,融合更多有效特征

💡统一使用 YOLOv7 代码框架,结合不同模块来构建不同的YOLO目标检测模型。文章目录 一、BiFPN论文理论部分代码部分YOLOv7+BiFPN在这篇文章中,将BiFPN结构加入到 YOLOv7 结构中 一、BiFPN论文理论部分 EfficientDet: Scalable and Efficient Object Detection BiFPN与P…

芯天下在创业板过会:预计全年收入将达到10亿元,净利润约2亿元

11月18日&#xff0c;深圳证券交易所创业板披露的信息显示&#xff0c;芯天下技术股份有限公司&#xff08;下称“芯天下”&#xff09;获得上市委会议通过&#xff0c;即IPO过会。据贝多财经了解&#xff0c;芯天下于2022年4月28日在创业板递交上市申请材料。 本次冲刺创业板上…

vins-mono初始化代码分析

大体流程 初始化主要分成2部分&#xff0c;第一部分是纯视觉SfM优化滑窗内的位姿&#xff0c;然后在融合IMU信息。 这部分代码在estimator::processImage()最后面。 主函数入口&#xff1a; void Estimator::processImage(const map<int, vector<pair<int, Eigen:…

maven大全(概述、maven安装配置、IDEA配置maven、IDEA创建maven项目并如何使用)

目录 一、概述 1.什么是maven&#xff1f; 2.maven有什么作用&#xff1f; &#xff08;1&#xff09;提供了一套标准化的项目结构 &#xff08;2&#xff09;提供了标准化的构建流程&#xff08;编译、测试、打包、发布&#xff09; &#xff08;3&#xff09;提供了一套…

Java -- 每日一问:后台服务出现明显“变慢”,谈谈你的诊断思路?

典型回答 首先&#xff0c;需要对这个问题进行更加清晰的定义: 服务是突然变慢还是长时间运行后观察到变慢&#xff1f;类似问题是否重复出现&#xff1f;“慢”的定义是什么&#xff0c;我能够理解是系统对其他方面的请求的反应延时变长吗? 第二&#xff0c;理清问题的症状…

【计算机考研必备常识】24考研你开始准备了吗?

前言 23考研只剩下一个多月了&#xff0c;准备 【24考研】 的小伙伴是否有一丝丝焦虑了呢&#xff1f; 对于考研相关的常识问题&#xff0c;你又是否有了解呢&#xff1f;考研全流程&#xff1f;计算机考研考什么&#xff1f;学硕和专硕怎么选 … 一系列考研相关的常识问题博…

JWT和token是什么?如何利用token进行身份验证?

什么是token&#xff1f;什么是JWT&#xff1f;如何基于token进行身份验证&#xff1f; 我们都知道session信息需要保存一份在服务器端。这种方式会带来一些麻烦&#xff0c;比如需要我们保证保存session信息服务器的可用性、不适合移动端等。 有没有一种不需要自己存放sessi…

五、DMSQL

五、数据类型与操作符和常用DMSQL语句 1、数据类型与操作符介绍 达梦数据库支持的数据类型有很多&#xff0c;具体如下&#xff1a; 其中&#xff1a; 常规数据类型 数值数据类型字符数据类型多媒体数据类型日期时间数据类型 一般日期时间类型时区数据类型时间间隔数据类型 B…

辰奕智能在创业板过会:计划募资约4亿元,约有五成来自境外

11月18日&#xff0c;深圳证券交易所创业板披露的信息显示&#xff0c;广东辰奕智能科技股份有限公司&#xff08;下称“辰奕智能”&#xff09;获得上市委会议通过&#xff0c;即IPO过会。据贝多财经了解&#xff0c;辰奕智能于2021年12月31日在创业板递交上市申请材料。 本次…

【论文阅读】社交网络传播最大化问题-01

问题定义&#xff1a;构建传播最大化模型&#xff08;最大化末态时的激活节点数量 &#xff09;& 确定最具影响力节点 思考问题&#xff1a; 影响节点影响力的因素&#xff1f;有向图和无向图的模型构建区别&#xff1f; 定义参数&#xff1a; 节点影响力的取值范围节点…

Thinkphp6.0.x反序列化漏洞复现

漏洞起点 起因: 在做 [安洵杯 2019]iamthinking 时发现是 thinkphp6 的反序列化&#xff0c;那么就去复现一下呗。 看了其他大佬的 wp&#xff0c;上面说 tp6 的反序列化漏洞的后半段利用和 tp5.2.x 是一样的&#xff0c;也就是 __toString 函数上。 第一步相信大家都知道&a…

USV合伙人反思FTX:应以更长远的眼光看待Web3

潜力博主推荐&#xff0c;点击上面关注博主 ↑↑ FTX的事件动摇了许多人的信心。那么&#xff0c;最大的加密货币交易所之一是如何迅速崩溃的&#xff1f;为什么加密世界的类似崩溃似乎一直在发生&#xff1f; 在这个时候&#xff0c;我们要对Web3整个行业&#xff0c;有一个更…

FA-PEG-N3,Folic acid-PEG-Azide,叶酸-聚乙二醇-叠氮一种叶酸PEG试剂

叶酸PEG试剂叶酸-聚乙二醇-叠氮&#xff0c;其英文名为Folic acid-PEG-Azide&#xff08;FA-PEG-N3&#xff09;&#xff0c;它所属分类为Azide PEG Folic acid&#xff08;FA&#xff09; PEG。 叶酸-PEG-叠氮的的分子量均可定制&#xff0c;有&#xff1a;FA-PEG-N3 5000、叶…