前言
以下内容为根据实习公司给的路线图做的总结,实力有限,如有错误,欢迎指正
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实现
- Spring Test:提供了对JUnit和TestNG测试的支持。
IOC与DI
IOC(Inversion Of Control)控制反转,DI(Dependence Injection)依赖注入。在Spring之前的开发中,使用的是Servlet+JSP架构,也就是说项目程序中用到的所有对象都需要由程序的开发者来创建好,在程序运行期间进行调用,而在Spring创造出来之后,提出了IOC与DI的概念,也就是说将对象的创建交给程序来动态管理,可以极大的简化开发效率,方便程序员用户进行开发。
IOC与DI技术可以减少程序之间的耦合性,因为我们吧程序的创建都交给了IOC容器管理,由DI来注入,可以实现松耦合。
以下内容来自博客:IOC与DI的理解
2.1、IoC(控制反转)
首先想说说IoC(Inversion of Control,控制反转)。这是spring的核心,贯穿始终。**所谓IoC,对于spring框架来说,就是由spring来负责控制对象的生命周期和对象间的关系。**这是什么意思呢,举个简单的例子,我们是如何找女朋友的?常见的情况是,我们到处去看哪里有长得漂亮身材又好的mm,然后打听她们的兴趣爱好、qq号、电话号、ip号、iq号………,想办法认识她们,投其所好送其所要,然后嘿嘿……这个过程是复杂深奥的,我们必须自己设计和面对每个环节。传统的程序开发也是如此,在一个对象中,如果要使用另外的对象,就必须得到它(自己new一个,或者从JNDI中查询一个),使用完之后还要将对象销毁(比如Connection等),对象始终会和其他的接口或类藕合起来。
那么IoC是如何做的呢?有点像通过婚介找女朋友,在我和女朋友之间引入了一个第三者:婚姻介绍所。婚介管理了很多男男女女的资料,我可以向婚介提出一个列表,告诉它我想找个什么样的女朋友,比如长得像李嘉欣,身材像林熙雷,唱歌像周杰伦,速度像卡洛斯,技术像齐达内之类的,然后婚介就会按照我们的要求,提供一个mm,我们只需要去和她谈恋爱、结婚就行了。简单明了,如果婚介给我们的人选不符合要求,我们就会抛出异常。整个过程不再由我自己控制,而是有婚介这样一个类似容器的机构来控制。Spring所倡导的开发方式就是如此,所有的类都会在spring容器中登记,告诉spring你是个什么东西,你需要什么东西,然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转。
2.2、DI(依赖注入)
IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的。比如对象A需要操作数据库,以前我们总是要在A中自己编写代码来获得一个Connection对象,有了 spring我们就只需要告诉spring,A中需要一个Connection,至于这个Connection怎么构造,何时构造,A不需要知道。在系统运行时,spring会在适当的时候制造一个Connection,然后像打针一样,注射到A当中,这样就完成了对各个对象之间关系的控制。A需要依赖 Connection才能正常运行,而这个Connection是由spring注入到A中的,依赖注入的名字就这么来的。那么DI是如何实现的呢? Java 1.3之后一个重要特征是反射(reflection),它允许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性,spring就是通过反射来实现注入的。
Spring中常见的概念介绍
- 核心容器(应用上下文):这是基本的Spring模块,提供spring 框架的基础功能,BeanFactory 是 任何以spring为基础的应用的核心。Spring 框架建立在此模块之上,它使Spring成为一个容器。
- BeanFactory:Bean 工厂是工厂模式的一个实现,提供了控制反转功能,用来把应用的配置和依赖从正真的应用代码中分离。以延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。
- ApplicationContext :它也是Spring的容器,只不过它是Spring的一个 子接口,且在容器启动时,一次性创建了所有的Bean。
- IOC:控制反转
- DI:依赖注入
- AOP:切面开发
Bean的生命周期
Spring在容器初始化的过程中对Bean完成初始化注入,之后对Bean的属性进行了设置,在之后通过PostProcessor对Bean进行了设置,再之后根据容器一起销毁。
①通过构造器或工厂方法创建bean实例
②为bean的属性设置值和对其他bean的引用
③将bean实例传递给bean后置处理器的
postProcessBeforeInitialization()
方法④调用bean的初始化方法
⑤将bean实例传递给bean后置处理器的
postProcessAfterInitialization()
方法⑥bean可以使用了
⑦当容器关闭时调用bean的销毁方法
自动装配
- XML自动装配
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2gqWR5l1-1595729319650)(spring%E6%80%9D%E6%83%B3.assets/image-20200725152958024.png)]
byName:据名字自动装配
byType:根据类型自动装配
constructor:根据构造器
default:默认情况下,default-autowire属性被设置为none,标示所有的Bean都不使用自动装配,除非Bean上配置了autowire属性。
no:不自动装配
- @Autowire
默认按照类型自动装配
可以使用@Qualifier(“name”)来指定名称
- @Resource
可以按照名称进行自动装配
- @inject
与@Autowire一样但是没有require属性
集合注入
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="department" class="com.LHB.collection.Department">
<property name="name" value="财务部门" />
<!-- 给数组注入值 -->
<property name="empName">
<list>
<value>小米</value>
<value>小明</value>
<value>小四</value>
</list>
</property>
<!-- 给list注入值 可以有相同的多个对象 -->
<property name="empList">
<list>
<ref bean="emp1" />
<ref bean="emp2"/>
</list>
</property>
<!-- 给set注入值 不能有相同的对象 -->
<property name="empSets">
<set>
<ref bean="emp1" />
<ref bean="emp2"/>
</set>
</property>
<!-- 给map注入值 只要map中的key值不一样就可以装配value -->
<property name="empMap">
<map>
<entry key="1" value-ref="emp1" />
<entry key="2" value-ref="emp2" />
</map>
</property>
<!-- 给属性集合配置 -->
<property name="pp">
<props>
<prop key="pp1">hello</prop>
<prop key="pp2">world</prop>
</props>
</property>
</bean>
<bean id="emp1" class="com.LHB.collection.Employee">
<property name="name">
<value>北京</value>
</property>
</bean>
<bean id="emp2" class="com.LHB.collection.Employee">
<property name="name">
<value>天津</value>
</property>
</bean>
</beans>
AOP
AOP(Aspect-OrientedProgramming)面向切面编程,可以理解为OOP的改进和完善,其编程思想是把散布于不同业务但功能相同的代码从业务逻辑中抽取出来,封装成独立的模块,这些独立的模块被称为切面,切面的具体功能方法被称为关注点。在业务逻辑执行过程中,AOP会把分离出来的切面和关注点动态切入到业务流程中,这样做的好处是提高了功能代码的重用性和可维护性。
- AOP的相关术语:
Joinpoint:连接点,可以被拦截到的点
Pointcut:切入点,真正被拦截到的点
Advice:通知、增强(方法层面)
Introduction:引介(类层面的增强)
Target:目标(被增强的对象)
Weaving:织入,讲通知应用到目标的过程
Proxy:代理对象
Aspect:切面
- 通知方法:
前置通知:在要执行的方法之前执行
后置通知:在要执行的方法之后执行
返回通知:在要执行的方法有返回值的时候执行
异常通知:在要执行的方法报异常的时候执行
环绕通知:在通知的内部执行要执行的方法,方法之前的内容就相当于前置通知,以此类推,形成环绕效果
-
配置方法:
- 基本配置流程:
1.配置切入点
2.配置切面
- xml方式
<!-- 配置srping的Ioc,把service对象配置进来--> <bean id="accountService" class="club.bolife.service.impl.AccountServiceImpl"></bean> <!--spring中基于XML的AOP配置步骤 1、把通知Bean也交给spring来管理 2、使用aop:config标签表明开始AOP的配置 3、使用aop:aspect标签表明配置切面 id属性:是给切面提供一个唯一标识 ref属性:是指定通知类bean的Id。 4、在aop:aspect标签的内部使用对应标签来配置通知的类型 我们现在示例是让printLog方法在切入点方法执行之前之前:所以是前置通知 aop:before:表示配置前置通知 method属性:用于指定Logger类中哪个方法是前置通知 pointcut属性:用于指定切入点表达式,该表达式的含义指的是对业务层中哪些方法增强 切入点表达式的写法: 关键字:execution(表达式) 表达式: 访问修饰符 返回值 包名.包名.包名...类名.方法名(参数列表) 标准的表达式写法: public void club.leyvan.service.impl.AccountServiceImpl.saveAccount() 访问修饰符可以省略 void club.bolife.service.impl.AccountServiceImpl.saveAccount() 返回值可以使用通配符,表示任意返回值 * club.bolife.service.impl.AccountServiceImpl.saveAccount() 包名可以使用通配符,表示任意包。但是有几级包,就需要写几个*. * *.*.*.*.AccountServiceImpl.saveAccount()) 包名可以使用..表示当前包及其子包 * *..AccountServiceImpl.saveAccount() 类名和方法名都可以使用*来实现通配 * *..*.*() 参数列表: 可以直接写数据类型: 基本类型直接写名称 int 引用类型写包名.类名的方式 java.lang.String 可以使用通配符表示任意类型,但是必须有参数 可以使用..表示有无参数均可,有参数可以是任意类型 全通配写法: * *..*.*(..) 实际开发中切入点表达式的通常写法: 切到业务层实现类下的所有方法 * club.bolife.service.impl.*.*(..) --> <bean id="aop" class="com.bolife.aop.Logger"/> <!--配置AOP--> <aop:config> <!-- 配置切入点表达式 id属性用于指定表达式的唯一标识。expression属性用于指定表达式内容 此标签写在aop:aspect标签内部只能当前切面使用。 它还可以写在aop:aspect外面,此时就变成了所有切面可用 --> <aop:pointcut id="pt1" expression="execution(* club.bolife.service.impl.*.*(..))"></aop:pointcut> <!--配置切面 --> <aop:aspect id="logAdvice" ref="aop"> <!-- 配置前置通知:在切入点方法执行之前执行 <aop:before method="beforePrintLog" pointcut-ref="pt1" ></aop:before>--> <!-- 配置后置通知:在切入点方法正常执行之后值。它和异常通知永远只能执行一个 <aop:after-returning method="afterReturningPrintLog" pointcut-ref="pt1"></aop:after-returning>--> <!-- 配置异常通知:在切入点方法执行产生异常之后执行。它和后置通知永远只能执行一个 <aop:after-throwing method="afterThrowingPrintLog" pointcut-ref="pt1"></aop:after-throwing>--> <!-- 配置最终通知:无论切入点方法是否正常执行它都会在其后面执行 <aop:after method="afterPrintLog" pointcut-ref="pt1"></aop:after>--> <!-- 配置环绕通知 详细的注释请看Logger类中--> <aop:around method="aroundPringLog" pointcut-ref="pt1"></aop:around> </aop:aspect> </aop:config>
- 注解方式
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 配置spring创建容器时要扫描的包--> <context:component-scan base-package="com.itheima"></context:component-scan> <!-- 配置spring开启注解AOP的支持 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>
//日志切面类 @Aspect public class LogAspects { @Pointcut("execution(public int com.enjoy.cap10.aop.Calculator.*(..))") public void pointCut(){}; //@before代表在目标方法执行前切入, 并指定在哪个方法前切入 @Before("pointCut()") public void logStart(){ System.out.println("除法运行....参数列表是:{}"); } @After("pointCut()") public void logEnd(){ System.out.println("除法结束......"); } @AfterReturning("pointCut()") public void logReturn(){ System.out.println("除法正常返回......运行结果是:{}"); } @AfterThrowing("pointCut()") public void logException(){ System.out.println("运行异常......异常信息是:{}"); } @Around("pointCut()") public Object Around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{ System.out.println("@Arount:执行目标方法之前..."); Object obj = proceedingJoinPoint.proceed();//相当于开始调div地 System.out.println("@Arount:执行目标方法之后..."); return obj; } }
声明式事务管理
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql:///mydb1?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8"/> <property name="username" value="root"/> <property name="password" value="root"/> <!-- 配置初始化大小、最小、最大 --> <property name="initialSize" value="1" /> <property name="minIdle" value="1" /> <property name="maxActive" value="20" /> <!-- 配置获取连接等待超时的时间 --> <property name="maxWait" value="60000" /> <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 --> <property name="timeBetweenEvictionRunsMillis" value="60000" /> <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 --> <property name="minEvictableIdleTimeMillis" value="300000" /> <property name="validationQuery" value="SELECT 'x'" /> <property name="testWhileIdle" value="true" /> <property name="testOnBorrow" value="false" /> <property name="testOnReturn" value="false" /> <!-- 打开PSCache,并且指定每个连接上PSCache的大小 --> <property name="poolPreparedStatements" value="true" /> <property name="maxPoolPreparedStatementPerConnectionSize" value="20" /> <!-- 配置监控统计拦截的filters,去掉后监控界面sql无法统计 --> <property name="filters" value="stat" /> </bean> <bean id="sqlSession" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="mapperLocations" value="classpath:mapper/*Mapper.xml"/> <property name="typeAliasesPackage" value="com.bolife"/> </bean> <!--mapper扫描器--> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!--如果需要扫描多个包,中间使用半角逗号隔开--> <property name="basePackage" value="com.bolife.dao"/> <property name="sqlSessionFactoryBeanName" value="sqlSession"/> </bean> <bean id="transitionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <tx:advice id="advice" transaction-manager="transitionManager"> <tx:attributes> <tx:method name="insert" propagation="REQUIRED" read-only="false"/> <tx:method name="selete" propagation="SUPPORTS"/> </tx:attributes> </tx:advice> <bean id="aop" class="com.bolife.aop.Logger"/> <aop:config> <aop:pointcut id="ponitcut" expression="execution(* com.bolife.service.impl.*.*(..))"/> <aop:advisor advice-ref="advice" pointcut-ref="ponitcut"/> <aop:aspect id="aspect" ref="aop" > <aop:before method="before" pointcut-ref="ponitcut"/> <aop:after method="before" pointcut-ref="ponitcut" /> <aop:after-returning method="returnNum" pointcut-ref="ponitcut" returning="result"></aop:after-returning> <aop:after-throwing method="throwsException" pointcut-ref="ponitcut" throwing="e"/> <!-- <aop:around method="arround" pointcut-ref="ponitcut"/>--> </aop:aspect> </aop:config> </beans>
2.1隔离级别
什么是事务的隔离级别?我们知道,隔离性是事务的四大特性之一,表示多个并发事务之间的数据要相互隔离,隔离级别就是用来描述并发事务之间隔离程度的大小
在并发事务之间如果不考虑隔离性,会引发如下安全性问题:
脏读 :一个事务读到了另一个事务的未提交的数据
不可重复读 :一个事务读到了另一个事务已经提交的 update 的数据导致多次查询结果不一致
幻读 :一个事务读到了另一个事务已经提交的 insert 的数据导致多次查询结果不一致
在 Spring 事务管理中,为我们定义了如下的隔离级别:
ISOLATION_DEFAULT:使用数据库默认的隔离级别
ISOLATION_READ_UNCOMMITTED:最低的隔离级别,允许读取已改变而没有提交的数据,可能会导致脏读、幻读或不可重复读
ISOLATION_READ_COMMITTED:允许读取事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
ISOLATION_REPEATABLE_READ:对同一字段的多次读取结果都是一致的,除非数据事务本身改变,可以阻止脏读和不可重复读,但幻读仍有可能发生
ISOLATION_SERIALIZABLE:最高的隔离级别,完全服从ACID的隔离级别,确保不发生脏读、不可重复读以及幻读,也是最慢的事务隔离级别,因为它通常是通过完全锁定事务相关的数据库表来实现的2.2传播行为
Spring事务传播机制规定了事务方法和事务方法发生嵌套调用时事务如何进行传播,即协调已经有事务标识的方法之间的发生调用时的事务上下文的规则
Spring定义了七种传播行为,这里以方法A和方法B发生嵌套调用时如何传播事务为例说明:
PROPAGATION_REQUIRED:A如果有事务,B将使用该事务;如果A没有事务,B将创建一个新的事务
PROPAGATION_SUPPORTS:A如果有事务,B将使用该事务;如果A没有事务,B将以非事务执行
PROPAGATION_MANDATORY:A如果有事务,B将使用该事务;如果A没有事务,B将抛异常
PROPAGATION_REQUIRES_NEW:A如果有事务,将A的事务挂起,B创建一个新的事务;如果A没有事务,B创建一个新的事务
PROPAGATION_NOT_SUPPORTED:A如果有事务,将A的事务挂起,B将以非事务执行;如果A没有事务,B将以非事务执行
PROPAGATION_NEVER:A如果有事务,B将抛异常;A如果没有事务,B将以非事务执行
PROPAGATION_NESTED:A和B底层采用保存点机制,形成嵌套事务2.3是否只读
如果将事务设置为只读,表示这个事务只读取数据但不更新数据, 这样可以帮助数据库引擎优化事务
2.4事务超时
事务超时就是事务的一个定时器,在特定时间内事务如果没有执行完毕,那么就会自动回滚,而不是一直等待其结束。在 TransactionDefinition 中以 int 的值来表示超时时间,默认值是-1,其单位是秒
2.5回滚规则
回滚规则定义了哪些异常会导致事务回滚而哪些不会。默认情况下,事务只有遇到运行期异常时才会回滚
- 配置XML事务的基本步骤:
- 引入tx约束
- 将dao、service交给spring进行管理
- 配置事务管理器
- 配置事务的通知
- 配置aop
- xml配置详解:
- 事务的属性:
- isolation:配置事务的隔离级别,默认值是DEFAULT
- propagation:配置事务的传播行为,默认值是REQUIRED
- timeout:指定事务的超时时间,默认值是-1,永不超时
- read-only:配置事务是否只读,默认值是false
- rollback-for:用于指定一个异常,当执行产生该异常时,事务回滚,产生其他异常时,事务不回滚,没有默认值,任何异常都回滚。
- no-rollback-for:用于指定一个异常,当执行产生该异常时,事务不回滚,产生其他异常时,事务回滚,没有默认值,任何一场都回滚。
- 事务的属性:
- 事务管理API:
- PlatformtransactionManager:平台事务管理器,接口,Spring用于管理事务的真正对象。
- DataSourceTransactionManager:底层使用JDBC管理事务。
- HibernateTransactionManager:得层使用Hibernate管理事务。
- TransactionDefinition:事务定义信息,用于定义事务的相关信息,隔离级别,超时信息,传播行为,是否只读。
- TransactionStatus:事物的状态,用于记录事务管理的过程中,事务状态的对象。
- PlatformtransactionManager:平台事务管理器,接口,Spring用于管理事务的真正对象。
-
注解方式:
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!--开启事务注解-->
<tx:annotation-driven transaction-manager="transactionManager" />
@Transactional
public void transferMoney(String source, String destination, Long amount) {
transferDao.payMoney(source, amount);
int i = 100/0;
transferDao.collectMoney(destination, amount);
}
表 1. @Transactional 注解的属性信息
属性名 | 说明 |
---|---|
name | 当在配置文件中有多个 TransactionManager , 可以用该属性指定选择哪个事务管理器。 |
propagation | 事务的传播行为,默认值为 REQUIRED。 |
isolation | 事务的隔离度,默认值采用 DEFAULT。 |
timeout | 事务的超时时间,默认值为-1。如果超过该时间限制但事务还没有完成,则自动回滚事务。 |
read-only | 指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true。 |
rollback-for | 用于指定能够触发事务回滚的异常类型,如果有多个异常类型需要指定,各类型之间可以通过逗号分隔。 |
no-rollback- for | 抛出 no-rollback-for 指定的异常类型,不回滚事务。 |