Spring入门系列:浅析知识点

spring,入门,系列,浅析,知识点 · 浏览次数 : 708

小编点评

**Spring 中的 Bean 生命周期** **Bean 的创建过程** 1. **构造器**:当对象被实例化时执行的初始化方法。 2. **单例缓存池**:如果对象是单例,Spring 将实例化对象并将其放入缓存池中。 3. **多例**:如果对象是多例,每次获取对象时都会重新创建它。 4. **AOP 代理**:如果存在 @AspectJ 注解,Spring 会生成代理对象,并为其方法注入属性。 **Bean 的创建条件** * **只有一个构造器**:使用该构造器创建对象。 * **存在多个构造器**:使用第一个无参构造器创建对象。 * **没有 @Autowired 注解**:使用默认构造器创建对象。 **AOP 的创建** 1. Spring 会在创建对象时寻找所有使用 @AspectJ 注解的切面方法。 2. 检查当前对象是否需要进行代理。 3. 生成代理对象,并为其方法注入属性。 **事务** 1. 如果对象有 @Transaction 注解,则会生成事务代理对象。 2. 在方法执行过程中,使用事务管理器获取数据库连接。 3. 执行业务方法。 4. 完成方法后,提交事务。 5. 出现异常,进行回滚。

正文

前言

讲解Spring之前,我们首先梳理下Spring有哪些知识点可以进行入手源码分析,比如:

  1. Spring IOC依赖注入
  2. Spring AOP切面编程
  3. Spring Bean的声明周期底层原理
  4. Spring 初始化底层原理
  5. Spring Transaction事务底层原理

Hello World

通过这些知识点,后续我们慢慢在深入Spring的使用及原理剖析,为了更好地理解Spring,我们需要先了解一个最简单的示例——Hello World。在学习任何框架和语言之前,Hello World都是必不可少的。

//在以前大家都是spring.xml进行注入bean后供Spring框架解析
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = (UserService) context.getBean("userService");
userService.test();

spring.xml中的内容为:

<context:component-scan base-package="com.zhouyu"/>
<bean id="userService" class="com.zhouyu.service.UserService"/>

如果对上面的代码或者xml形式很陌生,再看下面一种代码,也是目前流行的一种形式

//通过我们的配置类进行注入bean并解析
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MySpringConfig.class);
//ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = (UserService) context.getBean("userService");
userService.test()

MySpringConfig中的内容为:

@ComponentScan("com.xiaoyu")
public class MySpringConfig {
	@Bean
	public UserService userService(){
		return new UserService();
	}
}

相信很多人都会对上面的代码不陌生,那我们来看下这三行代码都做了那些工作:

  1. 构造一个ClassPathXmlApplicationContext类解析配置文件 或者AnnotationConfigApplicationContext类 解析配置类,那么调用该构造方法除开会实例化得到一个对象,还会做哪些事情?
  2. 从context中获取一个名字为"userService"的userService对象,那么为什么输入一个字符串就可以得到对象呢,好像跟Map<String,Object>有些类似,getBean()又是如何实现的?返回的UserService对象和我们自己直接new的UserService对象有区别吗?
  3. 通过获取到的UserService对象调用test方法,这个不难理解。

虽然我们目前很少直接使用这种方式来使用Spring,而是使用Spring MVC或者Spring Boot,但是它们都是基于上面这种方式的,都需要在内部去创建一个
ApplicationContext的,只不过:

  1. Spring MVC创建的是XmlWebApplicationContext,和ClassPathXmlApplicationContext类似,都是基于XML配置的
  2. Spring Boot创建的是AnnotationConfigApplicationContext

下面我们将重点讲解Spring对bean的创建,也就是大家常说的Bean的生命周期。虽然我们只是入门级别的学习,但我们仍将深入探讨流程,但不会涉及到具体的源码分析。在后续的源码分析系列中,我们将着重分析每一个知识点。

Bean的创建过程

那么,Spring是如何创建一个Bean的呢?这就是Bean的生命周期。其大体过程如下:

  1. 首先当我们的对象类被配置类使用@Bean又或者被包路径扫描到将会进入创建Bean的流程
  2. Spring会使用对象构造器进行实例化创建个对象
  3. 开始解析对象的属性是否有被@autowired注解修饰,如果有会从Spring的单例池中获取对象并进行注入就是属性赋值(依赖注入)
  4. 依赖注入后会判断对象时否实现了各种Aware接口,如:BeanNameAware接口、 BeanClassLoaderAware接口、BeanFactoryAware接口等,并自动调用其中的需要被实现的方法(Aware回调)
  5. 回调方法实现后,Spring会判断是否有包含了@postconstruct注解方法,如果有会进行调用此方法(实例化前)
  6. Spring判断对象是否实现了InitializingBean接口,实例化对象就必须调用afterPropertiesSet()方法,也是Spring帮调用的(初始化)
  7. 最后,Spring会判断我们的对象是否需要进行AOP,如果不需要那么Bean到此就完事,如果需要,Spring还会自动动态代理并生成一个代理对象做为Bean(初始化后)

通过以上步骤,我们可以了解到创建的对象可能存在两种结果。如果不需要AOP,Bean就是通过构造方法创建的对象;如果需要AOP,Bean就是代理类所实例化得到的对象,而不是构造方法所得到的对象。

Bean创建后:

  1. 如果当前Bean是单例的(默认),Spring在初始化Bean之后,会将当前已经初始化之后的对象放入Spring管理的单例缓存池中(Map结构),这样如果其他对象需要注入这个对象时,会直接从单例缓存池中取出来,进行属性注入。
  2. 如果当前Bean是多例的(即被@Scope("prototype")注解修饰),每次获取对象时,或者被其他对象引用时都会重新走一遍创建Bean的逻辑来创建一个新对象。这意味着,每次获取该Bean时都会创建一个新的实例,而不是从缓存池中获取已有的实例。因此,多例Bean的对象是不共享的,每个对象都是独立的。

构造器的初始化

在Spring中,每个对象都会有默认的构造器。但是在实际业务中,有时候会存在多个构造器的情况。那么,Spring如何去选择使用哪个构造器去创建对象呢?
Spring的判断逻辑如下:

  1. 如果当前bean有且仅有一个的构造器,那么直接使用这个构造器创建对象。不管它是有参还是无参。
  2. 如果存在多个构造器,Spring会从中选择一个无参构造器进行创建对象,如果没有无参构造器,那么直接报错。

Spring的设计思想是这样的:

  1. 如果只有一个构造器,那么没有选择,只能使用这个构造器

  2. 如果有多个,只选择没有入参的构造器,因为无参构造方法本身表示了一种默认的意义

  3. 还要一种就是使用了@Autowired注解修饰,那么就表示人工干预 了Spring选择的权利,直接选择程序员指定的构造器,如果有参,里面的参数bean对象(单例)会从单例缓存池中取。

    a.先按照bean类型进行查找,如果只找到一个实例,那么直接注入。
    b.如果找到多个实例,那么会进行匹配入参name名字来确定唯一一个实例。
    c如果没有找到,则会报错,无法创建当前Bean对象

综上所述,Spring会根据Bean的构造器情况进行选择,如果需要人工干预,可以使用@Autowired注解修饰。

AOP的大致流程

在创建对象时,Spring会判断当前对象是否需要进行AOP代理。为了确定当前Bean对象是否需要代理,大致流程如下:

  1. Spring启动时寻找所有使用@AspectJ注解的切面Bean对象
  2. 搜索切面bean的各个方法是否有包含了@Before、@After、@Around注解。
  3. 检查当前Bean或方法是否符合我们编写的Pointcut切面条件。
  4. Spring创建代理对象通常采用cglib代理,JDK代理是另一种模式。后续我们会详细分析此逻辑。

具体流程如下:

  1. 首先当前bean被确认为需要进行代理,将会生成BeanProxy对象,而不是我们构造出来的Bean。
  2. 当前BeanProxy会重写符合切面方法的方法
  3. 当前BeanProxy被依赖注入时,给其他Bean的属性赋值时也会是代理对象。
  4. BeanProxy代理对象都有一个target属性,用于注入被代理对象,即我们正常构造并进行属性注入的Bean对象
  5. 当BeanProxy的切面方法被调用时,首先调用我们编写的切面逻辑,然后调用被代理对象的方法(即我们编写的业务方法)。这里也不太准确,具体得看你写的什么样的切面注解,不过大致都是这样来调用的链路。

Spring事务

一谈到事务,大家首先想到的肯定是@Transaction注解,而这种注解也会被Spring创建对象时检测到,然后会生成代理对象,这种方式其实工作中用到的也特别多,用起来也特别爽,那我们也大概讲下事务的逻辑处理吧,其大致流程如下:

  1. 首先如果当前Bean有@Transaction注解,那么当前bean会成为代理对象。
  2. 当被调用到具体的方法时。Spring则会利用事务管理器TransactionManage获取一个数据库连接。
  3. 直接将当前数据库连接的自动提交关闭,autocommit=false。
  4. 开始执行我们的业务方法,执行业务SQL操作。
  5. 如果执行完方法后,没有异常则提交事务,如果出现异常则进行回滚。

以上是简单的事务处理流程,深入细节会涉及到Spring的事务传播级别,如果现在说的话,会陷入不必要的思考,自此我们的 [Spring入门系列] 也结篇了,在接下来的 [Spring源码系列] 中,我将更加详细地讲解这些内容。

「准备开车,可坐稳了别被摔下来」

先对这些流程有个印象,并带着疑惑来进一步探究Spring的内部机制,好了,今天就讲到这里,我是小雨,我们下期再见。
image

与Spring入门系列:浅析知识点相似的内容:

Spring入门系列:浅析知识点

本文介绍了学习Spring源码前需要掌握的核心知识点,包括IOC、AOP、Bean生命周期、初始化和Transaction事务。通过Hello World示例,讲解了如何使用Spring,并指出了深入了解Spring内部机制的方向。

Spring源码系列一:入门——Hello World

本文介绍了学习Spring源码前需要掌握的核心知识点,包括IOC、AOP、Bean生命周期、初始化和Transaction事务。通过Hello World示例,讲解了如何使用Spring,并指出了深入了解Spring内部机制的方向。

基于Spring事件驱动模式实现业务解耦

事件驱动模式 举个例子🌰 大部分软件或者APP都有会有会员系统,当我们注册为会员时,商家一般会把我们拉入会员群、给我们发优惠券、推送欢迎语什么的。 值得注意的是: 注册成功后才会产生后面的这些动作; 注册成功后的这些动作没有先后执行顺序之分; 注册成功后的这些动作的执行结果不能互相影响; 传统写法

Sentinel系列之SlotChain、NodeSelectorSlot、ClusterBuilderSlot分析

本文基于Sentinel 1.8.6版本分析 1. SlotChain 我们从入口com.alibaba.csp.sentinel.SphU#entry(java.lang.String) 开始分析。 一路走读下来,会进入到这个方法com.alibaba.csp.sentinel.CtSph#loo

[转帖]Redis入门(数据结构基础,分布式锁,性能调优)

目录 1、Redis基础 1.1 Redis是啥?能干啥? 1.2 安装Redis 1.3 Redis集成Spring 入门Demo 1.4 Redis支持数据类型 2、分布式锁解决方案-Redis(略) 3、Redis性能调优军规 3.1 缩短键值对的存储长度 3.2 使用 lazy free(延

【冷启动#2】实用的springboot tutorial入门demo

跟着官方文档熟悉一遍创建spring工程的步骤 https://spring.io/guides/gs/spring-boot https://juejin.cn/post/7077958723829760008 demo简介 整个demo的预期目标是: 管理一堆玩家的数据,数据库使用的是现成的我们

java基础 韩顺平老师的 异常 自己记的部分笔记

443,异常处理入门 package com.hspedu.exception_; public class Exception { public static void main(String[] args) { int num1 = 10; int num2 = 0; //老韩解读 //1,nu

入门篇-其之四-字符串String的简单使用

什么是字符串? 在Java编程语言中,字符串用于表示文本数据。 字符串(String)属于引用数据类型,根据String的源码,其头部使用class进行修饰,属于类,即引用数据类型。 字符串的表示 字符串使用双引号""表示,在双引号中你可以写任意字符。 和前面定义并初始化基本数据类型的变量一样,定义

[Go] string、int、int64相互转换

import "strconv" //先导入strconv包 // string到int int, err := strconv.Atoi(string) // string到int64 int64, err := strconv.ParseInt(string, 10, 64) // int到st

CS后门源码特征分析与IDS入侵检测

CS后门源码特征分析与IDS入侵检测考核作业 上线x64 getshell 抓心跳包,对特征字符解密Uqd3 用java的checksum8算法得到93,说明是x64的木马 public class EchoTest { public static long checksum8(String tex