Spring知识点小结
Spring知识点小结
文章目录
Spring概念
Spring是一个开源的轻量级的IOC和AOP容器框架。是为Java应用程序提供基础性服务的一套框架,目的是用于简化企业应用程序的开发,它使得开发者只需要关心业务需求。
Spring的优点
1.低侵入式设计,代码污染极低
2.独立于各种应用服务器,基于Spring框架的应用,可以真正实现Write Once,Run Anywhere的承诺
3.Spring的DI机制将对象之间的依赖关系交由框架处理,减低组件的耦合性;
4.Spring提供了AOP技术,支持将一些通用任务,如安全、事务、日志、权限等进行集中式管理,从而提供更好的复用。
5.Spring的ORM和DAO提供了与第三方持久层框架的良好整合,并简化了底层的数据库访问
6.Spring并不强制应用完全依赖于Spring,开发者可*选用Spring框架的部分或全部
7.Spring对于主流的应用框架提供了集成支持。
常用Spring模块
Spring Core:核心类库,提供IOC服务;
Spring Context:提供框架式的Bean访问方式,以及企业级功能(JNDI、定时任务等);
Spring AOP:AOP服务;
Spring DAO:对JDBC的抽象,简化了数据访问异常的处理;
Spring ORM:对现有的ORM框架的支持;
Spring Web:提供了基本的面向Web的综合特性,例如多方文件上传;
Spring MVC:提供面向Web应用的Model-View-Controller实现。
IOC和DI
IOC:是控制反转。组件向容器发起资源请求,容器查找资源并返回资源。容器会主动将资源提供给它所管理的组件,组件选择一个合适的方式来接收资源,这也是查找的被动式。实际就是你在xml文件控制,控制权的转移是所谓反转,侧重于原理。
DI:是依赖注入,组件以一些预先定义好的方式(如:setter方法)接收来自容器的资源注入。创建对象实例时,为这个对象注入属性值或其它对象实例,侧重于实现。有三种注入方式 :构造器注入、setter方法注入、根据注解注入。
IOC和DI的实质:他们描述的是同一件事情,就是创建对象实例的权限从应用程序控制剥离到容器控制,即容器通过XML等配置控制对象实例来代替使用new关键字实例化一个对象。这样的方式最大的好处就是解耦。
IOC和DI的区别:只是从不同的角度进行描述:IOC是从容器的角度,描述从,之前的由应用程序到容器中获取资源,到,现在的由应用程序请求之后容器进行资源提供,的一个过程。DI是从应用程序的角度,描述由,容器配置对对象实例进行注入,来代替应用程序到容器中获取资源。
什么是Spring Bean
Spring Bean是被实例的,组装的及被Spring容器管理的Java对象。Spring容器会自动完成@bean对象的实例化。创建应用对象之间的协作关系的行为称为:装配(wiring),这就是依赖注入的本质。
Spring Bean的生命周期
Servlet的生命周期:实例化,初始,接收请求,销毁
Spring Bean生命周期也类似与之类似
1.实例化Bean与依赖注入
-
对于BeanFactory容器,当客户向容器请求一个尚未初始化的bean时,或初始化bean的时候需要注入另一个尚未初始化的依赖时,容器就会调用createBean进行实例化。对于ApplicationContext容器,当容器启动结束后,通过获取BeanDefinition对象中的信息,实例化所有的bean
-
实例化后的对象被封装在BeanWrapper对象中,紧接着,Spring根据BeanDefinition中的信息 以及 通过BeanWrapper提供的设置属性的接口完成依赖注入
2.处理Aware接口
-
接着,Spring会检测该对象是否实现了xxxAware接口,并将相关的xxxAware实例注入给Bean
-
如果这个Bean已经实现了BeanNameAware接口,会调用它实现的setBeanName(String beanId)方法,此处传递的就是Spring配置文件中Bean的id值
-
如果这个Bean已经实现了BeanFactoryAware接口,会调用它实现的setBeanFactory()方法,传递的是Spring工厂自身
-
如果这个Bean已经实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文
3.InitializingBean 与 init-method
- 如果Bean在Spring配置文件中配置了 init-method 属性,则会自动调用其配置的初始化方法
4.BeanPostProcessor
- 如果这个Bean实现了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法;由于这个方法是在Bean初始化结束时调用的,所以可以被应用于内存或缓存技术
5.接收请求
- 以上几个步骤完成后,Bean就已经被正确创建了,之后就可以使用这个Bean了
6.DisposableBean与destroy-method
-
当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用其实现的destroy()方法
-
如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法
Spring中的工厂容器有哪两个
BeanFactory和ApplicationContext
BeanFactory接口是Spring最原始的接口,包含了各种Bean的定义,读取bean配置文档,管理bean的加载、实例化,控制bean的生命周期,维护bean之间的依赖关系。
ApplicationContext接口是BeanFactory的派生,处理BeanFactory包含的功能,还有以下的功能:
-
继承MessageSource,因此支持国际化。
-
统一的资源文件访问方式。
-
提供在监听器中注册bean的事件。
-
同时加载多个配置文件。
-
载入多个上下文 ,使得每一个上下文都专注于一个特定的层次,比如应用的web层。
BeanFactory和ApplicationContext的区别
BeanFactroy采用的是延迟加载形式来注入Bean,即通过new (BeanFactory的实现类)来启动Spring容器时,并不会创建Spring容器里面的对象,只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。
这样的流程会出现一些问题。在调试过程中,我们不能立刻发现Spring在配置中存在的问题,假设Bean的某一个属性没有注入,BeanFacotry加载后,直至使用getBean方法调用的时候才会抛出异常,这样很明显不利于后期的维护和异常处理。
通过new(BeanFactory的实现类)来启动Spring容器也就是说BeanFactory通常以编程的方式被创建。支持手动注册BeanPostProcessor、BeanFactoryPostProcessor。
ApplicationContext是在容器启动时,一次性创建了所有的Bean。ApplicationContext启动后预载入所有的单实例Bean,通过预载入单实例bean ,当你需要的时候,直接使用即可。
与BeanFactory相比,这样方式,让我们在容器启动时,就可以发现Spring配置中存在的错误,并及时处理,有利于检查所依赖属性是否注入。
ApplicationContext可以通过实现类创建,也可以通过声明的方式创建。支持自动注册BeanPostProcessor、BeanFactoryPostProcessor。
当然,ApplicationContext和基本的BeanFactory相比,存在占用内存空间不足的问题,当应用程序配置Bean较多时,程序启动较慢。
bean的作用域(scope)
-
singleton:默认,每个容器中只有一个bean的实例,单例的模式由BeanFactory自身来维护。
-
prototype:为每一个bean请求提供一个实例。
-
request:为每一个网络请求创建一个实例,在请求完成以后,bean会失效并被垃圾回收器回收。
-
session:每个session中有一个bean的实例,在session过期后,bean会随之失效。即同一个Http Session共享一个Bean实例
-
global-session:全局作用域,在一个全局的Http Session中,容器会返回该Bean的同一个实例,仅在使用Portlet context时有效。
bean的装配方式
- XML方式配置,直接在XML文件中,使用标签配置。
public class User {
}
<bean id="user" class="User"></bean>
- 基于注解的方式,在XML文件中设置需要被扫描注解的包,在被使用的类上添加注解,需要使用时,使用@Autowired即可。
@Component
public class Test {
public void testMethod() {
System.out.println("test Autowired");
}
}
<context:component-scan base-package="com.****.****"/>
@Autowired
private Test test;
@Test
public void test() {
test.testMethod();
}
- 使用Java类进行Config配置,使用@Configuration和@Bean来代替XML配置,效果其实是一致的,值得注意的是@Bean注解的都是默认单例模式。
@Configuration
public class BeanConfig {
@Bean
public BeanFactory beanFactory(){
return new BeanFactoryTest();
}
}
public class BeanFactoryTest implements BeanFactory {
@Override
public void Beantest() {
System.out.println("基于类的Java Config的bean!");
}
}
bean的注入方式
- 设值注入,直接在bean标签下,使用property标签一一对应写入。实体类中被注入的属性需要有setter方法
<bean id="user" class="com.***.UserInfo">
<property name="id" value="1" />
<property name="name" value="Tom" />
<property name="age" value="25" />
</bean>
- 构造器注入,通过含参的构造方法,接收注入的属性值。index是属性的顺序(0就是第一个属性)。
public class TestBean{
private User user;
private String name;
private List list;
//构造器
public TestBean(User user,String name,List list){
this.user = user;
this.name = name;
this.list = list;
}
}
<!--构造器方式注入-->
<bean id="testBean" class="com.***.TestBean">
<constructor-arg index="0" type="com.***.UserInfo" ref="user"/>
<constructor-arg index="1" type="java.lang.String" value="Tom"/>
<constructor-arg index="2" type="java.util.List">
<list>
<value>list1</value>
<value>list2</value>
<value>list3</value>
</list>
</constructor-arg>
</bean>
- 工厂注入,通过一个类(工厂),在工厂中完成初始化,在XML文件中通过工厂中的方法进行注入。
public class UserFactory {
// 静态方法
public static User getStaticUser() {
return new User(1,"Tom",25);
}
// 普通方法
public User getUser() {
return new User(1,"Tom",25);
}
}
<!-- 静态方法 -->
<bean id="staticUser" class="UserFactory" factory-method="getStaticUser"></bean>
<!-- 普通方法 -->
<bean id="userFactory" class="UserFactory"></bean>
<bean id="user" factory-bean="userFactory" factory-method="getUser"></bean>
用注解方式将对象注册到Spring容器中
分别是:@Component
、@Service
、@Controller
、@Respository
。
Spring框架最早出现的只有@Component
注解,但如果所有的对象都使用同一个注解,很难区分对象究竟属于哪一层架构。之后又推出了@Service
(Service层)、@Controller
(Controller层)、@Respository
(Dao层)三种注解,用于区分对象属于哪一层架构。4种注解方式从功能上来说没有任何区别,名称的不同只为区分对象的层级。
在类上添加注解后,在XML文件中,设置扫描注解的包
<context:component-scan base-package="com.***.***" />
在Spring框架xml配置的自动装配:
-
no:默认的方式是不进行自动装配的,通过手工设置ref属性来进行装配bean。
-
byName:通过bean的名称进行自动装配,如果一个bean的property与另一bean的name相同,那么可以不写这个property,就进行自动装配。
-
byType:通过参数的数据类型(class)进行自动装配,寻找与属性类型相同的bean,若一个bean的数据类型,兼容另一个bean中Property的数据类型,则自动装配。
使用byType首先需要保证同一类型的对象,在spring容器中唯一,若不唯一会报不唯一的异常
例如以下这样,通过byType就会报不唯一的错误
-
constructor:利用构造函数进行装配,其实就是根据构造方法的参数类型进行对象查找,相当于采用byType的方式,就是需要有含参的构造方法。
-
autodetect:自动探测,如果有构造方法,通过 construct的方式自动装配,如果失败再尝试使用byType。
若该XML文件中,所有的装配方式都是一致的,那么可设定默认装配方式,而不需要在每个bean上设置
<!-- 例如设置为byType -->
<beans default-autowire="byType">
注解实现的自动装配
@Autowired
默认使用byType来装配,如果匹配到类型的多个实例,再通过byName来确定Bean;若找不到,则报错。
Autowired的required的属性,默认为true,表示注入的时候,该bean必须存在,否则就会注入失败;当设置@Autowired(required=false)到时候,表示忽略当前要注入的bean,如果有,直接注入,没有跳过,不会报错。
@Qualifier:
通过byName来确定Bean,必须与@Autowired一起使用。
@Resource
这个注解属于J2EE,默认使用byName来装配,若不存在与名称匹配的实例,再通过byType来确定。
Resource的name和type属性可指定需要装配的name和type。若指定了name或者type或者两者都指定了,则必须找到唯一的bean进行装配,否则报错。若两者都未指定,则按默认的byName进行装配。
@Autowired和@Resource之间的区别
@Autowired默认是按照类型装配注入的,默认情况下它要求依赖对象必须存在(可以设置它required属性为false)
@Resource默认是按照名称来装配注入的,只有当找不到与名称匹配的bean才会按照类型来装配注入。
AOP
OOP:面向对象编程
AOP:称为面向切面编程,作为OOP的一种补充,用于将那些与业务无关,但却对多个对象产生影响的公共行为和逻辑,抽取并封装为一个可重复用的模块,这个模块被命名为“切面”(Aspect),减少系统中的重复代码,降低了模块间的耦合度,同时提高了系统的可维护性。简单来讲就是将纵向重复的代码,横向抽取出来。
大量使用于权限认证、日志、事务处理等。
AspectJ和Spring AOP
AOP实现的关键在于代理模式,AOP代理主要分为静态代理和动态代理。
静态代理的代表为AspectJ;动态代理则以Spring AOP为代表。
-
AspectJ:是静态代理的增强。静态代理,就是AOP框架会在编译阶段生成AOP代理类,因此也称为编译时增强,他会在编译阶段将AspectJ(切面)织入到Java字节码中,运行的就是增强之后的AOP对象。
-
Spring AOP使用的动态代理,所谓的动态代理就是说AOP框架不会去修改字节码,而是每次运行时在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。
静态代理与动态代理区别在于生成AOP代理对象的时机不同,相对来说AspectJ的静态代理方式具有更好的性能,但是AspectJ需要特定的编译器进行处理,而Spring AOP则无需特定的编译器处理。
Spring AOP的动态代理
Spring AOP中的动态代理主要有两种方式,JDK动态代理和CGLIB动态代理:
-
JDK动态代理只提供接口的代理,不支持类的代理。核心InvocationHandler接口和Proxy类,InvocationHandler 通过invoke()方法反射来调用目标类中的代码,动态地将横切逻辑和业务编织在一起;接着,Proxy利用 InvocationHandler动态创建一个符合某一接口的的实例, 生成目标类的代理对象。
-
如果代理类没有实现 InvocationHandler 接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现AOP。CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。
Spring采用的是JDK代理和CGLIB代理混合使用。如果被代理对象实现了接口,就优先使用JDK代理,如果没有实现接口,就用cglib代理。
Spring AOP里的名词以及关系
- Aspect(切面): Aspect 声明类似于 Java 中的类声明,在 Aspect 中会包含着一些 Pointcut 以及相应的 Advice。
- Joint point(连接点):表示在程序中明确定义的点,典型的包括方法调用,对类成员的访问以及异常处理程序块的执行等等,它自身还可以嵌套其它 joint point。
- Pointcut(切点):表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方。
- Advice(增强):Advice 定义了在 Pointcut 里面定义的程序点具体要做的操作,它通过 before、after 和 around 来区别是在每个 joint point 之前、之后还是代替执行的代码。
- Target(目标对象):织入 Advice 的目标对象.。
- Weaving(织入):将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程
可参考博文细说Spring——AOP详解
Advice(增强)的类型以及执行过程
-
before advice
, 在 join point 前被执行的 advice. 虽然 before advice 是在 join point 前被执行, 但是它并不能够阻止 join point 的执行, 除非发生了异常(即我们在 before advice 代码中, 不能人为地决定是否继续执行 join point 中的代码) -
after return advice
, 在一个 join point 正常返回后执行的 advice -
after throwing advice
, 当一个 join point 抛出异常后执行的 advice -
after advice
, 无论一个 join point 是正常退出还是发生了异常, 都会被执行的 advice. -
around advice
, 在 join point 前和 joint point 退出后都执行的 advice. 这个是最常用的 advice. -
introduction
,introduction可以为原有的对象增加新的属性和方法
对于Advice的理解可参考Spring AOP之坑:完全搞清楚advice的执行顺序
Spring的事务
事务就是对一系列的数据库操作(比如插入或修改多条数据)进行统一的提交或回滚操作,如果插入或修改成功,那么一起成功,如果中间有一条出现异常,那么回滚之前的所有操作。这样可以防止出现脏数据,防止数据库数据出现问题。简单来说就是一个方法内执行了多次数据库操作,要么完全地执行,要么完全地不执行。
现实世界中最常见的事务例子可能就是转账了。
编程式事务管理
编程式事务管理是侵入性事务管理,使用TransactionTemplate或者直接使用PlatformTransactionManager,对于编程式事务管理,Spring推荐使用TransactionTemplate。
声明式事务管理
声明式事务管理建立在AOP之上,其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,执行完目标方法之后根据执行的情况提交或者回滚。
编程式事务每次实现都要单独实现,但业务量大功能复杂时,使用编程式事务无疑是痛苦的;声明式事务属于无侵入式,不会影响业务逻辑的实现,只需要在配置文件中做相关的事务规则声明或者通过注解的方式,便可以将事务规则应用到业务逻辑中。
声明式事务唯一不足的地方就是声明式事务管理的粒度是方法级别,而编程式事务管理是可以到代码块的,但是可以通过提取方法的方式完成声明式事务管理的配置。
spring的事务传播行为
当多个事务同时存在的时候,spring如何处理这些事务的行为。
-
PROPAGATION_REQUIRED
Spring默认的传播机制,能满足绝大部分业务需求,如果外层有事务,则当前事务加入到外层事务,一块提交,一块回滚。如果外层没有事务,新建一个事务执行 -
PROPAGATION_REQUES_NEW
该事务传播机制是每次都会新开启一个事务,同时把外层事务挂起,当当前事务执行完毕,恢复上层事务的执行。如果外层没有事务,执行当前新开启的事务即可 -
PROPAGATION_SUPPORT
如果外层有事务,则加入外层事务,如果外层没有事务,则直接使用非事务方式执行。完全依赖外层的事务 -
PROPAGATION_NOT_SUPPORT
该传播机制不支持事务,如果外层存在事务则挂起,执行完当前代码,则恢复外层事务,无论是否异常都不会回滚当前的代码 -
PROPAGATION_NEVER
该传播机制不支持外层事务,即如果外层有事务就抛出异常 -
PROPAGATION_MANDATORY
与NEVER相反,如果外层没有事务,则抛出异常 -
PROPAGATION_NESTED
该传播机制的特点是可以保存状态保存点,当前事务回滚到某一个点,从而避免所有的嵌套事务都回滚,即各自回滚各自的,如果子事务没有把异常吃掉,基本还是会引起全部回滚的。
事务中有几种隔离级别
事务中会出现脏读、不可重复读、幻读这些并发问题,通过设置隔离级别来解决。
-
ISOLATION_DEFAULT:使用后端数据库默认的隔离级别
-
ISOLATION_READ_UNCOMMITTED:允许读取尚未提交的更改。可能导致脏读、幻读或不可重复读。
-
ISOLATION_READ_COMMITTED(Oracle 默认级别):允许从已经提交的并发事务读取。可防止脏读,但幻读和不可重复读仍可能会发生。
-
ISOLATION_REPEATABLE_READ(MYSQL默认级别):对相同字段的多次读取的结果是一致的,除非数据被当前事务本身改变。可防止脏读和不可重复读,但幻读仍可能发生。
-
ISOLATION_SERIALIZABLE:完全服从ACID的隔离级别,确保不发生脏读、不可重复读和幻影读。这在所有隔离级别中也是最慢的,因为它通常是通过完全锁定当前事务所涉及的数据表来完成的。
参考博文
【2】Spring知识点总结
上一篇: 独家解析:某些大型招聘网站为何没上市?
下一篇: 三大CMS建站系统助你免费建网站
推荐阅读
-
只需一步,在Spring Boot中统一Restful API返回值格式与统一处理异常
-
Spring注解
-
品Spring:帝国的基石
-
spring5 源码深度解析----- 事务的回滚和提交(100%理解事务)
-
Spring5源码解析5-ConfigurationClassPostProcessor (上)
-
spring5 源码深度解析----- AOP代理的生成
-
Spring5源码解析4-refresh方法之invokeBeanFactoryPostProcessors
-
Spring Boot Security OAuth2 实现支持JWT令牌的授权服务器
-
spring boot2.0.4集成druid,用jmeter并发测试工具调用接口,druid查看监控的结果
-
Linux里awk中split函数的用法小结