EJB实现原理:就是将原来在客户端实现的代码放至服务端,并依靠RMI进行通信
RMI实现原理:通过java可序列化机制实现分布式计算
补充序列化:就是将java对象转换为字节序列,以便保存到硬盘或进行网络传输。反序列化则是将字节序列转换为java对象。
服务器集群:通过RMI通信,连接不同功能模块的服务器,实现完整的功能
EJB存在问题:重量级框架,运行环境苛刻,代码移植性差
Spring定义:轻量级的javaee解决方案,整合众多优秀设计模式。轻量级对运行环境没有要求、代码移植性高,不需要额外实现接口。整合了工厂、代理、模板、策略等设计模式
设计模式广义概念:面向对象设计中,解决问题的经典代码;狭义概念:23种设计模式,工厂,适配器,装饰器,门面,代理,模板等
工厂设计模式:通过工厂类,创建对象。例如Service service = new ServiceImpl();
工厂设计模式优点:解耦合。耦合:代码间的强关联关系,一方的变动会影响另一方。
Spring本质:工厂ApplicationContext。通过ApplicationContext的getBean方法获取某个对象。
Spring概念:分层的Java SE/EE应用full stack轻量级框架,以IOC(控制反转)和AOP(面向切面)为内核,提供了展现层和持久层以及业务层事务管理等众多的企业应用技术,还可以整合其他第三方框架和类库。轻量级指的是Spting框架的非侵入性,即对象可不必依赖Spring的API类。
Spring核心容器:spring-core,spring-beans,spring-context,spring-expression等
spring-core:基本组成,包括IOC和DI等功能
spring-beans:提供了BeanFactory,工厂模式的微妙实现
spring-context:建立在core和beans的基础之上,以一种类似JNDI的方式去访问对象
spring-expression:提供强大的表达式语言,用于运行时查询和操作对象图
IOC:Inversion of control,本意是将原本在程序中手动创建对象的控制权交由Spring来管理
DI:Dependency Injection,依赖注入,Spring创建对象的过程中,将对象依赖属性通过配置设值给对象
Spring IOC容器:ApplicationContext接口
BeanFactory:spring古老的接口,表示spring ioc容器,生产bean对象的工厂,负责配置、创建和管理bean。注意和FactoryBean的区别。FactoryBean是一个Bean。
IOC管理Bean的原理(工厂设计模式):通过Resource加载配置文件;解析配置文件,得到指定名称的bean;解析bean元素,id作为bean的名字,class用于反射得到bean的实例;调用getBean方法,从容器中返回对象的实例。
spring ioc容器:
BeanFactory,spring 底层接口,只提供了IOC的功能,负责创建、组装、管理Bean;
ApplicationContext:是一个接口,继承了BeanFactory,还提供了AOP继承、国际化处理、时间传播、统一资源价值等功能、
BeanFactory和ApplicationContext的区别:BeanFactory有延迟初始化(懒加载)的特点,创建spring容器,不会立即创建管理的bean对象,而是等到从容器中获取对象时候,才会创建;ApplicationContext在创建spring容器,会将容器中的bean对象立刻初始化。想实现懒加载,可以使用@Lazy注解
bean实例化的四种方式:
无参构造实例化 (
静态工厂方法实例化、(
实例工厂方法实例化、
实现FactoryBean接口实例化
bean的作用域:
指的是bean对象相对于其他bean对象的请求可见范围,语法格式中使用scope标签来表示,例如
scope可取singleton,单例,在spring ioc容器中只有1个bean实例。默认缺省就是singleton。prototype,多例,每次调用getBean返回的都是新的实例,不是同一个对象。
spring工厂何时创建对象:作用域是单例时,工厂创建同时创建对象;作用域是多例,在getBean获取对象时创建对象。通过设置lazy-init=true也可设置获取对象时创建对象。
bean的初始化和销毁:
init-method:对象初始前,立即执行的方法
destory-method:销毁对象执行之前,调用的方法
bean的实例化过程:
bean生命周期:从创建到销毁的整个过程
启动spring容器-创建Bean对象-给bean添加属性-调用Bean对象的初始化前的方法-getBean获取bean对象,调用bean对象某个方法-调用bean对象销毁前的方法-spring容器销毁
DI操作
注入定义:给对象属性赋值,可采用setter方法或调用有参构造方法进行赋值
注入值类型:常量值-value元素,对象-ref元素,集合-集合类型元素
XML自动装配:设置
autowire选项说明:default表示不自动注入,缺省default表示no;no表示不自动注入;byName:按照名字注入;byType:按照类注入;constructor:按照对象构造器上的参数类型注入
setter方法注入:
注入常量(简单类型):
注入对象:
注入集合:
构造器注入:使用
注入常量:
注入对象:
注入集合:
bean元素继承:多个bean元素共同的属性配置抽取到另一个公共的bean元素中,
DI注解
@Auwowired:根据对象类型进行查询,如果存在多个类型的bean,可以配合@Qualifier("bean的id")一起使用。默认按照byType进行匹配,如果发现多个bean,则又按照byName进行匹配,如果还有多个,则抛出异常。用于字段或setter方法上方,
@Resource:先通过bean的id进行查找,找不到再根据类型查找。默认按byName自动注入,如果设置了name属性,只会按照byName查找,如果找不到,则不会根据byType进行查找。
如果设置了type属性,则注入类型相同的对象。找不到或找到多个,都会抛出异常。
@AutoWired和@Resource基本功能一样,都是用于注入对象,开发二选一即可。Auwowired是spring规范;Resource是javaee 规范
XML中配置DI注解解析器:
@Value:用于注入常量数据(简单类型数据)
IOC注解
bean组件版型,以下4个组件功能相同,只是用于标注不同类型的组件
@Component:泛指组件,组件不好归类,使用此注解进行标注。通常用于实体类
@Repository:标注数据访问组件 DAO
@Service: 标注业务组件
@Controller:标注控制层组件
XML中配置IOC注解解析器:
作用域注解:@Scope,默认不写就是单例的。使用示例:@Scope("prototype") 表示多例
初始化注解:@PostConstruct,标注在初始化方法上
销毁注解:@PreDestory,标注在销毁方法上
IOC和DI的关联及区别
IOC:强调的是spring容器创建对象;DI强调的是spring不仅创建对象,还需要为对象设置依赖的数据
@Mapper和@Repository的区别
使用@Mapper,不需要在spring配置文件中设置扫描地址;使用@Repository,则需要在spring配置文件中设置扫描地址
引出问题:在业务层,有些方法需要考虑事务管理,则需要繁琐的try-catch(打开资源,开启事务,保存操作,提交事务,回滚事务),设计上存在责任不分离的问题,即完成业务的同时,还需要考虑事务管理、日志管理、权限管理等问题,而且代码结构重复,维护成本大。
代理设计模式:客户端直接使用代理对象,并不清楚真实对象是谁,和真实对象之间没有直接关系,代理对象在客户端和真实对象之间起到中介的作用;
代理设计模式职责:将不是真实对象做的事情,从真实对象上撇开,划分清晰的责任界限
静态代理概述:程序运行前,已经存在代理类的字节码文件,代理对象和真实对象的关系在运行前就确定了 静态代理代码示例描述:和传统dao、service的区别是多了一个事务处理的类,和一个实现service接口的静态代理类,在此代理类中将事务处理类和真实对象/委托对象(Service接口)作为属性注入,在重写service的方法中将事务处理类和真实对象处理类进行整合加强,最后将如果使用service的方法, 注入代理类进行调用即可。
静态代理优点:业务类只需关注业务本身,保证业务重用性,保护了真实对象
静态代理缺点:每个真实对象都需要一个代理对象;接口增加方法,实现类和代理类都需要实现此方法。动态代理可以解决以上问题。
动态代理概述:程序运行中,通过JVM反射机制生成,所以不存在代理类的字节码文件,代理对象和真实对象在程序运行期确定
动态代理实现方式:有接口,可使用JDK动态代理;无接口,使用CGLIB或Javassist组件
如何动态加载字节码:JVM通过字节码的二进制加载类,因为在程序运行期间,生成(class对应的)二进制数据,将二进制转换成对应的类,即可完成动态创建类
JDK动态代理实现:前提是真实对象必须有接口。依赖于java.lang.reflect.Proxy类,此类是动态代理机制生成所有动态代理类的父类,提供newProxyInstance静态方法来为接口动态生成代理类及其对象;java.lang.reflect.InvocationHandler接口,此接口中的invoke方法负责集中处理动态代理类上的所有方法调用,返回真实方法的返回结果
JDK动态代理实现步骤:在原有dao,service,事务处理器的基础上,编写事务增强处理类实现InvocationHandler接口,重写invoke方法;调用Proxy的静态方法,创建代理对象;处理器中实现增强操作;最后引入实现InvationHandler的类即可进行使用
CGLIB动态代理:如果真实对象不存在接口(只有ServiceImpl,没有Service),JDK动态代理不适用的情况下,可采用此种代理方式。和JDK动态代理的区别是创建代理类(采用Enhancer进行创建)、和增强类实现net.sf.cglib.proxy.InvocationHandler接口
拦截器思想:在某业务action代码执行前后处理的代码,例如某个请求执行前,记录日志
JDK动态代理会拦截所有的public方法,即使接口中增加了新的方法,不用修改代码也会被拦截。JDK动态代理拦截的最小单位是类,如果指向拦截一部分方法,需要在invoke方法中根据方法名进行判断。
CGLIB可以生成委托类的子类,并重写父类非final修饰的方法;要求类不能是final修饰,拦截的方法是非final,非private,非static的;类中的所有方法都会被处理。
JDK动态代理和CGLIB动态代理性能选择:
性能从高到低:Javassit>CGLIB>JDK
接口创建代理优于实现类创建代理,更加符合松耦合的特性。如果委托对象实现了接口,优先使用JDK动态代理。否则使用Javassit和CGLIB。
横切面关注点:零散存在于业务方法中的功能代理,例如日志记录、权限检查、事务控制等。横切面关注点不属于业务范围,应从业务范围中剥离出来,解决此问题可使用AOP思想。
AOP:不修改源代码做到功能增强。
横切关注点放到某个模块中,称之为切面。那么每个切面都可以影响业务的某个功能,切面目的即实现功能增强。
AOP相关术语:
Joinpoint:连接点,被增强的方法位置
Pointcut:切入点,连接点的集合
Advice:增强功能,什么时间做什么样的增强,可分为前置增强、后置、异常、最终、环绕
Aspect:切面,Pointcut+Advice,那些地方,什么时间,做什么增强
Target:目标对象,被代理的对象
Weaving:织入,将Advice加到Target上,创建出Proxy对象的过程
Proxy:某个类被AOP织入增强后,产生的代理类
AOP规范被AOP联盟推出后,面向切面编程框架AspectJ应运而生,同时也确定了如何表示切入点
Aspect切入点语法:execution(要增强的那些类下的那些方法);
切入点表达式中的通配符:*匹配任何一个单词;..可用于全限定名中和方法参数中,分别表示包及子包和0到N个参数
execution常见写法:(对应表达式从后往前看)
execution(* cn.sunny.wms.service.*.*(..)) : 表示对service包下的所有类所有方法做增强
execution(* cn.sunny.wms.service.*Serice.*(..)) : 表示对service包下所有以Service为后缀的类的所有方法做增强
execution(* cn.sunny..service.*Service.*(..) : 表示对sunny包及其子包下的service包中的以Service为后缀的类的所有方法做增强
AOP开发:maven中导入 org.aspectj.aspectjweaver 依赖
注解配置AOP:事务管理器类上添加注解@Aspect,标识配置一个切面;事务类中编写public void txPoint实例方法,方法上添加注解@PointCut('配置需要切面处理的方法');
@Before("txPoint()"); 前置增强。常用于权限控制、记录调用日志
@AfterReturning("txPoint()");后置增强。常用于统计结果分析数据
@AfterThrowing(value = "txPoint()", throwing = "exception");异常增强,常用于 通过日志记录方法异常信息
@After("txPoint()");最终增强,常用于释放资源
@Around("txPoint()") 可以代替@Before、@AfterReturning、@AfterThrowing、@After。环绕增强,常用于缓存、性能日志、权限、事务管理
@annotation
概述:标识标注了某个注解的所有方法;
语法:@annotation("接口的完整路径")
示例:声明注解@Interface NeedTest;定义接口Waiter;编写接口实现类,实现类的某些方法上可添加注解;编写切面类(类上添加@Aspect注解即可),类中定义方法,
方法上添加增强方法注解(例如@AfterReturning("@annotation("com.xxx.xxx.NeedTest")"));然后调用实现类上使用NeedTest注解的方法,即可看到增强效果。
@execution():
概述:最常用的切点函数。
语法:execution(<修饰符模式>?<返回类型模式><方法名模式>(<参数模式>)<异常模式>)。返回类型模式、方法名模式、参数模式必填,其余可选
通配符说明:* 表示任意类型,..表示任意入参,..To表示任意To为后缀的方法
定义切点的常用方式
通过类包定义切换:.*表示包下所有的类;..*表示包、子孙包下所有的类
例如:execution(* com.abc.*(..)) 表示匹配abc下所有类的所有方法
execution(* com..*Dao.find*(..)) 匹配com任意包下Dao后缀所有类,且前缀为find的所有方法
通过类定义切点:execution(* com.xxx.xxx.Cleaner.*(..)) 匹配Cleaner接口的所有接口,也包括实现类中非重写的方法
通过方法定义切点:execution(* *To(..)) 匹配目标类以To为后缀的方法;execution(public * *(..)) 匹配目标类所有public方法
通过方法入参定义切点:方法入参定义切点部分相对负载,可使用*和..通配符。*表示任意类型的参数,..表示任意类型参数且参数数量不限
例如: execution(* joke(String,int))匹配joke(String,int)方法
execution(* joke(String,*))匹配joke(String,*)方法,第二个参数为任意类型
execution(* joke(String,..))匹配joke方法,第一个参数为String,后面为任意类型参数且数量不限制
execution(* joke(Object+))匹配joke方法,唯一参数,是Object或该类的子类
execution(* joke(Object)) 匹配joke方法,唯一参数,类型必须是Object
args和@args
概述:args函数的入参是类名,@args的入参必须是注解类的类名,虽然args允许在类名后使用"+"通配符,但没有意义
args():此函数接受类名,表示目标类方法指定入参是指定类(包含子类)时,切点匹配
@args :此函数接受注解类类名,当方法的运行时入参标注了指定的注解时,进入切点。
例如:方法为fun(T1 t),切面的切点定位为@args(M),T2类标注了M注解,则调用fun方法,传入T2或T3时,会匹配切点
方法为fun(T1 t),切面的切点定位为@args(M),使用注解M的类是T0,当调用方法传入T1、T2、T3、T4的实例时,不会匹配节点
★★注意:在类的继承中,处于方法签名中入参类型在继承树的位置,称为入参类型点,切入点注解类在继承树的位置,称为注解点。
如果注解点高于入参类型点,则该目标方法不可能匹配到切入点。如果注解点低于入参类型点,则注解点所在类及其子类作为方法入参时,该方法匹配切点@args(M)
within()
概述:通过类匹配模式串声明切点,连接点针对目标类而言,这一点和execution相同,不同的是,within连接点最小只能是类,而execution所指的连接点可以大到包,小到方法入参。所以某种意义上讲,execution是包括within
语法:within(<类匹配模式>)。例如within(
举例
within(
within(
within(<@com.sss.mark *>)匹配com.sss及其子包下带有@com.sss.mark注解的任何类(接口不行)的任何方法
使用示例:@AfterReturning("within("com.sss.Naviter")")
@within()和@target
概述:只接受注解名作为入参。@within(M)匹配标注了@M的类及其子孙类。@Target(M)匹配标注了@M的类
使用示例:@Around(@target("com.sss.interface.mark")),@within(@target("com.sss.interface.mark"))
注意:如果标注了@M注解的是1个接口,则实现此接口的类并不匹配@within(M)
target和this
概述:target函数是通过判断目标类是否按照类型匹配指定类来决定连接点是否匹配,用于匹配当前目标对象类型的执行方法,目标对象是类型匹配,不包括引入接口类型匹配
this函数是通过判断代理类是否按照类型匹配指定类来决定是否和切点匹配。用于匹配当前AOP代理对象类型的执行方法,包括引入接口类型。
this中的表达式必须是全限定名,不包含通配符。一般情况下,this和target等效,二者区别体现在通过引切界面产生代理对象的具体表现。
使用示例:
@AfterReturning("target("com.sss.NavInterface")") 匹配NavInterface接口所有实现类中的所有方法,包括接口中未声明的方法
@AfterReturning("this("com.sss.NauInterface") ") 匹配运行期任何对象为NauInterface的bean中
一.spring对持久层技术支持:spring操作持久层很简单(只需定义SQL和处理结果集即可),更强大在于提供了对事务的支持
1.spring支持的持久层技术
spring并没有提供持久层框架,但是提供了和持久层技术无缝整合的API。
如:org.springframework.jdbc.core.jdbcTemplete(spring提供对应模板类),org.springframework.jdbc.core.support.JdbcDaoSupport(spring提供的对应DAO基类)
2.Spring JDBC
2.1 JdbcTemplate.class
使用方法和org.apache.commons.dbutils.QueryRunner基本一致。构造方法参数传递数据源(javax.sql.DataSource).
常用API方法:update[执行insert、update、delete语句]、queryForObject[查询单个对象]、query[查询多个对象]
org.springframework.jdbc.core.RowMapper接口的实现类org.springframework.jdbc.core.BeanPropertyRowMapper是对查询结果集的封装,支持单个对象或集合
2.2 Spring JDBC CRUD操作
增删改执行update方法,查询执行query方法
迭代结果集并设置属性:
通过 RowMapper(接口)的 mapRow(ResultSet set,int vat)方法,最终需要依靠实现类 BeanPropertyRowMapper
创建实体对象并添加至List中:RowMapperResultSetExtractor 的 extractData(ResultSet set)方法,后续 while(rs.next)来进行添加
2.3 JdbcDaoSupport
定义了JdbcTemplete属性并提供了set和get方法,自定义的dao层直接继承即可,无需重复声明。但是xml文件中依然需要配置dataSource属性
二、spring事务控制
2.1 事务引出经典问题:账户A给账户B转账
2.2 事务回顾
事务:在数据库中,事务式一组逻辑操作,不论成功失败都是作为一个整体,要么全部成功,要么全部失败
事务特定
原子性:一个事务内的操作,要么全部成功,要么全部失败。例如经典银行转账。
一致性:和原子性是一件事情,区别在于描述角度不同。原子性从事务操作角度描述,一致性从数据角度描述。例如A账户有100块,B账户有100块,A转给B10块,最终数据状态应该为A90,B110,不会出现A90,B100或A100,B110。
隔离性:多个事务之间执行互不干扰。例如事务A给某用户增加100元,但此时事务还未提交,事务B读取某用户账号已经添加了100元,即脏读,解决脏读办法是建立事务之间的隔离机制。
持久性:事务一旦提交,变化生效,即便数据库宕机,恢复之后,也是事务执行之后的状态,不会回滚到事务执行之前。
编程式事务:事务控制逻辑(增强逻辑)和业务逻辑混合在一起
声明式事务:通过配置,不侵犯原有业务逻辑代码基础上实现事务控制。通过AOP进行实现
spring事务是spring提供的事务管理方式, 代码中无需手动控制事务
事务并发问题:
脏读:事务2读取了事务1修改还未提交的数据。例如某财务给员工A工资多增加了1000元,但是没有提交事务,事务2查询到了事务A修改的数据,后续事务1发现问题,将操作回滚
幻读:事务1查询数据,但事务还没结束,事务2增加或删除了一些数据并提交事务,后面事务1再次进行查询,发现数据添加或减少了。出现在insert和delete
不可重复读:事务1查询数据,但事务还未结束,事务2修改了数据并提交事务,后面事务1再次查询,发现数据和第一次查询的对不上。出现在update时
事务隔离级别:解决事务并发问题
由低到高:Read_uncommitted->Read_committed-> Repeatable_Read->Serializable
Read_uncommitted:读未提交 无任何事务并发控制,效率高,但风险也高
Read_committed:读已提交 只能读取到其他事务提交的数据。可以解决脏读,但是无法处理不可重复读与幻读
Repeatable_Read:可重复读 确保多个事务读取的数据一致。可解决脏读和不可重复读。
Serializable:串行化 所有事务一个一个执行,不会出现并发问题,但是效率较低
oracle默认读已提交 Read_committeed,mysql默认 可重复读 Repeatable_Read
查看当前隔离级别:select @@TX_ISOLATION;
涉及隔离级别:SET TRANSACTION ISOLATION LEVEL 4个级别之一(注意全小写,多个单词采用空格分隔)
事务隔离级别越高,数据库并发执行性能会越差,项目中考虑并发性能一般使用读已提交(Read committed),可以避免脏读,但是不可重复读和幻读无法避免,更多情况下采用乐观锁和悲观锁解决
2.3 spring 对事务管理的API(底层AOP-动态代理)
spring支持编程式事务和声明式事务。声明式事务更简介,代码可读性更强。通过注解或XML配置来管理事务
声明式事务是建立在AOP基础上的,本质是对方法前后进行拦截,在目标方法开始前创建或加入1个事务,执行完毕之后根据执行情况提交或回滚事务
spring 事务管理主要有3个接口
TransactionDefinition spring中,事务是通过TransactionDefinition来定义的,此接口包含事务属性相关的方法。
PlatformTransactionManager,此接口的getTransaction方法返回TransactionStatus接口,此接口代表新的或已存在的事务
TransactionStatus 此接口封装一些控制事务查询和执行的方法
使用spring管理事务,需告知spring使用的是那个事务管理器
常用事务管理器:DataSourceTransactionManager(支持JDBC和Mybatis)、HibernateTransactionManager(支持Hibernate)
spring事务回滚规则
在事务上下文内抛出异常,来进行事务回滚。spring事务管理器会捕捉任何未处理的异常,然后依据规则决定是否回滚抛出异常的事务。
默认配置,只有抛出异常是RuntimeException的子类,才会导致事务回滚,抛出checked异常不会进行回滚
可以自定义配置,那些异常抛出进行事务回滚,那些异常抛出不进行事务回滚
也可以编程性的通过setRollbackOnly方法来指示一个事务必须进行回滚,在调用完setRollbackOnly方法后,可以执行的唯一操作就是回滚
2.4 事务传播规则
事务传播行为是指,开始当前事务之前,一个事务上下文已存在,此时有若干选项可以指定一个事务方法的执行行为。
在TransactionDefinition接口中定义了以下几个传播规则的常量
遵从当前事务
TransactionDefinition.PROPAGATION_REQUIRED = 0 默认值,存在当前事务,则加入改事务,否则创建新事务
TransactionDefinition.PROPAGATION_SUPPORTS = 1 存在当前事务,则加入改事务,否则以非事务的方式运行
TransactionDefinition.PROPAGATION_MANDATORY = 2 存在当前事务,则加入改事务,否则抛出异常
不遵从当前事务
TransactionDefinition.PROPAGATION_REQUIRES_NEW = 3 无论是否存在当前事务,都创建新事务
TransactionDefinition.PROPAGATION_NOT_SUPPORTED = 4 以非事务当时运行,如果存在当前事务,则将当前事务挂起
TransactionDefinition.PROPAGATION_NEVER = 5 以非事务方式运行,如果存在当前事务,则抛出异常
寄生事务
TransactionDefinition.PROPAGATION_NESTED = 6 如果当前存在事务,则创建1个事务作为当前事务的嵌套事务运行,否则改取值等价于TransactionDefinition.PROPAGATION_REQUIRED
事务超时
一个事务允许执行的最长时间,超过了改时间但事务还未执行完毕,则自动回滚事务。在TransactionDefinition中使用TIMEOUT_DEFAULT表示默认值,为-1,单位为秒
事务类型
本地事务:普通事务,保证单台数据库上的ACID,限定1台数据库
分布式事务:涉及多个数据库源的事务,跨越多台同类或异类数据库的事务(由每台数据库的本地事务组成),旨在保证这些本地事务所有的ACID,事务可跨越多台数据库
JDBC事务:数据库事务类型中的本地事务,通过Connection对象的控制来管理事务
JTA事务(Java Transaction API):JAVA EE数据库事务规范,JTA只提供事务管理接口,具体实现由各大服务器厂商实现。JTA事务比JDBC事务更强大,支持分布式事务
编程式事务:通过编写代码管理事务
声明式事务:通过注解或配置XML管理事务
一.基于注解配置声明式事务
使用方式:在service中,使用@Transactional注解。使用此注解,避免异常通过try catch导致异常被吃掉,最好就是直接向上抛出异常即可,无需try catck处理
有以下属性
value(String) 可选的限定描述符,指定使用的事务管理器
propagation(enum Propagation) 可选的事务传播行为设置
isolation(enum Isolation) 可选的事务隔离级别设置
readOnly(boolean) 读写或只读事务,默认false读写。为true表示只读,如果事务中有增删改会报错。为false表示读写,用作增删改。
timeout(int) 事务超时时间设置
rollbackFor ( class对象数组,必须继承自Throwable ).导致事务回滚的异常类数组。如果抛出非运行时异常,则事务无法回滚,因此需要此属性指定抛出的异常
rollbackForClassName(类名数组,必须继承自Throwable),导致事务回滚的异常类名字数组
noRollbackFor(class对象数组,必须继承自Throwable) 不会导致事务回滚的异常类数组
noRollbackForClassName(类名数组,必须继承Throwable) 不会导致事务回滚的异常类名字数组
此注解可作用于在接口、接口方法、类、类方法上。
作用在类上,该类中所有Public修饰的方法都具备该类型的事务属性。
不建议此注解添加在接口及接口方法上,因为只有在使用基于接口的代理时才会生效。
如果此注解在类上有,也可以在方法级别上添加此注解来覆盖类级别的定义。例如类上添加@Transactional注解,同一个类中的某个方法添加@Transactional(readOnly = true) ,则此方法会设置为只读
@Transactional只会被应用到public方法上,如果是private、default、protected修饰的方法,不会触发事务,也不会抛出异常。这是由spring aop本质决定的
二.基于纯注解配置声明式事务(实际开发使用较多,效率较高)
@Configuration 只可作用在类上,指定当前类是配置类,好比是applicationContext.xml
@ComponentScan 指定spring创建容器时要扫描的包
@Bean("xxx") 把当前方法的返回值作为Bean对象存入spring的ioc容器中
@Import(Xxx.class) 导入其他的配置类,被引入的配置类可以不加@Configuration注解
@ PropertySource ("classpath:db.properties") 指定properties文件的位置
@Value 对变量成员赋值
@Qualifier 可使用在方法上,表明对应的形参注入的对象类型
@EnableTransactionManagement 开启事务注解
@Transactional 控制事务的注解,一般用于Service实现类上或指定方法
事务:当使用spring-boot-starter-jdbc或spring-boot-starter-data-jpa依赖时,框架会自动默认分别注入DataSourceTransactionManager或JpaTransactionManager,因此想使用事务首先使用注解@EnableTransactionManagement开启事务,其次在访问数据库交互的Service类或方法上添加@Transactional注解即可
AOP:面向切面,一般用于日志分析和事务操作,类似拦截图。
常用注解:@Aspect表示被定义类是切面类;@Pointcut定义切入点;@Before前置增强;@After后置增强;@AfterReturning方法执行返回值调用;@Around环绕增强
mybatis-spring是将mybatis代码无缝整合至spring中。存在mybatis-spring依赖。(org.mybatis.mybatis-spring)
spring整合mybatis目的:将mybatis核心配置文件(SqlSessionFactory和Mapper)的内容交由spring来处理。
说明:
要和spring一起使用mybatis,需要在spring上下文中至少定义SqlSessionFactory和至少一个数据映射类。
使用SqlSessionFactoryBean创建SqlSessionFactory,SqlSessionFactoryBean需要dataSource数据源作为属性
传统mybatis中使用SqlSessionFactoryBuilder创建SqlSessionFactory,mybatis-spring中使用SqlSessionFactoryBean创建SqlSessionFactory
SqlSessionFactory唯一必要属性:DataSource
SqlSessionFactoryBean会创建它自由的环境配置,并按照要求设置自定义环境的值
SqlSessionTelplate是mybatis-spring的核心,实现了SqlSession,因此可以无缝替代mybstis代码中的SqlSession
构建SqlSessionTemplate,使用SqlSessionFactory作为构造参数来创建(constructor-arg index="0" ref="sqlSessionFactory")
最后将SqlSessionTemplate注入到实际的Mapper中即可进行使用
整合实现方式一:
spring配置文件;配置数据源;配置SqlSessionFactory,关联DataSource;配置SqlSessionTenplete,关联SqlSessionFactory;自定义数据库交互Mapper私有化SqlSessionTemplete;注入Mapper
整合实现方式二:Mapper继承SqlSessionDaoSupport;方法中只需通过getInstance()即可得到SqlSessionTemplete,之后可用于执行sql;XML文件中Mapper注入SqlSessionFactory
整合实现方式三:前2种方式,都需要手动创建Mapper。此方式是用spring来创建mapper代理对象。配置MapperFactoryBean(注入SqlSessionFactory和表明给那个接口创建代理对象),由spring创建mapper代理对象;通过DI注入方式获取到Mapper对象,进行CRUD.
给容器注册组件
自定义类注入标注注解:@Component、@Reposirory、@Service、@Controller
导入第三方包注解:@Bean
快速给容器导入组件:@Import;[Import(类名.class):自动注册组件]
满足条件注入:@Conditional({conditional})
@Scope:singleton默认单例,IOC容器启动时会创建对象放进IOC容器中,后续使用都是从容器中获取;prorotype多例 IOC容器启动并不会创建对象放进容器中,而是每次获取才会调用方法创建对象
@Configuration:
将一个类标记为spring的配置类,相当于applicationContext.xml;
配置类可使用@Bean public Entity entity(){return new Entity()};向容器中注入Bean(类型为返回值类型,id为方法名);
可通过AnnotationConfigApplicationContext(配置类.class)来加载配置类,返回值是ApplicationContext,最后通过getBean即可获取注入的对象
@Bean
相当于配置文件中的
含义:被标注的方法名为Bean的id;方法返回值类型为Bean的class类型;除了使用方法名作为id,也可@Bean(name = {"自定义id","自定义id2"})来指定多个id名,然后通过name获取对象
配置initMethod、destoryMethod的方法
单例:在容器启动(加载配置类)时创建对象,容器启动先创建对象,然后调用init方法
多例:获取Bean时创建对象,容器创建完成后,才创建对象完成Init
方式一:在Bean的属性 initMethod、destoryMethod来指定初始化方法、销毁方法
方式二:通过@PostConstrut、@PreDestory来指定初始化和销毁方法
@Bean依赖注入的方式
相当于applicationContext.xml中各个bean之间通过property标签进行赋值ref等
方式一:通过内部类似bean的方式(setOtherBean( new XXBean()))。例如aBean.setOtherBean(new dBean());
方式二:调用需要注入的bean的方法名即可(setOtherBean(xxBean))。例如aBean.setOtherBean(otherBean)
方式三:将需要依赖的bean,放至参数列表,即可自动注入。
@ComponentScan
可完成spring组件的自动扫描,默认情况下扫描被标注的类对应的包及其子包中所有的类。也可通过basePackages自行指定扫描的范围。常用于修饰配置类。
相关属性(组件注册过滤条件);
value 指定要扫描的包
excludeFilters=Filter[];指定扫描包时,按照那些规则排除那些组件
includeFilters=Filter{};指定扫描包时,包含那些组件。需将 useDefaultFilters 设置为false
@Scope
用于表示bean的范围是单例还是多例
singleton单例:IOC容器启动就会创建对象,放至容器中
prototype多例:IOC容器启动不会创建对象,每次获取bean的时候,才会创建放到容器中
@Lazy 用于设置第一次使用bean的时候创建,不使用不创建,即便是单例模式,也是在getBean时才会创建
@PostConstruct 初始化相关方法,等同于实现InitializingBean接口。由JavaEE规范提供
@PreDestory 销毁方法 等同于实现DisposableBean接口 。由JavaEE规范提供
@Condition
按照条件进行判断,满足条件给容器注入Bean,如果不满足条件则不会注入至容器中
放在配置类上,当前配置类满足条件,类中所有的@Bean才会生效
放在@Bean方法上,只对当前方法有效
实现步骤:编写MyConditional实现implements接口,重写matches方法;随后在需要判断的方法或类上使用注解@Condition({自定义Condition实现类.class}).如果满足,则方法或类注入的bean才会生效。
@Import 快速导入其他的配置类;也可导入一个需要注册的组件(类),ID默认是全类名。
@ImportResource 引入xml配置文件,使xml和javaconfig共同使用。例如@ImportResource(classpath:applicationContext.xml)上
XML方式,recource文件夹下创建.xml文件,然后采用ClassPathXmlApplicationContext来获取XML文件,最后通过getBean方式获取。方式较老,很少使用
@Configuration @Bean 配置方式
@ComponentScan @Component、@Repository、@Service、@Controller
@Conditional 按照条件向spring 容器中注入bean
@Import 快速导入组件
使用FactoryBean注册组件 工厂Bean。第三方框架和spring整合,都是通过工厂bean进行实现。实现方式:定义类实现FactoryBean
@Value 注入基本类型和String类型的数据,将类中字段属性和配置文件中的数据进行匹配绑定。一般使用${}获取配置文件中的值
@PropertySource:加载资源文件
@Autowired、@Qualifier、@Primary。Spring的注解
@Autowired 自动注入 默认优先按照类型取容器中寻找对应的组件;如果有多个属性相同的bean,再根据属性名称作为bean的id去容器中查找。
@Qualifier("bean的ID")指定需要注入的bean组件id
需要将属性赋值好,否则会报错。
@Autowired(required=false)如果没有找到对应的bean,也不会报错
@Primary 有多个相同类型的bean,优先选择此注解标注的bean
@Resource、@Inject
@Resource 默认按照组件名称注入
@Inject 需要javax.inject的包,功能和@Autowired效果一致
注意:以上3中自动装配的注解,使用@Autowired足以满足需求
@RequiredArgsConstructor
在controller和service中会有很多的@Autowired,看起来很乱,lombok提供的@RequiredArgsConstructor注解,写在类上可以代替@Autowired注解。注意mapper和service需要使用final修饰或者使用@notnull注解。(使用final修饰较好)
@Profile和@ActiveProfiles
@Profile 可根据不同的运行环境,动态激活和切换一系列组件的功能
@ActiveProfile 可指定运行环境
例子:有两个配置文件,dev和prod,编写2个配置类,分别使用@Profile指向不同的配置文件,最终的配置类中将2个配置类都导入进去,最终运行添加注解@ActiveProfile("dev或prod")来灵活指定那种运行环境