Spring

0、spring  是什么?

Spring是分层的 Java SE/EE应用 full-stack 轻量级开源框架,以 IoC(Inverse Of Control:反转控制)和 AOP(Aspect Oriented Programming:面向切面编程)为内核,提供了展现层 SpringMVC 和持久层 Spring JDBC 以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的Java EE 企业应用开源框架。

0.1、spring  的优势?

1、方便解耦,简化开发
通过 Spring提供的 IoC容器,可以将对象间的依赖关系交由 Spring进行控制,避免硬编码所造成的过度程序耦合。用户也不必再为单例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。
2、AOP 编程的支持
通过 Spring的 AOP 功能,方便进行面向切面的编程,许多不容易用传统OOP 实现的功能可以通过 AOP 轻松应付。
3、声明式事务的支持
可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务的管理,提高开发效率和质量。
4、方便程序的测试
可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可做的事情。
5、方便集成各种优秀框架
Spring可以降低各种框架的使用难度,提供了对各种优秀框架(Struts、Hibernate、Hessian、Quartz等)的直接支持。
6、 降低 JavaEE API 的使用难度
Spring对 JavaEE API(如 JDBC、JavaMail、远程调用等)进行了薄薄的封装层,使这些 API 的使用难度大为降低。
7、Java 源码是经典学习范例
Spring的源代码设计精妙、结构清晰、匠心独用,处处体现着大师对Java 设计模式灵活运用以及对 Java技术的高深造诣。它的源代码无意是 Java 技术的最佳实践的范例。

1、IoC  的概念和作用

首先先明确:什么是耦合?

耦合性(Coupling),也叫耦合度,是对模块间关联程度的度量。耦合的强弱取决于模块间接口的复杂性、调用模块的方式以及通过界面传送数据的多少。模块间的耦合度是指模块之间的依赖关系,包括控制关系、调用关系、数据传递关系。模块间联系越多,其耦合性越强,同时表明其独立性越差( 降低耦合性,可以提高其独立性)。耦合性存在于各个领域,而非软件设计中独有的,但是我们只讨论软件工程中的耦合。在软件工程中,耦合指的就是就是对象之间的依赖性。对象之间的耦合越高,维护成本越高。因此对象的设计应使类和构件之间的耦合最小。软件设计中通常用耦合度和内聚度作为衡量模块独立程度的标准。 划分模块的一个准 则就是高内聚低耦合

解决程序耦合的思路:

1、当是我们讲解 jdbc 时,是通过反射来注册驱动的,代码如下:
Class.forName("com.mysql.jdbc.Driver");//此处只是一个字符串

此时的好处是,我们的类中不再依赖具体的驱动类,此时就算删除 mysql 的驱动 jar 包,依然可以编译(运
行就不要想了,没有驱动不可能运行成功的)。
同时,也产生了一个新的问题,mysql 驱动的全限定类名字符串是在 java 类中写死的,一旦要改还是要修改
源码。
解决这个问题也很简单,使用配置文件配置。
 

2、工厂模式解耦

在实际开发中我们可以把三层的对象都使用配置文件配置起来,当启动服务器应用加载的时候,让一个类中的
方法通过读取配置文件,把这些对象创建出来 并存起来。在接下来的使用的时候,直接拿过来用就好了。
那么,这个读取配置文件,创建和获取三层对象的类就是工厂

3、控制反转-Inversion Of Control   (敲重点。。)

工厂就是负责给我们从容器中获取指定对象的类。这时候我们获取对象的方式发生了改变。
原来:
     我们在获取对象时,都是采用 new 的方式。是主动的。
现在:
     我们获取对象时,同时跟工厂要,有工厂为我们查找或者创建对象。是被动的。

这种被动接收的方式获取对象的思想就是控制反转,它是 spring 框架的核心之一。

 IOC的作用:削减程序之间的耦合,接触代码中的依赖关系。


1.创建bean的三种方式
2.bean对象的作用范围
3.bean对象的生命周期

<!--把对象的创建交给spring来管理-->
    <!--spring对bean的管理细节
        1.创建bean的三种方式
        2.bean对象的作用范围
        3.bean对象的生命周期
    -->

    <!--创建Bean的三种方式 -->
    <!-- 第一种方式:使用默认构造函数创建。
            在spring的配置文件中使用bean标签,配以id和class属性之后,且没有其他属性和标签时。
            采用的就是默认构造函数创建bean对象,此时如果类中没有默认构造函数,则对象无法创建。

    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"></bean>
    -->

    <!-- 第二种方式: 使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入spring容器)
    <bean id="instanceFactory" class="com.itheima.factory.InstanceFactory"></bean>
    <bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"></bean>
    -->

    <!-- 第三种方式:使用工厂中的静态方法创建对象(使用某个类中的静态方法创建对象,并存入spring容器)
    <bean id="accountService" class="com.itheima.factory.StaticFactory" factory-method="getAccountService"></bean>
    -->

    <!-- bean的作用范围调整
        bean标签的scope属性:
            作用:用于指定bean的作用范围
            取值: 常用的就是单例的和多例的
                singleton:单例的(默认值)
                prototype:多例的
                request:作用于web应用的请求范围
                session:作用于web应用的会话范围
                global-session:作用于集群环境的会话范围(全局会话范围),当不是集群环境时,它就是session

    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl" scope="prototype"></bean>
    -->

    <!-- bean对象的生命周期
            单例对象
                出生:当容器创建时对象出生
                活着:只要容器还在,对象一直活着
                死亡:容器销毁,对象消亡
                总结:单例对象的生命周期和容器相同
            多例对象
                出生:当我们使用对象时spring框架为我们创建
                活着:对象只要是在使用过程中就一直活着。
                死亡:当对象长时间不用,且没有别的对象引用时,由Java的垃圾回收器回收
     -->

global-Session 作用场景:当在服务器集群时,服务器端的用户验证码存放在global-session中,这样所有的集群都可访问到。

 1.1、依赖注入的概念:

依赖注入:Dependency Injection。它是 spring 框架核心 ioc 的具体实现。
   我们的程序在编写时,通过控制反转,把对象的创建交给了 spring,但是代码中不可能出现没有依赖的情况。ioc 解耦只是降低他们的依赖关系,但不会消除。例如:我们的业务层仍会调用持久层的方法。那这种业务层和持久层的依赖关系,在使用 spring 之后,就让 spring 来维护了。
    简单的说,就是坐等框架把持久层对象传入业务层,而不用我们自己去获取。

 spring中的依赖注入:

    <!-- spring中的依赖注入
        依赖注入:
            Dependency Injection
        IOC的作用:
            降低程序间的耦合(依赖关系)
        依赖关系的管理:
            以后都交给spring来维护
        在当前类需要用到其他类的对象,由spring为我们提供,我们只需要在配置文件中说明
        依赖关系的维护:
            就称之为依赖注入。
         依赖注入:
            能注入的数据:有三类
                基本类型和String
                其他bean类型(在配置文件中或者注解配置过的bean)
                复杂类型/集合类型
             注入的方式:有三种
                第一种:使用构造函数提供
                第二种:使用set方法提供
                第三种:使用注解提供(明天的内容)
     -->

三种注入方式:

<!--构造函数注入:
        使用的标签:constructor-arg
        标签出现的位置:bean标签的内部
        标签中的属性
            type:用于指定要注入的数据的数据类型,该数据类型也是构造函数中某个或某些参数的类型
            index:用于指定要注入的数据给构造函数中指定索引位置的参数赋值。索引的位置是从0开始
            name:用于指定给构造函数中指定名称的参数赋值                                        常用的
            =============以上三个用于指定给构造函数中哪个参数赋值===============================
            value:用于提供基本类型和String类型的数据
            ref:用于指定其他的bean类型数据。它指的就是在spring的Ioc核心容器中出现过的bean对象

        优势:
            在获取bean对象时,注入数据是必须的操作,否则对象无法创建成功。
        弊端:
            改变了bean对象的实例化方式,使我们在创建对象时,如果用不到这些数据,也必须提供。
    -->
    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
        <constructor-arg name="name" value="泰斯特"></constructor-arg>
        <constructor-arg name="age" value="18"></constructor-arg>
        <constructor-arg name="birthday" ref="now"></constructor-arg>
    </bean>

    <!-- 配置一个日期对象 -->
    <bean id="now" class="java.util.Date"></bean>

    <!-- set方法注入                更常用的方式
        涉及的标签:property
        出现的位置:bean标签的内部
        标签的属性
            name:用于指定注入时所调用的set方法名称
            value:用于提供基本类型和String类型的数据
            ref:用于指定其他的bean类型数据。它指的就是在spring的Ioc核心容器中出现过的bean对象
        优势:
            创建对象时没有明确的限制,可以直接使用默认构造函数
        弊端:
            如果有某个成员必须有值,则获取对象是有可能set方法没有执行。
    -->
    <bean id="accountService2" class="com.itheima.service.impl.AccountServiceImpl2">
        <property name="name" value="TEST" ></property>
        <property name="age" value="21"></property>
        <property name="birthday" ref="now"></property>
    </bean>


    <!-- 复杂类型的注入/集合类型的注入
        用于给List结构集合注入的标签:
            list array set
        用于个Map结构集合注入的标签:
            map  props
        结构相同,标签可以互换
    -->
    <bean id="accountService3" class="com.itheima.service.impl.AccountServiceImpl3">
        <property name="myStrs">
            <set>
                <value>AAA</value>
                <value>BBB</value>
                <value>CCC</value>
            </set>
        </property>

        <property name="myList">
            <array>
                <value>AAA</value>
                <value>BBB</value>
                <value>CCC</value>
            </array>
        </property>

        <property name="mySet">
            <list>
                <value>AAA</value>
                <value>BBB</value>
                <value>CCC</value>
            </list>
        </property>

        <property name="myMap">
            <props>
                <prop key="testC">ccc</prop>
                <prop key="testD">ddd</prop>
            </props>
        </property>

        <property name="myProps">
            <map>
                <entry key="testA" value="aaa"></entry>
                <entry key="testB">
                    <value>BBB</value>
                </entry>
            </map>
        </property>
    </bean>
</beans>

基于注解的注入:

1)用于创建对象的

 * 曾经XML的配置:
 *  <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"
 *        scope=""  init-method="" destroy-method="">
 *      <property name=""  value="" | ref=""></property>
 *  </bean>
 *
 * 用于创建对象的
 *      他们的作用就和在XML配置文件中编写一个<bean>标签实现的功能是一样的
 *      Component:
 *          作用:用于把当前类对象存入spring容器中
 *          属性:
 *              value:用于指定bean的id。当我们不写时,它的默认值是当前类名,且首字母改小写。
 *      Controller:一般用在表现层
 *      Service:一般用在业务层
 *      Repository:一般用在持久层
 *      以上三个注解他们的作用和属性与Component是一模一样。
 *      他们三个是spring框架为我们提供明确的三层使用的注解,使我们的三层对象更加清晰
 *

2)用于注入数据的 

* 用于注入数据的
 *      他们的作用就和在xml配置文件中的bean标签中写一个<property>标签的作用是一样的
 *      Autowired:
 *          作用:自动按照类型注入。只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功
 *                如果ioc容器中没有任何bean的类型和要注入的变量类型匹配,则报错。
 *                如果Ioc容器中有多个类型匹配时:
 *          出现位置:
 *              可以是变量上,也可以是方法上
 *          细节:
 *              在使用注解注入时,set方法就不是必须的了。
 *      Qualifier:
 *          作用:在按照类中注入的基础之上再按照名称注入。它在给类成员注入时不能单独使用。但是在给方法参数注入时可以(稍后我们讲)
 *          属性:
 *              value:用于指定注入bean的id。
 *      Resource
 *          作用:直接按照bean的id注入。它可以独立使用
 *          属性:
 *              name:用于指定bean的id。
 *      以上三个注入都只能注入其他bean类型的数据,而基本类型和String类型无法使用上述注解实现。
 *      另外,集合类型的注入只能通过XML来实现。
 *
 *      Value
 *          作用:用于注入基本类型和String类型的数据
 *          属性:
 *              value:用于指定数据的值。它可以使用spring中SpEL(也就是spring的el表达式)
 *                      SpEL的写法:${表达式}
 *

 3)用于改变范围的、和声明周期的

* 用于改变作用范围的
 *      他们的作用就和在bean标签中使用scope属性实现的功能是一样的
 *      Scope
 *          作用:用于指定bean的作用范围
 *          属性:
 *              value:指定范围的取值。常用取值:singleton prototype
 *
 * 和生命周期相关 了解
 *      他们的作用就和在bean标签中使用init-method和destroy-methode的作用是一样的
 *      PreDestroy
 *          作用:用于指定销毁方法
 *      PostConstruct
 *          作用:用于指定初始化方法
 */

2、AOP的引入:

代理设计模式的原理:使用一个代理将原本对象包装起来,然后用该代理对象”取代”原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。

动态代理代码:(功能在不改变MathImpl代码的情况下,通过动态代理,在方法执行前后打印日志的操作)

 ProxyUtil:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;

public class ProxyUtil {

	private MathImpl mathImpl;

	public ProxyUtil(MathImpl mathImpl) {
		super();
		this.mathImpl = mathImpl;
	}
	public Object getProxy() {
		//获取当前类的类加载器,用来加载代理对象所属类
		ClassLoader loader = this.getClass().getClassLoader();
		//获取目标对象实现的所有接口的Class,代理类回合目标类实现相同的接口,最终通过代理对象实现功能
		Class[] interfaces = mathImpl.getClass().getInterfaces();
				
		return Proxy.newProxyInstance(loader, interfaces, new InvocationHandler() {
			
			//代理对象实现功能的方式
			@Override
			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
				// TODO Auto-generated method stub
				try {
					MyLogger.before(method.getName(), Arrays.toString(args));
					Object result = method.invoke(mathImpl, args);//动态代理对象实现功能
					MyLogger.after(method.getName(), result);
					return result;
				} catch (Exception e) {
					// TODO: handle exception
					MyLogger.throwing();
					e.printStackTrace();
				} finally {
					System.out.println("哪都有我");
				}
				return null;
			}
		});
	}
	
}
import java.util.Properties;

public class Test {

	public static void main(String[] args) {
		
		Properties properties = System.getProperties();
		properties.put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
		/*MathI math = new MathImpl();
		int result = math.div(1, 1);
		System.out.println(result);*/
		
		ProxyUtil proxy = new ProxyUtil(new MathImpl());
		MathI math = (MathI)proxy.getProxy();
		int i = math.div(4, 1);
		System.out.println(i);
		
	}
	
}

 2.1、什么是AOP?

 AOP的作用极其优势:

作用:在程序运行期间,不修改源码对已有方法进行增强。
优势:1、减少重复代码   2、提高开发效率  3、维护方便

图解专业术语:

1 横切关注点

从每个方法中抽取出来的同一类非核心业务。

2 切面(Aspect)

封装横切关注点信息的类,每个关注点体现为一个通知方法。

3 通知(Advice)

切面必须要完成的各个具体工作

4 目标(Target)

被通知的对象

5 代理(Proxy)

向目标对象应用通知之后创建的代理对象

6 连接点(Joinpoint)

横切关注点在程序代码中的具体体现,对应程序执行的某个特定位置。例如:类某个方法调用前、调用后、方法捕获到异常后等。在应用程序中可以使用横纵两个坐标来定位一个具体的连接点。

7 切入点(pointcut):

定位连接点的方式。每个类的方法中都包含多个连接点,所以连接点是类中客观存在的事物。如果把连接点看作数据库中的记录,那么切入点就是查询条件——AOP可以通过切入点定位到特定的连接点。切点通过org.springframework.aop.Pointcut 接口进行描述,它使用类和方法作为连接点的查询条件。

基于注解的代码例子:

切面类: 

@Component
@Aspect//标注当前类为切面
@Order(1)//定义切面作用的优先级,值越小优先级越高,默认值为int的最大值
public class MyloggerAspect {
	
	@Pointcut(value="execution(* com.atguigu.spring.aop.*.*(..))")
	public void test() {}

	/**
	 * @Before:将方法指定为前置通知
	 * 必须设置value,其值为切入点表达式
	 * 前置通知:作用于方法执行之前
	 * @Before(value="execution(* com.atguigu.spring.aop.*.*(..))")
	 * 第一个*代表任意的访问修饰符和返回值类型
	 * 第二个*代表任意类
	 * 第三个*代表类中任意方法
	 * ..代表任意的参数列表
	 */
	//@Before(value = "execution(public int com.atguigu.spring.aop.MathImpl.add(int, int))")
	@Before(value="test()")
	public void beforeMethod(JoinPoint joinPoint) {
		Object[] args = joinPoint.getArgs();//获取方法的参数
		String methodName = joinPoint.getSignature().getName();//获取方法名
		System.out.println("method:"+methodName+",arguments:"+Arrays.toString(args));
	}
	
	/**
	 * @After:将方法标注为后置通知
	 * 后置通知:作用于方法的finally语句块,即不管有没有异常都会执行
	 */
	//@After(value="execution(* com.atguigu.spring.aop.*.*(..))")
	@After(value="test()")
	public void afterMethod() {
		System.out.println("后置通知");
	}
	
	/**
	 * @AfterReturning:将方法标注为返回通知
	 * 返回通知:作用于方法执行之后
	 * 可通过returning设置接收方法返回值的变量名
	 * 要想在方法中使用,必须在方法的形参中设置和变量名相同的参数名的参数
	 */
	//@AfterReturning(value="execution(* com.atguigu.spring.aop.*.*(..))", returning="result")
	@AfterReturning(value="test()", returning="result")
	public void afterReturningMethod(JoinPoint joinPoint, Object result) {
		String methodName = joinPoint.getSignature().getName();
		System.out.println("method:"+methodName+",result:"+result);
	}
	
	/**
	 * @AfterThrowing:将方法标注为异常通知(例外通知)
	 * 异常通知(例外通知):作用于方法抛出异常时
	 * 可通过throwing设置接收方法返回的异常信息
	 * 在参数列表中课通过具体的异常类型,来对指定的异常信息进行操作
	 */
	//@AfterThrowing(value="execution(* com.atguigu.spring.aop.*.*(..))", throwing="ex")
	@AfterThrowing(value="test()", throwing="ex")
	public void afterThrowingMethod(ArithmeticException ex) {
		System.out.println("有异常了,messages:"+ex);
	}
	
	
	/*@Around(value="execution(* com.atguigu.spring.aop.*.*(..))")
	public Object aroundMethod(ProceedingJoinPoint joinPoint) {
		
		Object result = null;
		
		try {
			//前置通知
			System.out.println("前置通知");
			result = joinPoint.proceed();//执行方法
			//返回通知
			System.out.println("返回通知");
			return result;
		} catch (Throwable e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			//异常通知
			System.out.println("异常通知");
		} finally {
			//后置通知
			System.out.println("后置通知");
		}
		
		return -1;
	}*/
}

xml:

 

事务的四个关键属性(ACID)

         ①原子性(atomicity):“原子”的本意是“不可再分”,事务的原子性表现为一个事务中涉及到的多个操作在逻辑上缺一不可。事务的原子性要求事务中的所有操作要么都执行,要么都不执行。

         ②一致性(consistency):“一致”指的是数据的一致,具体是指:所有数据都处于满足业务规则的一致性状态。一致性原则要求:一个事务中不管涉及到多少个操作,都必须保证事务执行之前数据是正确的,事务执行之后数据仍然是正确的。如果一个事务在执行的过程中,其中某一个或某几个操作失败了,则必须将其他所有操作撤销,将数据恢复到事务执行之前的状态,这就是回滚

         ③隔离性(isolation):在应用程序实际运行过程中,事务往往是并发执行的,所以很有可能有许多事务同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏。隔离性原则要求多个事务在并发执行过程中不会互相干扰

         ④持久性(durability):持久性原则要求事务执行完成后,对数据的修改永久的保存下来,不会因各种系统错误或其他意外情况而受到影响。通常情况下,事务对数据的修改应该被写入到持久化存储器中。

 

数据库事务并发问题

假设现在有两个事务:Transaction01和Transaction02并发执行。

1、读未提交:脏读

         ①Transaction01将某条记录的AGE值从20修改为30。

         ②Transaction02读取了Transaction01更新后的值:30。

         ③Transaction01回滚,AGE值恢复到了20。

         ④Transaction02读取到的30就是一个无效的值。

2、读已提交:不可重复读

         ①Transaction01读取了AGE值为20。

         ②Transaction02将AGE值修改为30。

         ③Transaction01再次读取AGE值为30,和第一次读取不一致。

3、可重复读:幻读   (正在读的不能进行修改,但是可以添加新的)

         ①Transaction01读取了STUDENT表中的一部分数据。

         ②Transaction02向STUDENT表中插入了新的行。

         ③Transaction01读取了STUDENT表时,多出了一些行。

4、串行化:单线程一个一个来执行


隔离级别 

数据库系统必须具有隔离并发运行各个事务的能力,使它们不会相互影响,避免各种并发问题。一个事务与其他事务隔离的程度称为隔离级别。SQL标准中规定了多种事务隔离级别,不同隔离级别对应不同的干扰程度,隔离级别越高,数据一致性就越好,但并发性越弱。

1、读未提交:READ UNCOMMITTED

允许Transaction01读取Transaction02未提交的修改。

2、读已提交:READ COMMITTED

            要求Transaction01只能读取Transaction02已提交的修改。

3、可重复读:REPEATABLE READ

            确保Transaction01可以多次从一个字段中读取到相同的值,即Transaction01执行期间禁止其它事务对这个字段进行更新。

4、串行化:SERIALIZABLE

            确保Transaction01可以多次从一个表中读取到相同的行,在Transaction01执行期间,禁止其它事务对这个表进行添加、更新、删除操作。可以避免任何并发问题,但性能十分低下。

  1. 各个隔离级别解决并发问题的能力见下表

 

脏读

不可重复读

幻读

READ UNCOMMITTED

READ COMMITTED

REPEATABLE READ

SERIALIZABLE

  1. 各种数据库产品对事务隔离级别的支持程度

 

Oracle

MySQL

READ UNCOMMITTED

×

READ COMMITTED

√(默认)

REPEATABLE READ

×

√(默认)

SERIALIZABLE

book.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"
	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-4.0.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">

	<context:component-scan base-package="com.atguigu.book"></context:component-scan>
	
	<!-- 引入属性文件 -->
	<!-- <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="location" value="db.properties"></property>
	</bean> -->
	
	<!-- 引入属性文件 -->
	<context:property-placeholder location="db.properties"/>

	<!-- 创建数据源 -->
	<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
		<property name="driverClassName" value="${jdbc.driver}"></property>
		<property name="url" value="${jdbc.url}"></property>
		<property name="username" value="${jdbc.username}"></property>
		<property name="password" value="${jdbc.password}"></property>
	</bean>

	<!-- 通过数据源配置JdbcTemplate -->
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 配置事务管理器 -->
	<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>

	<!-- 开启注解驱动,即对事务相关的注解进行扫描,解析含义并执行功能 -->
	<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
	
</beans>

service层的注解事务使用:

package com.atguigu.book.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.atguigu.book.dao.BookDao;
import com.atguigu.book.exception.MyException;
import com.atguigu.book.service.BookService;

@Service
//@Transactional
public class BookServiceImpl implements BookService {

	@Autowired
	private BookDao dao;
	
	/**
	 * @Transactional:对方法中所有的操作作为一个事务进行管理
	 * 在方法上使用,只对方法有效果
	 * 在类上使用,对类中所有的方法都有效果
	 * @Transactional中可以设置的属性:
	 * 
	 * propagation:A方法和B方法都有事务,当A在调用B时,会将A中的事务传播给B方法,B方法对于事务的处理方式就是事务的传播行为
	 * Propagation.REQUIRED:必须使用调用者的事务
	 * Propagation.REQUIRES_NEW:将调用者的事务挂起,不使用调用者的事务,使用新的事务进行处理
	 * 
	 * isolation:事务的隔离级别,在并发的情况下,操作数据的一种规定
	 * 			读未提交:脏读   1
	 * 			读已提交:不可重复读   2
	 * 			可重复读:幻读   4
	 * 			串行化:性能低,消耗大   8
	 * 
	 * 
	 * timeout:在事务强制回滚前最多可以执行(等待)的时间
	 * 
	 * readOnly:指定当前事务中的一系列的操作是否为只读
	 * 若设置为只读,不管事务中有没有写的操作,mysql都会在请求访问数据的时候,不加锁,提高性能
	 * 但是如果有写操作的情况,建议一定不能设置只读
	 * 
	 * rollbackFor|rollbackForClassName|noRollbackFor|noRollbackForClassName:设置事务回滚的条件
	 */
	
	@Transactional(propagation=Propagation.REQUIRES_NEW, timeout=3, noRollbackFor= {NullPointerException.class, MyException.class})
	public void buyBook(String bid, String uid) {
		/*try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}*/
		Integer price = dao.selectPrice(bid);
		dao.updateSt(bid);
		dao.updateBalance(uid, price);
	}
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值