SpringIOC/AOP/事务管理

SpringIOC/AOP/事务管理学习笔记,方便后期查阅。

文章目录

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配置文件中进行配置:

  1. 根据数据源建立事务管理器
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    
  2. 对事务管理器的事务行为进行配置 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>
    
  3. 配置织入(指定切入点,然后把通知和切入点进行关联) 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&amp;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工厂类相关类如图:

SpringIOC/AOP/事务管理
  • 【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方式)

  1. 引入Spring的JAR包
  2. 新建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>
    
  3. 新建测试类,在main方法中创建Spring工厂,解析Spring配置文件
    ApplicationContext app1 = new ClassPathXmlApplicationContext("xxx.xml");
    
  4. 通过Spring工厂的getBean()方法,指定对应ID名称,返回对应类的对象。(接着就可以对xiaoming对象进行下一步操作)
    Person xiaoming = (Person) app1.getBean("xiaoming");
    

Spring使用步骤(注解方式)

  1. 除了需要引入spring基础包,还需要引入aop的jar包
  2. 新建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"
    
  3. 在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>
    
  4. 在com.qqfh.spring包下建的类中增加注解(表示该类交给Spring管理,注册Bean)
    //注解的方式注册Bean,表示该类交给Spring管理。括号内的内容是定义一个名称(既id的值)
    @Component("userService")
    
  5. 新建测试类,在main方法中创建Spring工厂,解析Spring配置文件
    ApplicationContext app1 = new ClassPathXmlApplicationContext("xxx.xml");
    
  6. 通过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管理,通过注解的方式进行属性注入。具体步骤如下:

  1. 修改spring配置文件,使spring支持注解方式(引入context命名空间)
  2. 在配置文件中增加标签,使类支持属性注入的相关注解
    <context:annotation-config/>
    
  3. 在配置文件中加入相关类的Bean
  4. 在的类中添加注解进行属性注入

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切面实现步骤:

  1. 引入aopalliance1.0(AOP联盟)、spring-aop的JAR包
  2. 创建spring配置文件,文件名xxx.xml(自定义)
  3. 在spring配置文件中增加目标类(实现类)
    <bean id="person" class="com.qqfh.spring.XiaoMing"/>
    
  4. 新建增强类,实现MethodBeforeAdvice接口,实现before方法。
  5. 在spring配置文件中加入增强类
    <bean id="myBeforeAdvice" class="com.qqfh.spring.myBeforeAdvice"/>
    
  6. 在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>
    
  7. 已配置完成,可以进行测试类的编写。

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构造正则表达式切点实现步骤:

  1. 新建需要目标类(需要被增强的类),无需实现接口
    <bean id="xiaoming" class="com.qqfh.spring.demo2.XiaoMing"/>
    
  2. 在spring配置文件中增加目标类信息
  3. 新建通知类型(实现MethodInterceptor接口,实现invoke方法)
  4. 在spring配置文件中增加通知信息
    <bean id="myAroundAdvice" class="com.qqfh.spring.demo2.MyAroundAdvice"/>
    
  5. 配置带有切入点的切面,通过正则表达式匹配对应的方法增强
    <!-- 用于配置带有切入点的切面,相当于告诉AOP哪些方法需要增强 -->
    <bean id="myAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
    	<!-- 通过正则表达式挑出需要增强的方法 -->
    	<property name="pattern" value=".*"/>
    	<!-- 应用环绕的通知 -->
    	<property name="advice" ref="myAroundAdvice"/>
    </bean>
    
  6. 在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>
    
  7. 已配置完成,可以进行测试类的编写。

使用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所有方法使用代理实现步骤:

  1. 新建好所有接口类、实体类和增强类
  2. 把上面的类新增到spring配置文件中
  3. 在配置文件中配置自动增强
    <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
    	<!--指定哪些bean需要增强-->
    	<property name="beanNames" value="*Dao"/>
    	<!--指定使用哪种增强-->
    	<property name="interceptorNames" value="myBeforeAdvice"/>
    </bean>
    
  4. 已经配置完成,可以进行相应测试。
基于切面信息的自动代理(DefaultAdvisorAutoProxyCreator)

基于切面信息产生代理实现步骤:

  1. 新建好所有接口类、实体类和增强类
  2. 把上面的类新增到spring配置文件中
  3. 在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>
    
  4. 在spring配置文件中配置根据切面产生代理
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"></bean>
    
  5. 已经配置完成,可以进行相应测试。
基于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使用步骤
  1. 引入AOP和AspectJ相关jar包。
  2. 配置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 />
    
  3. 建立目标类,可以不实现接口
  4. 把目标类添加进spring配置文档(等于是IOC方式)
    <bean id="productDao" class="com.qqfh.aspectJ.demo1"/>
    
  5. 建立切面类(增强类),在类的上一行增加@Aspect,代表是切面类
  6. 在切面类中编写增强方法,在方法的上一行添加@Before类型通知
    //代表把下方的方法增强到匹配到的目标类中
    @Before(value="execution(* com.qqfh.aspectJ.demo1.*(..))")
    
  7. 在spring配置文件中增加切面类
    <bean class="com.qqfh.aspectJ.demo1.MyAspectAnno"/>
    
  8. 此时就完成了,接着可以进行测试类的编写。
AspectJ的XML方式开发AOP使用步骤
  1. 引入AOP和AspectJ相关jar包。
  2. 配置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"
    
  3. 新建目标类,可以实现接口
  4. 在spring配置文件中增加目标类配置
  5. 新建一个切面类
  6. 在spring配置文件中增加切面类配置
    <bean id="myAspectXml" class="com.qqfh.aspectJ.demo2.MyAspectXml"/>
    
  7. 在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>
    
  8. 此时就完成了,接着可以进行测试类的编写。

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 (事务处理)

使用步骤

  1. 配置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&amp;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>
    
  2. 在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))");
    
  3. (若使用第三步则无需设置第二步)在实现过程中,也可以使用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中事务处理有三个主要的接口类,还有三个根据不同功能而不同实现的实现类。

SpringIOC/AOP/事务管理
【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的编程事务管理实现流程
  1. 建立JDBC Template实现数据库处理,新建好持久层DAO、业务层service和实体类entity
  2. 在业务类中,需要封装为一个事务处理的代码前面,设置开启事务
    //事务管理器
    @Autowired
    PlatformTransactionManager transactionManager;
    //事务定义
    @Autowired
    TransactionDefinition transactionDefinition;
    //设置事务管理的起始位置。需要一个事务定义的对象做参数,返回事务状态的对象
    TransactionStatus transactionStatus = transactionManager.getTransaction(transactionDefinition);
    try{
    	//这里就是具体的事务内容
    	//提交事务,需要传递事务状态对象。
    	transactionManager.commit(transactionStatus);
    }catch(Exception e){
    	transactionManager.rollback(transactionStatus);
    }
    
  3. 在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>
    
  4. 已经配置完成。可以新建测试类进行测试。
基于TransactionTemplate的编程式事务管理实现流程

使用流程

  1. 建立JDBC Template实现数据库处理,新建好持久层DAO、业务层service和实体类entity
  2. 在业务类中,创建TransactionTemplate对象(事务模板)
    prviate TransactionTemplate transactionTemplate;
    
  3. 配置事务,该方式会自动提交封装,只需要设置回滚(注意设置自动注册)
    //使用事务模板的方式来封装事务
    transactionTemplate.execute(new TransactionCallback(){
    	public Object doInTransaction(TransactionStatus transactionStatus){
    	try{
    		//需要封装的语句
    	}catch(Exception e){
    		e.printStackTrace();
    		//设置回滚。如果报错则回滚
    		transactionStatus.setRollbackOnly();
    	}
    	return null;
    	}
    })
    
  4. 在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>
    
  5. 已经配置完成。可以新建测试类进行测试。
Spring声明式事务处理(推荐)

Spring的声明式事务处理是建立在AOP的基础之上的。本质是在方法前后进行拦截,在方法开始前添加一个事务,待执行后根据情况提交事务或者回滚事务。

声明式事务通过AOP代理方式实现事务管理,利用环绕通知TransactionInterceptor实现事务的开启及关闭,而TransactionProxyFactoryBean内部也是通过该环绕通知实现的,因此可以认为是tx:tags/帮你定义了TransactionProxyFactoryBean,从而简化事务管理。

基于TransactionInterceptor(拦截器)的声明式事务管理实现流程
  1. 建立JDBC Template实现数据库处理,新建好持久层DAO、业务层service和实体类entity
  2. 建立业务impl类代码
  3. 新建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>
    
  4. 已经配置完成。可以新建测试类进行测试。
基于TransactionProxyFactoryBean(简化拦截器)的声明式事务管理实现流程
  1. 该方式是对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命名空间的声明式事务管理实现流程(常用)
  1. 建立JDBC Template实现数据库处理,新建好持久层DAO、业务层service和实体类entity
  2. 建立业务impl类代码
  3. 新建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>
    
  4. 已经配置完成。可以新建测试类进行测试。
基于@Transactional(注解)的声明式事务管理实现流程(常用)
  1. 建立JDBC Template实现数据库处理,新建好持久层DAO、业务层service和实体类entity
  2. 建立业务impl类代码
  3. 新建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>
    
  4. 建立业务类代码,需要在业务代码的方法上一行增加注解
    //可以定义事务传播行为,例如“@Transactional(propagation = Propagation.REQUIRED)”(默认也是这个)
    @Transactional