Spring

写在开始:
Spring : 春天 —>给软件行业带来了春天
2002年,Rod Jahnson首次推出了Spring框架雏形interface21框架。
本篇笔记主要从Spring, IoC, AOP, Bean, 和Spring数据访问几个方面回顾Spring的部分知识点,篇幅比较长,内容比较细,可用于平时面试题复习等!

一. Spring概述

Spring是什么?

Spring是一个轻量级Java开发框架,最早有Rod Johnson创建;目的解决企业级应用开发的业务逻辑层和其他层耦合问题.
依赖注入(dependency injection,DI)面向切面编程(aspect-oriented programming,AOP)
为了降低Java开发的复杂性,Spring采取了以下4种关键策略

  • 基于POJO的轻量级和最小侵入性编程;
  • 通过依赖注入和面向接口实现松耦合;
  • 基于切面和惯例进行声明式编程;
  • 通过切面和模板减少样板式代码。
Spring框架设计目标,理念,核心

Spring设计目标: 一站式(full-stack)轻量级应用开发平台
设计理念: 支持POJO和JavaBean开发方式,使应用面向接口开发,充分支持OO(面向对象)设计方法;
通过IoC容器实现对象耦合关系的管理,并实现依赖反转,将对象之间的依赖关系交给IoC容器,实现解耦;
核心: 通过IoC容器管理POJO对象以及他们之间的耦合关系;通过AOP以动态非侵入的方式增强服务。

Spring优缺点:
  • 方便解耦,简化开发 (Spring大工厂,将所有对象的创建和依赖关系的维护交给Spring管理)
  • AOP编程的支持 (面向切面编程,方便的实现对程序进行权限拦截、运行监控等)
  • 声明式事务的支持 (通过配置就可以完成对事务的管理)
  • 方便程序的测试 (对Junit4支持,可以通过注解方便的测试)
  • 方便集成各种优秀框架 (如:Struts、Hibernate、MyBatis等)
  • 降低JavaEE API的使用难度 (一些API [JDBC、JavaMail、远程调用等],都提供了封装,应用难度降低)

缺点: 感觉大而全,依赖反射,反射影响性能; 门槛升高
Spring价值:
Spring是非侵入式的框架,目标是使应用程序代码对框架依赖最小化;
Spring提供一个一致的编程模型,使应用直接使用POJO开发,与运行环境隔离开来;
Spring推动应用设计风格向面向对象和面向接口开发转变,提高了代码的重用性和可测试性;
Spring模块:
image.png
Spring 5 的模块结构图:

  • spring core:核心类库. 提供了框架的基本组成部分,包括控制反转(Inversion ofControl,IOC)和依赖注入(Dependency Injection,DI)功能。
  • spring beans:提供了BeanFactory,是工厂模式的一个经典实现,Spring将管理对象称为Bean。
  • spring context:构建于 core 封装包基础上的 context 封装包,提供了一种框架式的bean对象访问方法。
  • spring jdbc:提供了一个JDBC的抽象层,消除了烦琐的JDBC编码和数据库厂商特有的错误代码解析, 用于简化JDBC。
  • spring aop:提供了面向切面的编程实现,让你可以自定义拦截器、切点等。
  • spring Web:提供了针对 Web 开发的集成特性,例如文件上传,利用 servlet listeners 进行 ioc 容器初始化和针对 Web 的 ApplicationContext。
  • spring test:主要为测试提供支持的,支持使用JUnit或TestNG对Spring组件进行单元测试和集成测试。

核心容器(Core Container) 、
AOP(Aspect Oriented Programming)和设备支持(Instrmentation) 、
数据访问与集成(Data Access/Integeration) 、
Web 、
消息(Messaging) 、
Test 等 6个 模块中

Spring 框架中都用到了哪些设计模式
  1. 简单工厂模式:BeanFactory就是简单工厂模式的体现,用来创建对象的实例;根据传入唯一的标识获得bean对象.
  2. 单例模式:Bean默认为单例模式。‘scope=singleton’ 注册式单例模式,单例存放在Map中,k=bean name v=bean
  3. 代理模式:Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术;
  4. 模板方法:用来解决代码重复的问题。比如. RestTemplate,JmsTemplate, JpaTemplate。jdbcTemplate里的execute方法把整个算法步骤都定义好了;
  5. 观察者模式:定义对象键一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知被制动更新,如Spring中listener的实现–事件监听器:ApplicationListener,该接口继承了 EventListener 接口,JDK 建议所有的事件监听器都应该继承EventListener。spring 事件:ApplicationEvent,该抽象类继承了EventObject 类,JDK 建议所有的事件都应该继承自 EventObject。
    6.工厂模式: Spring 中的 FactoryBean 就是典型的工厂方法模式,实现了 FactoryBean 接口的 bean是一类叫做 factory 的 bean。其特点是,spring 在使用 getBean() 调用获得该 bean 时,会自动调用该 bean 的 getObject() 方法,所以返回的不是 factory 这个 bean,而是这个 bean.getOjbect()方法的返回值。
    7.原型模式: scope = prototype ,每次都是通过克隆生成新实例,修改时对原有实例对象无影响;
    8.迭代器模式: 在 Spring 中有个 CompositeIterator 实现了 Iterator,Iterable 接口和 Iterator 接口,这两个都是迭代相关的接口。可以这么认为,实现了 Iterable 接口,则表示某个对象是可被迭代的。Iterator 接口相当于是一个迭代器,实现了 Iterator 接口,等于具体定义了这个可被迭代的对象时如何进行迭代的。
    9.适配器模式:Spring 中的 AOP 中 AdvisorAdapter 类,它有三个实现:MethodBeforAdviceAdapter、AfterReturnningAdviceAdapter、ThrowsAdviceAdapter。Spring会根据不同的 AOP 配置来使用对应的 Advice,与策略模式不同的是,一个方法可以同时拥有多个Advice。Spring 存在很多以 Adapter 结尾的,大多数都是适配器模式。
    10.责任链模式: DispatcherServlet 中的 doDispatch() 方法中获取与请求匹配的处理HandlerExecutionChain,this.getHandler() 方法的处理使用到了责任链模式。
    其他: 还有享元模式,建造者模式等
核心容器(spring context应用上下文) 模块

Bean 工厂(BeanFactory)是工厂模式的一个实现,提供了控制反转功能,用来把应用的配置和依赖从真正的应用代码中分离。最常用的就是org.springframework.beans.factory.xml.XmlBeanFactory ,它根据XML文件中的定义加载beans。该容器从XML 文件读取配置元数据并用它去创建一个完全配置的系统或应用。

Spring框架中有哪些不同类型的事件
  1. 上下文更新事件(ContextRefreshedEvent):在调用ConfigurableApplicationContext 接口中的refresh()方法时被触发。
  2. 上下文开始事件(ContextStartedEvent):当容器调用ConfigurableApplicationContext的Start()方法开始/重新开始容器时触发该事件。
  3. 上下文停止事件(ContextStoppedEvent):当容器调用ConfigurableApplicationContext的Stop()方法停止容器时触发该事件。
  4. 上下文关闭事件(ContextClosedEvent):当ApplicationContext被关闭时触发该事件。容器被关闭时,其管理的所有单例Bean都被销毁。
  5. 请求处理事件(RequestHandledEvent):在Web应用中,当一个http请求(request)结束触发该事件。如果一个bean实现了ApplicationListener接口,当一个ApplicationEvent 被发布以后,bean会自动被通知。
Spring 应用程序有哪些不同组件?
  • 接口 - 定义功能。
  • Bean 类 - 它包含属性,setter 和 getter 方法,函数等。
  • Bean 配置文件 - 包含类的信息以及如何配置它们。
  • Spring 面向切面编程(AOP) - 提供面向切面编程的功能。
  • 用户程序 - 它使用接口。

二. Spring控制反转(IOC)

Spring IOC容器

控制反转即IoC (Inversion of Control),它把传统上由程序代码直接操控的对象的调用权交给容器,通过容器来实现对象组件的装配和管理。所谓的“控制反转”概念就是对组件对象控制权的转移,从程序代码本身转移到了外部容器。
Spring IOC 负责创建对象,管理对象(通过依赖注入(DI),装配对象,配置对象,并且管理这些对象的整个生命周期。
:::info

控制反转是一种思想, 依赖注入是实现方式

:::

IOC的优点
  • IOC 或 依赖注入把应用的代码量降到最低。
  • 它使应用容易测试,单元测试不再需要单例和JNDI查找机制。
  • 最小的代价和最小的侵入性使松散耦合得以实现。
  • IOC容器支持加载服务时的饿汉式初始化和懒加载
Spring IoC 的实现机制

实现原理就是工厂模式加反射机制

Spring 的 IoC支持哪些功能
  • ⭐依赖注入
  • 依赖检查
  • 自动装配
  • 支持集合
  • 指定初始化方法和销毁方法
  • 支持回调某些方法(但是需要实现 Spring 接口,略有侵入)
    :::info

依赖注入: xml配置上即ref标签,对应SpringRuntimeBeanReference对象; 对于 IoC 来说,最重要的就是容器。容器管理着 Bean 的生命周期,控制着Bean 的依赖注入。

:::

BeanFactory 和 ApplicationContext有什么区别?

image.png
BeanFactory和ApplicationContext是Spring的两大核心接口,都可以当做Spring的容器。其中ApplicationContext是BeanFactory的子接口。
依赖关系

  • BeanFactory:是Spring里面最顶层的接口,包含了各种Bean的定义,读取bean配置文档,管理bean的加载、实例化,控制bean的生命周期,维护bean之间的依赖关系。
  • ApplicationContext接口作为BeanFactory的派生,除了提供BeanFactory所具有的功能外,还提供了更完整的框架功能:
    • 继承MessageSource,因此支持国际化。
    • 统一的资源文件访问方式。
    • 提供在监听器中注册bean的事件。
    • 同时加载多个配置文件。
    • 载入多个(有继承关系)上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层。

加载方式

  • BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。这样,我们就不能发现一些存在的Spring的配置问题。如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常。
  • ApplicationContext,它是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误,这样有利于检查所依赖属性是否注入。 ApplicationContext启动后预载入所有的单实例Bean,通过预载入单实例bean ,确保当你需要的时候,你就不用等待,因为它们已经创建好了。

相对于基本的BeanFactory,ApplicationContext 唯一的不足是占用内存空间。当应用程序配置Bean较多 时,程序启动较慢。
创建方式

  • BeanFactory通常以编程的方式被创建,
  • ApplicationContext还能以声明的方式创建,如使用ContextLoader。

注册方式
BeanFactory和ApplicationContext都支持BeanPostProcessor、
BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册。
底层
BeanFactory 简单粗暴,可以理解为就是个 HashMap,Key 是 BeanName,Value 是 Bean 实例。通常只提供注册(put),获取(get)这两个功能。我们可以称之为 “低级容器”
ApplicationContext 可以称之为 “高级容器”。因为他比 BeanFactory 多了更多的功能。他继承了多个接口。因此
具备了更多的功能
。例如资源的获取,支持多种消息(例如 JSP tag 的支持),对 BeanFactory 多了工具级别的支持等待。所以你看他的名字,已经不是 BeanFactory 之类的工厂了,而是 “应用上下文”, 代表着整个大容器的所有功能。该接口定义了一个 refresh 方法,此方法是所有阅读 Spring 源码的人的最熟悉的方法,用于刷新整个容器,即重新加载/刷新所有的 bean。
BeanFactory实现 IOC 步骤
2 个步骤:

  1. 加载配置文件,解析成 BeanDefinition 放在 Map 里。
  2. 调用 getBean 的时候,从 BeanDefinition 所属的 Map 里,拿出Class 对象进行实例化,同时,如果有依赖关系,将递归调用 getBean 方法 —— 完成依赖注入。
ApplicationContext通常的实现是什么?

**FileSystemXmlApplicationContext **:此容器从一个XML文件中加载beans的定义,XML Bean 配置文件的全路径名必须提供给它的构造函数。
ClassPathXmlApplicationContext:此容器也从一个XML文件中加载beans的定义,这里,你需要正确设置classpath因为这个容器将在classpath里找bean配置。
WebXmlApplicationContext:此容器加载一个XML文件,此文件定义了一个WEB应用的所有bean。

什么是Spring的依赖注入?

控制反转IoC是一个很大的概念,可以用不同的方式来实现。其主要实现方式有两种:依赖注入和依赖查找
依赖注入:相对于IoC而言,依赖注入(DI)更加准确地描述了IoC的设计理念。所谓依赖注入(Dependency Injection),即组件之间的依赖关系由容器在应用系统运行期来决定,也就是由容器动态地将某种依赖关系的目标对象实例注入到应用系统中的各个关联的组件之中。组件不做定位查询,只提供普通的Java方法让容器去决定依赖关系。

依赖注入的基本原则和优势

依赖注入的基本原则是:应用组件不应该负责查找资源或者其他依赖的协作对象
让容器全权负责依赖查询,受管组件只需要暴露JavaBean的setter方法或者带参数的构造器或者接口,使容器可以在初始化时组装对象的依赖关系。其与依赖查找方式相比,主要优势为:

  • 查找定位操作与应用代码完全无关。
  • 不依赖于容器的API,可以很容易地在任何容器以外使用应用对象。
  • 不需要特殊的接口,绝大多数对象可以做到完全不必依赖容器。

依赖注入的几种方式:
一. 构造器注入: 将被依赖对象通过构造函数的参数注入给依赖对象,并且在初始化对象的时候注入.
优点: 对象初始化完成后便可获得可使用的对象。
缺点: 当需要注入的对象很多时,构造器参数列表将会很长; 不够灵活。若有多种注入方式,每种方式只需注入指定几个依赖,那么就需要提供多个重载的构造函数,麻烦。

/*带参数,方便利用构造器进行注入*/
public UserDaoImpl(String message){
	this. message = message;
}
<bean id="UserDaoImpl" class="com.UserDaoImpl">
	<constructor-arg value=" message "></constructor-arg>
</bean>

二. setter方法注入: IoC Service Provider通过调用成员变量提供的setter函数将被依赖对象注入给依赖类。
优点: 灵活。可以选择性地注入需要的对象。
缺点: 依赖对象初始化完成后由于尚未注入被依赖对象,因此还不能使用。

public class Id {
	private int id;
	public int getId() { return id; }
	public void setId(int id) { this.id = id; }
}
<bean id="id" class="com.id "> <property name="id" value="123"></property> </bean>

三、接口注入 依赖类必须要实现指定的接口,然后实现该接口中的一个函数,该函数就是用于依赖注入。该函数的参数就是要注入的对象。
优点 接口注入中,接口的名字、函数的名字都不重要,只要保证函数的参数是要注入的对象类型即可。
缺点: 侵入行太强,不建议使用
:::info

PS: 侵入性? 如果类A要使用别人提供的一个功能,若为了使用这功能,需要在自己的类中增加额外的代码,这就是侵入性

:::

构造函数(构造器参数实现强制依赖)Setter(可选依赖)
没有部分注入有部分注入
不会覆盖setter方法会覆盖setter方法
任意修改都会创建一个新实例任意修改都不会创建一个新实例
适用于设置很多属性适用于设置少量属性
Spring 是怎么解决循环依赖的?

image.png
整个流程大致如下:

  1. 首先 A 完成初始化第一步并将自己提前曝光出来(通过 ObjectFactory 将自己提前曝光),放到三级缓存中,此时k=BeanName,v=ObjectFactory , 在初始化的时候,发现自己依赖对象 B,此时就会去尝试 get(B),这个时候发现 B 还没有被创建出来;

  2. 然后 B 就走创建流程,在 B 初始化的时候,同样发现自己依赖 C,C 也没有被创建出来;

  3. 这个时候 C 又开始初始化进程,但是在初始化的过程中发现自己依赖 A,于是尝试 get(A)。这个时候由于 A 已经添加至缓存中(一般都是添加至三级缓存 singletonFactories),通过ObjectFactory 提前曝光,所以可以通过 ObjectFactory#getObject() 方法来拿到 A 对象。C 拿到 A 对象后顺利完成初始化,然后将自己添加到一级缓存中;

  4. 回到 B,B 也可以拿到 C 对象,完成初始化,A 可以顺利拿到 B 完成初始化。到这里整个链路就已经完成了初始化过程了。

关键字:三级缓存,提前曝光。
:::info

第三季缓存 考虑 代理 key=BeanName value=ObjectFactory
第二级缓存 考虑 性能
key=BeanName value=Bean(此时bean还没做完属性注入相关工作)

:::

三. Spring Beans

Spring Beans
注入bean的几种方式
  1. Set方法注入;
  2. 构造器注入:①通过index设置参数的位置;②通过type设置参数类型;
  3. 静态工厂注入;
  4. 实例工厂;
类的作用域

bean 定义中的scope属性来定义

scope备注
singletonbean在每个Spring ioc 容器中只有一个实例,单例模式由BeanFactory自身维护
prototype一个bean定义可以有多个实例,当Spring要在需要的时候每次生产一个新的bean实例
request每次http请求都会创建一个bean,
session在一个HTTP Session中,一个bean定义对应一个实例。
global-session在一个全局的HTTP Session中,一个bean定义对应一个实例。

:::info
注意:

  1. 缺省的Spring bean 的作用域是Singleton。使用 prototype 作用域需要慎重的思考,因为频繁创建和销毁 bean 会带来很大的性能开销。
  2. 以下作用域仅在基于web的Spring ApplicationContext情形下有效: req session global-session

:::

Spring框架中的单例bean是线程安全的吗?

不是,Spring框架中的单例bean不是线程安全的。
spring 中的 bean 默认是单例模式,spring 框架并没有对单例 bean 进行多线程的封装处理。
实际上大部分时候 spring bean 无状态的(比如 dao 类),某种程度上来说 bean 也是安全的,但如果 bean 有状态的话(比如 view model 对象),那就要开发者自己去保证线程安全了,最简单的就是改变 bean 的作用域,把“singleton”变更为“prototype”,这样请求 bean 相当于 new Bean(),所以就可以保证线程安全。

  • 有状态就是有数据存储功能。
  • 无状态就是不会保存数据。
Spring如何处理线程并发问题?

在一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域,因为Spring对一些Bean中非线程安全状态采用ThreadLocal进行处理,解决线程安全问题。
ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。同步机制采用了“时间换空间”的方式,仅提供一份变量,不同的线程在访问前需要获取锁,没获得锁的线程则需要排队。而ThreadLocal采用了“空间换时间”的方式。
ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。

Spring bean的生命周期

(1)实例化Bean:
对于BeanFactory容器,当客户向容器请求一个尚未初始化的bean时,或初始化bean的时候需要注
入另一个尚未初始化的依赖时,容器就会调用createBean进行实例化。对于ApplicationContext容
器,当容器启动结束后,通过获取BeanDefinition对象中的信息,实例化所有的bean。
(2)设置对象属性(依赖注入):
实例化后的对象被封装在BeanWrapper对象中,紧接着,Spring根据BeanDefinition中的信息 以
及 通过BeanWrapper提供的设置属性的接口完成依赖注入。
(3)处理Aware接口:
接着,Spring会检测该对象是否实现了xxxAware接口,并将相关的xxxAware实例注入给Bean:
①如果这个Bean已经实现了BeanNameAware接口,会调用它实现的setBeanName(String
beanId)方法,此处传递的就是Spring配置文件中Bean的id值;
②如果这个Bean已经实现了BeanFactoryAware接口,会调用它实现的setBeanFactory()方法,传
递的是Spring工厂自身。
③如果这个Bean已经实现了ApplicationContextAware接口,会调用
setApplicationContext(ApplicationContext)方法,传入Spring上下文;
(4)BeanPostProcessor:
如果想对Bean进行一些自定义的处理,那么可以让Bean实现了BeanPostProcessor接口,那将会
调用postProcessBeforeInitialization(Object obj, String s)方法。BeanPostProcessor 经常被用
作是 Bean 内容的更改,并且由于这个是在 Bean 初始化结束时调用那个的方法,也可以被应
用于内存或缓存技术。
(5)InitializingBean 与 init-method:
如果Bean在Spring配置文件中配置了 init-method 属性,则会自动调用其配置的初始化方法。
(6)如果这个Bean实现了BeanPostProcessor接口,将会调用
postProcessAfterInitialization(Object obj, String s)方法;由于这个方法是在Bean初始化结束时调
用的,所以可以被应用于内存或缓存技术;
以上几个步骤完成后,Bean就已经被正确创建了,之后就可以使用这个Bean了。
(7)DisposableBean:
当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用其实现
的destroy()方法;
(8)destroy-method:
最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方
法。
image.png
关键源码⽅法(强烈建议⾃⼰去撸⼀遍)

  • org.springframework.context.support.AbstractApplicationContext#refresh (⼊⼝)
  • org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization (初始化单例对象⼊⼝)
  • org.springframework.beans.factory.config.ConfigurableListableBeanFactory#preInstantiateSingletons (初始化单例对象⼊⼝)
  • org.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.String) (万恶之源,获取并创建Bean的⼊⼝)
  • org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean (实际的获取并创建Bean的实现)
  • org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#getSingleton(java.lang.String) (从缓存中尝试获取)
  • org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean(java.lang.String, org.springframework.beans.factory.support.RootBeanDefinition,java.lang.Object[]) (实例化Bean)
  • org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean (实例化Bean具体实现)
  • org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance (具体实例化过程)
  • org.springframework.beans.factory.support.DefaultSingletonBeanRegistry#addSingletonFactory (将实例化后的Bean添加到三级缓存)
  • org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean (实例化后属性注⼊)
  • org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean(java.lang.String, java.lang.Object,
  • org.springframework.beans.factory.support.RootBeanDefinition) (初始化⼊⼝)

4. Spring注解

@Component, @Controller, @Repository,@Service 有何区别?

@Component:这将 java 类标记为 bean。它是任何 Spring 管理组件的通用构造型。spring 的组件扫描机制现在可以将其拾取并将其拉入应用程序环境中。
@Controller:这将一个类标记为 Spring Web MVC 控制器。标有它的 Bean会自动导入到 IoC 容器中。
@Service:此注解是组件注解的特化。它不会对 @Component 注解提供任何其他行为。您可以在服务层类中使用 @Service 而不是 @Component,因为它以更好的方式指定了意图。
@Repository:这个注解是具有类似用途和功能的 @Component 注解的特化。它为 DAO 提供了额外的好处。它将 DAO 导入 IoC 容器,并使未经检查的异常有资格转换为 Spring DataAccessException。

@Required 注解

这个注解表明bean的属性必须在配置的时候设置,通过一个bean定义的显式的属性值或通过自动装配,若@Required注解的bean属性未被设置,容器将抛出BeanInitializationException

public class Person{
    private String name;
    @Required
    public void setName(String name){
        this.name = name;
    }
}
@Autowired 注解和@Resource

@Autowired默认是按照类型装配注入的,默认情况下它要求依赖对象必须存在(可以设置它required属性为false)。@Autowired 注解提供了更细粒度的控制,包括在何处以及如何完成自动装配。它的用法和@Required一样,修饰setter方法、构造器、属性或者具有任意名称和/或多个参数的PN方法。

public class Person{
    private String name;
    @Autowired
    public void setName(String name){
        this.name = name;
    }
}

class TestService{
    // 下面两种@Autowired只要使用一种即可
    @Autowired
    private UserDao userDao; // 用于字段上
    
    @Autowired
    public void setUserDao(UserDao userDao) { // 用于属性的方法上
    	this.userDao = userDao;
    }
}


 class TestServiceImpl {
    @Autowired
    @Qualifier("userDao")
    private UserDao userDao;
}
@Autowired@Resource
Springjavax.annotation.Resource
构造函数、成员变量、Setter方法字段和setter
默认是按照类型装配注入 byType按照名称来装配注入 ByName
默认情况下它要求依赖对象必须存在(如果允许null,可以设置它required属性为false)
如果我们想使用按照名称(byName)来装配,可以结合@Qualifier注解一起使用只有当找不到与名称匹配的bean才会按照类型来装配注入
public class TestServiceImpl {
    // 下面两种@Resource只要使用一种即可
    @Resource(name="userDao")
    private UserDao userDao; // 用于字段上
    
    @Resource(name="userDao")
    public void setUserDao(UserDao userDao) { // 用于属性的setter方法上
    	this.userDao = userDao;
    }
}

:::info

注:最好是将@Resource放在setter方法上,因为这样更符合面向对象的思想,通过set、get去操作属性,而不是直接去操作属性。

:::
@Qualifier 注解

@RequestMapping 注解
用于将特定 HTTP 请求方法映射到将处理相应请求的控制器中的特定类/方法。此注释可应用于两个级别:

  • 类级别:映射请求的 URL
  • 方法级别:映射 URL 以及 HTTP 请求方法

5. Spring数据访问

Spring支持的事务管理类型

编程式事务管理:这意味你通过编程的方式管理事务,给你带来极大的灵活性,但是难维护。
声明式事务管理:这意味着你可以将业务代码和事务管理分离,你只需用注解和XML配置来管理事务。

Spring事务的实现方式和实现原理

Spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,
spring是无法提供事务功能的。真正的数据库层的事务提交和回滚是通过binlog或者redo log实现的。
Spring的事务传播行为

propagation_required如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置
PAOPAGATION_REQUIRE_NEW若当前没有事务,则新建一个事务。若当前存在事务,则新建一个事务,新老事务相互独立
PROPAGATION_NESTED:如果当前存在事务,则嵌套在当前事务中执行。如果当前没有事务,则新建一个事务
propagation_supports支持当前事务,若当前不存在事务,以非事务的方式执行
PROPAGATION_NOT_SUPPORTED以非事务的方式执行,若当前存在事务,则把当前事务挂起
PROPAGATION_MANDATORY强制事务执行,若当前不存在事务,则抛出异常.
PROPAGATION_NEVER以非事务的方式执行,如果当前存在事务,则抛出异常
spring 的事务隔离

spring 有五大隔离级别,默认值为 ISOLATION_DEFAULT(使用数据库的设置),其他四个隔离级别和数据库的隔离级别一致:

  1. ISOLATION_DEFAULT:用底层数据库的设置隔离级别,数据库设置的是什么我就用什么;
  2. ISOLATION_READ_UNCOMMITTED:未提交读,最低隔离级别、事务未提交前,就可被其他事务读取(会出现幻读、脏读、不可重复读);
  3. ISOLATION_READ_COMMITTED:提交读,一个事务提交后才能被其他事务读取到(会造成幻读、不可重复读),SQL server 的默认级别;
  4. ISOLATION_REPEATABLE_READ:可重复读,保证多次读取同一个数据时,其值都和事务开始时候的内容是一致,禁止读取到别的事务未提交的数据(会造成幻读),MySQL 的默认级别;
  5. ISOLATION_SERIALIZABLE:序列化,代价最高最可靠的隔离级别,该隔离级别能防止脏读、不可重复读、幻读。
事务三要素:

数据源:表示具体的事务性资源,是事务的真正处理者,如MySQL等。
事务管理器:像一个大管家,从整体上管理事务的处理过程,如打开、提交、回滚等。
事务应用和属性配置:像一个标识符,表明哪些方法要参与事务,如何参与事务,以及一些相关属性如隔离级别、超时时间等。

事务注解的本质是什么

@Transactional 这个注解仅仅是一些(和事务相关的)元数据,在运行时被事务基础设施读取消费,并使用这些元数据来配置bean的事务行为。 大致来说具有两方面功能,一是表明该方法要参与事务,二是配置相关属性来定制事务的参与方式和运行行为
声明式事务主要是得益于Spring AOP。使用一个事务拦截器,在方法调用的前后/周围进行事务性增强(advice),来驱动事务完成。
@Transactional注解既可以标注在类上,也可以标注在方法上。当在类上时,默认应用到类里的所有方法。如果此时方法上也标注了,则方法上的优先级高。 另外注意方法一定要是public的。

AOP

Spring AOP and AspectJ AOP 有什么区别?AOP 有哪些实现方式?

AOP实现的关键在于 代理模式,AOP代理主要分为静态代理和动态代理。静态代理的代表为AspectJ;动态代理则以Spring AOP为代表。
(1)AspectJ是静态代理的增强,所谓静态代理,就是AOP框架会在编译阶段生成AOP代理类,因此也称为编译时增强,他会在编译阶段将AspectJ(切面)织入到Java字节码中,运行的时候就是增强之后的AOP对象。基于字节码操作 (Bytecode Manipulation);
(2)Spring AOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改字节码,而是每次运行时在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。运行时增强,基于代理(Proxying);
image.png

JDK动态代理和CGLIB动态代理的区别

Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理:

  • JDK动态代理只提供接口的代理,不支持类的代理。核心InvocationHandler接口和Proxy类,InvocationHandler 通过invoke()方法反射来调用目标类中的代码,动态地将横切逻辑和业务编织在一起;接着,Proxy利用 InvocationHandler动态创建一个符合某一接口的的实例,生成目标类的代理对象。
  • 如果代理类没有实现 InvocationHandler 接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现AOP。CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。
    @Aspect
    public class Demo{
       @Pointcut(value="execution(* com.yangxin.core.service.*.*.*(..))") 
        public void point(){
            
        }

        @Before(value="point()")
        public void before(){
            //输出一句话
        }
        @AfterReturning(value = "point()")
        public void after(){
        	System.out.println("transaction commit");
        }
        @Around("point()")
        public void around(ProceedingJoinPoint joinPoint) throws Throwable{
        	System.out.println("transaction begin");
        	joinPoint.proceed();
        	System.out.println("transaction commit");
        }
    }

静态代理与动态代理区别在于生成AOP代理对象的时机不同,相对来说AspectJ的静态代理方式具有更好的性能,但是AspectJ需要特定的编译器进行处理,而Spring AOP则无需特定的编译器处理。
:::info

InvocationHandler 的 invoke(Object proxy,Method method,Object[] args):
proxy是最终生成的代理实例; method 是被代理目标实例的某个具体方法; args 是被代理目标实例某个方法的具体入参, 在方法反射调用时使用。

:::

Spring通知有哪些类型?

在AOP术语中,切面的工作被称为通知,实际上是程序执行时要通过SpringAOP框架触发的代码段。
Spring切面可以应用5种类型的通知:

  1. 前置通知(Before):在目标方法被调用之前调用通知功能;
  2. 后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么;
  3. 返回通知(After-returning ):在目标方法成功执行之后调用通知;
  4. 异常通知(After-throwing):在目标方法抛出异常后调用通知;
  5. 环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值