-
简介:
SpringBoot其实不是什么新的框架,它默认配置了许多框架的使用方式,就像maven整合了所有的jar包。SpringBoot整合了所有的框架,并通过一行简单的main方法启动应用。
springMVC是解决web控制层的一些问题 -
SpringIOC
IOC:控制反转,程序本身不负责对象的创建和维护,对象的创建和维护都是由外部容器负责创建和维护。也就是说每次新需要一个对象的时候不需要在程序中去new一个对象出来,而是直接到外部容器中调用。
DI:依赖注入,是控制反转的实现方式,用来创建对象和组装对之间的依赖关系。IOC在初始化的时候通过DI创建一系列的对象,并且将这些对象的依赖关系通过注入的方式组装起来。具体实例可以参考下图:
Spring注入的常见方式:构造方法注入、setter注入、基于注解的注入。具体实现方法请参考博客https://blog.youkuaiyun.com/a909301740/article/details/78379720
Bean
Bean:在springIOC中,把一切配置到IOC容器中的对象或者是实体都称为Bean
Bean的配置项:参考博客https://blog.youkuaiyun.com/qq_35680499/article/details/82499312
Bean的作用域:singleton(单例模式)、prototype(原型模式)、request(HTTP请求)、session(会话)、global-session(全局会话)
在配置bean的作用域时候,可以在XML中这样定义:
<bean id="bean的ID" class="cn.test.Dao.userDao" scope="singleton/prototype/request/session/global-session">
也可以基于@Scope(“singleton/prototype/request/session/global-session”)的方式来配置bean的作用域。 具体可以参考博客:https://blog.youkuaiyun.com/fuzhongmin05/article/details/73389779
Bean的自动装配常用注解:@Component、@Repository、@Service、@Controller、@Autowired、@Scope、@Resource
@Component:spring 管理组件的通用形式,可以放在任何类头上,在项目开发中一般不使用。
@Repository:注解在数据访问层的Bean。
@Service:注解在业务逻辑层Bean。
@Controller:注解在控制层Bean,也就是是spring-mvc的注解,具有将请求进行转发,重定向的功能。
@Autowired:字面意思为自动装配,用来给指定的字段或方法注入所需的外部资源。
@Scope :Spring默认产生的bean是单例(即"singleton")的,如果设置为"prototype"表示原型即每次都会new一个新的出来。
@Resource:用来给指定的字段或方法注入所需的外部资源(和@Autowired一样),默认按名称装配,当找不到与名称匹配的bean才会按类型装配;注意:@Resource非spring注解,而是J2EE的注解。
注解整合实例:
//==================================Controller
@Controller
public class SimpleController {
@Autowired
private SimpleService simpleService;
}
//==================================Service
@Service("simpleService")
public class SimpleServiceImpl implements SimpleService {
@Autowired
private SimpleDao simpleDao;
}
//===================================Repository
@Repository("simpleDao")
public class SimpleDaoImpl implements SimpleDao {
}
整个装配流程是这样的:
1)在某一时刻Spring调用了 Bean工厂 的 getBean(beanName) 方法。beanName可能是simpleController,或者simpleService,simpleDao,顺序没关系(因为后面会有依赖关系的处理)。我们假设simpleController吧。
2)getBean方法首先会调用Bean工厂中定义的getSingleton(beanName)方法,来判断是否存在该名字的bean单例,若果存在则返回,方法调用结束。
3)否则,Spring会检查是否存在父工厂,如果有则返回,方法调用结束。
4)否则,Spring 会检查该bean 定义(BeanDefinition实例,用来描述Bean结构,component-scan 扫描后,就是将beanDefinition实例放入Bean工厂,此时Bean还没有被实例化。)是否有依赖关系,如果有,执行1)步,获取依赖的bean实例。
5)否则,Spring会尝试创建这个bean实例,创建实例前,Spring会检查确定调用的构造器,并实例化该Bean。
6)实例化完成后,Spring会调用Bean工厂的populateBean方法来填充bean实例的属性,也就是我们前面提到的自动转配了。populateBean方法便是调用了BeanPostProcessor实例来完成属性元素的自动装配工作。
7)在元素装配过程中,Spring会检查被装配的属性是否存在自动装配的其他属性,然后递归调用getBean方法,直到所有@Autowired的元素都被装配完成。如在装配simpleController中的simpleService属性时,发现SimpleServiceImpl实例中存在@Autowired属性simpleDao,然后调用getBean(simpleDao)方法,同样会执行1)-7)整个过程。所以可以看成一个递归过程。
8)装配完成后,Bean工厂会将所有的bean实例都添加到工厂中来。
注:Spring MVC是多线程单实例的MVC框架,就是说,对于同一个Controller,只会生成一个实例来处理所有的请求,因此bean实例只会实例化一次,并被存放在工厂中,以供其他请求使用。
Bean的生命周期流程:
在说明前可以思考一下Servlet的生命周期:实例化,初始init,接收请求service,销毁destroy;
Spring上下文中的Bean也类似,如下
1)实例化一个Bean--也就是我们常说的new;
2)按照Spring上下文对实例化的Bean进行配置--也就是IOC注入;
3)如果这个Bean已经实现了BeanNameAware接口,会调用它实现的setBeanName(String)方法,此处传递的就是Spring配置文件中Bean的id值
4)如果这个Bean已经实现了BeanFactoryAware接口,会调用它实现的setBeanFactory(setBeanFactory(BeanFactory)传递的是Spring工厂自身(可以用这个方式来获取其它Bean,只需在Spring配置文件中配置一个普通的Bean就可以);
5)如果这个Bean已经实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文(同样这个方式也可以实现步骤4的内容,但比4更好,因为ApplicationContext是BeanFactory的子接口,有更多的实现方法);
6)如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessBeforeInitialization(Object obj, String s)方法,BeanPostProcessor经常被用作是Bean内容的更改,并且由于这个是在Bean初始化结束时调用那个的方法,也可以被应用于内存或缓存技术;
7)如果Bean在Spring配置文件中配置了init-method属性会自动调用其配置的初始化方法。
8)如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法、;
注:以上工作完成以后就可以应用这个Bean了,那这个Bean是一个Singleton的,所以一般情况下我们调用同一个id的Bean会是在内容地址相同的实例,当然在Spring配置文件中也可以配置非Singleton,这里我们不做赘述。
9)当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用那个其实现的destroy()方法;
10)最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。
这里描述的是应用Spring上下文Bean的生命周期,如果应用Spring的工厂也就是BeanFactory的话去掉第5步就Ok了
Spring AOP
面向切面编程AOP,是为了将不同的问题交给不同的部分去解决。将通用化功能用代码实现,对应的就是所谓的切面(Aspect)。业务功能代码和切面代码分开后,架构便变得高内聚低耦合。
AOP的三种织入方式
编译时织入:需要特殊的Java编译器,如AspectJ
类加载时织入:需要特殊的Java编译器,如AspectJ和AspectWerkz
运行时织入:Spring采用的方式,通过动态代理的方式,实现简单
AOP的主要名词概念
Aspect:通用功能的代码实现
Target:被织入Aspect的对象
Join Point:可以作为切入点的机会,所有方法都可作为切入点
PointCut:Aspect实际被引用在的Join Point
Advice:类里的方法 以及这个方法如何织入到目标方法的方法
Weaving:AOP的实现过程
Advice的种类
前置通知(Before)
后置通知(AfterReturning)
异常通知(AfterThrowing)
最终通知(After)
环绕通知(Around)
Spring事务
事务的四大特性(ACID)
原子性(Atomicity):事务是一个原子操作,由一系列动作组成。事务的原子性确保动作要么全部完成,要么完全不起作用。
一致性(Consistency):一旦事务完成(不管成功还是失败),系统必须确保它所建模的业务处于一致的状态,而不会是部分完成部分失败。在现实中的数据不应该被破坏。
隔离性(Isolation):可能有许多事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏。
持久性(Durability):一旦事务完成,无论发生什么系统错误,它的结果都不应该受到影响,这样就能从任何系统崩溃中恢复过来。通常情况下,事务的结果被写到持久化存储器中。
事务的传播行为
当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。Spring 定义了如下七中传播行为,这里以A业务和B业务之间如何传播事务为例说明:
PROPAGATION_REQUIRED :required , 必须。默认值,A如果有事务,B将使用该事务;如果A没有事务,B将创建一个新的事务。
PROPAGATION_SUPPORTS:supports ,支持。A如果有事务,B将使用该事务;如果A没有事务,B将以非事务执行。
PROPAGATION_MANDATORY:mandatory ,强制。A如果有事务,B将使用该事务;如果A没有事务,B将抛异常。
PROPAGATION_REQUIRES_NEW :requires_new,必须新的。如果A有事务,将A的事务挂起,B创建一个新的事务;如果A没有事务,B创建一个新的事务。
PROPAGATION_NOT_SUPPORTED :not_supported ,不支持。如果A有事务,将A的事务挂起,B将以非事务执行;如果A没有事务,B将以非事务执行。
PROPAGATION_NEVER :never,从不。如果A有事务,B将抛异常;如果A没有事务,B将以非事务执行。
PROPAGATION_NESTED :nested ,嵌套。A和B底层采用保存点机制,形成嵌套事务。
事务的隔离级别
并发事务引起的问题:在典型的应用程序中,多个事务并发运行,经常会操作相同的数据来完成各自的任务。并发虽然是必须的,但可能会导致以下的问题:
脏读(Dirty reads)——脏读发生在一个事务读取了另一个事务改写但尚未提交的数据时。如果改写在稍后被回滚了,那么第一个事务获取的数据就是无效的。
不可重复读(Nonrepeatable read)——不可重复读发生在一个事务执行相同的查询两次或两次以上,但是每次都得到不同的数据时。这通常是因为另一个并发事务在两次查询期间进行了更新。
幻读(Phantom read)——幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录。
在 Spring 事务管理中,为我们定义了如下的隔离级别:
ISOLATION_DEFAULT:使用后端数据库默认的隔离级别
ISOLATION_READ_UNCOMMITTED:最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
ISOLATION_READ_COMMITTED:允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
ISOLATION_REPEATABLE_READ:对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生
ISOLATION_SERIALIZABLE:最高的隔离级别,完全服从ACID的隔离级别,确保阻止脏读、不可重复读以及幻读,也是最慢的事务隔离级别,因为它通常是通过完全锁定事务相关的数据库表来实现的