第三天:spring面向切面的编程

spring面向切面的编程

Spring提供2个代理模式,一个是jdk代理,另一个cglib代理

1.若目标对象实现了若干接口spring使用JDKjava.lang.reflect.Proxy类代理。

2.若目标对象没有实现任何接口spring使用CGLIB库生成目标对象的子类。

 

注意:开发时尽量使用接口的编程,

(1)对接口创建代理优于对类创建代理,因为会产生更加松耦合的系统。

(2)标记为final的方法不能够被通知。spring是为目标类产生子类。任何需要被通知的方法都被复写,将通知织入。final方法是不允许重写的。

 (3) spring只支持方法连接点,不支持属性的连接点

 

 

 

 

1)配置:

要进行AOP编程首先我们要在spring的配置文件中引入aop命名空间

<beans xmlns="http://www.springframework.org/schema/beans"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xmlns:aop="http://www.springframework.org/schema/aop"

       xsi:schemaLocation="http://www.springframework.org/schema/beans

           http://www.springframework.org/schema/beans/spring-beans-4.3.xsd

           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">

</beans>

 

Spring提供了两种切面使用方式,实际工作中我们可以选用其中一种:

基于XML配置方式进行AOP开发。

基于注解方式进行AOP开发。

 

SpringAOP编程需要引入的jar

   *  cglib-3.2.4.jar

           cglib代理

*   aopalliance-1.0.jar

      *   aspectjtools-1.8.4.jar

      *   spring-aop-4.3.6.RELEASE.jar

             spring的面向切面编程,提供AOP(面向切面编程)实现

      *  spring-aspects-4.3.6.RELEASE.jar

             spring提供对AspectJ框架的整合

 

2)基于XML方式的配置:测试代码

结构:

 

 

 

l IuserService接口

public interface IUserService {

 

public void saveUser(String name,String password);

public void updateUser(String name,String password);

public void deleteUser(String name);

public String findUser();

 

}

 

l UserServiceImpl实现类

public class UserServiceImpl implements IUserService {

 

public void saveUser(String name, String password) {

System.out.println("【新增】用户名:"+name+",密码:"+password);

}

 

public void updateUser(String name, String password) {

System.out.println("【修改】用户名:"+name+",密码:"+password);

}

 

public void deleteUser(String name) {

System.out.println("【删除】用户名:"+name);

}

 

public String findUser() {

System.out.println("【查询】用户");

return "小强";

}

 

 

 

}

 

测试类App.java

public class App {

 

public static void main(String[] args) {

//直接访问目标对象

//使用代理对象访问目标对象,当在spring的容器中添加<aop>

ApplicationContext ac = new ClassPathXmlApplicationContext("com/offcn/e_xml/a_before/beans.xml");

IUserService userService = (IUserService)ac.getBean("userServiceImpl");

userService.saveUser("超级强", "123");

userService.updateUser("超级强", "123");

userService.deleteUser("超级强");

String str = userService.findUser();

System.out.println("str:"+str);

 

}

}

 

1】前置通知

切面类Security

/**切面*/

public class Security {

 

/**通知*/

/**

 * 任何通知方法可以将第一个参数定义为org.aspectj.lang.JoinPoint类型 (

 * 环绕通知需要定义第一个参数为ProceedingJoinPoint类型, 它是 JoinPoint 的一个子类)。

 * JoinPoint 接口提供了一系列有用的方法,

 * 比如 getArgs()(返回方法参数)、

 * getThis()(返回代理对象)、

 * getTarget()(返回目标)、

 * getSignature()(返回正在被通知的方法相关信息)、

 *  toString() (打印出正在被通知的方法的有用信息)。

 */

public void checkSecurity(JoinPoint joinPoint){

System.out.println("正在执行验证...");

Object [] args = joinPoint.getArgs();

if(args!=null && args.length>0){

for(Object o:args){

System.out.println("参数:"+o);

}

}

System.out.println("代理对象:"+joinPoint.getThis().getClass());

System.out.println("目标对象:"+joinPoint.getTarget().getClass());

System.out.println("访问目标对象方法的名称:"+joinPoint.getSignature().getName());

System.out.println("spring容器中定义切入点的表达式:"+joinPoint.toString());

 

}

}

 

l Spring容器(beans.xml

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xmlns:context="http://www.springframework.org/schema/context"

       xmlns:aop="http://www.springframework.org/schema/aop"

       xsi:schemaLocation="http://www.springframework.org/schema/beans

              http://www.springframework.org/schema/beans/spring-beans-4.3.xsd

              http://www.springframework.org/schema/context

              http://www.springframework.org/schema/context/spring-context-4.3.xsd

              http://www.springframework.org/schema/aop

              http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">

    <!-- 创建目标对象 -->

    <bean id="userServiceImpl" class="com.offcn.a_before.UserServiceImpl"></bean>

    

    <bean id="security" class="com.offcn.a_before.Security"></bean>

    

    <!-- springaop编程,所有的操作(切面、通知、切入点)都要放置到aop:config -->

    <aop:config>

     <!--

     定义个切面,此时切面的类具有了灵魂

     id:惟一标识

     ref:注入对象

      -->

     <aop:aspect id="aa" ref="security">

     <!--

     声明切入点

     id:切入点的惟一标识

     expression:切入点的表达式语言,指定项目中哪个类哪个方法作为切入点

      -->

     <aop:pointcut id="save" expression="execution(* com.offcn.a_before.UserServiceImpl.saveUser(..))" />

     <aop:pointcut id="update" expression="execution(* com.offcn.a_before.UserServiceImpl.updateUser(..))" />

     <!--

     定义通知(切入点要做的事情)

     前置通知:在访问目标对象方法之前,先执行通知定义的方法

     特点:如果代理对象(切面)中的方法(通知)抛出异常,此时不会执行目标对象

     * pointcut-ref:注入切入点,这样才能让切入点关联通知

     * method:指定切面中定义的通知的方法

      -->

     <aop:before pointcut-ref="save" method="checkSecurity"/>

    <aop:before pointcut-ref="update" method="checkSecurity"/>

     </aop:aspect>

    </aop:config>

</beans>

2】后置通知

切面类Security

/**切面*/

public class Security {

 

/**通知*/

/**

 * 任何通知方法可以将第一个参数定义为org.aspectj.lang.JoinPoint类型 (

 * 环绕通知需要定义第一个参数为ProceedingJoinPoint类型, 它是 JoinPoint 的一个子类)。

 * JoinPoint 接口提供了一系列有用的方法,

 * 比如 getArgs()(返回方法参数)、

 * getThis()(返回代理对象)、

 * getTarget()(返回目标)、

 * getSignature()(返回正在被通知的方法相关信息)、

 *  toString() (打印出正在被通知的方法的有用信息)。

 */

public void checkSecurity(JoinPoint joinPoint,Object returnValue){

System.out.println("正在执行验证...");

Object [] args = joinPoint.getArgs();

if(args!=null && args.length>0){

for(Object o:args){

System.out.println("参数:"+o);

}

}

System.out.println("代理对象:"+joinPoint.getThis().getClass());

System.out.println("目标对象:"+joinPoint.getTarget().getClass());

System.out.println("访问目标对象方法的名称:"+joinPoint.getSignature().getName());

System.out.println("spring容器中定义切入点的表达式:"+joinPoint.toString());

 

System.out.println("目标对象方法的返回值:"+returnValue);

}

}

 

l Spring容器(beans.xml

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xmlns:context="http://www.springframework.org/schema/context"

       xmlns:aop="http://www.springframework.org/schema/aop"

       xsi:schemaLocation="http://www.springframework.org/schema/beans

              http://www.springframework.org/schema/beans/spring-beans-4.3.xsd

              http://www.springframework.org/schema/context

              http://www.springframework.org/schema/context/spring-context-4.3.xsd

              http://www.springframework.org/schema/aop

              http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">

    <!-- 创建目标对象 -->

    <bean id="userServiceImpl" class="com.offcn.b_afterreturning.UserServiceImpl"></bean>

    <!-- 声明切面 (无灵魂)-->

    <bean id="security" class="com.offcn.b_afterreturning.Security"></bean>

    

    

    <aop:config>

     <aop:aspect id="aa" ref="security">

     <aop:pointcut id="save" expression="execution(* com.offcn.b_afterreturning.UserServiceImpl.saveUser(..))" />

     <aop:pointcut id="find" expression="execution(* com.offcn.b_afterreturning.UserServiceImpl.findUser(..))" />

     <!--

     后置通知:在访问目标对象方法之后,再执行通知定义的方法

     特点:1:如果在目标对象中抛出异常,此时不会执行通知

       2:因为是先执行目标对象中的方法,再执行通知,所以能不能在通知中获取目标对象的方法的返回值?能

        第一步:在spring容器中定义:returning="returnValue"

        第二步:在通知的方法中的第二个参数,可以指定Object类型,

        例如public void checkSecurity(JoinPoint joinPoint,Object returnValue){

        总结: 参数一定要放在到第二个参数的位置

               参数一定一个Object类型

               参数的属性名称一定要与spring容器中定义的returning相一致

      -->

     <aop:after-returning pointcut-ref="save" method="checkSecurity" returning="returnValue"/>

    <aop:after-returning pointcut-ref="find" method="checkSecurity" returning="returnValue"/>

     </aop:aspect>

    </aop:config>

</beans>

 

3】异常通知

切面类Security

/**切面*/

public class Security {

 

/**通知*/

/**

 * 任何通知方法可以将第一个参数定义为org.aspectj.lang.JoinPoint类型 (

 * 环绕通知需要定义第一个参数为ProceedingJoinPoint类型, 它是 JoinPoint 的一个子类)。

 * JoinPoint 接口提供了一系列有用的方法,

 * 比如 getArgs()(返回方法参数)、

 * getThis()(返回代理对象)、

 * getTarget()(返回目标)、

 * getSignature()(返回正在被通知的方法相关信息)、

 *  toString() (打印出正在被通知的方法的有用信息)。

 */

public void checkSecurity(JoinPoint joinPoint,Throwable throwingValue){

System.out.println("正在执行验证...");

Object [] args = joinPoint.getArgs();

if(args!=null && args.length>0){

for(Object o:args){

System.out.println("参数:"+o);

}

}

System.out.println("代理对象:"+joinPoint.getThis().getClass());

System.out.println("目标对象:"+joinPoint.getTarget().getClass());

System.out.println("访问目标对象方法的名称:"+joinPoint.getSignature().getName());

System.out.println("spring容器中定义切入点的表达式:"+joinPoint.toString());

System.out.println("目标对象方法抛出的异常是:"+throwingValue);

 

 

}

}

 

l Spring容器(beans.xml

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xmlns:context="http://www.springframework.org/schema/context"

       xmlns:aop="http://www.springframework.org/schema/aop"

       xsi:schemaLocation="http://www.springframework.org/schema/beans

              http://www.springframework.org/schema/beans/spring-beans-4.3.xsd

              http://www.springframework.org/schema/context

              http://www.springframework.org/schema/context/spring-context-4.3.xsd

              http://www.springframework.org/schema/aop

              http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">

    <!-- 创建目标对象 -->

    <bean id="userServiceImpl" class="com.offcn.c_afterthrowing.UserServiceImpl"></bean>

    <!-- 声明切面 (无灵魂)-->

    <bean id="security" class="com.offcn.c_afterthrowing.Security"></bean>

    

    

    <aop:config>

     <aop:aspect id="aa" ref="security">

     <aop:pointcut id="save" expression="execution(* com.offcn.c_afterthrowing.UserServiceImpl.saveUser(..))" />

     <aop:pointcut id="find" expression="execution(* com.offcn.c_afterthrowing.UserServiceImpl.findUser(..))" />

     <!--

     异常通知:在访问目标对象方法之后,前提是目标对象方法中抛出异常,此时才会执行通知定义的方法

     特点:1:只有目标对象方法中抛出异常,此时才会执行通知

       2:在通知的方法中捕获异常

        第一步:在spring容器中定义

        第二步:在通知的方法中的第二个参数的位置,可以指定,例如public void checkSecurity(JoinPoint joinPoint,Throwable throwingValue){

        * 要求一:获取目标对象抛出的异常的参数要放置在第二个参数的位置

        * 要求二:类型必须指定Throwable类型

        * 要求三:Throwable对应的属性值要和spring容器中定义的throwing="throwingValue"值要相匹配

      -->

     <aop:after-throwing pointcut-ref="save" method="checkSecurity" throwing="throwingValue"/>

    <aop:after-throwing pointcut-ref="find" method="checkSecurity" throwing="throwingValue"/>

     </aop:aspect>

    </aop:config>

</beans>

 

4】环绕通知 ProceedingJoinPoint

切面类Security

/**切面*/

public class Security {

 

/**通知*/

/**

 * 普通通知类型:

 * 任何通知方法可以将第一个参数定义为org.aspectj.lang.JoinPoint类型 (

 * 环绕通知需要定义第一个参数为ProceedingJoinPoint类型, 它是 JoinPoint 的一个子类)。

 * JoinPoint 接口提供了一系列有用的方法,

 * 比如 getArgs()(返回方法参数)、

 * getThis()(返回代理对象)、

 * getTarget()(返回目标)、

 * getSignature()(返回正在被通知的方法相关信息)、

 *  toString() (打印出正在被通知的方法的有用信息)。

 *  

 *  环绕通知类型

 *  通知的第一个参数必须是 ProceedingJoinPoint类型。

 *  在通知体内,调用 ProceedingJoinPointproceed()方法会导致 后台的连接点方法执行。

 *  proceed 方法也可能会被调用并且传入一个 Object[]对象-该数组中的值将被作为方法执行时的参数。

 *  

 *  环绕通知将通知的方法的返回值要定义成Object类型,只有这样才能将目标对象方法的返回值,传递给客户端

 */

public Object checkSecurity(ProceedingJoinPoint joinPoint){

//如果调用joinPoint.proceed();方法放置在通知的最前面,此时就相当于后置通知

System.out.println("环绕通知:在目标执行前调用:"+joinPoint.getTarget().getClass());

Object value = null;

try {

value = joinPoint.proceed();

} catch (Throwable e) {

e.printStackTrace();

}

/*System.out.println("正在执行验证...");

Object [] args = joinPoint.getArgs();

if(args!=null && args.length>0){

for(Object o:args){

System.out.println("参数:"+o);

}

}

System.out.println("代理对象:"+joinPoint.getThis().getClass());

System.out.println("目标对象:"+joinPoint.getTarget().getClass());

System.out.println("访问目标对象方法的名称:"+joinPoint.getSignature().getName());

System.out.println("spring容器中定义切入点的表达式:"+joinPoint.toString());*/

// //如果调用joinPoint.proceed();方法放置在通知的最后,此时就相当于前置通知

// Object value = null;

// try {

// value = joinPoint.proceed();

// } catch (Throwable e) {

// e.printStackTrace();

// }

System.out.println("环绕通知:在目标执行后调用:"+joinPoint.getTarget().getClass()+" 获取返回结果:"+value);

return value;

}

}

 

l Spring容器(beans.xml

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xmlns:context="http://www.springframework.org/schema/context"

       xmlns:aop="http://www.springframework.org/schema/aop"

       xsi:schemaLocation="http://www.springframework.org/schema/beans

              http://www.springframework.org/schema/beans/spring-beans-4.3.xsd

              http://www.springframework.org/schema/context

              http://www.springframework.org/schema/context/spring-context-4.3.xsd

              http://www.springframework.org/schema/aop

              http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">

    <!-- 创建目标对象 -->

    <bean id="userServiceImpl" class="com.offcn.e_around.UserServiceImpl"></bean>

    <!-- 声明切面 (无灵魂)-->

    <bean id="security" class="com.offcn.e_around.Security"></bean>

    

    

    <aop:config>

     <aop:aspect id="aa" ref="security">

     <aop:pointcut id="save" expression="execution(* com.offcn.e_around.UserServiceImpl.saveUser(..))" />

     <aop:pointcut id="find" expression="execution(* com.offcn.e_around.UserServiceImpl.findUser(..))" />

     <!--

     环绕通知:

     最后一种通知是环绕通知。环绕通知在一个目标对象方法执行之前和之后执行。

     它使得通知有机会 在一个方法执行之前和执行之后运行。而且它可以决定这个方法在什么时候执行,如何执行,甚至是否执行。

      环绕通知经常在某线程安全的环境下,你需要在一个方法执行之前和之后共享某种状态的时候使用。

      请尽量使用最简单的满足你需求的通知。(比如如果简单的前置通知也可以适用的情况下不要使用环绕通知)。

      -->

     <aop:around pointcut-ref="save" method="checkSecurity"/>

    <aop:around pointcut-ref="find" method="checkSecurity"/>

     </aop:aspect>

    </aop:config>

</beans>

 

3基于注解方式的配置测试代码

首先为了在Spring配置中使用@AspectJ切面,你首先必须启用Spring@AspectJ切面配置的支持,并确保自动代理 (蓝色部分)

<beans xmlns="http://www.springframework.org/schema/beans"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xmlns:aop="http://www.springframework.org/schema/aop"

       xsi:schemaLocation="http://www.springframework.org/schema/beans

           http://www.springframework.org/schema/beans/spring-beans-4.3.xsd

           http://www.springframework.org/schema/aop   http://www.springframework.org/schema/aop/spring-aop-4.0.xsd>

  <!--启用Spring@AspectJ的支持 -->

  <aop:aspectj-autoproxy/>

    <!-- 声明切面对象 -->

    <bean id="security" class="com.offcn.service.Security" />

    <!-- 创建接口实现类对象 -->

    <bean id="userManager" class="com.offcn.service.UserManagerImpl" />

</beans>

 

结构:

 

 

 

 

l IuserService接口

public interface IUserService {

 

public void saveUser(String name,String password);

public void updateUser(String name,String password);

public void deleteUser(String name);

public String findUser();

 

}

 

l UserServiceImpl实现类

public class UserServiceImpl implements IUserService {

 

public void saveUser(String name, String password) {

System.out.println("【新增】用户名:"+name+",密码:"+password);

}

 

public void updateUser(String name, String password) {

System.out.println("【修改】用户名:"+name+",密码:"+password);

}

 

public void deleteUser(String name) {

System.out.println("【删除】用户名:"+name);

}

 

public String findUser() {

System.out.println("【查询】用户");

return "小强";

}

 

 

 

}

 

测试类App.java

public class App {

 

public static void main(String[] args) {

//直接访问目标对象

//使用代理对象访问目标对象,当在spring的容器中添加<aop>

ApplicationContext ac = new ClassPathXmlApplicationContext("com/offcn/a_before/beans.xml");

IUserService userService = (IUserService)ac.getBean("userServiceImpl");

userService.saveUser("超级强", "123");

userService.updateUser("超级强", "123");

userService.deleteUser("超级强");

String str = userService.findUser();

System.out.println("str:"+str);

 

}

}

 

1】前置通知

l 切面类Security

/**切面*/

@Aspect  //表示在spring容器中定义:<aop:aspect id="aa" ref="security">

public class Security {

 

/**

 * 声明定义目标对象方法的切入点(指的是方法)

 *  要求:1、方法的修饰符任意

 *   2、要求方法没有返回值

 *   3、方法的名称任意(一个类中,不允许出现同名方法,表明的切入点的id惟一)

 *   4、方法没有参数

 *   5、方法体为空

 */

//相当于spring容器中定义:<aop:pointcut id="save" expression="execution(* com.offcn.a_before.UserServiceImpl.saveUser(..))" />

@Pointcut(value="execution(* com.offcn.a_before.UserServiceImpl.saveUser(..))")

public void save(){};

 

@Pointcut(value="execution(* com.offcn.a_before.UserServiceImpl.updateUser(..))")

public void update(){};

/**通知*/

/**

 * 任何通知方法可以将第一个参数定义为org.aspectj.lang.JoinPoint类型 (

 * 环绕通知需要定义第一个参数为ProceedingJoinPoint类型, 它是 JoinPoint 的一个子类)。

 * JoinPoint 接口提供了一系列有用的方法,

 * 比如 getArgs()(返回方法参数)、

 * getThis()(返回代理对象)、

 * getTarget()(返回目标)、

 * getSignature()(返回正在被通知的方法相关信息)、

 *  toString() (打印出正在被通知的方法的有用信息)。

 */

//相当于spring容器中定义:<aop:before pointcut-ref="save" method="checkSecurity"/><aop:before pointcut-ref="update" method="checkSecurity"/>

@Before(value="save() || update()")

public void checkSecurity(JoinPoint joinPoint){

System.out.println("正在执行验证...");

Object [] args = joinPoint.getArgs();

if(args!=null && args.length>0){

for(Object o:args){

System.out.println("参数:"+o);

}

}

System.out.println("代理对象:"+joinPoint.getThis().getClass());

System.out.println("目标对象:"+joinPoint.getTarget().getClass());

System.out.println("访问目标对象方法的名称:"+joinPoint.getSignature().getName());

System.out.println("spring容器中定义切入点的表达式:"+joinPoint.toString());

if(true){

throw new RuntimeException("抛出运行时异常!");

}

}

}

 

l Spring容器(beans.xml

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xmlns:context="http://www.springframework.org/schema/context"

       xmlns:aop="http://www.springframework.org/schema/aop"

       xsi:schemaLocation="http://www.springframework.org/schema/beans

              http://www.springframework.org/schema/beans/spring-beans-4.3.xsd

              http://www.springframework.org/schema/context

              http://www.springframework.org/schema/context/spring-context-4.3.xsd

              http://www.springframework.org/schema/aop

              http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">

    <!-- 启用Spring@AspectJ切面配置的支持 -->

    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

    <!-- 创建目标对象 -->

    <bean id="userServiceImpl" class="com.offcn.a_before.UserServiceImpl"></bean>

    <!-- 声明切面 (无灵魂)-->

    <bean id="security" class="com.offcn.a_before.Security"></bean>

    

</beans>

 

 

 

2】后置通知

切面类Security

/**切面*/

@Aspect  //表示在spring容器中定义:<aop:aspect id="aa" ref="security">

public class Security {

 

/**

 * 声明定义目标对象方法的切入点(指的是方法)

 *  要求:1、方法的修饰符任意

 *   2、要求方法没有返回值

 *   3、方法的名称任意(一个类中,不允许出现同名方法,表明的切入点的id惟一)

 *   4、方法没有参数

 *   5、方法体为空

 */

//相当于spring容器中定义:<aop:pointcut id="save" expression="execution(* com.offcn.a_before.UserServiceImpl.saveUser(..))" />

@Pointcut(value="execution(* com.offcn.b_afterreturning.UserServiceImpl.saveUser(..))")

public void save(){};

 

@Pointcut(value="execution(* com.offcn.b_afterreturning.UserServiceImpl.findUser(..))")

public void find(){};

/**通知*/

/**

 * 任何通知方法可以将第一个参数定义为org.aspectj.lang.JoinPoint类型 (

 * 环绕通知需要定义第一个参数为ProceedingJoinPoint类型, 它是 JoinPoint 的一个子类)。

 * JoinPoint 接口提供了一系列有用的方法,

 * 比如 getArgs()(返回方法参数)、

 * getThis()(返回代理对象)、

 * getTarget()(返回目标)、

 * getSignature()(返回正在被通知的方法相关信息)、

 *  toString() (打印出正在被通知的方法的有用信息)。

 */

//相当于spring容器中定义:<aop:before pointcut-ref="save" method="checkSecurity"/><aop:before pointcut-ref="update" method="checkSecurity"/>

@AfterReturning(value="save() || find()",returning="returnValue")

public void checkSecurity(JoinPoint joinPoint,Object returnValue){

System.out.println("正在执行验证...");

Object [] args = joinPoint.getArgs();

if(args!=null && args.length>0){

for(Object o:args){

System.out.println("参数:"+o);

}

}

System.out.println("代理对象:"+joinPoint.getThis().getClass());

System.out.println("目标对象:"+joinPoint.getTarget().getClass());

System.out.println("访问目标对象方法的名称:"+joinPoint.getSignature().getName());

System.out.println("spring容器中定义切入点的表达式:"+joinPoint.toString());

System.out.println("目标对象方法的返回值:"+returnValue);

// if(true){

// throw new RuntimeException("抛出运行时异常!");

// }

}

}

 

l Spring容器(beans.xml

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xmlns:context="http://www.springframework.org/schema/context"

       xmlns:aop="http://www.springframework.org/schema/aop"

       xsi:schemaLocation="http://www.springframework.org/schema/beans

              http://www.springframework.org/schema/beans/spring-beans-4.3.xsd

              http://www.springframework.org/schema/context

              http://www.springframework.org/schema/context/spring-context-4.3.xsd

              http://www.springframework.org/schema/aop

              http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">

    <!-- 启用Spring@AspectJ切面配置的支持 -->

    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

    <!-- 创建目标对象 -->

    <bean id="userServiceImpl" class="com.offcn.b_afterreturning.UserServiceImpl"></bean>

    <!-- 声明切面 (无灵魂)-->

    <bean id="security" class="com.offcn.b_afterreturning.Security"></bean>

    

</beans>

 

3】异常通知

切面类Security

/**切面*/

@Aspect  //表示在spring容器中定义:<aop:aspect id="aa" ref="security">

public class Security {

 

/**

 * 声明定义目标对象方法的切入点(指的是方法)

 *  要求:1、方法的修饰符任意

 *   2、要求方法没有返回值

 *   3、方法的名称任意(一个类中,不允许出现同名方法,表明的切入点的id惟一)

 *   4、方法没有参数

 *   5、方法体为空

 */

//相当于spring容器中定义:<aop:pointcut id="save" expression="execution(* com.offcn.a_before.UserServiceImpl.saveUser(..))" />

@Pointcut(value="execution(* com.offcn.c_afterthrowing.UserServiceImpl.saveUser(..))")

public void save(){};

 

@Pointcut(value="execution(* com.offcn.c_afterthrowing.UserServiceImpl.findUser(..))")

public void find(){};

/**通知*/

/**

 * 任何通知方法可以将第一个参数定义为org.aspectj.lang.JoinPoint类型 (

 * 环绕通知需要定义第一个参数为ProceedingJoinPoint类型, 它是 JoinPoint 的一个子类)。

 * JoinPoint 接口提供了一系列有用的方法,

 * 比如 getArgs()(返回方法参数)、

 * getThis()(返回代理对象)、

 * getTarget()(返回目标)、

 * getSignature()(返回正在被通知的方法相关信息)、

 *  toString() (打印出正在被通知的方法的有用信息)。

 */

//相当于spring容器中定义:<aop:before pointcut-ref="save" method="checkSecurity"/><aop:before pointcut-ref="update" method="checkSecurity"/>

@AfterThrowing(value="save() || find()",throwing="throwingValue")

public void checkSecurity(JoinPoint joinPoint,Throwable throwingValue){

System.out.println("正在执行验证...");

Object [] args = joinPoint.getArgs();

if(args!=null && args.length>0){

for(Object o:args){

System.out.println("参数:"+o);

}

}

System.out.println("代理对象:"+joinPoint.getThis().getClass());

System.out.println("目标对象:"+joinPoint.getTarget().getClass());

System.out.println("访问目标对象方法的名称:"+joinPoint.getSignature().getName());

System.out.println("spring容器中定义切入点的表达式:"+joinPoint.toString());

System.out.println("目标对象抛出的异常:"+throwingValue);

}

}

l Spring容器(beans.xml

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xmlns:context="http://www.springframework.org/schema/context"

       xmlns:aop="http://www.springframework.org/schema/aop"

       xsi:schemaLocation="http://www.springframework.org/schema/beans

              http://www.springframework.org/schema/beans/spring-beans-4.3.xsd

              http://www.springframework.org/schema/context

              http://www.springframework.org/schema/context/spring-context-4.3.xsd

              http://www.springframework.org/schema/aop

              http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">

    <!-- 启用Spring@AspectJ切面配置的支持 -->

    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

    <!-- 创建目标对象 -->

    <bean id="userServiceImpl" class="com.offcn.c_afterthrowing.UserServiceImpl"></bean>

    <!-- 声明切面 (无灵魂)-->

    <bean id="security" class="com.offcn.c_afterthrowing.Security"></bean>

    

</beans>

4】环绕通知

切面类Security

/**切面*/

@Aspect  //表示在spring容器中定义:<aop:aspect id="aa" ref="security">

public class Security {

 

/**

 * 声明定义目标对象方法的切入点(指的是方法)

 *  要求:1、方法的修饰符任意

 *   2、要求方法没有返回值

 *   3、方法的名称任意(一个类中,不允许出现同名方法,表明的切入点的id惟一)

 *   4、方法没有参数

 *   5、方法体为空

 */

//相当于spring容器中定义:<aop:pointcut id="save" expression="execution(* com.offcn.a_before.UserServiceImpl.saveUser(..))" />

@Pointcut(value="execution(* com.offcn.e_around.UserServiceImpl.saveUser(..))")

public void save(){};

 

@Pointcut(value="execution(* com.offcn.e_around.UserServiceImpl.findUser(..))")

public void find(){};

/**通知*/

/**

 * 任何通知方法可以将第一个参数定义为org.aspectj.lang.JoinPoint类型 (

 * 环绕通知需要定义第一个参数为ProceedingJoinPoint类型, 它是 JoinPoint 的一个子类)。

 * JoinPoint 接口提供了一系列有用的方法,

 * 比如 getArgs()(返回方法参数)、

 * getThis()(返回代理对象)、

 * getTarget()(返回目标)、

 * getSignature()(返回正在被通知的方法相关信息)、

 *  toString() (打印出正在被通知的方法的有用信息)。

 */

//相当于spring容器中定义:<aop:before pointcut-ref="save" method="checkSecurity"/><aop:before pointcut-ref="update" method="checkSecurity"/>

@Around(value="save() || find()")

public Object checkSecurity(ProceedingJoinPoint joinPoint){

System.out.println("正在执行验证...");

Object [] args = joinPoint.getArgs();

if(args!=null && args.length>0){

for(Object o:args){

System.out.println("参数:"+o);

}

}

System.out.println("代理对象:"+joinPoint.getThis().getClass());

System.out.println("目标对象:"+joinPoint.getTarget().getClass());

System.out.println("访问目标对象方法的名称:"+joinPoint.getSignature().getName());

System.out.println("spring容器中定义切入点的表达式:"+joinPoint.toString());

 

//调用proceed,放置到最后,此时相当于前置通知

Object value = null;

try {

value = joinPoint.proceed();

} catch (Throwable e) {

e.printStackTrace();

}

return value;

}

}

 

l Spring容器(beans.xml

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xmlns:context="http://www.springframework.org/schema/context"

       xmlns:aop="http://www.springframework.org/schema/aop"

       xsi:schemaLocation="http://www.springframework.org/schema/beans

              http://www.springframework.org/schema/beans/spring-beans-4.3.xsd

              http://www.springframework.org/schema/context

              http://www.springframework.org/schema/context/spring-context-4.3.xsd

              http://www.springframework.org/schema/aop

              http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">

    <!-- 启用Spring@AspectJ切面配置的支持 -->

    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

    <!-- 创建目标对象 -->

    <bean id="userServiceImpl" class="com.offcn.e_around.UserServiceImpl"></bean>

    <!-- 声明切面 (无灵魂)-->

    <bean id="security" class="com.offcn.e_around.Security"></bean>

    

</beans>

 

总结:

1spring5种类型,目的在访问目标对象方法之前或者之后,先执行通知定义的方法,这种编程就是aop方式的编程思想,即面向切面的编程。

 * 应用(spring提供的声明式事务处理),后面讲

 

2:对于环绕通知来说:

Object value = joinPoint.proceed();就类似于jdk代理和cglib代理的Object returnValue = method.invoke(this.targetObject, args);,此时效果是一样的

二、综合案例:

#账号信息表

create table account

(

   accountid varchar(18) primary key, #账号

   balance   double(10,2)      #余额

)

 

#存款信息表(存款的过程)

create table inaccount

(

   accountid varchar(18),   #账号

   inbalance double(10,2)  #存款金额

)

 

 

完成业务要求

需求:向8888的账号存款200元钱

1:向存款信息表中的8888账号存款200元,存放一条记录

2:使用8888的账号查询账号信息表,获取8888账号对应的余额

3:更新8888账号的余额(余额=存款的金额+新增的金额)

4:更新账号信息表,对8888的账号,将余额更新最新的余额

 

结构:

 

 

 

 

l Account.java

//账号信息

public class Account {

 

private String accountid; //账号

private Double balance; //余额

 

public String getAccountid() {

return accountid;

}

public void setAccountid(String accountid) {

this.accountid = accountid;

}

public Double getBalance() {

return balance;

}

public void setBalance(Double balance) {

this.balance = balance;

}

 

 

}

 

l InAccount.java

//存款信息

public class InAccount {

 

private String accountid; //账号

private Double inbalance; //存款金额

 

public String getAccountid() {

return accountid;

}

public void setAccountid(String accountid) {

this.accountid = accountid;

}

public Double getInbalance() {

return inbalance;

}

public void setInbalance(Double inbalance) {

this.inbalance = inbalance;

}

 

 

}

 

 

l IAccountDao.java接口

public interface IAccountDao {

 

Account findAccountById(String accountid);

 

void updateAccount(Account account);

 

}

l IInAccountDao.java接口

public interface IInAccountDao {

 

void save(InAccount inAccount);

 

}

 

l AccountDaoImpl.java实现类

/**账号DAO*/

public class AccountDaoImpl implements IAccountDao {

 

private JdbcTemplate jdbcTemplate;

 

public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {

this.jdbcTemplate = jdbcTemplate;

}

 

/**2:使用8888的账号查询账号信息表,获取8888账号对应的余额*/

@Override

public Account findAccountById(String accountid) {

String sql = "select accountid,balance from account where accountid=?";

Object [] args = {accountid};

RowMapper<Account> rowMapper = new RowMapper<Account>() {

 

@Override

public Account mapRow(ResultSet rs, int rowNum) throws SQLException {

Account account = new Account();

account.setAccountid(rs.getString("accountid"));

account.setBalance(rs.getDouble("balance"));

return account;

}

};

Account account = jdbcTemplate.queryForObject(sql, args, rowMapper);

return account;

}

 

/**4:更新账号信息表,对8888的账号,将余额更新最新的余额*/

@Override

public void updateAccount(Account account) {

try {

String sql = "update account set balance=? where accountid=?";

Object [] args = {account.getBalance(),account.getAccountid()};

jdbcTemplate.update(sql, args);

} catch (Exception e) {

e.printStackTrace();

throw new RuntimeException("抛出运行时异常!");

}

}

}

 

l InAccountDaoImpl.java实现类

public class InAccountDaoImpl implements IInAccountDao {

 

private JdbcTemplate jdbcTemplate;

 

public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {

this.jdbcTemplate = jdbcTemplate;

}

 

/**1:向存款信息表中的8888账号存款200元,存放一条记录*/

@Override

public void save(InAccount inAccount) {

String sql = "insert into inaccount(accountid,inbalance) values (?,?)";

Object [] args = {inAccount.getAccountid(),inAccount.getInbalance()};

jdbcTemplate.update(sql, args);

}

 

}

l IAccountService.java接口

public interface IAccountService {

 

void saveAccount(InAccount inAccount);

 

}

l AccountServiceImpl.java实现类

public class AccountServiceImpl implements IAccountService {

 

private IInAccountDao inAccountDao;

 

private IAccountDao accountDao;

 

public void setInAccountDao(IInAccountDao inAccountDao) {

this.inAccountDao = inAccountDao;

}

 

public void setAccountDao(IAccountDao accountDao) {

this.accountDao = accountDao;

}

 

/**8888的账号存款200元钱*/

@Override

public void saveAccount(InAccount inAccount) {

//1:向存款信息表中的8888账号存款200元,存放一条记录

inAccountDao.save(inAccount);

//2:使用8888的账号查询账号信息表,获取8888账号对应的余额

String accountid = inAccount.getAccountid();

Account account = accountDao.findAccountById(accountid);

//3:更新8888账号的余额(余额=存款的金额+新增的金额)

Double balance = account.getBalance()+inAccount.getInbalance();

account.setBalance(balance);

//4:更新账号信息表,对8888的账号,将余额更新最新的余额

accountDao.updateAccount(account);

}

}

 

l Spring容器beans.xml的配置

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xmlns:context="http://www.springframework.org/schema/context"

       xmlns:aop="http://www.springframework.org/schema/aop"

       xsi:schemaLocation="http://www.springframework.org/schema/beans

              http://www.springframework.org/schema/beans/spring-beans-4.3.xsd

              http://www.springframework.org/schema/context

              http://www.springframework.org/schema/context/spring-context-4.3.xsd

              http://www.springframework.org/schema/aop

              http://www.springframework.org/schema/aop/spring-aop-4.0.xsd

    <!-- 配置DBCP连接池 -->

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">

     <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>

     <property name="url" value="jdbc:mysql://localhost:3306/0403_demo02?useUnicode=true&characterEncoding=utf8"></property>

     <property name="username" value="root"></property>

     <property name="password" value="123"></property>

    

     <!-- 连接池启动时的初始值 -->

  <property name="initialSize" value="1"/>

  <!-- 连接池的最大值 -->

  <property name="maxActive" value="500"/>

  <!-- 最大空闲值.当经过一个高峰时间后,连接池可以慢慢将已经用不到的连接慢慢释放一部分,一直减少到maxIdle为止 -->

  <property name="maxIdle" value="2"/>

  <!--  最小空闲值.当空闲的连接数少于该值时,连接池就会预申请一些连接,以避免洪峰来时再申请而造成的性能开销 -->

  <property name="minIdle" value="1"/>

    

    </bean>

    

    <!-- 创建spring提供的Jdbc模板,用来操作数据库 -->

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">

         <property name="dataSource" ref="dataSource"></property>

    </bean>

    

    <!-- 创建Service -->

    <bean id="accountService" class="com.offcn.service.impl.AccountServiceImpl">

        <property name="inAccountDao" ref="inAccountDao"></property>

        <property name="accountDao" ref="accountDao"></property>

    </bean>

    <!-- 创建Dao -->

    <bean id="accountDao" class="com.offcn.dao.impl.AccountDaoImpl">

         <property name="jdbcTemplate" ref="jdbcTemplate"></property>

    </bean>

    <bean id="inAccountDao" class="com.offcn.dao.impl.InAccountDaoImpl">

        <property name="jdbcTemplate" ref="jdbcTemplate"></property>

    </bean>

    

</beans>

测试类App.java

@RunWith(value=SpringJUnit4ClassRunner.class)

@ContextConfiguration(value="classpath:beans.xml")

public class App {

 

@Resource(name="accountService")

private IAccountService accountService;

 

 

/**需求:向8888的账号存款200元钱*/

@Test

public void saveAccount(){

InAccount inAccount = new InAccount();

inAccount.setAccountid("8888");

inAccount.setInbalance(200d);

accountService.saveAccount(inAccount);

}

 

}

 

 

三、Spring事务处理

1:事务的概念

 

事务处理:

* 声明式事务管理(*****)

如果你并不需要细粒度的事务控制,你可以使用声明式事务,在Spring中,你只需要在Spring配置文件中做一些配置,即可将操作纳入到事务管理中,解除了和代码的耦合, 这是对应用代码影响最小的选择,从这一点再次验证了Spring关于AOP的概念。当你不需要事务管理的时候,可以直接从Spring配置文件中移除该设置

 

* 事务管理器:

 

 

 

 

 

事务是一组操作的执行单元,相对于数据库操作来讲,事务管理的是一组SQL指令,比如增加,修改,删除等,事务的一致性,要求,这个事务内的操作必须全部执行成功,如果在此过程种出现了差错,比如有一条SQL语句没有执行成功,那么这一组操作都将全部回滚

 

仅用四个词解释事务(ACID

        atomic(原子性):要么都发生,要么都不发生。

        consistent(一致性):数据应该不被破坏。

        Isolate(隔离性):用户间操作不相混淆

        Durable(持久性):永久保存,例如保存到数据库中等

 

Spring提供了两种事务管理方式

     编程事务管理

编写程序式的事务管理可以清楚的定义事务的边界,可以实现细粒度的事务控制,比如你可以通过程序代码来控制你的事务何时开始,何时结束等,与后面介绍的声明式事务管理相比,它可以实现细粒度的事务控制。

    

     声明式事务管理(*****)

如果你并不需要细粒度的事务控制,你可以使用声明式事务,在Spring中,你只需要在Spring配置文件中做一些配置,即可将操作纳入到事务管理中,解除了和代码的耦合, 这是对应用代码影响最小的选择,从这一点再次验证了Spring关于AOP的概念。当你不需要事务管理的时候,可以直接从Spring配置文件中移除该设置

之前手工方式控制事务:例如:

 

 

 

如果使用spring的声明式事务处理,这些方法都是可以省略的。

2spring提供的事务管理器

介绍spring的事务管理器

   spring没有直接管理事务,而是将管理事务的责任委托给JTA或相应的持久性机制所提供的某个特定平台的事物实现

 

 

 

 

 

3:传播行为

传播行为:定义关于客户端和被调用方法的事物边界

例如一个操作:

 

 

 

声明对应的传播行为

事务的传播级别定义的是事务的控制范围,主要是父子事务之间的相互影响关系;事务的隔离级别定义的是事务读写的控制范围,主要是两个事务之间的相互影响关系。

 

传播级别:

 

 

 

    

总结:

REQUIRED(默认值):也是项目应用最多的传播行为,因为他表示如果业务方法存在一个事务中,则直接使用这个事务。如果业务方法不存在事务,则开启一个新的事务。

保证一个业务方法在一个事务中完成。

 

 

 

4:隔离级别

 

 

 

脏读:一个事务读取了另一个事务改写但还未提交的数据,如果这些数据被回滚,则读到的数据是无效的。

不可重复读:在同一事务中,多次读取同一数据返回的结果有所不同。换句话说就是,后续读取可以读到另一事务已提交的更新数据。相反,“可重复读”在同一事务中多次读取数据时,能够保证所读数据一样,也就是,后续读取不能读到另一事务已提交的更新数据。

幻读虚度一个事务读取了几行记录后,另一个事务插入一些记录,幻读就发生了。再后来的查询中,第一个事务就会发现有些原来没有的记录。

总结:

开发时使用DEFAULT(默认值),表示数据库采用那个隔离级别,我们就使用哪种隔离级别

 

5:声明式事务处理(XML

1)在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-4.3.xsd

              http://www.springframework.org/schema/context

              http://www.springframework.org/schema/context/spring-context-4.3.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">

    <!-- 配置DBCP连接池 -->

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">

     <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>

     <property name="url" value="jdbc:mysql://localhost:3306/0403_demo02?useUnicode=true&characterEncoding=utf8"></property>

     <property name="username" value="root"></property>

     <property name="password" value="123"></property>

    

     <!-- 连接池启动时的初始值 -->

  <property name="initialSize" value="1"/>

  <!-- 连接池的最大值 -->

  <property name="maxActive" value="500"/>

  <!-- 最大空闲值.当经过一个高峰时间后,连接池可以慢慢将已经用不到的连接慢慢释放一部分,一直减少到maxIdle为止 -->

  <property name="maxIdle" value="2"/>

  <!--  最小空闲值.当空闲的连接数少于该值时,连接池就会预申请一些连接,以避免洪峰来时再申请而造成的性能开销 -->

  <property name="minIdle" value="1"/>

    

    </bean>

    

    <!-- 创建spring提供的Jdbc模板,用来操作数据库 -->

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">

     <property name="dataSource" ref="dataSource"></property>

    </bean>

    

    <!-- 创建Service -->

    <bean id="accountService" class="com.offcn.service.impl.AccountServiceImpl">

     <property name="inAccountDao" ref="inAccountDao"></property>

     <property name="accountDao" ref="accountDao"></property>

    </bean>

    <!-- 创建Dao -->

    <bean id="accountDao" class="com.offcn.dao.impl.AccountDaoImpl">

     <property name="jdbcTemplate" ref="jdbcTemplate"></property>

    </bean>

    <bean id="inAccountDao" class="com.offcn.dao.impl.InAccountDaoImpl">

     <property name="jdbcTemplate" ref="jdbcTemplate"></property>

    </bean>

    

    <!-- 添加声明式事务处理(XMLbegin -->

    <!--

     声明事务管理器

     (相当于切面,也就是说在切面的类中定义的是事务控制)

     -->

    <bean id="trManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

    <property name="dataSource" ref="dataSource"></property>

    </bean>

    <!--

     tx:advice:定义通知(通知关联事务,通知的方法要放置到切面的类中)

     * id:通知方法的在容器中的惟一标识

     * transaction-manager:事务管理,指定对哪个事务进行管理

     tx:method:对切入点方法的细化

     * name:表示切入点中的方法名称

        * 实例:

           * name="saveAccount":表示业务层的saveAccount的方法 (1

           * name="save*":表示业务层的以save开头的方法(2

           * name="*":表示业务层的所有的方法(3

           * 执行的优先级:(1>2>3

     * isolation="DEFAULT":事务的隔离级别,数据库用什么隔离级别,该方法就使用什么隔离级别

     * propagation="REQUIRED":如果业务方法存在一个事务中,直接使用这个事务,如果业务方法运行不存在一个事务,自己会开启一个新的事务

     * read-only="false":可写数据库,比如增、删、改的操作

read-only=”true”:只读数据库比如查询的操作

     -->

    <tx:advice id="trManagerAdvice" transaction-manager="trManager">

    <tx:attributes>

    <tx:method name="save*" isolation="DEFAULT" propagation="REQUIRED" read-only="false"/>

    <tx:method name="update*" isolation="DEFAULT" propagation="REQUIRED" read-only="false"/>

    <tx:method name="delete*" isolation="DEFAULT" propagation="REQUIRED" read-only="false"/>

    <tx:method name="*" read-only="true"/>

    </tx:attributes>

    </tx:advice>

    <!-- 定义切入点点,而且使得切入点要关联通知 -->

    <aop:config>

    <!--

    aop:pointcut:定义切入点,(业务层的方法就是切入点)

     -->

<aop:pointcut id="servicePointCut" expression="execution(* com.offcn.service..*.*(..))" />    

<!--

aop:advisor:配置切入点要关联通知(事务控制业务层的方法)

 -->

<aop:advisor advice-ref="trManagerAdvice" pointcut-ref="servicePointCut"/>

    </aop:config>

    

    <!-- 添加声明式事务处理(XMLend-->

</beans>

 

测试再次测试我们的练习,发现事务可以被控制。

* 即如果没有错误,则提交

        如果出现错误,则回滚

2Spring事务控制的原理:

Spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是无法提供事务功能的。对于纯JDBC操作数据库,想要用到事务,可以按照以下步骤进行:

  1. 获取连接 Connection con = DriverManager.getConnection()
  2. 开启事务con.setAutoCommit(true/false);
  3. 执行CRUD
  4. 提交事务/回滚事务 con.commit() / con.rollback();
  5. 关闭连接 conn.close();

使用Spring的事务管理功能后,我们可以不再写步骤 2 4 的代码,而是由Spirng 自动完成

 

在控制层访问业务的方法时,实质上产生一个代理对象$Proxy,在代理对象中对业务层的方法用事务进行控制,这样就将业务层的方法用一个事务做了控制

实质上代理产生的代码(因为在容器中进行配置)进行事务控制:这个配置再次验证spring关于aop的思想,即在业务层的方法之前,创建一个代理类,由代理类控制事务的提交和回滚。

例如:

 

 

 

 

3)测试代码

结构:

 

 

   

 

l Account.java

//账号信息

public class Account {

 

private String accountid; //账号

private Double balance; //余额

 

public String getAccountid() {

return accountid;

}

public void setAccountid(String accountid) {

this.accountid = accountid;

}

public Double getBalance() {

return balance;

}

public void setBalance(Double balance) {

this.balance = balance;

}

 

 

}

 

l InAccount.java

//存款信息

public class InAccount {

 

private String accountid; //账号

private Double inbalance; //存款金额

 

public String getAccountid() {

return accountid;

}

public void setAccountid(String accountid) {

this.accountid = accountid;

}

public Double getInbalance() {

return inbalance;

}

public void setInbalance(Double inbalance) {

this.inbalance = inbalance;

}

 

 

}

 

 

l IAccountDao.java接口

public interface IAccountDao {

 

Account findAccountById(String accountid);

 

void updateAccount(Account account);

 

}

l IInAccountDao.java接口

public interface IInAccountDao {

 

void save(InAccount inAccount);

 

}

 

l AccountDaoImpl.java实现类

/**账号DAO*/

public class AccountDaoImpl implements IAccountDao {

 

private JdbcTemplate jdbcTemplate;

 

public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {

this.jdbcTemplate = jdbcTemplate;

}

 

/**2:使用8888的账号查询账号信息表,获取8888账号对应的余额*/

@Override

public Account findAccountById(String accountid) {

String sql = "select accountid,balance from account where accountid=?";

Object [] args = {accountid};

RowMapper<Account> rowMapper = new RowMapper<Account>() {

 

@Override

public Account mapRow(ResultSet rs, int rowNum) throws SQLException {

Account account = new Account();

account.setAccountid(rs.getString("accountid"));

account.setBalance(rs.getDouble("balance"));

return account;

}

};

Account account = jdbcTemplate.queryForObject(sql, args, rowMapper);

return account;

}

 

/**4:更新账号信息表,对8888的账号,将余额更新最新的余额*/

@Override

public void updateAccount(Account account) {

try {

//故意抛出报错

String sql = "update account set balancexxxxxxxxxxxx=? where accountid=?";

Object [] args = {account.getBalance(),account.getAccountid()};

jdbcTemplate.update(sql, args);

} catch (Exception e) {

e.printStackTrace();

throw new RuntimeException("Dao抛出运行时异常!");

}

}

}

 

l InAccountDaoImpl.java实现类

public class InAccountDaoImpl implements IInAccountDao {

 

private JdbcTemplate jdbcTemplate;

 

public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {

this.jdbcTemplate = jdbcTemplate;

}

 

/**1:向存款信息表中的8888账号存款200元,存放一条记录*/

@Override

public void save(InAccount inAccount) {

String sql = "insert into inaccount(accountid,inbalance) values (?,?)";

Object [] args = {inAccount.getAccountid(),inAccount.getInbalance()};

jdbcTemplate.update(sql, args);

}

 

}

l IAccountService.java接口

public interface IAccountService {

 

void saveAccount(InAccount inAccount);

 

}

l AccountServiceImpl.java实现类

public class AccountServiceImpl implements IAccountService {

 

private IInAccountDao inAccountDao;

 

private IAccountDao accountDao;

 

public void setInAccountDao(IInAccountDao inAccountDao) {

this.inAccountDao = inAccountDao;

}

 

public void setAccountDao(IAccountDao accountDao) {

this.accountDao = accountDao;

}

 

/**8888的账号存款200元钱*/

@Override

public void saveAccount(InAccount inAccount) {

try {

//1:向存款信息表中的8888账号存款200元,存放一条记录

inAccountDao.save(inAccount);

//2:使用8888的账号查询账号信息表,获取8888账号对应的余额

String accountid = inAccount.getAccountid();

Account account = accountDao.findAccountById(accountid);

//3:更新8888账号的余额(余额=存款的金额+新增的金额)

Double balance = account.getBalance()+inAccount.getInbalance();

account.setBalance(balance);

//4:更新账号信息表,对8888的账号,将余额更新最新的余额

accountDao.updateAccount(account);

} catch (Exception e) {

e.printStackTrace();

throw new RuntimeException("Service层抛出运行时异常!");

}

}

}

 

l Spring容器beans.xml的配置

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       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-4.3.xsd

              http://www.springframework.org/schema/context

              http://www.springframework.org/schema/context/spring-context-4.3.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">

    <!-- 配置DBCP连接池 -->

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">

     <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>

     <property name="url" value="jdbc:mysql://localhost:3306/0403_demo02?useUnicode=true&characterEncoding=utf8"></property>

     <property name="username" value="root"></property>

     <property name="password" value="123"></property>

    

     <!-- 连接池启动时的初始值 -->

  <property name="initialSize" value="1"/>

  <!-- 连接池的最大值 -->

  <property name="maxActive" value="500"/>

  <!-- 最大空闲值.当经过一个高峰时间后,连接池可以慢慢将已经用不到的连接慢慢释放一部分,一直减少到maxIdle为止 -->

  <property name="maxIdle" value="2"/>

  <!--  最小空闲值.当空闲的连接数少于该值时,连接池就会预申请一些连接,以避免洪峰来时再申请而造成的性能开销 -->

  <property name="minIdle" value="1"/>

    

    </bean>

    

    <!-- 创建spring提供的Jdbc模板,用来操作数据库 -->

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">

     <property name="dataSource" ref="dataSource"></property>

    </bean>

    

    <!-- 创建Service -->

    <bean id="accountService" class="com.offcn.service.impl.AccountServiceImpl">

     <property name="inAccountDao" ref="inAccountDao"></property>

     <property name="accountDao" ref="accountDao"></property>

    </bean>

    <!-- 创建Dao -->

    <bean id="accountDao" class="com.offcn.dao.impl.AccountDaoImpl">

     <property name="jdbcTemplate" ref="jdbcTemplate"></property>

    </bean>

    <bean id="inAccountDao" class="com.offcn.dao.impl.InAccountDaoImpl">

     <property name="jdbcTemplate" ref="jdbcTemplate"></property>

    </bean>

    

    <!-- 添加声明式事务处理(XMLbegin -->

    <!--

     声明事务管理器

     (相当于切面,也就是说在切面的类中定义的是事务控制)

     -->

    <bean id="trManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

    <property name="dataSource" ref="dataSource"></property>

    </bean>

    <!--

     tx:advice:定义通知(通知关联事务,通知的方法要放置到切面的类中)

     * id:通知方法的在容器中的惟一标识

     * transaction-manager:事务管理,指定对哪个事务进行管理

     tx:method:对切入点方法的细化

     * name:表示切入点中的方法名称

        * 实例:

           * name="saveAccount":表示业务层的saveAccount的方法 (1

           * name="save*":表示业务层的以save开头的方法(2

           * name="*":表示业务层的所有的方法(3

           * 执行的优先级:(1>2>3

     * isolation="DEFAULT":事务的隔离级别,数据库用什么隔离级别,该方法就使用什么隔离级别

     * propagation="REQUIRED":如果业务方法存在一个事务中,直接使用这个事务,如果业务方法运行不存在一个事务,自己会开启一个新的事务

     * read-only="false":可写数据库,比如增、删、改的操作,需要对数据库进行可写,查询的操作,需要只读

     -->

    <tx:advice id="trManagerAdvice" transaction-manager="trManager">

    <tx:attributes>

    <tx:method name="save*" isolation="DEFAULT" propagation="REQUIRED" read-only="false"/>

    <tx:method name="update*" isolation="DEFAULT" propagation="REQUIRED" read-only="false"/>

    <tx:method name="delete*" isolation="DEFAULT" propagation="REQUIRED" read-only="false"/>

    <tx:method name="*" read-only="true"/>

    </tx:attributes>

    </tx:advice>

    <!-- 定义切入点点,而且使得切入点要关联通知 -->

    <aop:config>

     <!--

     aop:pointcut:定义切入点,(业务层的方法就是切入点)

      -->

<aop:pointcut id="servicePointCut" expression="execution(* com.offcn.service..*.*(..))" />    

<!--

aop:advisor:配置切入点要关联通知(事务控制业务层的方法)

 -->

<aop:advisor advice-ref="trManagerAdvice" pointcut-ref="servicePointCut"/>

    </aop:config>

    

    <!-- 添加声明式事务处理(XMLend-->

</beans>

测试类App.java

@RunWith(value=SpringJUnit4ClassRunner.class)

@ContextConfiguration(value="classpath:beans.xml")

public class App {

 

@Resource(name="accountService")

private IAccountService accountService;

 

 

/**需求:向8888的账号存款200元钱*/

@Test

public void saveAccount(){

InAccount inAccount = new InAccount();

inAccount.setAccountid("8888");

inAccount.setInbalance(200d);

accountService.saveAccount(inAccount);

}

 

}

 

6:声明式事务处理(注解)

1)在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-4.3.xsd

              http://www.springframework.org/schema/context

              http://www.springframework.org/schema/context/spring-context-4.3.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.offcn"/>

    <!-- 配置DBCP连接池 -->

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">

     <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>

     <property name="url" value="jdbc:mysql://localhost:3306/0403_demo02?useUnicode=true&characterEncoding=utf8"></property>

     <property name="username" value="root"></property>

     <property name="password" value="123"></property>

    

     <!-- 连接池启动时的初始值 -->

  <property name="initialSize" value="1"/>

  <!-- 连接池的最大值 -->

  <property name="maxActive" value="500"/>

  <!-- 最大空闲值.当经过一个高峰时间后,连接池可以慢慢将已经用不到的连接慢慢释放一部分,一直减少到maxIdle为止 -->

  <property name="maxIdle" value="2"/>

  <!--  最小空闲值.当空闲的连接数少于该值时,连接池就会预申请一些连接,以避免洪峰来时再申请而造成的性能开销 -->

  <property name="minIdle" value="1"/>

    

    </bean>

    

    <!-- 创建spring提供的Jdbc模板,用来操作数据库 -->

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">

     <property name="dataSource" ref="dataSource"></property>

    </bean>

    

    

    

    <!-- 添加声明式事务处理(注解的方式)begin -->

 

    <bean id="trManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

    <property name="dataSource" ref="dataSource"></property>

    </bean>

    <!-- 注解需要在业务层的类的上面或者方法的上面添加一个注解@Transcational -->

    <tx:annotation-driven transaction-manager="trManager"/>

    

    <!-- 添加声明式事务处理(注解的方式) end-->

</beans>

 

2)在业务层的类中定义:

/**

 * @Transcational注解

 *   * 放置到类的上面,默认对类中的所有方法都有效,而且默认是可写的操作

 *   * 放置到方法的上面:此时方法级别的事务,会覆盖类级别的事务

 *   

 *   注解方式总结:

1在业务层的类上面定义:@Transactional(readOnly=true),表示业务层的所有方法都只读。

 *    2在业务层类的方法上定义@Transactional(isolation=Isolation.DEFAULT,propagation=Propagation.REQUIRED,readOnly=false):表示方法可写,例如新增、删除、修改的方法上:

 *    3查询的方法无需定义@Transactional,因为表示只读即可

 *

 * 相当于spring容器中定义:

 * <tx:advice id="trManagerAdvice" transaction-manager="trManager">

     <tx:attributes>

     <tx:method name="save*" isolation="DEFAULT" propagation="REQUIRED" read-only="false"/>

     <tx:method name="update*" isolation="DEFAULT" propagation="REQUIRED" read-only="false"/>

     <tx:method name="delete*" isolation="DEFAULT" propagation="REQUIRED" read-only="false"/>

     <tx:method name="*" read-only="true"/>

     </tx:attributes>

    </tx:advice>

    <!-- 定义切入点点,而且使得切入点要关联通知 -->

    <aop:config>

<aop:pointcut id="servicePointCut" expression="execution(* com.offcn.service..*.*(..))" />    

<aop:advisor advice-ref="trManagerAdvice" pointcut-ref="servicePointCut"/>

    </aop:config>

 *

 */

@Service("accountService")

@Transactional(isolation = Isolation.DEFAULT,propagation = Propagation.REQUIRED)

public class AccountServiceImpl implements IAccountService {

 

@Resource(name="inAccountDao")

private IInAccountDao inAccountDao;

@Resource(name="accountDao")

private IAccountDao accountDao;

 

/**8888的账号存款200元钱*/

@Override

@Transactional(isolation=Isolation.DEFAULT,propagation=Propagation.REQUIRED,readOnly=false)

public void saveAccount(InAccount inAccount) {

//1:向存款信息表中的8888账号存款200元,存放一条记录

inAccountDao.save(inAccount);

//2:使用8888的账号查询账号信息表,获取8888账号对应的余额

String accountid = inAccount.getAccountid();

Account account = accountDao.findAccountById(accountid);

//3:更新8888账号的余额(余额=存款的金额+新增的金额)

Double balance = account.getBalance()+inAccount.getInbalance();

account.setBalance(balance);

//4:更新账号信息表,对8888的账号,将余额更新最新的余额

accountDao.updateAccount(account);

}

}

3)总结:

1方法级别的事务,会覆盖类级别的事务

2)注解@Transactional放置到类的上面,默认对类中的所有方法都有效,如果不添加任何属性,此时默认是可写的操作,即类中的所有方法都可写。

3) 在业务层的类上面定义:@Transactional(readOnly=true),此时表示业务层类上的所有方法都是只读操作

4@Transactional(isolation=Isolation.DEFAULT,propagation=Propagation.REQUIRED,readOnly=false)放到方法上,表示该方法是可写的的操作,例如新增、修改、删除的方法。此时查询的方法无需定义@Transactional

 

4)测试代码

结构:

 

 

 

 

 

l Account.java

//账号信息

public class Account {

 

private String accountid; //账号

private Double balance; //余额

 

public String getAccountid() {

return accountid;

}

public void setAccountid(String accountid) {

this.accountid = accountid;

}

public Double getBalance() {

return balance;

}

public void setBalance(Double balance) {

this.balance = balance;

}

 

}

 

l InAccount.java

//存款信息

public class InAccount {

 

private String accountid; //账号

private Double inbalance; //存款金额

 

public String getAccountid() {

return accountid;

}

public void setAccountid(String accountid) {

this.accountid = accountid;

}

public Double getInbalance() {

return inbalance;

}

public void setInbalance(Double inbalance) {

this.inbalance = inbalance;

}

 

 

}

 

 

l IAccountDao.java接口

public interface IAccountDao {

 

Account findAccountById(String accountid);

 

void updateAccount(Account account);

 

}

l IInAccountDao.java接口

public interface IInAccountDao {

 

void save(InAccount inAccount);

 

}

 

l AccountDaoImpl.java实现类

/**账号DAO*/

@Repository("accountDao")

public class AccountDaoImpl implements IAccountDao {

 

@Resource(name="jdbcTemplate")

private JdbcTemplate jdbcTemplate;

 

/**2:使用8888的账号查询账号信息表,获取8888账号对应的余额*/

@Override

public Account findAccountById(String accountid) {

String sql = "select accountid,balance from account where accountid=?";

Object [] args = {accountid};

RowMapper<Account> rowMapper = new RowMapper<Account>() {

 

@Override

public Account mapRow(ResultSet rs, int rowNum) throws SQLException {

Account account = new Account();

account.setAccountid(rs.getString("accountid"));

account.setBalance(rs.getDouble("balance"));

return account;

}

};

Account account = jdbcTemplate.queryForObject(sql, args, rowMapper);

return account;

}

 

/**4:更新账号信息表,对8888的账号,将余额更新最新的余额*/

@Override

public void updateAccount(Account account) {

try {

String sql = "update account set balancexxxxxxxxxxxx=? where accountid=?";

Object [] args = {account.getBalance(),account.getAccountid()};

jdbcTemplate.update(sql, args);

} catch (Exception e) {

e.printStackTrace();

throw new RuntimeException("抛出运行时异常!");

}

}

}

 

l InAccountDaoImpl.java实现类

@Repository("inAccountDao")

public class InAccountDaoImpl implements IInAccountDao {

 

@Resource(name="jdbcTemplate")

private JdbcTemplate jdbcTemplate;

 

/**1:向存款信息表中的8888账号存款200元,存放一条记录*/

@Override

public void save(InAccount inAccount) {

String sql = "insert into inaccount(accountid,inbalance) values (?,?)";

Object [] args = {inAccount.getAccountid(),inAccount.getInbalance()};

jdbcTemplate.update(sql, args);

}

 

}

l IAccountService.java接口

public interface IAccountService {

 

void saveAccount(InAccount inAccount);

 

}

l AccountServiceImpl.java实现类

/**

 * @Transcational注解

 *   * 放置到类的上面,默认对类中的所有方法都有效,而且默认是可写的操作

 *   * 放置到方法的上面:此时方法级别的事务,会覆盖类级别的事务

 *   

 *   注解方式总结:

1在业务层的类上面定义:@Transactional(readOnly=true),表示业务层的所有方法都只读。

 *    2在业务层类的方法上定义@Transactional(isolation=Isolation.DEFAULT,propagation=Propagation.REQUIRED,readOnly=false):表示方法可写,例如新增、删除、修改的方法上:

 *    3查询的方法无需定义@Transactional,因为表示只读即可

 *

 * 相当于spring容器中定义:

 * <tx:advice id="trManagerAdvice" transaction-manager="trManager">

     <tx:attributes>

     <tx:method name="save*" isolation="DEFAULT" propagation="REQUIRED" read-only="false"/>

     <tx:method name="update*" isolation="DEFAULT" propagation="REQUIRED" read-only="false"/>

     <tx:method name="delete*" isolation="DEFAULT" propagation="REQUIRED" read-only="false"/>

     <tx:method name="*" read-only="true"/>

     </tx:attributes>

    </tx:advice>

    <!-- 定义切入点点,而且使得切入点要关联通知 -->

    <aop:config>

<aop:pointcut id="servicePointCut" expression="execution(* com.offcn.service..*.*(..))" />    

<aop:advisor advice-ref="trManagerAdvice" pointcut-ref="servicePointCut"/>

    </aop:config>

 *

 */

@Service("accountService")

@Transactional(readOnly=true)

public class AccountServiceImpl implements IAccountService {

 

@Resource(name="inAccountDao")

private IInAccountDao inAccountDao;

@Resource(name="accountDao")

private IAccountDao accountDao;

 

/**8888的账号存款200元钱*/

@Override

@Transactional(isolation=Isolation.DEFAULT,propagation=Propagation.REQUIRED,readOnly=false)

public void saveAccount(InAccount inAccount) {

//1:向存款信息表中的8888账号存款200元,存放一条记录

inAccountDao.save(inAccount);

//2:使用8888的账号查询账号信息表,获取8888账号对应的余额

String accountid = inAccount.getAccountid();

Account account = accountDao.findAccountById(accountid);

//3:更新8888账号的余额(余额=存款的金额+新增的金额)

Double balance = account.getBalance()+inAccount.getInbalance();

account.setBalance(balance);

//4:更新账号信息表,对8888的账号,将余额更新最新的余额

accountDao.updateAccount(account);

}

}

l Spring容器beans.xml的配置

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       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-4.3.xsd

              http://www.springframework.org/schema/context

              http://www.springframework.org/schema/context/spring-context-3.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.offcn"/>

    <!-- 配置DBCP连接池 -->

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">

     <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>

     <property name="url" value="jdbc:mysql://localhost:3306/0403_demo02?useUnicode=true&characterEncoding=utf8"></property>

     <property name="username" value="root"></property>

     <property name="password" value="123"></property>

    

     <!-- 连接池启动时的初始值 -->

  <property name="initialSize" value="1"/>

  <!-- 连接池的最大值 -->

  <property name="maxActive" value="500"/>

  <!-- 最大空闲值.当经过一个高峰时间后,连接池可以慢慢将已经用不到的连接慢慢释放一部分,一直减少到maxIdle为止 -->

  <property name="maxIdle" value="2"/>

  <!--  最小空闲值.当空闲的连接数少于该值时,连接池就会预申请一些连接,以避免洪峰来时再申请而造成的性能开销 -->

  <property name="minIdle" value="1"/>

    

    </bean>

    

    <!-- 创建spring提供的Jdbc模板,用来操作数据库 -->

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">

     <property name="dataSource" ref="dataSource"></property>

    </bean>

    

    

    

    <!-- 添加声明式事务处理(注解的方式)begin -->

 

    <bean id="trManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">

    <property name="dataSource" ref="dataSource"></property>

    </bean>

    <!-- 注解需要在业务层的类的上面或者方法的上面添加一个注解@Transcational -->

    <tx:annotation-driven transaction-manager="trManager"/>

    

    <!-- 添加声明式事务处理(注解的方式) end-->

</beans>

测试类App.java

@RunWith(value=SpringJUnit4ClassRunner.class)

@ContextConfiguration(value="classpath:beans.xml")

public class App {

 

@Resource(name="accountService")

private IAccountService accountService;

 

 

/**需求:向8888的账号存款200元钱*/

@Test

public void saveAccount(){

InAccount inAccount = new InAccount();

inAccount.setAccountid("8888");

inAccount.setInbalance(200d);

accountService.saveAccount(inAccount);

}

 

}

 

 

 

 

posted @ 2020-10-20 16:11  master_hxh  阅读(112)  评论(0编辑  收藏  举报