Java技术栈总结:Spring框架篇

一、SpringBean

1、定义方式

  • </bean>标签(XML文件)
  • @Bean注解
  • @Component注解
  • BeanDefinition方法
  • FactoryBean
  • Supplier

注:BeanDefinition方法为“</bean>标签、@Bean注解、@Component注解”的底层实现。

(1)</bean>标签。Spring容器实例化时,会将XML配置的</bean>信息封装成一个BeanDefinition对象Spring根据BeanDefinition对象创建Bean对象


【实例化bean的方法】:

  • 通过构造方法实例化Bean;

  • 通过静态方法实例化Bean;

  • 通过实例方法实例化Bean;

  • Bean的别名。

2、生命周期

【流程概述】

  1. 通过BeanDefinition获取bean的定义信息;
  2. 调用构造函数实例化bean;
  3. bean的依赖注入;
  4. 处理Aware接口(BeanNameAwareBeanFactoryAwareApplicationContextAware);
  5. Bean的后置处理器BeanPostProcessor-前置;
  6. 初始化方法(InitializingBeaninit-method);
  7. Bean的后置处理器BeanPostProcessor-后置;(完成此步后,Bean对象创建完成)
  8. 销毁bean。

【具体流程】

  1. Spring 对 bean实例化
  2. Spring 将值和 bean 的引入注入到 bean 对应的属性中;
  3. 如果 bean 实现了 BeanNameAware 接口,Spring 将 bean 的 ID 传递给 setBeanName() 方法;
  4. 如果 bean 实现了 BeanFactoryAware 接口,Spring 将调用 setBeanFactory() 方法,将BeanFactory 容器实例传入;
  5. 如果 bean 实现了 ApplicationContextAware 接口,Spring 将调用 setApplicationContext() 方法,将 bean 所在的应用上下文传递进来;
  6. 如果 bean 实现了 BeanPostProcessor 接口,Spring 将调用它们的 postProcessBeforeInitialization( ) 方法; 
  7. 如果 bean 实现了 InitializingBean 接口,Spring 将调用它们的 afterPropertiesSet( ) 方法;
  8. 如果 bean 实现了  BeanPostProcessor 接口,Spring 将调用它们的 postProcessAfterInitialization( ) 方法;
  9. 此时,bean 已经准备就绪,可以被程序使用了,它们将一直驻留在应用上下文中,直到该应用上下文被销毁;
  10. 如果 bean 实现了 DisposableBean 接口,Spring 将调用它的 destory( ) 接口方法。同样,如果 bean 使用了 destory-method 声明了销毁方法,该方法也会被调用。

注:实例化和初始化是两个完全不同的过程。实例化只是给Bean分配了内存空间,而初始化是将程序的执行权,从系统级别转换到用户级别,并开始执行用户添加的业务代码。

3、线程安全

SpringBean类型(@Scope注解):

  • singleton(默认,单例):  bean在每个Spring IOC容器中只有一个实例;
  • prototype:一个bean的定义可以有多个实例。

Q:Spring中的单例Bean是线程安全的吗?

A:不是线程安全的。(解决,多例模式prototype 或 加锁)

多个用户请求同一个服务,容器会给每个请求分配一个线程,这时多个线程会并发执行该请求对应的逻辑(成员方法)。如果该处理逻辑中有对单例状态的修改(单例的成员属性),则需要考虑线程同步问题。

Spring没有对单例bean进行任何多线程的封装处理,因此单例bean的线程安全及并发问题需要开发者自行处理。

注:通常项目中用到的Spring Bean都是不可变的状态,比如Service类和Dao类,所以在一定程度上可以说Spring的单例Bean是线程安全的。但是,如果用到的Bean有多种状态,比如View Model对象,就需要自行保证线程安全。一种解决方法是,将多态Bean的作用由 singleton 变为 prototype。

4、循环依赖

 循环依赖其实就是循环引用,也就是两个或两个以上的bean互相持有对方,bean对象的创建依赖对方。例如,A依赖于B,B依赖于A。

(1)出现的情形

A、B同时被Spring容器管理,A、B互相依赖(或多个对象互相依赖,A - B - C - A)。

对象创建过程:

(2)三级缓存

Spring通过三级缓存解决循环依赖问题,对应的三级缓存如下所示:

// 单实例对象注册器
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
    private static final int SUPPRESSED_EXCEPTIONS_LIMIT = 100;
    // 一级缓存
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap(256);
    // 二级缓存
    private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap(16);
    // 三级缓存
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap(16);
}

 【各级缓存信息】:

【使用三级缓存,对象创建流程】:

A 创建过程中需要 B,于是 A 将自己放到三级缓里面 ,去实例化 B;
B 实例化的时候发现需要 A,于是 B 先查一级缓存,没有;再查二级缓存,还是没有,再查三级缓存,找到了!然后把三级缓存里面的这个 A 放到二级缓存里面(通过getObject方法创建对象),并删除三级缓存里面的 A;
B 初始化完成,将自己放到一级缓存里面(此时B里面的A依然是创建中状态);
然后回来接着创建 A,此时 B 已经创建结束,直接从一级缓存里面拿到 B ,然后完成创建,并将自己放到一级缓存里面。

(3)构造方法产生的循环依赖

@Component
public class A {
    // 成员变量B
    private B b;
    
    public A(B b) {
        this.b = b;
    }
}

@Component
public class B {
    // 成员变量A
    private A a;

    public B(A a) {
        this.a = a;
    }
}

由于Spring Bean的创建过程,在实例化分配内存空间后,首先要调用构造方法,此阶段还无法创建出“半成品对象”,因此无法通过三级缓存的方式解决。

解决:在构造方法参数上增加 “@Lazy” 注解。

@Component
public class A {
    // 成员变量B
    private B b;
    
    public A(@Lazy B b) {
        this.b = b;
    }
}

二、IOC 控制反转 && DI 依赖注入

IoC 是一种设计思想,而 DI 是一种具体的实现技术。

1、IOC 控制反转

Inversion of Control 的缩写。将我们之前由客户端代码来创建的对象交由IOC容器来进行控制,对象的创建,初始化以及后面的管理都由IOC容器完成。

耦合:两个或两个以上对象存在依赖,当一方修改之后会影响另一方,那么就说这些对象间存在耦合。

解耦:解除两个或两个以上对象,修改一方影响另一方的问题。

new 的方式创建对象,对象的管理权是由当前类控制的;而有了 IoC 之后,对象的管理权就交给IoC 容器管理,而不是当前类了,此时对象的管理权就发生了反转和改变,这就是 IoC,即控制反转。

2、DI 依赖注入

 Dependency Injection 的缩写。在 IoC 容器运行期间,动态地将某个依赖对象注入到当前对象的技术就叫做 DI(依赖注入)。


三、AOP

AOP (Aspect Oriented Programming)称为面向切面编程,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重用的模块,这个模块被命名为“切面”(Aspect),减少系统中的重复代码,降低了模块间的耦合度,同时提高系统的可维护性。

1、使用场景

  • 记录日志
  • 缓存处理
  • Spring内置的事务处理

2、实现原理

这部分待补充完善

JDK动态代理、CGlib动态代理;

  • 如果要使用JDK动态代理,被代理的类必须实现了接口,否则无法代理;
  • JDK动态代理是JDK原生的,不需要任何依赖即可使用;


四、事务

1、实现方式

Spring支持编程式事务管理和声明式事务管理两种方式。

  • 编程式事务控制:需使用TransactionTemplate来进行实现,对业务代码有侵入性,项目中很少使用
  • 声明式事务管理:声明式事务管理建立在AOP之上的。其本质是通过AOP功能,对方法前后进行拦截,将事务处理的功能编织到拦截的方法中。在目标方法开始之前加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。(@Transactional)

【具体实现】

  1. 编程式事务管理需要在代码中调用beginTransaction()、commit()、rollback()等事务管理相关的方法(对基于 POJO 的应用来说是唯一选择);

  2. 基于 TransactionProxyFactoryBean的声明式事务管理;

  3. 基于 @Transactional 的声明式事务管理;

  4. 基于Aspectj AOP配置事务。

Spring的事务管理是通过AOP代理实现的,对被代理对象的每个方法进行拦截,在方法执行前启动事务,在方法执行完成后根据是否有异常及异常的类型进行提交或回滚。

原理:当在某个类或者方法上使用@Transactional注解后,spring会基于该类生成一个代理对象,并将这个代理对象作为bean。当调用这个代理对象的方法时,如果有事务处理,则会先关闭事务的自动功能,然后执行方法的具体业务逻辑,如果业务逻辑没有异常,那么代理逻辑就会直接提交,如果出现任何异常,那么直接进行回滚操作。当然我们也可以控制对哪些异常进行回滚操作。

2、失效的场景

  • Spring事务注解“@Transactional”只能放在public修饰的方法上才能生效,放在其他非pubic方法上不会报错,但是不起作用;
  • 使用了try...catch..语句块对异常进行了捕获,而catch语句块没有throw  new RuntimeExecption异常,事务也不会回滚;
  • 在业务代码中如果抛出非RuntimeException异常,且注解为“@Transactional”事务不回滚(可以通过修改注解为“@Transactional(rollbackFor=Exception.class)” 解决);
  • 数据库不支持事务的情况:如使用mysql且引擎是MyISAM,则事务会不起作用,原因是MyISAM不支持事务,可以改成InnoDB引擎;
  • “@Transactional”用在方法上,由该方法所属类的其他方法调用该方法;

    在类A里面有方法a 和方法b, 然后方法b上面用 @Transactional加了方法级别的事务,在方法a里面 调用了方法b, 方法b里面的事务不会生效。原因是在同一个类之中,方法互相调用,切面无效 ,而不仅仅是事务。这里事务之所以无效,是因为spring的事务是通过aop实现的。

  • 如果采用spring+spring mvc,则context:component-scan重复扫描问题可能会引起事务失败;

  •  @Transactional 注解开启配置,必须放到listener里加载,如果放到DispatcherServlet的配置里,事务也不起作用;

  • Spring团队建议在具体的类(或类的方法)上使用 @Transactional 注解,而不要使用在类所要实现的任何接口上。在接口上使用 @Transactional 注解,只能当你设置了基于接口的代理时它才生效。因为注解是 不能继承 的,这就意味着如果正在使用基于类的代理时,那么事务的设置将不能被基于类的代理所识别,而且对象也将不会被事务代理所包装。


五、SpringBoot

1、配置&&配置文件

(1)读取配置的几种方式

可以通过“@PropertySource,@Value,@Environment, @ConfigurationProperties”注解绑定变量。

(2)核心配置文件

bootstrap (.yml 或者 .properties):boostrap 由父 ApplicationContext 加载的,比applicaton 优先加载,配置在应用程序上下文的引导阶段生效。一般来说我们在 Spring Cloud 配置就会使用这个文件。且 boostrap 里面的属性不能被覆盖;

application (.yml 或者 .properties): 由ApplicatonContext 加载,用于 spring boot 项目的自动化配置。

bootstrap.properties 和application.properties 有何区别 ?

单纯做 Spring Boot 开发,可能不太容易遇到 bootstrap.properties 配置文件,但是在结合Spring Cloud 时,这个配置就会经常遇到了,特别是在需要加载一些远程配置文件的时侯。

(3)JavaConfig

【Spring JavaConfig】是 Spring 社区的产品,Spring 3.0引入,它提供了配置 Spring IOC 容器的

纯Java 方法。因此它有助于避免使用 XML 配置。使用 JavaConfig 的优点在于:

  • 面向对象的配置。由于配置被定义为 JavaConfig 中的类,因此用户可以充分利用 Java 中的面向对象功能。一个配置类可以继承另一个,重写它的@Bean 方法等。
  • 减少或消除 XML 配置。基于依赖注入原则的外化配置的好处已被证明。但是,许多开发人员不希望在 XML 和 Java 之间来回切换。JavaConfig 为开发人员提供了一种纯 Java 方法来配置与 XML 配置概念相似的 Spring 容器。从技术角度来讲,只使用 JavaConfig 配置类来配置容器是可行的,但实际上很多人认为将JavaConfig 与 XML 混合匹配是理想的。
  • 类型安全和重构友好。JavaConfig 提供了一种类型安全的方法来配置 Spring容器。由于 Java5.0 对泛型的支持,现在可以按类型而不是按名称检索 bean,不需要任何强制转换或基于字符串的查找。

【常用的Java config】:

  • @Configuration:在类上打上写下此注解,表示这个类是配置类;
  • @ComponentScan:在配置类上添加 @ComponentScan 注解。该注解默认会扫描该类所在的包下所有的配置类,相当于之前的 <context:component-scan >;
  • @Bean:bean的注入:相当于以前的< bean id=“objectMapper”class=“org.codehaus.jackson.map.ObjectMapper” />;
  • @EnableWebMvc:相当于xml的 <mvc:annotation-driven >;
  • @ImportResource: 相当于xml的 < import resource=“applicationContextcache.xml”>。

(4) Spring Profiles

在项目的开发中,有些配置文件在开发、测试或者生产等不同环境中可能是不同的。例如,数据库连接、redis的配置等等。那我们如何在不同环境中自动实现配置的切换呢?Spring给我们提供了profiles机制给我们提供的就是来回切换配置文件的功能。Spring Profiles 允许用户根据配置文件(dev,test,prod 等)来注册 bean。因此,当应用程序在开发中运行时,只有某些 bean 可以加载,而在 PRODUCTION中,某些其他 bean 可以加载。

2、自动配置原理

主要是Spring Boot的启动类上的核心注解@SpringBootApplication注解主配置类,有了这个主配置类启动时就会为SpringBoot开启一个@EnableAutoConfiguration注解自动配置功能。有了这个EnableAutoConfiguration的话就会:

  • 从配置文件META_INF/Spring.factories加载可能用到的自动配置类;
  • 去重,并将exclude和excludeName属性携带的类排除;
  • 过滤,将满足条件(@Conditional)的自动配置类返回。

3、常见注解

(1)Spring注解

注解对象作用说明
@Component、@Controller、@Service、@Repository用于实例化Bean
@Autowired字段用于根据类型依赖注入
@Qualifier字段结合@Autowired一起使用,根据名称进行依赖注入
@Scope标注Bean的作用范围
@Configuration指定当前类是一个配置类,创建容器时会从该类上加载注解
@ComponentScan指定Spring初始化容器时需要扫描的包
@Bean方法将该方法的返回值存储到Spring容器中
@Import使用@Import导入的类,会被加载到IOC容器中
@Aspect、@Before、@After、@Around、@Pointcut

类:@Aspect

方法:其他

切面编程AOP使用。

1、@Aspect,声明当前类为创建切面的类;

2、@Before、@After、@Around,用于invoke() 方法;分别对应前置、后置、环绕通知;

3、@PointCut,定义AOP生效的方法位置

(2)SpringMVC 注解

注解对象作用说明
@RequestMapping类、方法用在类上表示所有的方法都以此路径作为父路径
@RequestBody方法参数实现接收http请求的json数据,将json转为Java对象
@RequestParam方法参数指定请求参数的名称
@PathVariable方法参数从请求路径中获取请求参数(/user/{id}),传给方法的形参
@ResponseBody方法参数将Controller方法返回的对象转为json对象返回给客户端
@RequestHeader方法获取指定的请求头数据
@RestController@Controller + @RequestBody

(3)SpringBoot 注解

@SpringBootApplication 注解包括了@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan

注解对象作用说明
@SpringBootConfiguration组合了@Configuration注解,实现配置文件功能
@EnableAutoConfiguration

打开自动配置的功能,也可以关闭某个自动配置的选项

@ComponentScanSpring组件扫描

 


参考内容:

B站视频课程:https://www.bilibili.com/video/BV1yT411H7YK

掘金:说一下 Spring 中 Bean 的生命周期;

spring是如何解决循环依赖的?

书籍:《Spring实战》

CSDN:Spring事务失效的几种原因_spring事物失效-CSDN博客

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【K8s】专题六(5):Kubernetes 稳定性之重启策略、滚动更新策略

以下内容均来自个人笔记并重新梳理&#xff0c;如有错误欢迎指正&#xff01;如果对您有帮助&#xff0c;烦请点赞、关注、转发&#xff01;欢迎扫码关注个人公众号&#xff01; 目录 一、重启策略 1、基本介绍 2、资源清单&#xff08;示例&#xff09; 二、滚动更新策略 …

深度网络现代实践 - 深度前馈网络之反向传播和其他的微分算法篇-续

序言 反向传播&#xff08;Backpropagation&#xff0c;简称backprop&#xff09;是神经网络训练过程中最关键的技术之一&#xff0c;尤其在多层神经网络中广泛应用。它是一种与优化方法&#xff08;如梯度下降法&#xff09;结合使用的算法&#xff0c;用于计算网络中各参数的…

Zabbix自动发现

目录 自动发现的主要特点包括&#xff1a; 如何配置自动发现&#xff1a; 实验步骤 1. 创建自动发现规则 2. 给自动发现规则创建动作 3. 给新主机安装agent 在 Zabbix 中&#xff0c;自动发现&#xff08;Auto Discovery&#xff09;是一种强大的功能&#xff0c;用于自…

Vue CoreVideoPlayer 一款基于 vue.js 的轻量级、优秀的视频播放器组件

大家好,我是程序视点的小二哥!今天小二哥给大家推荐一款非常优秀的视频播放组件 效果欣赏 介绍 Vue-CoreVideoPlayer 一款基于vue.js的轻量级的视频播放器插件。 采用Adobd XD进行UI设计&#xff0c;支持移动端适配,不仅功能强大&#xff0c;颜值也是超一流&#xff01; Vue-…

读书记录《SQL从小白到大牛》01

读书记录《SQL从小白到大牛》01 接地气的书名&#xff0c;内容应当值得一读。 第一篇 SQL基础 01 一些基础概念 SQL是结构化查询语言&#xff08;Structured Query Language&#xff09;&#xff0c;是一套用来输入、更改和查看关系数据库内容的命令。数据库发展经历三个阶…

ROS-机械臂-基础概念

课纲 话题通信模型&#xff08;多对多&#xff09; RPC RPC&#xff0c;全称为 Remote Procedure Call&#xff0c;即远程过程调用&#xff0c;是一种通信协议&#xff0c;允许一个计算机程序在另一个网络上的计算机上执行一组函数或过程&#xff0c;而不需程序员显式编码网络…

SketchUp如何阵列?

sketchup如何阵列&#xff1f;请看下面方法。 方法 打开SketchUp。 以默认人物为例。 按M&#xff0c;选中人物&#xff0c;再按住Ctrl移动鼠标&#xff08;不要点击鼠标键&#xff09; 按键盘数字键输入距离&#xff0c;按回车&#xff0c;不要动鼠标。 按*10设置阵列数。 按…

前端开发攻略---webSocket的简单实现与使用

1、演示 2、实现流程 安装依赖 npm i ws 服务端代码 const WebSocket require(ws)// 创建一个 WebSocket 服务器&#xff0c;监听端口 3000 const wss new WebSocket.Server({ port: 3000 })// 监听连接事件 wss.on(connection, function connection(ws) {console.log(客户端…

MySQL篇四:表的约束

文章目录 前言1. 空属性2. 默认值3. 列描述4. zerofill5. 主键6. 自增长7. 唯一键8. 外键 前言 真正约束字段的是数据类型&#xff0c;但是数据类型约束很单一&#xff0c;需要有一些额外的约束&#xff0c;更好的保证数据的合法性&#xff0c;从业务逻辑角度保证数据的正确性。…

香橙派编译linux内核支持ebpf和虚拟WIFI

前言 上一篇文章香橙派5plus上跑云手机方案一 redroid(带硬件加速)中说了怎么运行redroid&#xff0c;这篇补一下怎么修改参数编译内核。 补充 上篇文章有个内容需要补充一下&#xff1a;更新完内核需要用下面的命令防止内核被apt更新&#xff0c;不然后面使用apt update又回…

MySQL 8.0新特性INTERSECT和EXCEPT用于集合运算

MySQL8.0.31 新版本的推出&#xff0c;MySQL增加了对SQL标准INTERSECT和EXCEPT运算符的支持。 1、INTERSECT INTERSECT输出多个SELECT语句查询结果中的共有行。INTERSECT运算符是ANSI/ISO SQL标准的一部分(ISO/IEC 9075-2:2016(E))。 我们运行两个查询&#xff0c;第一个会列…

HTML5新增的input元素类型:number、range、email、color、date等

HTML5 大幅度地增加与改良了 input 元素的种类&#xff0c;可以简单地使用这些元素来实现 HTML5 之前需要使用 JavaScript 才能实现的许多功能。 到目前为止&#xff0c;大部分浏览器都支持 input 元素的种类。对于不支持新增 input 元素的浏览器&#xff0c;input 元素被统一…

位置编码的具体计算方式(公式解释)

公式 (10.6.2) 描述了位置编码的具体计算方式&#xff0c;这种位置编码基于正弦和余弦函数&#xff0c;用于在自注意力机制中引入位置信息。下面我们详细解释公式和代码。 公式 (10.6.2) 公式 (10.6.2) 的目的是为输入序列中的每个词元添加一个位置编码&#xff0c;以保留序列…

厦门大学-中央空调分户计费预付费管理系统案例

厦门大学-中央空调分户计费预付费管理系统案例 实现中央空调节能与舒适的双重目标随着社会的发展和人们生活水平的提高&#xff0c;空调已成为现代建筑中不可或缺的设备。传统的集中计费方式已无法满足多样化的用户需求和节能减排的市场趋势。中央空调如何公平、公正、合理的收…

笔记本电脑投屏怎么操作?一看就会!

日常工作或办公都会用到笔记本电脑&#xff0c;但很多新手用户不知道笔记本电脑的投屏要怎么操作&#xff1f;接下来系统之家给大家介绍三种简单的操作方法&#xff0c;帮助大家轻松完成笔记本电脑投屏投屏操作&#xff0c;从而满足自己的办公或学习使用需求。 方法一 1. 直接W…

解决Ubuntu虚拟机卡死的一种可能情况:文件系统可用率不足

Ubuntu虚拟机卡死 界面挂在/dev/sda3上开不了机了&#xff0c;情况可能的很多&#xff0c;由于我这里是虚拟机&#xff0c;给内存才分配了20G&#xff0c;我一猜就是硬存炸了&#xff0c;果不其然。。。 进入recovery mode 我们进入recovery mode先&#xff0c;在VM虚拟机开…

IOC、DI<4> Unity

IOC&#xff08;&#xff09;&#xff1a;控制反转&#xff0c;把程序上层对下层的依赖&#xff0c;转移到第三方的容器来装配 是程序设计的目标&#xff0c;实现方式包含了依赖注入和依赖查找&#xff08;.net里面只有依赖注入&#xff09; DI&#xff1a;依赖注入&#xff0c…

【Mathematical14.0最新进阶教学】-1-基础计算拓展

我在真正使用Mathematica后&#xff0c;才发觉这个软件的神奇&#xff0c;但是又有对于不知道如何使用这个神奇软件&#xff0c;因此我将我学习《The Student’s Introduction to Mathematica and the Wolfram Language (Bruce F. Torrence, Eve A. Torrence) 》的一些心得进行…

【Go】常见的变量与常量

变量 常见的变量声明方式 一、声明单个变量的多种方式 1.声明一个变量初始化一个值 //声明变量 默认值是0&#xff0c;var a int//初始化一个值a 1fmt.Println(a) 2. 在初始化的时候省去数据类型&#xff0c;通过值自动匹配当前的变量的数据类型 var b 2fmt.Println(&quo…

分享四种CAD图纸加密方法,防止盗图!

保护CAD图纸不受盗用和非法传播是设计行业中的一个重要课题&#xff0c;以下四种CAD图纸加密方法可以帮助防止图纸被未授权使用。 1.使用专业的加密软件&#xff08;最安全的方法&#xff09; 专门的加密软件&#xff0c;如安企神软件&#xff0c;可以提供更高级别的保护。它使…