文章目录
Spring
1 基本使用
pom文件
<dependencies>
<!--核心依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
</dependencies>
applicationContext.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/beans/spring-beans.xsd">
<!--导入分支配置文件-->
<import resource="classpath:spring-school.xml"/>
<!--自定义对象
scope:指定作用域
-->
<bean id="someService" class="com.beiyin.service.SomeServiceImpl" scope="prototype"/>
<!--非自定义对象-->
<bean id="myDate" class="java.util.Date"/>
</beans>
Test
@Test
public void test6() {
// 指定配置文件的位置和名称
String resource = "applicationContext.xml";
// String resource = "D:\\applicationContext.xml";
// 创建spring容器对象
// 配置文件在根路径下
ApplicationContext ac = new ClassPathXmlApplicationContext(resource);
// 配置文件在磁盘路径下
// ApplicationContext ac = new FileSystemXmlApplicationContext(resource);
// 从容器对象中获得bean
SomeService someService = (SomeService) ac.getBean("someService");
// 执行方法
someService.doSome();
Date myDate = (Date) ac.getBean("myDate");
System.out.println(myDate.toString());
}
2 Bean的装配
默认调用无参构造器,创建实例对象。
Bean 的作用域
- singleton 单例模式,默认;
- prototype 原型模式,每次调用,获得新的实例;
- request 每次HTTP请求,产生新的实例,只能用于Web应用;
- session 不同的HTTP session,产生不同的实例,只能用于Web应用。
3 注入/DI
对 bean 对象的属性进行初始化。
3.1 基于XML的DI
设置注入和构造注入
<?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/beans/spring-beans.xsd">
<bean id="school" class="com.beiyin.service.School"/>
<!--设值注入(使用setter/getter方法)-->
<bean id="student" class="com.beiyin.service.Student">
<!--基本类型-->
<property name="name" value="ding"/>
<property name="age" value="20"/>
<!--引用类型-->
<property name="school" ref="school"/>
<!--使用ref标签-->
<!--<property name="school">-->
<!--<ref bean="school"/>-->
<!--</property>-->
</bean>
<!--构造注入-->
<bean id="myStudent" class="com.beiyin.service.Student">
<constructor-arg name="name" value="ding"/>
<constructor-arg name="age" value="14"/>
<constructor-arg name="school" ref="school"/>
</bean>
</beans>
引用类型自动注入
byName
<?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/beans/spring-beans.xsd">
<bean id="school" class="com.beiyin.service.School"/>
<!--设值注入(使用setter/getter方法)-->
<bean id="student" class="com.beiyin.service.Student" autowire="byName">
<!--基本类型-->
<property name="name" value="ding"/>
<property name="age" value="20"/>
</bean>
</beans>
byType
<?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/beans/spring-beans.xsd">
<!--注意同byName的区别-->
<bean id="mySchool" class="com.beiyin.service.School"/>
<!--设值注入(使用setter/getter方法)-->
<bean id="student" class="com.beiyin.service.Student" autowire="byType">
<!--基本类型-->
<property name="name" value="ding"/>
<property name="age" value="20"/>
</bean>
</beans>
3.2 基于注解的DI
配置组件扫描器
<?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 https://www.springframework.org/schema/context/spring-context.xsd">
<import resource="classpath:spring-school.xml"/>
<!--配置组件扫描器,扫描多个包-->
<context:component-scan base-package="com.beiyin.service,com.beiyin.dao"/>
</beans>
创建bean
// 指定bean的id
@Component("someService")
public class SomeServiceImpl implements SomeService {
public SomeServiceImpl(){
System.out.println("无参构造");
}
@Override
public void doSome() {
System.out.println("实现doSmoe方法");
}
}
- @Component:创建基本的bean;
- @Repository:创建持久层bean;
- @Service:创建业务层bean;
- @Component:创建控制层bean。
基本类型属性注入
public class Student {
@Value("张三") // 无需setter
private String name;
}
引用类型属性byType自动注入
@Component("mySchool")
public class School {
}
public class Student {
@Autowired // 按类型自动注入,无需setter
private School school;
}
引用类型属性byName自动注入
public class Student {
@Autowired // 按名称自动注入,无需setter
@Qualifier("mySchool")
private School school;
}
required
public class Student {
@Autowired(required = false) // 当注入失败时,赋予null值
private School school;
}
@Resource
默认按名称,找不到时再按类型。
public class Student {
@Resource(name = "mySchool") // 指定名称
private School school;
}
4 AOP概要
AOP 为 Aspect Oriented Programming 的缩写,意为:面向切面编程,可通过运行期动态代理实现程序功能的统一维护的一种技术。
优势 :
- 减少重复;
- 专注业务;
AOP术语
- 切面(Aspect ):泛指交叉业务逻辑,比如事务处理/日志处理。
- 连接点(JoinPoint ):可以被切面织入的具体方法。通常业务接口中的方法均为连接点。
- 切入点(Pointcut):指声明的一个或多个连接点的集合。
- 目标对象(Target ):将要被增强的对象。即包含主业务逻辑的类的对象。
- 通知(Advice ):通知是切面的一种实现,可以完成简单织入功能(织入功能就是在这里完成的)。
5 AspectJ对AOP的实现
5.1 通知类型
- 前置通知
- 后置通知
- 环绕通知
- 异常通知
- 最终通知
5.2 切入点表达式
execution ( [modifiers-pattern] 访问权限类型 ret-type-pattern 返回值类型 [declaring-type-pattern] 全限定性类名 name-pattern(param-pattern)方法名(参数类型和参数个数) [throws-pattern] 抛出异常类型 )
符号 | 意义 |
---|---|
* | 0至多个任意字符 |
… | 方法参数中表示任意多个参数;包名后表示当前包及子包 |
+ | 用在类/接口名后,表示当前类/接口及其子类/实现类 |
execution(public * *(..))
指定切入点为:任意公共方法。
execution(* set*(..))
指定切入点为:任何一个以“set”开始的方法。
execution(* com.xyz.service.*.*(..))
指定切入点为:定义在 service 包里的任意类的任意方法。
execution(* com.xyz.service..*.*(..))
指定切入点为:定义在 service 包或者子包里的任意类的任意方法。“..”出现在类名中时,后
面必须跟“*”,表示包、子包下的所有类。
execution(* *..service.*.*(..))
指定所有包下的 serivce 子包下所有类(接口)中所有方法为切入点
execution(* *.service.*.*(..))
指定只有一级包下的 serivce 子包下所有类(接口)中所有方法为切入点
execution(* *.ISomeService.*(..))
指定只有一级包下的 ISomeSerivce 接口中所有方法为切入点
execution(* *..ISomeService.*(..))
指定所有包下的 ISomeSerivce 接口中所有方法为切入点
execution(* com.xyz.service.IAccountService.*(..))
指定切入点为:IAccountService 接口中的任意方法。
execution(* com.xyz.service.IAccountService+.*(..))
指定切入点为:IAccountService 若为接口,则为接口中的任意方法及其所有实现类中的任意方法;若为类,则为该类及其子类中的任意方法。
execution(* joke(String,int)))
指定切入点为:所有的 joke(String,int)方法,且 joke()方法的第一个参数是 String,第二个参数是 int。如果方法中的参数类型是 java.lang 包下的类,可以直接使用类名,否则必须使用全限定类名,
如 joke( java.util.List, int)。
execution(* joke(String,*)))
指定切入点为:所有的 joke()方法,该方法第一个参数为 String,第二个参数可以是任意类型,
如joke(String s1,String s2)和joke(String s1,double d2)都是,
但joke(String s1,double d2,String s3)不是。
execution(* joke(String,..)))
指定切入点为:所有的 joke()方法,该方法第一个参数为 String,后面可以有任意个参数且参数类型不限,如 joke(String s1)、joke(String s1,String s2)和 joke(String s1,double d2,String s3)都是。
execution(* joke(Object))
指定切入点为:所有的 joke()方法,方法拥有一个参数,且参数是 Object 类型。joke(Object ob)是,但,joke(String s)与 joke(User u)均不是。
execution(* joke(Object+)))
指定切入点为:所有的 joke()方法,方法拥有一个参数,且参数是 Object 类型或该类的子类。不仅 joke(Object ob)是,joke(String s)和 joke(User u)也是。
5.3 基于注解的实现
pom.xml
<!--AOP依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.16.RELEASE</version>
</dependency>
java实现
// 接口
public interface OneService {
int sayHello(String name,int age);
}
// 实现类
@Component
public class OneServiceImpl implements OneService {
@Override
public int sayHello(String name, int age) {
// 实现抛出异常
// age = 1/0;
System.out.println("hello" +name);
return age + 5;
}
}
// 切面类
@Component
@Aspect
public class MyAspect {
/**
* 前置通知
* @param joinPoint 代表切入点表达式,可获取切入带你表达式/方法签名/目标对象,所有通知都可包含
*/
@Before(value = "execution(* com.beiyin.service.aop.OneServiceImpl.*(..))")
public void before(JoinPoint joinPoint){
// 连接点的方法定义
Signature signature = joinPoint.getSignature();
System.out.println(signature);
/*
int com.beiyin.service.aop.OneService.sayHello(String,int)
*/
// 方法参数信息
Object[] args = joinPoint.getArgs();
for (Object arg : args) {
System.out.println(arg);
/*
丁
15
*/
}
System.out.println("前置通知");
}
/**
*后置通知
* @param result 定义在注解属性returning中,代表执行结果
*/
@AfterReturning(value = "execution(* com.beiyin.service.aop.OneServiceImpl.*(..))",returning = "result")
public void after(Object result){
// 获得目标方法执行结果
System.out.println("执行结果:"+result);
System.out.println("后置通知");
}
/**
* 环绕通知
* @param pjp 用于执行目标方法
* @return
* @throws Throwable
*/
@Around(value ="execution(* com.beiyin.service.aop.OneServiceImpl.*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕通知:在目标前");
// 执行目标方法
Object proceed = pjp.proceed();
// 修改目标执行结果
proceed = 5;
System.out.println("环绕通知:在目标后");
return proceed;
}
/**
* 异常通知 (注意:只有在切入点的方法发生异常时,才会执行)
* @param ex 异常对象
*/
@AfterThrowing(value ="execution(* com.beiyin.service.aop.OneServiceImpl.*(..))",throwing = "ex")
public void throwing(Throwable ex){
System.out.println("发生了异常;"+ex.getMessage());
}
/**
* 最终通知
*/
@After(value = "pt()")
public void after(){
System.out.println("无论如何,都会执行");
}
/**
* 定义切入点,简化配置
*/
@Pointcut(value = "execution(* com.beiyin.service.aop.OneServiceImpl.*(..))")
private void pt(){
}
}
/*无异常通知顺序:
环绕通知:在目标前
前置通知
环绕通知:在目标后
最终通知:一定执行
后置通知
*/
/*有异常通知顺序:
环绕通知:在目标前
前置通知
最终通知:一定执行
异常通知
*/
5.4 基于XML的实现
java
// 目标类
public class OneServiceImpl implements OneService {
@Override
public int sayHello(String name, int age) {
// age = 1/0;
System.out.println("hello" +name);
return age + 5;
}
}
// 切面类
public class MyAspect {
public void before(JoinPoint joinPoint) {
System.out.println("前置通知");
}
public void afterReturn(Object result) {
System.out.println("后置通知");
}
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕通知:在目标前");
// 执行目标方法
Object proceed = pjp.proceed();
// 修改目标执行结果
proceed = 5;
System.out.println("环绕通知:在目标后");
return proceed;
}
public void throwing(Throwable ex) {
System.out.println("发生了异常;" + ex.getMessage());
}
public void after() {
System.out.println("无论如何,都会执行");
}
}
配置文件
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--创建切面对象-->
<bean id="myAspect" class="com.beiyin.service.aop.MyAspect"/>
<!--创建目标对象-->
<bean id="oneServiceImpl" class="com.beiyin.service.aop.OneServiceImpl"/>
<!--切面配置-->
<aop:config>
<!--指定切入点-->
<aop:pointcut id="pt" expression="execution(* com.beiyin.service.aop.OneServiceImpl.*(..))"/>
<!--配置通知-->
<aop:aspect id="ap" ref="myAspect">
<!--前置通知-->
<aop:before method="before" pointcut-ref="pt"/>
<!--后置通知-->
<aop:after-returning method="afterReturn" pointcut-ref="pt" returning="result"/>
<!--环绕通知-->
<aop:around method="around" pointcut-ref="pt"/>
<!--异常通知-->
<aop:after-throwing method="throwing" pointcut-ref="pt" throwing="ex"/>
<!--最终通知-->
<aop:after method="after" pointcut-ref="pt"/>
</aop:aspect>
</aop:config>
</beans>
6 Spring实现AOP
java
// 接口
public interface OneService {
int sayHello(String name,int age);
}
// 实现类
public class OneServiceImpl implements OneService {
@Override
public int sayHello(String name, int age) {
age = 1/0;
System.out.println("hello" +name);
return age + 5;
}
}
/*
切面类
必须实现相应的接口
*/
// 前置通知
public class MyBefore implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("前置通知");
}
}
// 后置通知
public class MyAfter implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("后置通知");
}
}
// 环绕通知
public class MyAround implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("环绕通知:目标前");
Object proceed = invocation.proceed();
System.out.println("环绕通知:目标后");
return proceed;
}
}
// 异常通知 该接口没有方法,需要自己写方法,名字必须是afterThrowing
public class MyThrow implements ThrowsAdvice {
/**
*
* @param e 测试的是必须只有这一个参数
*/
public void afterThrowing(Throwable e){
System.out.println("异常通知");
}
}
配置文件
<?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/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--创建目标对象-->
<bean id="oneServiceImpl" class="com.beiyin.service.aop.OneServiceImpl"/>
<!--创建切面对象-->
<bean id="before" class="com.beiyin.service.aop.sp.MyBefore"/>
<bean id="after" class="com.beiyin.service.aop.sp.MyAfter"/>
<bean id="around" class="com.beiyin.service.aop.sp.MyAround"/>
<bean id="myThrow" class="com.beiyin.service.aop.sp.MyThrow"/>
<!--定义代理对象-->
<bean id="proxyOneService" class="org.springframework.aop.framework.ProxyFactoryBean">
<!--目标-->
<property name="target" ref="oneServiceImpl"/>
<!--实现的接口-->
<property name="proxyInterfaces" value="com.beiyin.service.aop.OneService"/>
<!--指定切面对象-->
<property name="interceptorNames">
<list>
<value>before</value>
<value>around</value>
<value>after</value>
<value>myThrow</value>
</list>
</property>
</bean>
</beans>
7 集成MyBatis
pom.xml
<dependencies>
<!--spring核心依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<!--spring事务依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<!--springjdbc依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<!--mybatis依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.4</version>
</dependency>
<!--spring集成mybatis依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.4</version>
</dependency>
<!--连接池依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
<!--mysql依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.19</version>
</dependency>
</dependencies>
java
// 实体类
@Data
public class User {
private Integer id;
private String name;
private Character sex;
private Integer age;
private String address;
}
// dao接口
public interface UserDao {
List<User> selectAll();
}
// 业务类
public class OneServiceImpl {
private UserDao userDao;
public void AllName() {
List<User> users = userDao.selectAll();
StringBuilder stringBuilder = new StringBuilder();
users.forEach(user -> stringBuilder.append(user.getName()));
System.out.println("所有名字:" + stringBuilder);
}
// 为了设置注入
public OneServiceImpl setUserDao(UserDao userDao) {
this.userDao = userDao;
return this;
}
}
Mapper文件:UserDao.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.beiyin.dao.UserDao">
<select id="selectAll" resultType="com.beiyin.entity.User">
select * from user;
</select>
</mapper>
mybatis主配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<typeAliases>
<package name="com.beiyin.entity"></package>
</typeAliases>
<mappers>
<package name="com.beiyin.dao"></package>
</mappers>
</configuration>
数据库配置文件
# 注意mysql8的写法
jdbc.url=jdbc:mysql://localhost:3306/test?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true
jdbc.name=root
jdbc.pwd = root
spring配置
<?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 https://www.springframework.org/schema/context/spring-context.xsd">
<!--引入属性配置文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--配置数据库连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="clone">
<!--基本配置-->
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.name}"/>
<property name="password" value="${jdbc.pwd}"/>
<!--配置初始化大小/最小数/最大数-->
<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"/>
</bean>
<!--注册工厂Bean-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:mybatis.xml"/>
</bean>
<!--动态代理对象-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<property name="basePackage" value="com.beiyin.dao"/>
</bean>
<!--业务对象-->
<bean class="com.beiyin.service.aop.OneServiceImpl" id="oneService">
<property name="userDao" ref="userDao"/>
</bean>
</beans>
测试
@Test
public void test1(){
String resource = "applicationContext.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(resource);
UserDao userDao = (UserDao) ac.getBean("userDao");
List<User> users = userDao.selectAll();
users.forEach(user-> System.out.println(user));
OneServiceImpl oneService = (OneServiceImpl) ac.getBean("oneService");
oneService.AllName();
}
8 Spring事务
事务原本是数据库中的概念,在 Dao 层。但一般情况下,需要将事务提升到业务层,即 Service 层。
8.1 事务管理器接口
事务管理器是 PlatformTransactionManager 接口对象。其主要用于完成事务的提交、回滚,及获取事务的状态信息。
常用的实现类:
- DataSourceTransactionManager:使用 JDBC 或 MyBatis 进行数据库操作时使用。
- HibernateTransactionManager:使用 Hibernate 进行持久化数据时使用。
8.2 Spring 的回滚方式
Spring 事务的默认回滚方式是:发生运行时异常和 error 时回滚,发生受查(编译)异常时提交。不过,对于受查异常,程序员也可以手工设置其回滚方式。
注意:当程序出现运行时异常时,java不要求必须处理(抛出/捕获),因此,当出现运行时异常时,实质上是未处理的状态,或者说是程序员未预料到的状态,此时就需要回滚。
8.3 事务定义接口
事务定义接口TransactionDefinition中定义了事务描述相关的三类常量:事务隔离级别、事务传播行为、事务默认超时时限,及对它们的操作。
五个事务隔离级别常量
- DEFAULT:采用 DB 默认的事务隔离级别。MySql 的默认为 REPEATABLE_READ; Oracle默认为READ_COMMITTED。
- READ_UNCOMMITTED:读未提交。未解决任何并发问题。
- READ_COMMITTED:读已提交。解决脏读,存在不可重复读与幻读。
- REPEATABLE_READ:可重复读。解决脏读、不可重复读,存在幻读。
- SERIALIZABLE:串行化。不存在并发问题。
七个事务传播行为常量
- PROPAGATION_REQUIRED:指定的方法必须在事务内执行。若当前存在事务,就加入到当前事务中;若当前没有事务,则创建一个新事务。这是默认行为。
- PROPAGATION_REQUIRES_NEW:指定的方法支持当前事务,但若当前没有事务,也可以以非事务方式执行。
- PROPAGATION_SUPPORTS:总是新建一个事务,若当前存在事务,就将当前事务挂起,直到新事务执行完毕。
- PROPAGATION_MANDATORY
- PROPAGATION_NESTED
- PROPAGATION_NEVER
- PROPAGATION_NOT_SUPPORTED
事务默认超时时限
常量 TIMEOUT_DEFAULT 定义了事务底层默认的超时时限,sql 语句的执行时长。比较复杂,一般选择默认。
8.4 相关程序
java
// 实体类
@Data
public class User {
private Integer id;
private String name;
private Character sex;
private Integer age;
private String address;
}
// dao接口
public interface UserDao {
List<User> selectAll();
int insertByName(User user);
int deleteByName(String name);
}
// 自定义异常
public class MyException extends RuntimeException {
public MyException() {
}
public MyException(String message) {
super(message);
}
}
// 业务类
public class UserOptionImpl implements UserOption {
private UserDao userDao;
@Override
public void userDo() {
List<User> users = userDao.selectAll();
for (User user : users) {
if ("zhao".equals(user.getName())) {
int i = userDao.insertByName(user);
System.out.println("插入记录数;"+i);
}
}
if (1==1){
// throw new MyException("操作user异常");
}
userDao.deleteByName("liu");
}
public UserOptionImpl setUserDao(UserDao userDao) {
this.userDao = userDao;
return this;
}
}
// 测试
@Test
public void test1(){
String resource = "applicationContext.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(resource);
UserOption userOption = (UserOption) ac.getBean("userOption");
userOption.userDo();
}
8.5 使用 Spring 的事务注解管理事务
通过@Transactional 注解方式,可将事务织入到相应 public 方法中,实现事务管理。
@Transactional的可选属性:
- propagation:事务传播属性,默认值为Propagation.REQUIRED。
- isolation:事务的隔离级别,默认值为Isolation.DEFAULT。
- readOnly:设置对数据库只读,默认值为 false。
- timeout:设置本操作与数据库连接的超时时限,单位为秒,默认-1。
- rollbackFor:指定需要回滚的异常类,可以是一个,也可以是数组。
- rollbackForClassName:指定需要回滚的异常类类名。
- noRollbackFor:指定不需要回滚的异常类。
- noRollbackForClassName:指定不需要回滚的异常类类名。
注意:@Transactional只能用于public方法上,用于其他方法不会出错,但也无作用。
spring配置
<?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" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--引入属性配置文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--配置数据库连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="clone">
<!--基本配置-->
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.name}"/>
<property name="password" value="${jdbc.pwd}"/>
<property name="timeBetweenEvictionRunsMillis" value="60000"/>
</bean>
<!--注册工厂Bean-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:mybatis.xml"/>
</bean>
<!--动态代理对象-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<property name="basePackage" value="com.beiyin.dao"/>
</bean>
<!--定义业务对象-->
<bean class="com.beiyin.service.UserOptionImpl" id="userOption">
<property name="userDao" ref="userDao"/>
</bean>
<!--声明事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--声明事务注解驱动-->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
业务方法
public class UserOptionImpl implements UserOption {
private UserDao userDao;
@Transactional(rollbackFor = {MyException.class,NullPointerException.class})
@Override
public void userDo() {
List<User> users = userDao.selectAll();
for (User user : users) {
if ("zhao".equals(user.getName())) {
int i = userDao.insertByName(user);
System.out.println("插入记录数;"+i);
}
}
if (1==1){
// throw new MyException("操作user异常");
}
userDao.deleteByName("liu");
}
}
8.6 使用 AspectJ 的 AOP 配置管理事务
spring配置
<?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" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://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 https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--引入属性配置文件-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--配置数据库连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="clone">
<!--基本配置-->
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.name}"/>
<property name="password" value="${jdbc.pwd}"/>
<property name="timeBetweenEvictionRunsMillis" value="60000"/>
</bean>
<!--注册工厂Bean-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:mybatis.xml"/>
</bean>
<!--动态代理对象-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<property name="basePackage" value="com.beiyin.dao"/>
</bean>
<!--定义业务对象-->
<bean class="com.beiyin.service.UserOptionImpl" id="userOption">
<property name="userDao" ref="userDao"/>
</bean>
<!--声明事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置事务通知-->
<tx:advice id="userTransaction" transaction-manager="transactionManager">
<tx:attributes>
<!--指定相应方法的事务-->
<tx:method name="user*" rollback-for="com.beiyin.exception.MyException"/>
<!--指定除上面个的其他方法的事务-->
<tx:method name="*" propagation="SUPPORTS"/>
</tx:attributes>
</tx:advice>
<!--aop配置-->
<aop:config>
<aop:pointcut id="pt" expression="execution(* com.beiyin.service.UserOptionImpl.*(..))"/>
<!--声明增强器-->
<aop:advisor advice-ref="userTransaction" pointcut-ref="pt"/>
</aop:config>
</beans>
优势: 无需为每一个目标类添加注解配置,只需在spring中统一定义即可。
9 Spring与Web
pom.xml
<dependencies>
<!--spring容器依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<!--spring-web依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<!--servlet依赖-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- jsp依赖 -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2.1-b03</version>
<scope>provided</scope>
</dependency>
</dependencies>
java
// 业务类
public class MyService {
public void print(){
System.out.println("---------");
}
}
// servlet
public class RegServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获得ServletContext容器
ServletContext context = req.getServletContext();
// 获取spring容器第一种方法
WebApplicationContext wc = (WebApplicationContext) context.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
MyService myService = (MyService) wc.getBean("myService");
// 获取spring容器第二种方法
// WebApplicationContext rc = WebApplicationContextUtils.getRequiredWebApplicationContext(context);
// MyService myService = (MyService) rc.getBean("myService");
myService.print();
// 获得参数
String name = req.getParameter("name");
String age = req.getParameter("age");
System.out.println("name:"+name+" age:"+age);
//重定向
req.getRequestDispatcher("/result.jsp").forward(req,resp);
}
}
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_0.xsd">
<display-name>Archetype Created Web Application</display-name>
<!--注册servlet-->
<servlet>
<servlet-name>regServlet</servlet-name>
<servlet-class>com.RegServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>regServlet</servlet-name>
<url-pattern>/reg</url-pattern>
</servlet-mapping>
<!--注册监听器-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--指定spring配置文件-->
<context-param>
<param-name>contextConfigLocation</param-name>
<!--默认为WEB-INF/applicationContext.xml,但是一般不用-->
<param-value>classpath:application.xml</param-value>
</context-param>
</web-app>
index.jsp
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h2>Hello World!</h2>
<form action="reg" method="post">
姓名:<input type="text" name="name"/>
年龄:<input type="text" name="age">
<input type="submit" value="提交">
</form>
</body>
</html>
10 其他
手动获取spring上下文对象
/**
* 手动获取Spring上下文和Bean对象
*
* @Author YinWenBing
* @Date 2017/1/6 17:07
*/
@Component
public class ApplicationUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
/**
* 加载Spring配置文件时,如果Spring配置文件中所定义的Bean类实现了ApplicationContextAware接口,会自动调用该方法
*
* @param applicationContext
* @throws BeansException
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
ApplicationUtil.applicationContext = applicationContext;
}
/**
* 获取Spring上下文
* @return
*/
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
/**
* 获取Spring Bean
* @param name
* @return
* @throws BeansException
*/
public static Object getBean(String name) throws BeansException {
return applicationContext.getBean(name);
}
}
手动获取ServletContext
@Component
public class Test {
@Autowired
private WebApplicationContext webApplicationContext;
public ServletContext get(){
ServletContext servletContext = webApplicationContext.getServletContext();
return servletContext;
}
}
关于上下文问题
spring存在一个ApplicationContext容器,servlet技术存在一个ServletContext容器,ApplicationContext中含有ServletContext。
监听器
@Component
public class MyServerContextListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
// 建立映射表
Map<String, String> map = new HashMap<>();
ApplicationContext applicationContext = event.getApplicationContext();
ServletContext bean = applicationContext.getBean(ServletContext.class);
// 将映射表放入上下文域中
bean.setAttribute("myMap",map);
}
}