Spring学习 (一): IoC容器

2023-09-15 00:32:31

前言

参考
廖雪峰Spring教程

一、什么是IoC容器

容器的意思可以理解为一个提供供程序正常运行,提供各种依赖的组件的包的环境。

IoC,控制反转,实际上就是将原本由代码编写者控制的各个对象(组件)的生命周期托管给底层的容器,应用层不需要一个个定义好什么时候初始化,什么时候析构释放,所有组件不再由应用程序自己创建和配置,而是由IoC容器负责,这样,应用程序只需要直接使用已经创建好并且配置好的组件。为了能让组件在IoC容器中被“装配”出来,需要某种“注入”机制。

什么是注入呢,假设类A中存在成员类B,这个B可以通过A的setter方式实例化,即依赖的函数提供setter的注入接口,然后coder需要在外部定义好依赖关系(xml文件)/或者通过注解,然后让IoC自己解析并创建依赖的各个组件

在设计上,Spring的IoC容器是一个高度可扩展的无侵入容器。所谓无侵入,是指应用程序的组件无需实现Spring的特定接口,或者说,组件根本不知道自己在Spring的容器中运行。这种无侵入的设计有以下好处:1. 应用程序组件既可以在Spring的IoC容器中运行,也可以自己编写代码自行组装配置;2. 测试的时候并不依赖Spring容器,可单独进行测试,大大提高了开发效率


二、装配Bean

什么是Bean? 简单理解就是需要被托管的组件

可以看以下代码,

public class MailService {
	public void sendLoginMail(User user) {
		
	}

	public void sendRegisterMail(User user) {
	}
}


/***********************************************************/
public class UserService {
    private MailService mailService;

    public void setMailService(MailService mailService) {
        this.mailService = mailService;
    }

    private List<User> users;

    public User login(String email, String password) {
        for (User user : users) {
            if (user.getEmail().equalsIgnoreCase(email) && user.getPassword().equals(password)) {
                mailService.sendLoginMail(user);
                return user;
            }
        }
    }

    public User getUser(long id) {
		//do something
    }

    public User register(String email, String password, String name) {
        users.forEach((user) -> {
            if (user.getEmail().equalsIgnoreCase(email)) {
                throw new RuntimeException("email exist.");
            }
        });
        User user = new User(users.stream().mapToLong(u -> u.getId()).max().getAsLong() + 1, email, password, name);
        users.add(user);
        mailService.sendRegistrationMail(user);
        return user;
    }
}

UserService依赖MailService的两个函数,实际设置mailService时,通过设置setter搞定,IoC接口通过这个setter方法完成注入,但需要额外信息从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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="userService" class="com.itranswarp.learnjava.service.UserService">
        <property name="mailService" ref="mailService" />
    </bean>

    <bean id="mailService" class="com.itranswarp.learnjava.service.MailService" />
</beans>
  • 每个<bean …>都有一个id标识,相当于Bean的唯一ID;
  • 在userServiceBean中,通过注入了另一个Bean;
  • Bean的顺序不重要,Spring根据依赖关系会自动正确初始化

然后创建IoC实例,加载配置文件,让Spring容器为我们创建并装配好配置文件中指定的所有Bean

ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
// 获取Bean:
UserService userService = context.getBean(UserService.class);
// 正常调用:
User user = userService.login("bob@example.com", "password");

三、使用注解

使用注解则可以直接不用再写繁琐的XML文件了,常用的注解组合有@Component @Autowired@Configuration @Bean

@Component 注解用于将一个类标记为组件(Component),表示它将被Spring容器实例化为一个Bean,就等同于XML中的bean 标签;@Autowired注解用于在需要依赖注入的地方标记,以实现自动装配。Spring容器会自动在上下文中查找匹配的Bean,并将其注入到被标记的字段、构造函数或方法参数中,对应bean下面的property标签。

用法如下

@Component
public class MyBean {
    private AnotherBean anotherBean;

    @Autowired
    public MyBean(AnotherBean anotherBean) {
        this.anotherBean = anotherBean;
    }

    @Autowired
    public void setAnotherBean(AnotherBean anotherBean) {
        this.anotherBean = anotherBean;
    }

    // ...
}

在这个示例中,MyBean 类中的 anotherBean 字段和 setAnotherBean() 方法都被标记为 @Autowired,表示它们需要被自动装配。Spring容器会自动查找匹配类型的Bean,并将其注入到这些位置。除了字段和方法参数,还可以在构造函数上使用 @Autowired 注解,以实现构造函数注入。

当在一个类中使用 @Autowired 注解标记一个字段、方法或构造函数时,底层的IOC容器会负责实例化这个类,并自动注入所需的依赖项。具体来说,当一个类被实例化时,IOC容器会检查该类中标记为 @Autowired 的字段、方法或构造函数参数。它会查找与这些依赖项类型匹配的Bean,并将它们自动注入到对应的位置。如果没有找到匹配的Bean,或者存在多个匹配的Bean时,Spring框架会抛出异常,提示依赖项无法自动注入或存在歧义。
如果组件既没有使用 @Autowired 注解标注构造函数,也没有使用 @Autowired 注解标注setter方法,IOC容器将不会自动进行依赖项的注入。在这种情况下,需要自己负责手动实例化依赖项并将其注入到组件中。

当存在多个匹配的Bean时,Spring会抛出 NoUniqueBeanDefinitionException 异常,提示依赖项的注入歧义。因此需要解决这个问题。

@Configuration和@Bean用法代码如下:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {
    @Bean
    public MyBean myBean() {
        return new MyBean();
    }
}
  • @Configuration 是一个注解,用于标识一个类作为配置类。配置类中可以定义Bean的创建和配置信息,以及其他配置项,如AOP、事务管理等。它表明这个类被用来声明Bean的定义。
  • @Bean 注解用于在配置类中声明一个Bean。通过在一个方法上添加 @Bean 注解,该方法的返回值将被注册为一个Bean,并由Spring容器进行管理。可以通过在 @Configuration 类中的方法上使用 @Bean 注解来创建和配置Bean。(这样子可以让一个返回的对象自动注册为Bean)

当你需要定义和配置多个Bean时,可以使用 @Configuration 注解将一个类标识为配置类。在配置类中,你可以使用 @Bean 注解声明方法,该方法的返回值将被注册为一个Bean。通常,你可以在配置类中进行一些复杂的Bean创建和配置,包括外部依赖的集成、条件化的Bean定义等。
示例场景:创建和配置数据源、声明第三方库的连接器、定义自定义的Bean等。


四、其他注解

  1. @Value:用法@Value(resourcePath),用于注入文件,方便程序读取;@Value("${app.zone:Z}"); @Value("#{smtpConfig.host}")
  2. @Order:用法@Order(Num) 当多个Bean被注入到一个List时,若List中组件存在顺序要求,可以通过@Order指定顺序
  3. @PostConstruct:一个Bean在注入必要的依赖后,需要进行初始化(监听消息等,我们通常会定义一个init()方法进行初始化,用@PostConstruct修饰
  4. @PreDestory:在容器关闭时,有时候还需要清理资源(关闭连接池等)定义一个shutdown()方法进行清理,用@PreDestory修饰
  5. @PropertySource(“app.properties”) :读取以key=value的形式写在.properties文件中的配置
  6. @Profile: 条件装配,用于表示不同环境下的装配, 分为test,native, production三个环境

@Scope
对于Spring容器来说,当我们把一个Bean标记为@Component后,它就会自动为我们创建一个单例(Singleton),即容器初始化时创建Bean,容器关闭前销毁Bean。在容器运行期间,我们调用getBean(Class)获取到的Bean总是同一个实例。

还有一种Bean,我们每次调用getBean(Class),容器都返回一个新的实例,这种Bean称为Prototype(原型),它的生命周期显然和Singleton不同。声明一个Prototype的Bean时,需要添加一个额外的@Scope注解:

@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) // @Scope("prototype")
public class MailSession {
    ...
}

@Bean(Name) || @Qualifier(Name)
默认情况下,对一种类型的Bean,容器只创建一个实例。但有些时候,我们需要对一种类型的Bean创建多个实例。例如,同时连接多个数据库,就必须创建多个DataSource实例。

如果我们在类中创建了多个同类型的Bean:
Spring会报NoUniqueBeanDefinitionException异常,意思是出现了重复的Bean定义。

这个时候,需要给每个Bean添加不同的名字
可以用@Bean(“name”)指定别名,也可以用@Bean+@Qualifier(“name”)指定别名。

@Configuration
@ComponentScan
public class AppConfig {
    @Bean("z")
    ZoneId createZoneOfZ() {
        return ZoneId.of("Z");
    }

    @Bean
    @Qualifier("utc8")
    ZoneId createZoneOfUTC8() {
        return ZoneId.of("UTC+08:00");
    }
}

补充解释

Spring的IoC容器会自动注册标记为 @Component 或其衍生注解的Bean。

当一个类被标记为 @Component、@Service、@Repository、@Controller 等注解之一时,Spring会自动扫描并注册这个类作为一个Bean。这个过程称为组件扫描(Component Scanning),Spring会自动扫描指定的包及其子包,找到所有标记为组件注解的类,并将其实例化并注册到IoC容器中。

通过自动扫描,Spring会根据类的命名约定或自定义的Bean名称规则为这些类创建默认的Bean名称。例如,对于一个标记为 @Component 的类,默认的Bean名称将是类名的首字母小写形式。你也可以通过 @Component(“beanName”) 的方式显式指定Bean的名称。

此外,Spring还支持在XML配置文件中显式地定义Bean的配置信息,无论是通过 @Bean 在 @Configuration 类中进行定义,还是直接在XML配置文件中进行定义。这些显式定义的Bean也会被注册到IoC容器中。

更多推荐

2023/09/21 day5 qt

将注册的账号密码存储到数据库中登录的账号密码与数据库中的账号密码进行匹配头文件#ifndefDENGLU_H#defineDENGLU_H#include<QMainWindow>#include<QDebug>#include<QIcon>#include<QLabel>#include<QLineEdit>#inc

大模型Tuning分类

类型总结微调(Fine-tunning)语言模型的参数需要一起参与梯度更新轻量微调(lightweightfine-tunning)冻结了大部分预训练参数,仅添加任务层,语言模型层参数不变适配器微调(Adapter-tunning)Adapter在预训练模型每层中插入用于下游任务的参数,在微调时将模型主体冻结,仅训练特

FPGA计数器边界问题解析

FPGA计数器边界问题解析一次作者在处理AMBE2000数据接收过程中,遇到一个问题,对该计数器边界总是模糊不清。现在予以说明,以警示以后工作时书写错误代码。AMBE2000数据一旦准备好后,一次会输出24个字,其中第1个字0x13ec是同步头,连上同步头的前12个字为控制字,后12个字为数据字,我们需要提前数据字,抛

人脸三维重建

人脸三维重建调研目标根据某人一张或多张二维人脸图像重建出三维人脸模型(这里主要讨论单张)。人脸三维重建发展概述三维人脸重建主要有两种思路:model-based和model-free。model-based以[AMorphableModelForTheSynthesisOf3DFaces,1999](AMorphabl

控价与数据分析的关系

品牌在做线上控价时,会面对许多的数据,如店铺数据、行业数据,当这些数据仅仅只是拿来做监测低价输出低价报表使用,是没有发挥其最大作用的,因为商品链接的字段较丰富,涉及的内容会包含销量、评价量、促销信息等,所以可以通过这些字段内容分析不同维度的数据,输出各种对品牌有帮助的分析报告。力维网络有专业的数据采集能力,同时也有一支

电子商务平台市场动向的数据分析平台:阿里商品指数,包括淘宝采购指数,淘宝供应指数,1688供应指数。

项目设计集合(人工智能方向):助力新人快速实战掌握技能、自主完成项目设计升级,提升自身的硬实力(不仅限NLP、知识图谱、计算机视觉等领域):汇总有意义的项目设计集合,助力新人快速实战掌握技能,助力用户更好利用CSDN平台,自主完成项目设计升级,提升自身的硬实力。专栏订阅:项目大全提升自身的硬实力[专栏详细介绍:项目设计

相比SiteGPT,用HelpLook创建Chatbot有哪些优势?

在当今快节奏的数字时代,很多企业都在不断寻找新的方法来改善客户支持和简化运营。一种广受欢迎的解决方案是使用AI问答机器人(Chatbot)。聊天机器人凭借其理解自然语言查询和实时响应的能力,已成为各行业企业不可或缺的工具。SiteGPT和HelpLook是两个比较强大的人工智能工具,在技术和人工智能社区中获得了不错的关

HTTPS加密流程

HTTPSHTTPS一.什么是HTTPS二.什么是"加密"三.加密的方式有哪些1.对称加密2.非对称加密3.中间人攻击4.引入证书HTTPS一.什么是HTTPSHTTPS与HTTP一样都是应用层协议,与HTTPS不同的是:HTTP的协议内容都是按照文本方式进行明文传输的,这导致在传输过程第三方者能够轻易获取传输的内容,

【操作系统】实验一 Linux初步

文章目录Linux初步一、实验目的二、实验内容Linux初步一、实验目的通过proc文件系统观察整个Linux内核和系统的一些重要特征,并编写一个程序,使用proc文件系统获得以及修改系统的各种配置参数。本实验需要学生具有Linux的基本操作技能,以及采用C语言编写程序的能力。二、实验内容以超级用户的身份登录Linux

【面试刷题】——TCP三次握手,以及为什么要三次握手

TCP(传输控制协议)的三次握手是建立TCP连接的过程,它确保了通信双方的正常启动和参数协商。三次握手的过程如下:客户端发送请求:客户端首先向服务器发送一个特殊的TCP报文,称为SYN(同步)报文。这个报文包含一个随机的序列号(ClientISN),并请求建立连接。此时客户端进入"SYN-SENT"状态。服务器回应:服

C2基础设施威胁情报对抗策略

威胁情报是指在信息安全和安全防御领域,收集、分析和解释与潜在威胁相关的信息,以便预先发现并评估可能对组织资产造成损害的潜在威胁,是一种多维度、综合性的方法,其通过信息的收集、分析和研判,帮助组织了解可能对其安全构成威胁的因素。这种方法不仅仅着重于技术层面,还包括了社会、心理、政治等多个维度,以此更好地应对不断变化和复杂

热文推荐