SpringIOC/AOP/事务管理
SpringIOC/AOP/事务管理学习笔记,方便后期查阅。
文章目录
- Spring简介
- Spring IOC详解
- Spring Bean管理
- Spring AOP详解
- 什么是Spring AOP?
- AOP相关术语
- AOP的底层实现
- AOP通知类型
- AOP切面类型
- AOP的手动代理
- AOP的动态代理
- 基于Bean名称的自动代理(BeanNameAutoProxyCreator)
- 基于切面信息的自动代理(DefaultAdvisorAutoProxyCreator)
- 基于Bean中的AspectJ注解进行自动代理(AnnotationAwareAspectJAutoProxyCreator)
- 基于AspectJ的AOP开发(常用,重点)
- Spring的JDBC Template
- Spring事务管理
Spring简介
什么是Spring?
Spring是一个开源的设计层面框架,它解决的是业务逻辑层和其他各层的松耦合问题。简单来说,Spring是一个分层的JavaSE/EE full-stack(一站式)轻量级开源框架。
什么是Spring DI?
DI(Dependency Injection,依赖注入)是IOC实现的典型方式。IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI来实现的。可以把DI理解为是IOC的另一种说法,例如A类需要B类的B对象,那么A类通过IOC的设计方式,A类从Spring工厂中获得了B类,这个过程就叫做依赖注入。
下载与安装
官方网站的说明:http://spring.io/tools
Spring下载地址:https://repo.spring.io/libs-release-local/org/springframework/spring/
Spring 4.2版本文档说明:
- docs - api文档和开发规范
- libs - 开发需要的jar包(源码)
- schema - 开发对应的schema文件
需要引入的JAR包
- spring-beans-4.0.0.RELEASE.jar
- spring-context-4.0.0.RELEASE.jar
- spring-core-4.0.0.RELEASE.jar
- spring-expression-4.0.0.RELEASE.jar
- spring-aop (注解相关需要此包)
- commons-logging-1.1.3.jar 日志相关
- spring集成log4j,需要导入log4j的jar包
实际开发中常用代码
声明式事务(常用)步骤简述
此为事务常用配置步骤,在spring配置文件中进行配置:
- 根据数据源建立事务管理器
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean>
- 对事务管理器的事务行为进行配置 tx:advice(既通知)
<tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="get*" read-only="true"/> <tx:method name="find*" read-only="true"/> <tx:method name="search*" read-only="true"/> <tx:method name="*" propagation="REQUIRED"/> </tx:attributes> </tx:advice>
- 配置织入(指定切入点,然后把通知和切入点进行关联) aop:configaop:pointcutaop:advisor
<aop:config> <aop:pointcut id="txPointcut" expression="execution(* com.imooc.sm.service.*.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/> </aop:config>
Spring整合MyBatis步骤简述
在spring配置文件中进行配置:
<!--
Spring整合Mybatis
此处使用的是数据源,还可以使用一下两种:
org.apache.commons.dbcp.BasicDataSource
com.mchange.v2.c3p0.ComboPooledDataSource
-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/sm?useUnicode=true&characterEncoding=utf-8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</bean>
<!--创建sqlSessionFactory对象-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--指定数据源-->
<property name="dataSource" ref="dataSource"/>
<!--新建别名,可以使实体类的类名作为别名来使用-->
<property name="typeAliasesPackage" value="com.imooc.sm.entity"/>
</bean>
<!--自动映射,同时绑定sqlSessionFactory-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--指定一个包,包中的类是包含映射注解的持久化操作接口-->
<property name="basePackage" value="com.imooc.sm.dao"/>
<!--指定sqlSessionFactory-->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
</bean>
Spring IOC详解
什么是Sping IOC?
Ioc—Inversion of Control,即“控制反转”,是一种设计思想。在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制(例如传统开发中用new来进行创建对象)。IOC既由spring来负责控制对象的生命周期和对象间的关系。
如何实现Spring IOC?
在Java中,可以通过XML配置文件的方式和使用注解的方式(可以独立使用也可以混合使用),来实现Spring的配置。
Spring的工厂类
我们需要通过Spring的工厂类获得Bean实例。Spring工厂类相关类如图:
- 【BeanFactory接口】 是早期版本的工厂接口
- 【ApplicationContext接口】新版本的工厂接口,由BeanFactory派生而来
- 【FileSystemXmlApplicationContext类】工厂类的实现类,加载类路径下的配置文件
- 【ClassPathXmlApplicationContext类】工厂类的实现类,加载系统中的配置文件
Bean的作用域
Spring IOC容器创建一个Bean实例时,可以为Bean指定实例的作用域,不同作用域处理Bean的方式不同。作用域包括singleton(单例模式)、prototype(原型模式)、request(HTTP请求)、session(会话)、global-session(全局会话)。
类别 | 说明 |
---|---|
singleton | (默认)在SpringIOC容器中仅存在一个Bean实例,Bean以单实例的方式存在 |
prototype | 每次调用getBean()时都会返回一个新的实例 |
request | 每次HTTP请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境 |
session | 同一个HTTP Session共享一个Bean,不同的HTTP Session使用不同的Bean。该作用域仅适用于WebApplicationContext环境 |
例子(XML方式):
使用prototype作用域,每次调用getBean()时都会返回一个新的实例。
<bean id="bean1" class="com.qqfh.spring.Bean1" scope="prototype">
Bean的生命周期
Bean完整的生命周期:
执行顺序 | 简要说明 | 详细说明 |
---|---|---|
第一步 | Bean对象实例化 | 执行Bean构造器中的内容 |
第二步 | Bean属性注入 | 根据Spring配置文件中定义的属性信息,调用对应属性的set方法 |
第三步 | BeanNameAware接口的 setBeanName()方法 |
如果Bean实现org.springframework.beans.BeanNameAware类 则执行setBeanName方法传递Bean的ID |
第四步 | ApplicationContextAware接口的 setApplicationContext(ApplicationContext applicationContet)方法 |
如果Bean实现BeanFactoryAware接口 或者 ApplicationContextAware接口(新版本) 则执行setBeanFactory()方法或者setApplicationContext(ApplicationContext applicationContet)方法 |
第五步 | BeanPostProcessor接口的 postProcessBeforeInitialization(Object obj, String s)方法 |
如果Bean类有实现org.springframework.beans.factory.BeanPostProcessor接口,则执行这个Bean类的postProcessBeforeInitialization(Object obj, String s)方法(初始化前方法) |
第六步 | InitializingBean接口的 afterPropertiesSet()方法 |
如果Bean类实现了org.springframework.beans.factory.InitializingBean接口,则执行其afterPropertiesSet()方法 |
第七步 | 执行Bean标签中init-method属性指定的方法 | 执行<bean init-method=“initMethod”>中的"initMethod"(自定义名称)方法 |
第八步 | BeanPostProcessor接口的 postProcessAfterInitialization(Object obj, String s)方法 |
如果Bean类有实现org.springframework.beans.factory.BeanPostProcessor接口,则执行这个Bean类的postProcessAfterInitialization(Object obj, String s)方法(初始化后方法) |
第九步 | 此时Bean可以使用,执行业务相关 | |
第十步 | DisposableBean接口的 destroy()方法 |
如果Bean实现 org.springframework.beans.factory.DisposableBean接口,则执行destroy()方法 |
第十一步 | 执行Bean标签中destroy-method属性指定的方法 | 执行<bean destroy-method=“customerDestroy”>中的"customerDestroy"(自定义名称)方法 |
例子:
Spring配置文件中的加载和销毁。在com.qqfh.spring.XiaoMing类中创建start和end方法,Spring配置文件进行如下配置后,每次Bean被容器加载会调用start方法,Bean销毁时会调用end方法。
<bean id="xiaoming" class="com.qqfh.spring.XiaoMing" init-method="start" destroy-method="end">
生命周期的作用——BeanPostProcessor接口为例
根据Bean的生命周期来看,BeanPostProcessor接口的方法是在实例化之前和实例化之后运行的。所以可以根据生命周期的执行顺序,在BeanPostProcessor的对应方法中对实例进行内部成员变量的改造。
例子:
A_TEST类实现了BeanPostProcessor接口,那么所有Spring里的类实例化之后都会调用postProcessAfterInitialization()方法,此时就可以使用代理对实例的请求进行权限校验。
【MyBeanPostProcessor类文件全文】
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class MyBeanPostProcessor implements BeanPostProcessor {
public Object postProcessAfterInitialization(final Object bean, String beanName) throws BeansException {
if("xiaoming".equals(beanName)){
Object proxy = Proxy.newProxyInstance(bean.getClass().getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if("eat".equals(method.getName())){
System.out.println("权限校验");
return method.invoke(bean,args);
}
return method.invoke(bean,args);
}
});
return proxy;
}else{
return bean;
}
}
}
【XML文件重要部分】
<bean id="xiaoming" class="com.qqfh.spring.XiaoMing">
<property name="name" value="小明"></property>
<property name="foodname" value="苹果"></property>
<property name="taste" value="甜甜的"></property>
</bean>
<bean class="com.qqfh.spring.MyBeanPostProcessor"></bean>
【测试类调用的主方法】
/*
*执行结果:
*权限校验
*小明在吃东西
*/
public static void main(String[] args) {
ApplicationContext app1 = new ClassPathXmlApplicationContext("springTest.xml");
Person xiaoming = (Person) app1.getBean("xiaoming");
xiaoming.eat();
}
Spring使用步骤(XML方式)
- 引入Spring的JAR包
- 新建Spring配置文件(名称可以自定义,格式为xml),添加需要Spring管理的类信息。(注册Bean)
<?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="userService" class="com.qqfh.test.UserServiceImpl"></bean> </beans>
- 新建测试类,在main方法中创建Spring工厂,解析Spring配置文件
ApplicationContext app1 = new ClassPathXmlApplicationContext("xxx.xml");
- 通过Spring工厂的getBean()方法,指定对应ID名称,返回对应类的对象。(接着就可以对xiaoming对象进行下一步操作)
Person xiaoming = (Person) app1.getBean("xiaoming");
Spring使用步骤(注解方式)
- 除了需要引入spring基础包,还需要引入aop的jar包
- 新建spring配置文件。若使用注解方式,需要在spring文件中修改beans标签,增加如下内容:
xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"
- 在spring配置文件中指定对某个包进行注解扫描(用于获取哪些类需要注册到Bean)
<?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"> <!--设置对指定包进行注解扫描--> <context:component-scan base-package="com.qqfh.spring"/> </beans>
- 在com.qqfh.spring包下建的类中增加注解(表示该类交给Spring管理,注册Bean)
//注解的方式注册Bean,表示该类交给Spring管理。括号内的内容是定义一个名称(既id的值) @Component("userService")
- 新建测试类,在main方法中创建Spring工厂,解析Spring配置文件
ApplicationContext app1 = new ClassPathXmlApplicationContext("xxx.xml");
- 通过Spring工厂的getBean()方法,指定对应ID名称,返回对应类的对象。(接着就可以对xiaoming对象进行下一步操作)
Person xiaoming = (Person) app1.getBean("userService");
Spring Bean管理
什么是Spring Bean?
Spring Bean是Spring框架中管理的类,可以说是类的一个身份。例如A类注册到Bean之后,就相当于实例化了,其他类需要A类的对象时,直接通过Spring获取即可。
Bean管理(XML方式)
三种实例化方式
在Spring配置文件中配置:
-
使用构造器实例化(默认使用无参数构造器)
<bean id="bean1" class="com.qqfh.spring.Bean1">
-
使用静态工厂方法实例化(简单工厂模式)
<bean id="bean2" class="com.qqfh.spring.Bean2Factory" factory-method="createBean2"/>
-
使用实例工厂方法实例化(工厂方法模式)
<bean id="bean3Factory" class="com.qqfh.spring.Bean3Factory"/> <bean id="bean3" factory-bean="bean3Factory" factory-method="createBean3"/>
配置文件常用属性
属性名称 | 说明 |
---|---|
id | Bean名称,不包含特殊字符 |
name | Bean名称,和ID属性相同,区别是可以包含特殊字符 |
scope | 可以配置Bean的作用域,用于控制不同环境不同的对象处理方式 |
init-method | Bean生命周期相关: 在Bean加载到容器时调用的方法名称 |
destory-method | Bean生命周期相关: 当Bean从容器中删除的时调用的方法名称(仅在作用域为singleton时有效) |
属性注入
Spring中属性注入有多种方式:
- 通过构造器属性注入
<!--spring配置文件--> <bean id="user" class="com.qqfh.spring.User"> <constructor-arg name="name" value="张三"/> <constructor-arg name="age" value="23"/> </bean>
- 通过setXXX方法进行属性注入(对应类中必须要有属性的set方法)
<!--spring配置文件--> <bean id="person" class="com.qqfh.spring.Person"> <property name="name" value="张三"/> <property name="age" value="32"/> <!--ref属性表示引入一个对象,值为其他bean的id或name--> <property name="pet" ref="cat"/> </bean> <bean id="cat" class="com.qqfh.spring.Cat"> <property name="name" value="ketty"> </bean>
- p命名空间属性注入
<!--spring配置文件--> <!--顶部beans标签处必须新增:xmlns:p="http://www.springframework.org/schema/p"--> <bean id="person" class="com.qqfh.spring.Person" p:name="张三" p:cat-ref="cat"/> <bean id="cat" class="com.qqfh.spring.Cat" p:name="小黄"/>
- SpEL(spring expression language,表达式语言),对依赖注入进行简化(类似EL表达式)
<!--spring配置文件--> <bean id="category" calss="com.qqfh.spring.Category"> <property name="name" value="#{'服装'}"/> </bean> <bean id="productInfo" class="com.qqfh.spring.ProductInfo"/> <bean id="product" class="com.qqfh.spring.Product"> <property name="name" value="#{'男装'}"/> <!--此处是调用了productInfo类的result()方法,把返回结果当做price的值--> <property name="price" value="#{productInfo.result()}"/> <property name="category" value="#{category}"/> </bean>
- 数组和List类型注入
<!--spring配置文件--> <bean id="collectionBean" class="com.qqfh.spring.CollectionBean"> <property name="arrs"> <list> <value>aaa</value> <value>bbb</value> </list> </property> </bean>
- Set类型注入
<!--spring配置文件--> <bean id="collectionBean" class="com.qqfh.spring.CollectionBean"> <property name="arrs"> <set> <value>aaa</value> <value>bbb</value> </set> </property> </bean>
- Map类型注入(两种方式均可)
<!--spring配置文件--> <bean id="collectionBean" class="com.qqfh.spring.CollectionBean"> <property name="arrs"> <map> <entry key="aaa" value="111"/> <entry key="bbb" value="222"/> </map> </property> </bean> <!--下面是另一种方式实现--> <bean id="collectionBean" class="com.qqfh.spring.CollectionBean"> <property name="arrs"> <props> <prop key="username">root</prop> <prop key="password">root</prop> </props> </property> </bean>
Bean管理(注解方式)
Spring 2.5引入使用注解去定义Bean,以下的注解名称用于标识类,表示把类交给Spring管理。目前下面不同名称的功能是一样的,只是代表的含义不同。(可以在不同层使用不同含义的注解)
注解名称 | 说明 |
---|---|
@Component | 描述Spring框架中Bean |
@Repository | 用于对DAO实现类进行标注 |
@Service | 用于对Service实现类进行标注 |
@Controller | 用于对Controller实现类进行标注 |
属性注入
注解名称 | 说明 |
---|---|
@Value() | 注入普通类型。放在变量的上一行,用于传递变量的值。如果有set方法,也可以放在set方法上 |
@Autowired | 注入对象。按类型对应,放在变量的上一行 |
@Qualifier(“userDao”) | 注入对象。按指定名称对应,需要和Autowired一起使用,放在变量的上一行 |
@Resource(name=“userDao”) | 注入对象。按指定名称进行对应。相当于@[email protected]的功能,放在变量的上一行 |
其他注解
注解名称 | 说明 |
---|---|
@PostConstruct | 标记为类初始化时执行的方法,放在方法的上一行 |
@PreDestroy | 标记为类销毁时执行的方法,放在方法的上一行 |
@Scope() | 指定Bean作用范围,放在方法的上一行 |
XML和注解方式混合使用
通常会使用XML的方式把类交给Spring管理,通过注解的方式进行属性注入。具体步骤如下:
- 修改spring配置文件,使spring支持注解方式(引入context命名空间)
- 在配置文件中增加标签,使类支持属性注入的相关注解
<context:annotation-config/>
- 在配置文件中加入相关类的Bean
- 在的类中添加注解进行属性注入
Spring AOP详解
什么是Spring AOP?
AOP(Aspect Oriented Programming),在运行期通过代理方式向目标类织入增强代码。被称为一种"横切"的技术,采用横向抽取机制(代理机制),利用AOP可以把与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。
AOP相关术语
名称 | 说明 |
---|---|
Joinpoint(连接点) | 指可以被拦截到的点 |
Pointcut(切入点) | 真正被拦截到的连接点 |
Advice(通知/增强) | 拦截后要做的事情 |
Introducton(引介) | 动态在类添加新的方法或属性,需要使用第三方技术 |
Target(目标对象) | 被增强的对象 |
Weaving(织入) | 将Advice应用到Targe的过程 |
Proxy(代理) | 被应用增强后,产生一个代理对象 |
Aspect(切面) | 切入点和通知的组合 |
AOP的底层实现
Spring AOP的底层是通过JDK动态代理或CGLib动态代理技术,为目标Bean执行横向织入。Spring只支持方法连接点。
- JDK动态代理:对实现接口的类进行代理
- CGLIB动态代理:底层字节码技术,通过创建子类解决无接口代理问题
AOP通知类型
SpringAOP按照通知Advice在目标类方法的连接点位置,可以分为5种通知类型,分别是前置通知,后置通知,环绕通知,异常抛出通知,引介通知。
- 前置通知 org.springframework.aop.MethodBeforeAdvice
- 在目标方法执行前实施增强
- 后置通知 org.springframework.aop.AfterReturningAdvice
- 在目标方法执行后实施增强
- 环绕通知 org.aopalliance.intercept.MethodInterceptor
- 在目标方法执行前后实施增强
- 异常抛出通知 org.springframework.aop.ThrowsAdvice
- 在方法抛出异常后实施增强
- 引介通知 org.springframework.aop.IntroductionInterceptor
- 在目标类中添加一些新的方法和属性
AOP切面类型
名称 | 说明 |
---|---|
Advisor | 代表一般切面,Advice本身就是一个切面,对目标类所有方法进行拦截 |
PointcutAdvisor | 代表具有切点的切面,可以指定拦截目标类哪些方法 |
IntroductionAdvisor | 代表引介切面,针对引介通知而使用切面(不要求掌握) |
AOP的手动代理
常用的的属性说明:
属性名称 | 说明 |
---|---|
proxyTargetClass | 是否对类代理而不是接口,设置为true时,使用CGLib代理 |
interceptorNames | 需要织入目标的Advice名称 |
singleton | 返回代理是否单例,默认为单例 |
optimize | 当设置为true时,强制使用CGLib代理 |
Advisor切面(一般切面)
使用Advisor切面需要有一个接口类和一个实现类,通过接口类和
Advisor切面实现步骤:
- 引入aopalliance1.0(AOP联盟)、spring-aop的JAR包
- 创建spring配置文件,文件名xxx.xml(自定义)
- 在spring配置文件中增加目标类(实现类)
<bean id="person" class="com.qqfh.spring.XiaoMing"/>
- 新建增强类,实现MethodBeforeAdvice接口,实现before方法。
- 在spring配置文件中加入增强类
<bean id="myBeforeAdvice" class="com.qqfh.spring.myBeforeAdvice"/>
- 在spring配置文件中加入AOP产生代理对象的信息
<!--Spring的AOP产生代理对象--> <bean id="xiaomingProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <!--配置目标类--> <property name="target" ref="xiaoming"/> <!--实现的接口--> <property name="proxyInterfaces" value="com.qqfh.spring.Person"/> <!--采用拦截名称--> <property name="interceptorNames" value="myBeforeAdvice"/> </bean>
- 已配置完成,可以进行测试类的编写。
Advisor切面完整例子:通过代理实现前置增强
a. Person.java文件:
package com.qqfh.spring;
public interface Person {
public void eat();
public void play();
}
b. XiaoMing.java文件:
package com.qqfh.spring;
public class XiaoMing implements Person {
public void eat() {
System.out.println("在吃东西");
}
public void play() {
System.out.println("在玩耍");
}
public void start() {
System.out.println("要开始啦");
}
public void end() {
System.out.println("要结束啦");
}
}
c.myBeforeAdvice.java文件:
package com.qqfh.spring;
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
public class myBeforeAdvice implements MethodBeforeAdvice {
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("!!!!!!!!前置增强");
}
}
d. springTest.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">
<!-- 目标类 -->
<bean id="xiaoming" class="com.qqfh.spring.XiaoMing"/>
<!-- 增强类 -->
<bean id="myBeforeAdvice" class="com.qqfh.spring.myBeforeAdvice"/>
<!--Spring的AOP产生代理对象-->
<bean id="xiaomingProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!--配置目标类-->
<property name="target" ref="xiaoming"/>
<!--实现的接口-->
<property name="proxyInterfaces" value="com.qqfh.spring.Person"/>
<!--采用拦截名称-->
<property name="interceptorNames" value="myBeforeAdvice"/>
<property name="optimize" value="true"></property>
</bean>
</beans>
e.Spring_Demo1.java文件:
package com.qqfh.spring;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
/*
*执行结果:
*!!!!!!!!前置增强
*在吃东西
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:springTest.xml")
public class Spring_Demo1 {
@Resource(name="xiaomingProxy")
private XiaoMing xiaoming;
@Test
public void demo1() {
xiaoming.eat();
}
}
PointcutAdvisor 切点切面(带有切入点的切面)
使用JdkRegexpMethodPointcut构造正则表达式切点实现步骤:
- 新建需要目标类(需要被增强的类),无需实现接口
<bean id="xiaoming" class="com.qqfh.spring.demo2.XiaoMing"/>
- 在spring配置文件中增加目标类信息
- 新建通知类型(实现MethodInterceptor接口,实现invoke方法)
- 在spring配置文件中增加通知信息
<bean id="myAroundAdvice" class="com.qqfh.spring.demo2.MyAroundAdvice"/>
- 配置带有切入点的切面,通过正则表达式匹配对应的方法增强
<!-- 用于配置带有切入点的切面,相当于告诉AOP哪些方法需要增强 --> <bean id="myAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <!-- 通过正则表达式挑出需要增强的方法 --> <property name="pattern" value=".*"/> <!-- 应用环绕的通知 --> <property name="advice" ref="myAroundAdvice"/> </bean>
- 在spring配置文件中配置生产代理
<!-- 配置生产代理 --> <bean id="customerDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="xiaoming"/> <!-- 表示需要用没有接口的代理 --> <property name="proxyTargetClass" value="true"/> <property name="interceptorNames" value="myAdvisor"/> </bean>
- 已配置完成,可以进行测试类的编写。
使用JdkRegexpMethodPointcut构造正则表达式切点完整案例:通过代理实现指定的eat方法增强
a. XiaoMing.java文件:
package com.qqfh.spring.demo2;
public class XiaoMing{
public void eat() {
System.out.println("在吃东西");
}
public void play() {
System.out.println("在玩耍");
}
public void start() {
System.out.println("要开始啦");
}
public void end() {
System.out.println("要结束啦");
}
}
b. MyAroundAdvice.java文件:
package com.qqfh.spring.demo2;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class MyAroundAdvice implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("!!!!!!!!!!!!环绕前增强");
//执行目标方法
Object obj = invocation.proceed();
System.out.println("环绕后增强!!!!!!!!!!!!");
return obj;
}
}
c. springDemo2.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">
<!-- 目标类 -->
<bean id="xiaoming" class="com.qqfh.spring.demo2.XiaoMing"/>
<!-- 配置通知 -->
<bean id="myAroundAdvice" class="com.qqfh.spring.demo2.MyAroundAdvice"/>
<!-- 用于配置带有切入点的切面,相当于告诉AOP哪些方法需要增强 -->
<bean id="myAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<!-- 通过正则表达式挑出需要增强的方法 -->
<property name="pattern" value=".*eat"/>
<!-- 应用环绕的通知 -->
<property name="advice" ref="myAroundAdvice"/>
</bean>
<!-- 配置生产代理 -->
<bean id="customerDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="xiaoming"/>
<!-- 表示需要用没有接口的代理 -->
<property name="proxyTargetClass" value="true"/>
<property name="interceptorNames" value="myAdvisor"/>
</bean>
</beans>
d. TestDemo2.java文件:
package com.qqfh.spring.demo2;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.annotation.Resource;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:springDemo2.xml")
public class TestDemo2 {
@Resource(name="customerDaoProxy")
private XiaoMing xiaoming;
@Test
public void demo1() {
xiaoming.eat();
xiaoming.play();
}
}
AOP的动态代理
使用自动创建代理,可以便捷的创建多个Bean。通常用三种方式实现自动代理。
基于Bean名称的自动代理(BeanNameAutoProxyCreator)
把ID以Dao结尾的Bean所有方法使用代理实现步骤:
- 新建好所有接口类、实体类和增强类
- 把上面的类新增到spring配置文件中
- 在配置文件中配置自动增强
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <!--指定哪些bean需要增强--> <property name="beanNames" value="*Dao"/> <!--指定使用哪种增强--> <property name="interceptorNames" value="myBeforeAdvice"/> </bean>
- 已经配置完成,可以进行相应测试。
基于切面信息的自动代理(DefaultAdvisorAutoProxyCreator)
基于切面信息产生代理实现步骤:
- 新建好所有接口类、实体类和增强类
- 把上面的类新增到spring配置文件中
- 在spring配置文件中配置切面信息
<bean id="myAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <!-- 指定XiaoMing类下的save方法为切面 --> <property name="pattern" value="com.qqfh.spring.XiaoMing.save"/> <!-- 应用环绕的通知 --> <property name="advice" ref="myAroundAdvice"/> </bean>
- 在spring配置文件中配置根据切面产生代理
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"></bean>
- 已经配置完成,可以进行相应测试。
基于Bean中的AspectJ注解进行自动代理(AnnotationAwareAspectJAutoProxyCreator)
暂无相关内容
基于AspectJ的AOP开发(常用,重点)
什么是AspectJ?
AspectJ是一个基于Java语言的AOP框架。是目前最常用的AOP实现技术。
AspectJ需要的JAR包
AspectJ需要导入以下JAR包:
spring-aop
com.rpingsource.org.aopalicance-1.0.0.jar
spring-aspects
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
AspectJ的通知类型(增强类型)
通知名称 | 说明 | 附加信息 |
---|---|---|
@Befor | 前置通知 相当于BeforeAdvice |
可以在方法中传入JoinPoint对象,用来获取切点信息 |
@AfterReturning | 后置通知 相当于AfterReturningAdvice |
通过配置属性returning=“result”,方法中增加参数Object result,则可以通过result获取方法返回值 |
@Around | 环绕通知 相当于MethodInterceptor |
方法中的参数ProceedingJoinPoint可以执行目标的方法。可以配置返回值。 |
@AfterThrowing | 异常抛出通知 相当于ThrowAdvice |
通过配置属性throwing=“e”,方法中增加参数Throwable e,可以获得异常信息 |
@After | 最终通知,不管是否异常,该通知都会执行 | |
@DeclareParents | 引介通知 相当于IntroductionInterceptor |
AspectJ定义切点语法
语法:
execution([访问修饰符] 返回类型 方法名(参数)异常)
例子:
语法 | 说明 |
---|---|
execution(public * *(…)) | 匹配所有类public方法 |
execution(* com.qqfh.dao.*(…)) | 匹配指定包下的所有方法,不包含子包 |
execution(* com.qqfh.dao…*(…)) | 匹配指定包下的所有方法,包含子包 |
execution(* com.qqfh.UserService.*(…)) | 匹配指定类所有方法 |
execution(* com.qqfh.dao.GenericDAO+.*(…)) | 匹配实现特定接口类所有方法 |
execution(* save*(…)) | 匹配所有save开头的方法 |
AspectJ切点命名语法
可以通过切点命名的方法,定义一个切点,让别的注解调用。
使用方法:
使用@Pointcut注解定义切点名,放在空方法上。
//方法名称“myPointcut1”也是切点的名称,其他切点可以调用该名称
@Pointcut(value="execution(* com.qqfh.aspectJ.ProductDao.save(..))")
private void myPointcut1(){}
//以下是调用,放在方法上一行
@Before(value="myPointcut1()")
AspectJ的注解方式开发AOP使用步骤
- 引入AOP和AspectJ相关jar包。
- 配置spring配置文件:
xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd" <!--开启AspectJ的注解开发,自动代理--> <aop:aspectj-autoproxy />
- 建立目标类,可以不实现接口
- 把目标类添加进spring配置文档(等于是IOC方式)
<bean id="productDao" class="com.qqfh.aspectJ.demo1"/>
- 建立切面类(增强类),在类的上一行增加@Aspect,代表是切面类
- 在切面类中编写增强方法,在方法的上一行添加@Before类型通知
//代表把下方的方法增强到匹配到的目标类中 @Before(value="execution(* com.qqfh.aspectJ.demo1.*(..))")
- 在spring配置文件中增加切面类
<bean class="com.qqfh.aspectJ.demo1.MyAspectAnno"/>
- 此时就完成了,接着可以进行测试类的编写。
AspectJ的XML方式开发AOP使用步骤
- 引入AOP和AspectJ相关jar包。
- 配置spring配置文件:
xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"
- 新建目标类,可以实现接口
- 在spring配置文件中增加目标类配置
- 新建一个切面类
- 在spring配置文件中增加切面类配置
<bean id="myAspectXml" class="com.qqfh.aspectJ.demo2.MyAspectXml"/>
- 在spring配置文件中配置aop
<aop:config> <!--配置切入点--> <aop:pointcut id="pointcut1" expression="execution(* com.qqfh.aspectJ.demo2.CustomerDao.save(..))"/> <!--配置AOP的切面--> <aop:aspect ref="myAspectXml"> <!--配置前置通知,method属性是切面类里的方法--> <aop:before method="before1" pointcut-ref="pointcut1"/> </aop:aspect> </aop>
- 此时就完成了,接着可以进行测试类的编写。
Spring的JDBC Template
什么是JDBC Template?
Spring组件JDBC Template可以简化持久化(JDBC)操作。使用JDBC Template操作数据库非常简单灵活。
需要的JAR包
mysql-connector-java
spring-core
spring-beans
spring-context
spring-aop
spring-jdbc (JDBC)
spring-tx (事务处理)
使用步骤
- 配置spring配置文件
<!--配置数据源--> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"> <property name="url" value="jdbc:mysql://127.0.0.1:3306/selection_course?useUnicode=true&characterEncoding=utf-8"> <property name="username" value="root"> <property name="password" value="root"> </bean> <!--配置JDBC Template工具类--> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"/> </bean>
- 在Java中使用JDBC Template
//在配置文件获取上下文对象。 ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml"); //获取JdbcTemplate对象 JdbcTemplate jdbcTemplate = (JdbcTemplate)context.getBean("jdbcTemplate"); jdbcTemplate.execute("create talbe user1(id int,name varchar(20))");
- (若使用第三步则无需设置第二步)在实现过程中,也可以使用spring ioc来获取JdbcTemplate对象。需要在spring配置文件中开启自动扫描,然后对应的类文件设置注册Bean。
<!--spring文件中:--> <!--开启自动扫描--> <context:component-scan base-package="com.qqfh.jdbctemplate"/>
//Dao文件中: @Repository //开启自动注册,在类的上一行添加 @Autowired //进行属性注入,在JdbcTemplate对象的上一行添加
JdbcTemplate类
JDBC Template中,执行SQL语句都是通过JdbcTemplate类的方法实现。
方法 | 说明 | 返回值 |
---|---|---|
execute() | 通常用来执行DDL语句 | |
update(String sql,Object[] args) | 对数据进行增删改操作,返回影响的行数 | int |
update(String sql,Object… args) | 比较新的语句。对数据进行增删改操作,返回影响的行数 | int |
batchUpdate(String[] sql) | 批量增删改操作 | int[] |
batchUpdate(String sql,List<Object[]> args) | 批量增删改操作 | int[] |
queryForObject(String sql,Class type) | 查询获取一个数据,通过type定义返回值类型 | T |
queryForObject(String sql,Object[] args, Class type) | 查询获取一个数据,args代表占位符的内容,通过type定义返回值类型 | T |
queryForObject(String sql,Class type,Object… arg) | 查询获取一个数据,args代表占位符的内容,通过type定义返回值类型 | T |
queryForList(String sql,Class type) | 查询获取多个数据,通过type定义返回值类型 | List |
queryForList(String sql,Object[] args,Class type) | 查询获取多个数据,args代表占位符的内容,通过type定义返回值类型 | List |
queryForList(String sql,Class type,Object… arg) | 查询获取多个数据,args代表占位符的内容,通过type定义返回值类型 | List |
queryForMap(String sql) | 查询获取一个数据 | Map |
queryForMap(String sql,Object[] args) | 查询获取一个数据,通过args传递占位符的内容 | Map |
queryForMap(String sql,Object… arg) | 查询获取一个数据,通过arg传递占位符的内容 | Map |
queryForList(String sql) | 查询获取多个数据 | List<Map<String,Object>> |
queryForList(String sql,Object[] args) | 查询获取多个数据,通过args传递占位符的内容 | List<Map<String,Object>> |
queryForList(String sql,Object… arg) | 查询获取多个数据,通过arg传递占位符的内容 | List<Map<String,Object>> |
queryForObject(String sql,RowMapper mapper)) | 查询获取数据,并封装到实现RowMapper接口的实体对象中。 | T |
queryForObject(String sql,Object[] args,RowMapper mapper) | 查询获取数据,并封装到实现RowMapper接口的实体对象中,通过args传递占位符的内容 | T |
queryForObject(String sql,RowMapper mapper),Object… arg) | 查询获取数据,并封装到实现RowMapper接口的实体对象中,通过arg传递占位符的内容 | T |
query(String sql,RowMapper mapper) | 查询获取多个数据,并封装到实现RowMapper接口的实体对象中。 | List |
query(String sql,Object[] args,RowMapper mapper) | 查询获取多个数据,并封装到实现RowMapper接口的实体对象中,通过args传递占位符的内容 | List |
query(String sql,RowMapper mapper,Object… arg) | 查询获取多个数据,并封装到实现RowMapper接口的实体对象中,通过arg传递占位符的内容 | List |
call | 调用存储过程 |
例子:
新增数据update(String sql,Object[] args)形式
String sql="insert into sutdent(name.sex) value(?,?)";
jdbcTemplate.update(sql,new Object[]{"张飞","男"});
修改数据update(String sql,Object… args)形式
String sql="update sutdent set sex=? where id=?";
jdbcTemplate.update(sql,"女",1003);
批量操作数据batchUpdate(String[] sql)形式
String[] sqls={
"insert into sutdent(name.sex) value("张三","男")",
"insert into sutdent(name.sex) value("李四","男")",
"insert into sutdent(name.sex) value("王五","女")",
"update sutdent set sex="女" where id=2001"
};
jdbcTemplate.batchUpdate(sqls);
批量新增数据batchUpdate(String sql,List<Object[]> args)方式
String sql="insert into selection(student,course) value(?,?)";
List<Object[]> list = new ArrayList<Object[]>();
list.add(new Object[]{1005,1001});
list.add(new Object[]{1005,1003});
jdbcTemplate.batchUpdate(sql,list);
查询总数queryForObject(String sql,Class type)形式
String sql="select count(*) from student";
int count = jdbcTemplate.queryForObject(sql,Integer.class)
按照某个条件查询queryForList(String sql,Class type,Object… arg)形式
String sql = "select name from student where sex=?";
List<String> names = jdbcTemplate.queryForList(sql,String.class,"女");
查询多条数据queryForList(String sql)形式
String sql = "select * from student";
List<Map<String,Object>> stus = jdbcTemplate.queryForList(sql);
查询一调数据并封装为实体对象queryForObject(String sql,RowMapper
mapper),Object... arg)形式
String sql="select * from student where id=?";
Student stu = jdbcTemplate.queryForObject(sql,newRowMapper<Student>(){
//resultSet代表结果集,int代表结果集的行号
public Student mapRow(ResultSet resultSet,int i)throws SQLException{
Student stu = new Student();
stu.setId(resultSet.getInt("id"));
stu.setName(resultSet.getString("name"));
stu.setSex(resultSet.getString("sex"));
stu.setBorn(resultSet.getDate("born"));
return stu;
}
},1004);
Spring事务管理
什么是事务?
指作为一个程序执行单元执行的一系列操作,要么执行完成,要么完成不执行。事务一般特指数据库事务(Database Transaction)。
需要的JAR包
spring-tx(事务处理)
事务的特性
特性名称 | 说明 |
---|---|
原子性(atomicity) | 一个事务是一个不可分割的工作单位。(只有执行成功和执行不成功两种状态) |
一致性(consistency) | 事务必须是使数据库从一个一致性状态变道另一个一致性状态。(业务中传递数据时必须完整一致) |
隔离性(isolation) | 一个事务的执行不能被其他事务干扰。 |
持久性(durability) | 一个事务一但提交,它对数据库中数据的改变就应该是永久性的。 |
事务并发的问题
问题名称 | 说明 |
---|---|
脏读 | 事务A提交了商品库存修改,此时事务B提交查询获得商品库存,这时事务A进行回滚导致。这就叫脏读 |
不可重复读 | 因为事务B的修改操作在事务A在两次查询之间,导致事务A两次查询结果不一致。这就叫不可重复读 |
幻读 | 事务A修改所有商品库存为0,此时事务B增加了一个库存为100的商品,事务A再查询时发现还有一个商品库存为100。这就叫幻读 |
MySQL的事务处理
MySQL事务特点和命令
特点
- 只有使用Innodb数据库引擎的数据库或表才支持事务
- showengines; --查看服务器支持的引擎
- default-storage-engine = Innodb --在my.ini中修改默认引擎
- MySQL默认以自动提交(autocommit)模式运行。
- 如果不想MySQL自动提交事务,可以使用MySQL命令改变事务提交
命令
MySQL命令 | 说明 |
---|---|
BEGIN | 显式地开启一个事务 |
START TRANSACTION | 显式地开启一个事务,和BEGIN一样,只是写法不一样 |
COMMIT | 提交事务(把BEGIN后的语句打包成一个事务),并使已对数据库进行的所有修改变为永久性的。 |
ROLLBACK | 回滚事务(把BEGIN后的语句打包成一个事务),并撤销正在进行的所有未提交的修改。 |
MySQL的事务隔离级别
通过隔离解级别解决事务并发导致的问题。
隔离级别设置语法:
select @@tx_isolation --查询默认隔离级别
set session transaction isolation level XXX --设置当前会话隔离级别
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交(read-uncommitted) | 是 | 是 | 是 |
读已提交(read-committed) | 否 | 是 | 是 |
可重复读(repeatable-red)(MySQL默认) | 否 | 否 | 是 |
串行化(serializable) | 否 | 否 | 否 |
JDBC的事务处理
JDBC事务特点和命令
特点
- JDBC的事务处理基于Connection。可以把通过一个Connection对象的SQL语句封装成一个事务。不能把多个Connection对象里的SQL语句封装到一个事务。
- JDBC默认事务处理行为是自动提交。(既每一句SQL语句都是一个单独的事务)
- JDBC中通过Statement类的相关方法处理事务
命令
【Statement类】可以设置事务
方法名称 | 说明 |
---|---|
setAutoCommit() | 设置自动提交,true为自动提交,false为不自动提交 |
commit | 提交事务 |
rollback | 回滚事务 |
JDBC事务隔离级别
通过隔离解级别解决事务并发导致的问题。可以在Connection中设置事务隔离级别。
事务隔离级别 | 对应数值 |
---|---|
TRANSACTION_NONE(不支持事务) | 0 |
TRNSACTION_READ_UNCOMMITTED(读未提交) | 1 |
TRNSACTION_READ_COMMITTED(读已提交) | 2 |
TRNSACTION_REPEATABLE_READ(可重复读) | 4 |
TRNSACTION_SERIZLIZABLE(串行化) | 8 |
设置隔离级别:【Connection类】
方法名称 | 说明 |
---|---|
getTransactionIsolation() | 获取当前隔离级别 |
setTransactionIsolation(int level) | 设置隔离级别,level参数值代表隔离级别 |
Spring事务处理实现方式
Spring对于事务处理有两大实现方式(编程式和声明式)。以下图片为事务处理API主要的接口。spring中事务处理有三个主要的接口类,还有三个根据不同功能而不同实现的实现类。
【PlatformTransactionManager接口】
事务管理器,用于开启、提交和回滚事务。
方法 | 说明 | 返回值 |
---|---|---|
getTransaction(TD td) | 开启、获取一个事务,需要传入事务定义 | TS |
commit(TS ts) | 提交事务,需要传入事务状态 | void |
rollback(TS ts) | 回滚事务,需要传入事务状态 | void |
【TransactionDefinition接口】
用于事务定义,定义事务的特点和属性,还声明了事务传播行为、事务隔离级别、事务默认超时的全局变量。在事务管理器创建事务管理时,该类需要传递给事务管理器。
方法名称 | 说明 | 返回值 |
---|---|---|
getIsolationLevel() | 定义隔离级别 | int |
getPropagationBehavior() | int | |
getTimeout() | 超时时间 | int |
isReadOnly() | 是否是只读事务 | boolean |
全局变量
隔离级别 | 说明 |
---|---|
ISOLATION_DEFAULT | 使用数据库默认 |
ISOLATION_READ_UNCOMMITTED | 读未提交 |
ISOLATION_READ_COMMITTED | 读已提交 |
ISOLATION_REPEATABLE_READ | 可重复读 |
ISOLATION_SERIALIZABLE | 串行化 |
默认超时 | 说明 |
---|---|
TIMEOUT_DEFAULT | 默认30秒,超过30秒没有响应则销毁事务 |
事务传播行为 | 说明 |
---|---|
PROPAGATION_REQUIRED | 支持当前事务,如果当前没有事务,就新建一个事务。 |
PROPAGATION_SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行。 |
PROPAGATION_MANDATORY | 支持当前事务,如果当前没有事务,就抛出异常。 |
PROPAGATION_REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起。 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
PROPAGATION_NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,就新建一个事务。 |
【transactionStatus接口】
代表事务状态,由事务管理器创建。反应了当前的正在运行的事务状态
方法 | 说明 | 返回值 |
---|---|---|
isNewTransaction() | boolean | |
isRollbackOnly() | boolean | |
setRollbackOnly() | void | |
isCompleted() | boolean |
【DataSourceTransactionManager类】
基于数据源的事务管理器,jdbc/mybatis/jdbc template这几种方式可以使用该类。PlatformTransactionManager的实现类
【HibernateTransactionManager类】
基于Hibernate的事务管理器。PlatformTransactionManager的实现类
【JpaTransactionManager类】
基于Jpa的事务管理器。PlatformTransactionManager的实现类
Spring编程式事务处理
编程式是通过代码的方式封装事务。
基于底层API的编程事务管理实现流程
- 建立JDBC Template实现数据库处理,新建好持久层DAO、业务层service和实体类entity
- 在业务类中,需要封装为一个事务处理的代码前面,设置开启事务
//事务管理器 @Autowired PlatformTransactionManager transactionManager; //事务定义 @Autowired TransactionDefinition transactionDefinition; //设置事务管理的起始位置。需要一个事务定义的对象做参数,返回事务状态的对象 TransactionStatus transactionStatus = transactionManager.getTransaction(transactionDefinition); try{ //这里就是具体的事务内容 //提交事务,需要传递事务状态对象。 transactionManager.commit(transactionStatus); }catch(Exception e){ transactionManager.rollback(transactionStatus); }
- 在spring配置文件中进行配置
<!--引入其他配置文件--> <import resource="spring-dao.xml"> <!--开启自动扫描--> <context:component-scan base-package="com.qqfh.service.impl"/> <!--事务管理器的配置。用jdbctemplate实现的话,需要选择类型DataSourceTransactionManager--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!--关联datasource--> <property name="dataSource" ref="dataSource"/> </bean> <!--事务定义--> <bean id="transactionDefinition" class="org.springframework.transaction.support.DefaultTransactionDefinition"> <!--配置事务传播行为--> <property name="propagationBehaviorName" value="PROPAGATION_REQUIRED"/> </bean>
- 已经配置完成。可以新建测试类进行测试。
基于TransactionTemplate的编程式事务管理实现流程
使用流程
- 建立JDBC Template实现数据库处理,新建好持久层DAO、业务层service和实体类entity
- 在业务类中,创建TransactionTemplate对象(事务模板)
prviate TransactionTemplate transactionTemplate;
- 配置事务,该方式会自动提交封装,只需要设置回滚(注意设置自动注册)
//使用事务模板的方式来封装事务 transactionTemplate.execute(new TransactionCallback(){ public Object doInTransaction(TransactionStatus transactionStatus){ try{ //需要封装的语句 }catch(Exception e){ e.printStackTrace(); //设置回滚。如果报错则回滚 transactionStatus.setRollbackOnly(); } return null; } })
- 在spring配置文件中,配置事务管理器,声明TransactionTemplate来进行事务处理。(注意设置自动扫描)
<!--事务管理器的配置。用jdbctemplate实现的话,需要选择类型DataSourceTransactionManager--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!--关联datasource--> <property name="dataSource" ref="dataSource"/> </bean> <!--开启自动扫描--> <context:component-scan base-package="com.qqfh.service.impl"/> <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"> <property name="transactionManager" ref="transactionManager"/> </bean>
- 已经配置完成。可以新建测试类进行测试。
Spring声明式事务处理(推荐)
Spring的声明式事务处理是建立在AOP的基础之上的。本质是在方法前后进行拦截,在方法开始前添加一个事务,待执行后根据情况提交事务或者回滚事务。
声明式事务通过AOP代理方式实现事务管理,利用环绕通知TransactionInterceptor实现事务的开启及关闭,而TransactionProxyFactoryBean内部也是通过该环绕通知实现的,因此可以认为是tx:tags/帮你定义了TransactionProxyFactoryBean,从而简化事务管理。
基于TransactionInterceptor(拦截器)的声明式事务管理实现流程
- 建立JDBC Template实现数据库处理,新建好持久层DAO、业务层service和实体类entity
- 建立业务impl类代码
- 新建spring配置文件,配置事务管理器,
<!--事务管理器的配置。用jdbctemplate实现的话,需要选择类型DataSourceTransactionManager--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!--关联datasource--> <property name="dataSource" ref="dataSource"/> </bean> <!--配置拦截目标对象--> <bean id="orderServiceTarget" class="com.qqfh.spring.OrderServiceImpl"/> <!--配置拦截器--> <bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor"> <!--注入对应属性--> <!--采用哪个事务管理器--> <property name="transactionManager" ref="transactionManager"/> <!--对应事务的属性,比如隔离级别、事务传播行为等等--> <property name="transactionAttributes"> <props> <!--key代表业务类中的方法名,表示配置该方法用什么样的事务属性--> <!-- PROPAGATION_REQUIRED:传播行为为必须。 readOnly:因为是get,所以配置为只读 --> <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop> <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop> <prop key="search*">PROPAGATION_REQUIRED,readOnly</prop> <!----> <prop key="*">PROPAGATION_REQUIRED</prop> </props> </property> </bean> <!--把拦截器和目标进行关联--> <bean id="orderService" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target" ref="orderServiceTarget"/> <property name="interceptorNames"> <list> <idref bean="transactionInterceptor"/> </list> </property> </bean>
- 已经配置完成。可以新建测试类进行测试。
基于TransactionProxyFactoryBean(简化拦截器)的声明式事务管理实现流程
- 该方式是对TransactionInterceptor(拦截器)的简化操作,把拦截器配置和关联配置合成一段代码。(其他代码和拦截器的一样)
<!--事务管理器的配置。用jdbctemplate实现的话,需要选择类型DataSourceTransactionManager--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!--关联datasource--> <property name="dataSource" ref="dataSource"/> </bean> <!--配置拦截目标对象--> <bean id="orderServiceTarget" class="com.qqfh.spring.OrderServiceImpl"/> <bean id="orderService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <!--采用哪个事务管理器--> <property name="transactionManager" ref="transactionManager"/> <!--对应事务的属性,比如隔离级别、事务传播行为等等--> <property name="transactionAttributes"> <props> <!--key代表业务类中的方法名,表示配置该方法用什么样的事务属性--> <!-- PROPAGATION_REQUIRED:传播行为为必须。 readOnly:因为是get,所以配置为只读 --> <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop> <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop> <prop key="search*">PROPAGATION_REQUIRED,readOnly</prop> <!----> <prop key="*">PROPAGATION_REQUIRED</prop> </props> </property> <!--目标对象--> <property name="target" ref="orderServiceTarget"/> </bean>
基于tx命名空间的声明式事务管理实现流程(常用)
- 建立JDBC Template实现数据库处理,新建好持久层DAO、业务层service和实体类entity
- 建立业务impl类代码
- 新建spring配置文件,通过tx命名空间实现事务。
<?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: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.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.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!-- 加载其他配置文件 --> <import resource="spring-dao.xml"/> <!-- 配置自动扫描 --> <context:component-scan base-package="com.qqfh.os.service.impl"/> <!-- 配置事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!-- 配置通知,既增强的内容,transaction-manager为指定事务管理器--> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <!-- 匹配目标对象的方法,增加事务属性(propagation事务传播行为=必须,开启只读) --> <tx:method name="get*" propagation="REQUIRED" read-only="true"/> <tx:method name="find*" propagation="REQUIRED" read-only="true"/> <tx:method name="search*" propagation="REQUIRED" read-only="true"/> <!-- 所有方法都以事务的方式执行 --> <tx:method name="*" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <!-- 配置增强作用的目标 --> <aop:config> <!-- 配置切入点。指定哪些类和哪些方法是切入点 --> <aop:pointcut id="pointcut" expression="execution(* com.qqfh.os.service.impl.*.*(..))"/> <!-- 把切入点和通知进行关联( advice-ref=通知,pointcut-ref=切入点)--> <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/> </aop:config> </beans>
- 已经配置完成。可以新建测试类进行测试。
基于@Transactional(注解)的声明式事务管理实现流程(常用)
- 建立JDBC Template实现数据库处理,新建好持久层DAO、业务层service和实体类entity
- 建立业务impl类代码
- 新建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: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.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.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <import resource="spring-dao.xml"/> <context:component-scan base-package="com.imooc.os.service.impl6"/> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <tx:annotation-driven transaction-manager="transactionManager"/> </beans>
- 建立业务类代码,需要在业务代码的方法上一行增加注解
//可以定义事务传播行为,例如“@Transactional(propagation = Propagation.REQUIRED)”(默认也是这个) @Transactional