文章目录
一、前言
1.1原生web开发中存在哪些问题?
· 传统web开发存在硬编码所造成的过度程序耦合(例如:Service中作为属性Dao对象)。
· 部分JavaEE API较为复杂,使用效率低(例如:JDBC开发步骤)。
· 侵入性强,移植性差(例如:DAO实现的更换,从Connection到SqlSession)。
二、Spring框架
2.1概念
· Spring是一个项目管理框架,同时也是一套Java EE解决方案。
· Spring是众多优秀设计模式的组合(工厂、单例、代理、适配器、包装器、观察者、模板、策略)。
· Spring 并未替代现有框架产品,而是将众多框架进行有机整合,简化企业开发,俗称“胶水框架”‘。
2.2访问与下载
官方网站:http://spring.io/
下载地址:https://repo.spring.io/release/org/springframework/spring
三、Spring架构组成
Spring架构由诸多模块组成,可分类为:
1、核心技术:**依赖注入**、事件、资源、验证、数据绑定、类型转换、SpEL、**AOP**。
2、测试:;模拟对象、TestContext框架,Spring MVC测试、WebTestClient。
3、数据访问:事务,DAO支持,JDBC、ORM、封送XML
4、Spring MVC和Spring web Flux Web框架。
5、集成:远程处理,JMS,JCA,JMX,电子邮件,任务,调度,缓存。
6、语言:Kotlin,Groovy,动态语言。
四、自定义工厂
4.1配置文件(bean.properties)
1、userDao=cn.kgc.dao.UserDaoImpl
2、userService=cn.kgc.service.UserServiceImpl
4.2工厂类
//工厂类 1、加载配置文件 2、生产配置中记录的对应对象
public class MyFactory {
private Properties properties = new Properties();
public MyFactory(){}
public MyFactory(String config) throws IOException {
InputStream resourceAsStream = MyFactory.class.getResourceAsStream(config);
properties.load(resourceAsStream);//读取配置文件 properties
}
//获取对象
public Object getBean(String name) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
//1、通过那么,获取对应类路径
String classPath = properties.getProperty(name);
if(classPath!=null){
Class claz = null;
//2、反射 构建对象
Class claz = Class.forName(classPath);
return claz.newInstance();
}
return null;
}
}
五、Spring环境搭建
5.1 pom.xml中引入Spring常用依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>spring_demo_01</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!--Spring依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
</dependencies>
</project>
5.2创建Spring配置文件
命名无限制,约定俗成命名由:Spring-context.xml、applicationContext.xml、beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<!--要工厂生产的对象-->
<bean id="userDao" class="cn.kgc.dao.UserDaoImpl"></bean>
<bean id="userService" class="cn.kgc.service.UserServiceImpl"></bean>
</beans>
5.3 Spring工厂
public class SpringFactory {
@Test
public void testSpringFactory(){
//启动工厂
ApplicationContext context = new ClassPathXmlApplicationContext("/spring-context.xml");
//获取对象,相当于我们之前硬编码的创建对象,现在交由Spring处理
UserDao userDao = (UserDao) context.getBean("userDao");
UserService userService = (UserService)context.getBean("userService");
userDao.delUser(1);
userService.delUser(2);
}
}
六、依赖与配置文件详解
Spring框架包含多个模块,每个模块各司其职,可结合需求引入相关依赖jar包实现功能。
6.1 Spring依赖关系
6.2 schema
配置文件中的顶级标签中包含了语义化标签的相关信息
·xmlns:语义化标签所在的命名空间。
xmlns:xsi:XMLSchema-instance标签遵循Schema标签标准。
xsi:schemaLocation:xsd文件位置,用以描述标签语义、属性、取值范围等。
七、IoC(Inversion of Control)控制反转【重点】
**Inversion of Control:**控制反转
反转了依赖关系的能满足方式,由之前的自己创建依赖对象,变为由工厂推送。(变主动为被动,即反转)
解决了具有依赖关系的组件之间的强耦合,是的项目形态更加稳健
7.1 项目中的强耦合问题
public class UserDaoImpl implements UserDao {
public void delUser(Integer id) {
System.out.println("删除id为"+id+"的user in dao!");
}
}
public class UserServiceImpl implements UserService {
//强耦合了UserDaoImpl,使得UserServiceImpl变得不健壮
private UserDao userDao = new UserDaoImpl();
public void delUser(Integer id) {
userDao.delUser(id);
}
}
7.2解决方案
//不引用任何一个具体的组件(实现类),在需要其他组件的位置预留存取值入口(set/get)
public class UserServiceImpl implements UserService {
//不再耦合任何Dao实现,消除不稳健因素
private UserDao userDao;
//为userDao定义set/get,允许userDao属性接受spring赋值
//Getters and Setters
public void delUser(Integer id) {
userDao.delUser(id);
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
<bean id="userDao" class="cn.kgc.dao.UserDaoImpl"></bean>
<bean id="userService" class="cn.kgc.service.UserServiceImpl">
<!--userDao属性赋值,值是id为userDao的bean-->
<property name="userDao" ref="userDao"/>
</bean>
如果此时需要更换UserDao实现类,则UserServiceImpl不用任何改动!,则此时的UserServiceImpl组件变得更加稳健!
八、DI(Dependency Injection)依赖注入【重点】
8.1概念
在Spring创建对象的同时,为其属性赋值,称之为依赖注入。
8.2 set注入
创建对象时,Spring工厂会通过Set方法为对象的属性赋值。
8.2.1 定义目标Bean类型
public class User implements Serializable {
private Integer id;
private String name;
private String password;
private Status status;//自建类型
private Date bornDate;
private String[] hobbys;
private Set<String> phones;
private List<String> names;
private Map<String,String> countries;
private Properties files;
//Getter/Setter
public class Status implements Serializable {
private Integer id;
private String status;
//Getter/Setter
}
8.2.2 基本类型+字符串类型+日期类型
<!--简单:JDK8种基本数据类型-->
<property name="id" value="10"/>
<property name="name" value="liwang"/>
<property name="password" value="123456"/>
<property name="bornDate" value="2020/12/11"/>
8.2.3 容器类型
<!--数组-->
<property name="hobbys">
<array>
<value>football</value>
<value>basketball</value>
</array>
</property>
<!--集合-->
<property name="phones">
<set>
<value>1111111</value>
<value>2222222</value>
</set>
</property>
<property name="names">
<list>
<value>tom</value>
<value>jack</value>
</list>
</property>
<property name="countries">
<map>
<entry key="zh" value="china"/>
<entry key="en" value="english"/>
</map>
</property>
<property name="files">
<props>
<prop key="url">jdbc:mysql:XXX</prop>
<prop key="username">root</prop>
</props>
</property>
8.2.4自建类型
<!--次要bean,被作为属性-->
<bean id="sta" class="cn.kgc.pojo.Status">
<property name="id" value="1"/>
<property name="status" value="管理员"/>
</bean>
<!--主要bean,操作的主体-->
<bean id="user" class="cn.kgc.pojo.User">
<property name="status" ref="sta"/>
</bean>
<!--次要bean,被作为属性-->
<bean id="userDao" class="cn.kgc.dao.UserDaoImpl"></bean>
<!--主要bean,操作的主体-->
<bean id="userService" class="cn.kgc.service.UserServiceImpl">
<!--userDao属性赋值,值是id为userDao的bean
注入:属性值 依赖关系
-->
<property name="userDao" ref="userDao"/>
</bean>
8.3 构造注入【了解】
创建对象时,spring工厂会通过构造方法为对象的属性赋值
8.3.1 定义目标Bean类型
public class Student {
private Integer id;
private String name;
private String sex;
private Integer age;
//Constructors
public Student(Integer id, String name, String sex, Integer age) {
this.id = id;
this.name = name;
this.sex = sex;
this.age = age;
}
}
8.3.2 注入
<!--构造注入-->
<bean id="student" class="cn.kgc.pojo.Student">
<constructor-arg name="id" value="1"/>
<constructor-arg name="name" value="liwang"/>
<constructor-arg name="sex" value="男"/>
<constructor-arg name="age" value="22"/>
</bean>
8.4 自动注入【了解】
autowire:默认是byType
<!--注解注入 byName-->
<bean id="userDao" class="cn.kgc.dao.UserDaoImpl"/>
<bean id="userService" class="cn.kgc.service.UserServiceImpl" autowire="byName"/>
public class UserServiceImpl implements UserService {
//强耦合了UserDaoImpl,使得UserServiceImpl变得不健壮
private UserDao userDao;
public void delUser(Integer id) {
System.out.println("service中");
userDao.delUser(id);
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
}
九、Bean细节
9.1控制简单对象的单例、多例模式
配置<bean scope="singleton | prototype"/>
``xml
``` 注意:需要根据场景决定对象的单例、多例模式。 可以共用:Service、Dao、SqlSessionFactory(或者是所有的工厂) 不可共用:Connection、SqlSession、ShoppingCart。9.2 FactoryBean创建复杂对象【了解】
作用:让Spring可以创建复杂对象,或者无法直接通过反射创建的对象
9.2.1 实现factoryBean接口
//需要添加mysql的依赖
public class MyConnectionFactoryBean implements FactoryBean<Connection> {
public Connection getObject() throws Exception {
Class.forName("com.mysql.jdbc.Driver");
return DriverManager.getConnection("jdbc:mysql://localhost:3306/member","root","root");
}
public Class<?> getObjectType() {
return Connection.class;
}
public boolean isSingleton() {
return false;
}
}
9.2.2配置spring-context.xml
<!--Connection
SqlSessionFactory
复杂对象
FactoryBean
当从工厂索要一个bean时,如果是Factory,
实际返回的是工厂Bean的getObject方法的返回值 如果要返回工厂就要在getBean("&conn")
-->
<bean id="conn" class="cn.kgc.factorybean.MyConnectionFactoryBean"/>
十、Spring工厂特性
10.1 饿汉模式创造优势
工厂创建之后,会将Spring配置文件中的所有对象都创建完成(饿汉式)。
提高程序运行效率。避免多次IO,减少对象创建时间。(概念接近连接池,一次性创建好,使用时直接获取)
10.2 生命周期方法
1、自定义初始化方法:添加“init-method”属性,Spring则会在创建对象之后,调用此方法。
2、自定义销毁方法:添加“destroy-method”属性,spring则会在销毁对象之前,调用此方法。
3、销毁:工厂的close()方法被调用之后,Spring会毁掉所有已创建的单例对象。
4、分类:Singleton对象由Spring容器销毁、Prototype对象由JCM销毁
10.3 生命周期注解
初始化注解、销毁注解
//后处理器
//构造、set、init、destroy
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("后处理器在init之前执行:"+bean+" :"+beanName);
return bean;
}
/**
* 在bean的init方法执行后
* @param bean postProcessBeforeInitialization返回bean
* @param beanName
* */
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("后处理器在init之后执行"+bean+" :"+beanName);
return bean;//此处返回的是getBean()最终的返回值
}
}
10.4 生命周期阶段
单例bean:singleton
随着工厂启动创建==》构造方法==》set方法(注入值)init(初始化)==》构建完成==》随工厂关闭销毁
多例bean:prototype
被使用时创建==》构造方法==》set方法(注入值)==》init(初始化)==》构建完成==》JVM垃圾回收销毁
十一、代理设计模式
11.1概念
将核心功能与辅助功能(事务、日志、性能监控代码)分离,达到核心业务功能更纯粹、辅助业务功能可复用
11.2 静态代理设计模式
通过代理类的对象,为原始类的对象(目标类的对象)添加辅助功能,更容易更换代理实现类、力与维护
· 代理类=实现原始类相同接口+添加辅助功能+调用原始类的业务方法。
·静态代理问题:
代理类数量过多,不利于项目管理。
多个代理类的辅助功能代码冗余,维护性差。
11.3 动态代理设计模式
动态创建代理类的对象,为原始类的对象添加辅助功能。
11.3.1JDK动态代理实现(基于接口)
11.3.2 CGlib动态代理实现(基于继承)
十二、面向切面编程【重点】
12.1概念
AOP(Aspect Oriented Programming)即面向切面编程,利用一种称为“横切”的技术,剖开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为“Aspect”,即切面。所谓“切面”,简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。
12.2 AOP开发术语
1、连接点(Joinpoint):连接点时程序类中客观存在的方法,可被Spring拦截并切入内容。
2、切入点(Pointcut):被Spring切入连接点。
3、通知、增强(Advice):可以为切入点添加额外功能,分为:前置通知、后置功能、异常通知、环绕通知等。
4、目标对象(Target):代理的目标对象
5、引介(Introduction):一种特殊的增强,可在运行期间为类动态添加Field和Method。
6、织入(Weaving):把通知应用到具体的类,进而创建新的代理类的过程。
7、代理(Proxy):被AOP织入通知后,产生的结果类。
8、切面(Aspect):由切点和通知组成,将横切逻辑织入切面所制定的连接点中
作用
Spring的AOP编程即是通过动态代理类为原始类的方法添加辅助功能。
12.4 环境搭建
引入AOP相关依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
<!--spring-context.xml引入AOP命名空间-->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
">
12.5 开发流程
定义原始类
public interface UserService {
//查询
public List<User> queryUsers();
}
public class UserServiceImpl implements UserService {
public void queryUsers() {
System.out.println("查询User");
}
}
//定义通知类(添加额外功能)
public class MyAdvice {
//前置通知
public void before(JoinPoint a) throws Throwable {
System.out.println("前置通知!");
}
//后置通知
public void afterReturning(JoinPoint a,Object ret) throws Throwable {
System.out.println("后置通知");
}
//环绕通知
public Object invoke(ProceedingJoinPoint p) throws Throwable {
System.out.println("环绕开始。。。。");
Object proceed = p.proceed();
System.out.println("环绕结束");
return proceed;
}
//异常通知
public void afterThrowing(JoinPoint a,Exception ex){
System.out.println("异常通知");
}
}
<!--定义切入点(PointCut) 形成切面-->
<aop:config>
<!--切点-->
<aop:pointcut id="pc_tx" expression="execution(* cn.kgc.service.UserServiceImpl.*(..))"/>
<!--组装切面-->
<aop:advisor advice-ref="interceptor" pointcut-ref="pc_tx"/>
</aop:config>
12.6 AOP小结
1、通过AOP提供的编码流程,更便利的定制切面,更方便的定制了动态代理。
2、进而彻底解决了辅助功能冗余的问题;
3、业务类中职责单一性得到更好保障;
辅助功能也有很好的复用性。
12.7通知类
定义通知类,达到通知效果
1、前置通知:MethodBefore'Advice
2、后置通知:AfterAdvice
3、异常通知:ThrowsAdvice
4、环绕通知:MethodInterceptor
12.8 通配切入点
根据表达式通配切入点
<!--以携带参数为-->
<aop:pointcut id="pc01" expression="execution(* *(cn.kgc.pojo.User))"/>
<!--任意没有参数的方法-->
<aop:pointcut id="pc02" expression="execution(* *())"/>
<!--方法名为savaUser,参数随意-->
<aop:pointcut id="pc03" expression="execution(* savaUser(..))"/>
<!--返回值为User,但是方法名和参数是任意的-->
<aop:pointcut id="pc04" expression="execution(cn.kgc.pojo.User *(..))"/>
<!--指定类下的所有方法-->
<aop:pointcut id="pc05" expression="execution(* cn.kgc.service.UserServiceImpl.*(..))"/>
<!--指定包下的的所有类中的方法-->
<aop:pointcut id="pc06" expression="execution(* cn.kgc.service.*.*(..))"/>
<!--指定cn包下的所有类和其下的所有子包中的类的方法-->
<aop:pointcut id="pc07" expression="execution(* cn..*.*(..))"/>
12.9 JDK和CGLIB选择
1、spring底层,包含jdk代理和cglib代理两种动态代理生成机制
2、基本规则是:目标业务类如果有接口则用JDK代理,没有接口则用CGLIB代理
class DefaultAopProxyFactory{
//该方法中明确定义了 JDK代理和CGLIB代理的选取规则
//基本规则是:目标业务类如果有接口则用JDK代理,没有接口则用CGLIB代理
public AopProxy createAopProxy(){...}
}
十三、Spring+MyBatis 【重点】
13.1 配置数据源
将数据配置到项目中
13.1.1 引入jdbc.properties配置文件
#db.properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/students?characterEncoding=utf-8
user=root
password=root
13.1.2 整合Spring配置文件和properties配置文件
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
">
<!--配置文件参数化(参数占位符)-->
<context:property-placeholder location="classpath:db.properties"/>
<!--与pooledDataSource集成 [二选一]-->
<bean id="dataSource" class="org.apache.ibatis.datasource.pooled.PooledDataSource">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${user}"/>
<property name="password" value="${password}"/>
</bean>
<!--与DruidDataSource集成 [二选一]-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<!--基本配置-->
<property name="driverClassName" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${user}"/>
<property name="password" value="${password}"/>
<!--配置初始化大小、最小、最大-->
<property name="initialSize" value="${jdbc.initialPoolSize}"/>
<property name="minIdle" value="${jdbc.minPoolSize}"/>
<property name="maxActive" value="${jdbc.maxPoolSize}"/>
<!--配置获取连接等待超时的时间-->
<property name="maxWait" value="60000"/>
<!--配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒-->
<property name="timeBetweenEvictionRunsMillis" value="60000"/>
<!--配置一个连接在池中最小生存的时间,单位是毫秒-->
<property name="minEvictableIdleTimeMillis" value="30000"/>
</bean>
</beans>
13.1.3Druid连接池可选参数
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<!--基本配置-->
<property name="driverClassName" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${user}"/>
<property name="password" value="${password}"/>
<!--配置初始化大小、最小、最大-->
<property name="initialSize" value="${jdbc.initialPoolSize}"/>
<property name="minIdle" value="${jdbc.minPoolSize}"/>
<property name="maxActive" value="${jdbc.maxPoolSize}"/>
<!--配置获取连接等待超时的时间-->
<property name="maxWait" value="60000"/>
<!--配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒-->
<property name="timeBetweenEvictionRunsMillis" value="60000"/>
<!--配置一个连接在池中最小生存的时间,单位是毫秒-->
<property name="minEvictableIdleTimeMillis" value="30000"/>
</bean>
13.2整合MyBatis
将SqlSessionDFactory、Dao、Service配置到项目中
13.2.1 导入依赖
<!--将Mybatis与Spring进行整合-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
13.2.2 配置SqlSessionFactory
<!--生产一个SqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--注入连接池-->
<property name="dataSource" ref="dataSource"/>
<!--注入dao-mapper文件信息,如果映射文件和dao接口同包且同名,则此配置可以省略-->
<property name="mapperLocations">
<list>
<value>cn/kgc/dao/UserDaoMapper.xml</value>
</list>
</property>
<!--为dao-mapper文件中的实体 定义缺省包路径
如:<select id="queryAll" resultType="User">中User类可以不定义包
-->
<property name="typeAliasesPackage" value="cn.kgc.pojo"/>
</bean>
13.2.3 配置MapperScannerConfigurer
管理Dao实现类的创建,并创建Dao对象,存入工厂管理
1、扫描所有Dao接口,去构建Dao实现
2、将Dao实现存入工厂管理
3、Dao实现对象在工厂中的id是:“首字母小写-接口的类名”,
例如:UserDao===》userDao,OrderDao===》orderDao
<!--mapperScannerConfigurer映射器,相当于自动实现dao实现类-->
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--dao接口所在的包 如果有多个包,可以用逗号或分号分隔-->
<property name="basePackage" value="cn.kgc.dao"/>
<!--如果工厂中只有一个SqlSessionFactory的bean,此配置可省略-->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>
13.2.4 配置Service
<bean id="userService" class="cn.kgc.service.UserServiceImpl">
<property name="userDao" ref="userDao"/>
</bean>
十四、事务
14.1配置DataSourceTransactionManager
事务管理器,其中持有DataSource,可以控制事务(commit、rollback等)
<!--引入一个事务管理器,其中依赖dataSource,介意获得连接,进而控制事务逻辑-->
<bean id="tx" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
**注意:**DataSourceTransactionManager和SqlSessionFactoryBean要注入同一个DataSource的Bean,否则事务控制失败!!!
14.2 配置事务通知
基于事务管理器,进一步定制,生成一个额外功能:Advice。
此Advice可以切入任何需要事物的方法,通过事务管理器为方法控制事务。
<tx:advice id="interceptor" transaction-manager="tx">
<tx:attributes>
<!--针对具体的方法,采用对应事务实行-->
<!--<tx:method name="addUser" rollback-for="Exception" isolation="DEFAULT" propagation="REQUIRED" read-only="false"/>-->
<!--以User结尾的方法,切入此方法时,采用对应事务实行-->
<tx:method name="*User" rollback-for="Exception"/>
<!--以query开头的方法,切入此方法时,采用对应事务实行-->
<tx:method name="query*" propagation="SUPPORTS"/>
<!--剩余所有方法-->
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
14.3事务属性
14.3.1隔离级别
14.3.1.1 概念
isolation 隔离级别
名称 | 描述 |
---|---|
default | (默认值)采用数据库的默认设置 |
read-uncommited | 度未提交 |
read-commited | 都提交(Oracle数据库默认的隔离级别) |
repeatable-read | 可重复读(mysql数据库默认的隔离级别) |
seralized-read | 序列化 |
隔离级别由低到高为: read-uncommited < read-commited < repeatable-read < seralized-read
14.3.1.2 特性
1、安全性:级别越高,多事务并发时,越安全。因为共享的数据越来越少,十五件彼此干扰减少。
2、并发性:级别越高,多事务并发时,并发越差。因为共享的数据越来越少,事务间阻塞情况增多。
14.3.1.3 并发问题
事务并发时的安全问题
14.3.2 传播行为
propagation 传播行为
当涉及到事务嵌套(Service调用Service)时,可以设置:
1、SUPPORTS = 不存在外部事务,则不开启新事务;存在外部事务,则合并到外部事务中(适合查询)
2、REQUIRED = 不存在外部事务,则开启新事务;存在外部事务,则合并到外部事务中(适合增删改)
14.3.3读写性
readonly 读写性
1、true:只读,可以提高查询效率(适合查询)
2、false:可读可写。默认值(蛇和增删改)
14.3.4 事务超时
timeout 事务超时时间
当事务所需操作的数据被其他事务占用,则等待。
100:自定义等待时间100(秒)
-1:由数据库指定等待时间,默认值。
14.3.5 事务回滚
rollback-for 会回滚事务
1、如果事务中抛出RuntimeException,则自动回滚
2、如果事务中抛出CheckException(非运行时异常Exception),不会自动回滚,而是默认提交事务
3、处理方案:将CheckException转换成RuntimeException上跑,或设置rollback-for="Exception"
14.4 编织
将事务管理的Advice切入需要事务的业务方法中
<aop:config>
<aop:pointcut id="pc_tx" expression="execution(* cn.kgc.service.UserServiceImpl.*(..))"/>
<!--组织切面-->
<aop:advisor advice-ref="interceptor" pointcut-ref="pc_tx"/>
十五、注解开发
15.1声明bean
用于替换自建类型组件的<bean...>标签;可以更快速的声明bean
1、@Service业务类专用 @Repository dao实现类专用 @Controller web层专用
2、@Component 通用
3、@Scope用户控制bean的创建模式
//@Service //相当于 <bean id="userServiceImpl" class="XX.XX.XX.UserServiceImpl"></bean>
@Service("userService2") //相当于 <bean id="userServiceI" class="XX.XX.XX.UserServiceImpl"></bean>
@Scope("singleton")//声明创建摩士,默认为单例模式; @Scope("prototype"),设置成多例模式
//类中的每个方法都切入事务(有自己的事务控制的方法除外)
@Transactional(isolation = Isolation.DEFAULT,propagation = Propagation.REQUIRED,readOnly = false,rollbackFor = Exception.class,timeout = -1)
public class UserServiceImpl implements UserService {
//@Autowired 类型 自动注入
//@Resource //名称 自动注入
//基于类型自动注入,并挑选beanid="userDao"
@Autowired
@Qualifier("userDao")
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
@Transactional(propagation = Propagation.SUPPORTS)//该方法自己的事务控制,仅对此方法有效
public List<User> queryUsers() {
return userDao.queryUsers();
}
}
15.2 注入(DI)
用于完成bean中属性值的注入
1、@Autowired基于类型自动注入
2、@Resource基于名称自动注入
3、@Qualifier("userDao")限定要自动注入的bean的id,一般和@Autowired联用
4、@value注入简单类型数据(jdk8种+String)
15.3 事务控制
用于控制事务切入
1、@Transactional
2、工厂配置中的<tx:advice.....>和<aop:config...>可以省略
//类中的每个方法都切入事务(有自己的事务控制的方法除外)
@Transactional(isolation = Isolation.DEFAULT,propagation = Propagation.REQUIRED,readOnly = false,rollbackFor = Exception.class,timeout = -1)
public class UserServiceImpl implements UserService {
@Transactional(propagation = Propagation.SUPPORTS)//该方法自己的事务控制,仅对此方法有效
public List<User> queryUsers() {
return userDao.queryUsers();
}
15.4 注解所需配置
<!--告知spring注解位置-->
<context:component-scan base-package="cn.kgc"></context:component-scan>
<!--@Transational注解启动-->
<tx:annotation-driven transaction-manager="tx"/>
15.5AOP开发
15.5.1注解使用
package cn.kgc.advice;
import org.aopalliance.intercept.MethodInvocation;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.aop.ThrowsAdvice;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Aspect //声明此类是一个切面类,会包含切入点(pointcut)和通知(advice)
@Component //声明组件,进入工厂
public class MyAdvice {
//定义切入点
@Pointcut("execution(* cn.kgc.service.UserServiceImpl.*(..))")
public void pc(){}
//前置通知
@Before("pc()")
public void before(JoinPoint a) throws Throwable {
System.out.println("前置通知!");
}
//后置通知
@AfterReturning(value = "pc()",returning = "ret")
public void afterReturning(JoinPoint a,Object ret) throws Throwable {
System.out.println("后置通知");
}
//环绕通知
@Around(("pc()"))
public Object invoke(ProceedingJoinPoint p) throws Throwable {
System.out.println("环绕开始。。。。");
Object proceed = p.proceed();
System.out.println("环绕结束");
return proceed;
}
//异常通知
@AfterThrowing(value = "pc()",throwing = "ex")
public void afterThrowing(JoinPoint a,Exception ex){
System.out.println("异常通知");
}
}
15.5.2 配置
<!--添加如下配置 启用aop注释-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
十六、集成JUnit
16.1 导入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.3.6.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
16.2 编码
可以免去工厂的创建过程;
可以直接将要测试的组件注入到测试类。
//测试启动 启动spring工厂 并且当前测试类也会被工厂生产
@RunWith(SpringJUnit4ClassRunner.class)//负责启动
@ContextConfiguration("classpath:applicationContext.xml")//负责制定配置文件位置
public class TestSpringMyBatis {
@Autowired
@Qualifier("userService2")
private UserService userService;
@Autowired
private SqlSessionFactory factory;
@Test
public void testSpringJunit(){
/* List<User> users = userService.queryUsers();
for (User user : users) {
System.out.println(user.toString());
}*/
SqlSession sqlSession = factory.openSession();
UserDao mapper = sqlSession.getMapper(UserDao.class);
List<User> users = mapper.queryUsers();
for (User user : users) {
System.out.println(user.toString());
}
}
}