java深入探究12-框架之Spring

1.引入Spring

  我们在搭建框架时常常会解决问题:对象创建,对象之间依赖关系如何处理,Spring就是来解决这类问题的:控制反转依赖注入

2.环境搭建

  1)下载源码:其中3.0以下版本源码中有Spring相关所有包【核心包+依赖包】

          3.0以上版本源码中只有spring核心包

  2)导入jar包:spring-framework-3.2.5.RELEASE

    commons-logging-1.1.3.jar           日志

    spring-beans-3.2.5.RELEASE.jar        bean节点

    spring-context-3.2.5.RELEASE.jar       spring上下文节点

    spring-core-3.2.5.RELEASE.jar         spring核心功能

    spring-expression-3.2.5.RELEASE.jar    spring表达式相关表

    以上是必须引入的5jar文件,在项目中可以用户库管理!

  3)核心配置文件applicationContext.xml 和bean的属性描述   

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    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">
    
</beans>   
View Code

bean属性描述

/**
     * 1) 对象创建: 单例/多例
     *     scope="singleton", 默认值, 即 默认是单例    【service/dao/工具类】
     *  scope="prototype", 多例;                 【Action对象】
     * 
     * 2) 什么时候创建?
     *       scope="prototype"  在用到对象的时候,才创建对象。
     *    scope="singleton"  在启动(容器初始化之前), 就已经创建了bean,且整个应用只有一个。
     * 3)是否延迟创建
     *       lazy-init="false"  默认为false,  不延迟创建,即在启动时候就创建对象
     *       lazy-init="true"   延迟初始化, 在用到对象的时候才创建对象
     *    (只对单例有效)
     * 4) 创建对象之后,初始化/销毁
     *       init-method="init_user"       【对应对象的init_user方法,在对象创建爱之后执行 】
     *    destroy-method="destroy_user"  【在调用容器对象的destriy方法时候执行,(容器用实现类)】
     */
    @Test
    public void testIOC() throws Exception {
        // 得到IOC容器对象  【用实现类,因为要调用销毁的方法】
        ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("cn/itcast/a_hello/applicationContext.xml");
        System.out.println("-----容器创建-----");
        
        // 从容器中获取bean
        User user1 = (User) ac.getBean("user");
        User user2 = (User) ac.getBean("user");
        
        System.out.println(user1);
        System.out.println(user2);
        
        // 销毁容器对象 
        ac.destroy();
    }
View Code

  4)获取IOC两种方式:

    通过BeanFactory获得IOC容器

// 现在,把对象的创建交给spring的IOC容器
        Resource resource = new ClassPathResource("cn/itcast/a_hello/applicationContext.xml");
        // 创建容器对象(Bean的工厂), IOC容器 = 工厂类 + applicationContext.xml
        BeanFactory factory = new XmlBeanFactory(resource);
        // 得到容器创建的对象
        User user = (User) factory.getBean("user");
View Code

    直接获得IOC容器:ApplicationContext ac = new ClassPathXmlApplicationContext("cn/itcast/a_hello/applicationContext.xml");

//2. (方便)直接得到IOC容器对象 
    @Test
    public void testAc() throws Exception {
        // 得到IOC容器对象
        ApplicationContext ac = new ClassPathXmlApplicationContext("cn/itcast/a_hello/applicationContext.xml");
        // 从容器中获取bean
        User user = (User) ac.getBean("user");
        
        System.out.println(user);
    }
View Code 

3.专业术语

  1)侵入式设计:引入框架对现有类结构产生影响struts框架;非侵入式框架:引入框架对现有类结构没有影响

  2)控制反转(Inversion on Control  IOC),依赖注入( dependency injection   DI)

    区别:IOC解决对象创建问题;DI:对象创建完后对对象属性赋值,对象间关系依赖的处理【通过set方法依赖注入】

  3)AOP:面向切面编程:切面简单来说就是由很多重复代码组成,如:事务,日志,权限

4.Spring提供的一站式解决方法

  1Spring Core  spring的核心功能: IOC容器, 解决对象创建及依赖关系

  2Spring Web  Springweb模块的支持。

    1. 可以与struts整合,strutsaction创建交给spring

        2. spring mvc模式

  3Spring DAO  Spring jdbc操作的支持  【JdbcTemplate模板工具类】

  4Spring ORM  springorm的支持:

    1. 既可以与hibernate整合,【session

    2. 也可以使用spring的对hibernate操作的封装

  5Spring AOP  切面编程

  6SpringEE   spring javaEE其他模块的支持

5.与applicationContext.xml配置文件有关的配置

  1)配置创建对象的三种方式:

    调用无参数构造器;带参数构造器constructor-arg;工厂创建对象factory-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:p="http://www.springframework.org/schema/p"
    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">
    
    <!-- ###############对象创建############### -->
    
    <!-- 1. 默认无参数构造器 
    <bean id="user1" class="cn.itcast.b_create_obj.User"></bean>
    -->
    
    <!-- 2. 带参数构造器 -->
    <bean id="user2" class="cn.itcast.b_create_obj.User">
        <constructor-arg index="0" type="int" value="100"></constructor-arg>
        <constructor-arg index="1" type="java.lang.String" value="Jack"></constructor-arg>
    </bean>
    
    <!-- 定义一个字符串,值是"Jack" ;  String s = new String("jack")-->
    <bean id="str" class="java.lang.String">
        <constructor-arg value="Jacks"></constructor-arg>
    </bean>
    <bean id="user3" class="cn.itcast.b_create_obj.User">
        <constructor-arg index="0" type="int" value="100"></constructor-arg>
        <constructor-arg index="1" type="java.lang.String" ref="str"></constructor-arg>
    </bean>
    
    
    <!-- 3. 工厂类创建对象 -->
    <!-- # 3.1 工厂类,实例方法 -->
    <!-- 先创建工厂 -->
    <bean id="factory" class="cn.itcast.b_create_obj.ObjectFactory"></bean>
    <!-- 在创建user对象,用factory方的实例方法 -->
    <bean id="user4" factory-bean="factory" factory-method="getInstance"></bean>
    
    
    <!-- # 3.2 工厂类: 静态方法 -->
    <!-- 
        class 指定的就是工厂类型
        factory-method  一定是工厂里面的“静态方法”
     -->
    <bean id="user" class="cn.itcast.b_create_obj.ObjectFactory" factory-method="getStaticInstance"></bean>
    
</beans>      



  
View Code

  2)给对象属性赋值(DI 依赖注入)    

    1.通过构造方法;2.通过set方式;3.p名称空间;4.自动装配;5.注解

    1.通过set方式:bean中加入<property name="userDao" ref="userDao">

     内部bean方式:

<!-- ##############内部bean############## -->
    <bean id="userAction" class="cn.itcast.c_property.UserAction">
        <property name="userService">
            <bean class="cn.itcast.c_property.UserService">
                <property name="userDao">
                    <bean class="cn.itcast.c_property.UserDao"></bean>
                </property>
            </bean>
        </property>
    </bean>
View Code

    2.p名称空间:p:userDao-ref=""代替了<property>set配置

<?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:p="http://www.springframework.org/schema/p"
    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">
    
    <!-- ###############对象属性赋值############### -->
    
    <!-- 
        给对象属性注入值:
            # p 名称空间给对象的属性注入值
             (spring3.0以上版本才支持)
     -->
     <bean id="userDao" class="cn.itcast.c_property.UserDao"></bean>
     
     <bean id="userService" class="cn.itcast.c_property.UserService" p:userDao-ref="userDao"></bean>
     
     <bean id="userAction" class="cn.itcast.c_property.UserAction" p:userService-ref="userService"></bean>
    
    
    <!-- 传统的注入: 
     <bean id="user" class="cn.itcast.c_property.User" >
         <property name="name" value="xxx"></property>
     </bean>
    -->
    <!-- p名称空间优化后 -->
    <bean id="user" class="cn.itcast.c_property.User" p:name="Jack0001"></bean>
     
</beans>   
View Code

    3.自动装配:autowire="byName";autowire="byType"可以在<bean>属性上设置也可以在全局配置在头部default-autowire="byName">   根据名称自动装配(全局)    

<!-- ###############自动装配############### -->  
自动去IOC容器中找与属性名同名的引用的对象,并自动注入
    <bean id="userDao" class="cn.itcast.d_auto.UserDao"></bean>    
    <bean id="userService" class="cn.itcast.d_auto.UserService" autowire="byName"></bean>
    <!-- 根据“名称”自动装配: userAction注入的属性,会去ioc容器中自动查找与属性同名的对象 -->
    <bean id="userAction" 
class="cn.itcast.d_auto.UserAction" autowire="byName"></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:p="http://www.springframework.org/schema/p"
    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" default-autowire="byName">   根据名称自动装配(全局)
    
    <!-- ###############自动装配############### -->  
    <bean id="userDao" class="cn.itcast.d_auto.UserDao"></bean>    
    <bean id="userService" class="cn.itcast.d_auto.UserService"></bean>
    <bean id="userAction" class="cn.itcast.d_auto.UserAction"></bean>
</beans>   

根据类型byType
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    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" default-autowire="byType">
    
    <!-- ###############自动装配############### -->  
    <bean id="userDao" class="cn.itcast.d_auto.UserDao"></bean>    
    <bean id="userService" class="cn.itcast.d_auto.UserService"></bean>
    
    <!-- 如果根据类型自动装配: 必须确保IOC容器中只有一个该类型的对象 -->
    <bean id="userAction" class="cn.itcast.d_auto.UserAction"></bean>
    
    
    <!--   报错: 因为上面已经有一个该类型的对象,且使用了根据类型自动装配
    <bean id="userService_test" class="cn.itcast.d_auto.UserService" autowire="byType"></bean>
     -->
</beans>  
View Code

    总结:pring提供的自动装配主要是为了简化配置; 但是不利于后期的维护   

    4.注解:

    使用步骤:

      1.先引入context名称空间

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

      2.开始扫描:

      <context:component-scan base-package="cn.itcast.e_anno2"></context:component-scan>

      3.使用注解:通过注解的方式,把对象加入ioc容器

            @Component   指定把一个对象加入IOC容器

          @Repository   作用同@Component 在持久层使用

          @Service      作用同@Component 在业务逻辑层使用

          @Controller    作用同@Component 在控制层使用

          @Resource     属性注入

      例子:

bean.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:p="http://www.springframework.org/schema/p"
    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="cn.itcast.e_anno2"></context:component-scan>
    
    <bean id="userDao" class="cn.itcast.e_anno2.UserDao" scope="prototype">
    </bean>
        
</beans>      

Dao
// 把当前对象加入ioc容器
//@Component("userDao")   //  相当于bean.xml 【<bean id=userDao class=".." />】

//@Component  // 加入ioc容器的UserDao对象的引用名称, 默认与类名一样, 且第一个字母小写

//@Repository   // 在持久层可以选择用这个注解
public class UserDao {
    
    public UserDao(){
        System.out.println("UserDao.UserDao()");
    }
    
    public UserDao(int id){
        System.out.println("UserDao.UserDao(int id)" + id);
    }

    public void save() {
        System.out.println("DB:保存用户!!!");
    }
}

Service
//@Component("userService")  // userService加入ioc容器

//@Component

@Service   // 表示业务逻辑层的组件
public class UserService {
    
//    @Resource                    //  根据类型查找 【在容器中要确保该类型只有一个变量】
    
    @Resource(name = "userDao")  // 根据名称查找
    private UserDao userDao;  // 去容器中招UserDao类型的变量,找到后就赋值

    public void save() {
        userDao.save();
    }
}

Action
//@Component("userAction")  // 加入IOC容器

//@Component

@Controller  // 控制层的组件
public class UserAction {

    @Resource
    private UserService userService;

    public String execute() {
        userService.save();
        return null;
    }
}
View Code

 6.Spring与Struts结合

  关键点:struts的action创建交给spring

  导入jar包

  配置xml:Struts.xml;bean.xml;web.xml

    struts.xml:struts路径与action映射配置

    bean.xml:spring ioc 配置

    web.xml:核心过滤器,初始化spring ioc容器

将Spring配置到web.xml中

<!-- 2. spring 配置 -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/classes/bean-*.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
View Code

 -----------------------------------------------------------

1.代理模式

  1)理解:代理(Proxy)是一种设计模式,想在目标对象上实现功能扩展可用代理,是用户访问目标对象另一种访问方式

      举例:明星《--经纪人<--用户:另一种方式访问对象给对象添加新功能

  2)静态代理

    1.原理:代理对象实现目标对象一样的接口也可以添加额外功能

    2.优缺点:可以在不改变目标对象功能前提下,对目标对象功能扩展;但是可能会有很多代理的对象,且代理对象和目标对象都要维护

    3.实现方式:与目标对象实现相同接口,在代理对象中添加自己的功能写上目标对象功能--------与装饰者设计模式类似

    4.例子:保存用户(模拟)-》Dao(直接保存)-》DaoProxy(给保存添加事务处理)

IUserDao
package 代理.a_static;

public interface IUserDao {
    void save();
}
UserDao
package 代理.a_static;

public class UserDao implements IUserDao{

    @Override
    public void save() {
        System.out.println("用户保存对象");
    }

}
UserDaoProxy
package 代理.a_static;

public class UserDaoProxy implements IUserDao{
    private IUserDao target;
    public UserDaoProxy(IUserDao target){
        this.target=target;
    }
    @Override
    public void save() {
        System.out.println("事务开始");
        target.save();
        System.out.println("事务结束");
    }

}
测试APP
package 代理.a_static;

public class App {
    public static void main(String[] args){
        //目标对象
        IUserDao target=new UserDao();
        //代理对象
        IUserDao proxy=new UserDaoProxy(target);
        proxy.save();
    }
}
View Code

  3)动态代理

    1.原理:代理对象不需要实现接口,而是动态的在内存中构建代理对象(需要我们提供代理对象/目标对象 实现接口的类型

    2.核心API:Proxy代理对象工具类

            static Object newProxyInstance(

            ClassLoader loader,       指定当前目标对象使用类加载器

            Class<?>[] interfaces,     目标对象实现的接口的类型

            InvocationHandler h       事件处理器

    3.动态代理总结:代理对象不需要实现接口,但是目标对象必须实现接口,但是遇到目标对象没有实现接口就要用到新技术Cglib代理

    4.例子:

IUserDao
package 代理.b_dynamic;

public interface IUserDao {
    void save();
}
UserDao
package 代理.b_dynamic;

public class UserDao implements IUserDao{

    @Override
    public void save() {
        System.out.println("-------用户已保存-------");
    }

}
代理对象动态创建,需要提供目标对象
package 代理.b_dynamic;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
 * 给所有的到创建代理对象【动态代理】
 * 
 * 代理对象不需要实现接口,内存内部自己自动生成
 * @author Administrator
 *
 */
public class ProxyFactory {
    private Object target;
    public ProxyFactory(Object target){
        this.target=target;
    }
    //内存自动生成的
    public Object getProxyInstance(){
        return Proxy.newProxyInstance(
                //目标对象类加载器
                target.getClass().getClassLoader(),
                //目标对象实现的接口类型
                target.getClass().getInterfaces(),
                //核心事务处理器
                new InvocationHandler() {    
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args)
                            throws Throwable {
                        System.out.println("事务开始");
                        
                        //执行目标对象方法
                        Object returnValue=method.invoke(target, args);//调用目标对象的方法
                        System.out.println("事务结束");
                        return returnValue;
                    }
                }            
                );
    }
}
测试App
package 代理.b_dynamic;

public class App {
    public static void main(String[] args){
        //目的对象
        IUserDao target=new UserDao();
        //动态代理对象创建
        IUserDao proxy=(IUserDao) new ProxyFactory(target).getProxyInstance();
        //内存中动态代理对象就是$Proxy0
        System.out.println(proxy.getClass());
        
        //执行方法
        proxy.save();
    }
    
}
View Code

  4)Cglib代理:子类代理

    1.原理:在内存中构建一个子类对象从而实现对目标对象功能扩展;

        底层原理:通过小儿快的字节码处理框架ASM,来转换字节码生产新类

    2.引入:普通jdk动态代理必须目标对象有接口实现,当遇到没有接口的就需要用CGLIB了

    3.使用步骤:

      1)引入cglib-jar,spring核心包中有了

      2)开始动态在内存中构建子类

      3)代理的类不能为final,否则报错;目标对象的方法如果为final/staic,那么就不会被拦截,不会执行目标对象额外的业务方法

  总结:在Spring的AOP编程中,如果容器的目标对象有实现接口,用JDK代理,没有实现接口可以用cglib子类代理

    4.例子:

    实现MethodInterceptor接口实现逻辑代码-》写获得子代理对象getProxyInsance()->通过Enhancer工具类赋值父类,创建子类代理对象

UserDao
package h_cglib;

public class UserDao {
    public void save(){
        System.out.println("User保存");
    }
}
Proxy
package h_cglib;

import java.lang.reflect.Method;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

public class ProxyFactory implements MethodInterceptor{
    private Object target;
    public ProxyFactory(Object target){
        this.target=target;
    }
    //给目标创建代理自对象
    public Object getProxyInsance(){
        //1.工具类
        Enhancer en=new Enhancer();
        //2.设置父类
        en.setSuperclass(target.getClass());
        //3.设置回调函数
        en.setCallback(this);
        //4.创建子类(代理对象)
        return en.create();
    }
    @Override
    public Object intercept(Object obj, Method method, Object[] args,
            MethodProxy arg3) throws Throwable {
        // TODO Auto-generated method stub
        System.out.println("开始事务。。。");
        Object returnValue=method.invoke(target, args);
        System.out.println("提交事务。。。");
        return returnValue;
    }
    
}
测试类
package h_cglib;

import g_dynamic.IUserDao;

public class App {
    
    public static void main(String[] args) {
        //目标对象
        UserDao target=new UserDao();
        System.out.println(target.getClass());
        
        //代理子对象
        UserDao proxy=(UserDao) new ProxyFactory(target).getProxyInsance();
        proxy.save();
    }
}
View Code

2.手动实现AOP编程【代码模式】面向切面编程

  1)理解:切片编程,就是将重复代码拿出来放在一个切边类中,之后再和核心逻辑代码组合叫AOP编程

  2)代码例子:都时抽象出重复代码放在AOP切片类中

IUserDao
package e_AOP.myAOP2;

public interface IUserDao {
    void save();
}
UserDao
package e_AOP.myAOP2;

import org.springframework.stereotype.Component;

@Component
public class UserDao implements IUserDao{

    @Override
    public void save() {
        System.out.println("User保存");
    }

}
ProxyFactory
package e_AOP.myAOP2;

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

public class ProxyFactory {
    private static Object target;
    private static Aop aop;
    
    public static Object getProxyInstance(Object target_,Aop aop_){
        target=target_;
        aop=aop_;
        
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(), 
                target.getClass().getInterfaces(), 
                new InvocationHandler() {
                    
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args)
                            throws Throwable {
                        aop.begin();
                        Object returnValue=method.invoke(target, args);
                        aop.end();
                        return returnValue;
                    }
                });
                
    }
}
Aop
package e_AOP.myAOP2;

import org.springframework.stereotype.Component;
/**
 * 切片代码,与逻辑代码分离
 * @author Administrator
 *
 */
@Component
public class Aop {
    public void begin(){
        System.out.println("开始事务。。。");
    }
    public void end(){
        System.out.println("结束事务。。。");
    }
}
App
package e_AOP.myAOP2;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {
    ApplicationContext ac=new ClassPathXmlApplicationContext("e_AOP/myAOP2/bean.xml");
    @Test
    public void test(){
        IUserDao proxy=(IUserDao) ac.getBean("userDao_proxy");
        proxy.save();
        
    }
}
bean.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:p="http://www.springframework.org/schema/p"
    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="e_AOP.myAOP2.ProxyFactory" ></context:component-scan>
    
    <!-- 调用工厂类静态方法放回UserDao 代理后的对象 -->
    <bean id="userDao_proxy" class="e_AOP.myAOP2.ProxyFactory" factory-method="getProxyInstance">
        <constructor-arg index="0" ref="userDao"></constructor-arg>
        <constructor-arg index="1" ref="aop"></constructor-arg>
    </bean>
    
</beans>      
View Code

3.AOP编程

  1)关键字

    AOP: aspect object programming  面向切面编程,关注点代码与业务代码分离!

    关注点:重复代码

    切面:关注点组成的类

    切入点:执行目标对象方法,动态植入切面代码

        可以通过切入点表达式,指定拦截哪些类的哪些方法,给指定的类在运行时候植入切面类代码

  2)步骤

    1.引入aop的jar包  (aspectj aop优秀组件)

      spring-aop-3.2.5.RELEASE.jar   spring3.2源码】

      aopalliance.jar   spring2.5源码/lib/aopalliance】

      aspectjweaver.jar   spring2.5源码/lib/aspectj】或【aspectj-1.8.2lib】

      aspectjrt.jar   spring2.5源码/lib/aspectj】或【aspectj-1.8.2lib】

    2.bean.xml中引入aop名称空间

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

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

    3.开始aop注解

<?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:p="http://www.springframework.org/schema/p"
    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.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">
    
    <!-- 开启注解扫描 -->
    <context:component-scan base-package="cn.itcast.e_aop_anno"></context:component-scan>
    
    <!-- 开启aop注解方式 -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>      

  
View Code

    4.使用注解

切入点表达式:返回类型+包名+访问的类+类的方法(* cn.itcast.e_aop_anno.*.*(..))


@Aspect 指定一个类为切面类 @Pointcut("execution(* cn.itcast.e_aop_anno.*.*(..))") 指定切入点表达式 @Before("pointCut_()") 前置通知: 目标方法之前执行 @After("pointCut_()") 后置通知:目标方法之后执行(始终执行) @AfterReturning("pointCut_()") 返回后通知: 执行方法结束前执行(异常不执行) @AfterThrowing("pointCut_()") 异常通知: 出现异常时候执行 @Around("pointCut_()") 环绕通知: 环绕目标方法执行

    5.例子:

package e_AOP.myAOPanno1;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;


@Component
@Aspect    //指定当前面为切面类
public class Aop {
    //指定切入点表单式:拦截表达式,返回类型+包+哪一类+类方法(有参数可以写参数没有..)
    @Pointcut("execution(* e_AOP.myAOPanno1.*.*(..))")
    public void pointCut_(){
        
    }
    //前置通知:在执行目标方法之前执行
    @Before("pointCut_()")
    public void begin(){
        System.out.println("开始事务/异常");
    }
    //后置通知:在执行目标方法之后执行
    @After("pointCut_()")
    public void after(){
        System.out.println("提交事务/关闭");
    }
    //返回后通知:在调用目标方法之后执行
    @AfterReturning("pointCut_()")
    public void afterRunning(){
        System.out.println("afterRunning()");
    }
    //异常执行
    @AfterThrowing("pointCut_()")
    public void afterThrowing(){
        System.out.println("afterThrowing()");
    }
    //环绕执行
    @Around("pointCut_()")
    public void around(ProceedingJoinPoint pjp) throws Throwable{
        System.out.println("环绕前...");
        pjp.proceed();//执行目标方法
        System.out.println("环绕后...");
        
    }
}
View Code

测试类

package e_AOP.myAOPanno1;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {
    ApplicationContext ac = 
            new ClassPathXmlApplicationContext("e_AOP/myAOPanno1/bean.xml");
    // 目标对象有实现接口,spring会自动选择“JDK代理”
    @Test
    public void testApp() {
        IUserDao userDao = (IUserDao) ac.getBean("userDao");
        System.out.println(userDao.getClass());//$Proxy001  
        userDao.save();
    }
    //目标对象没有实现接口,spring会用“cglib代理”
    @Test
    public void testCglib(){
        OrderDao orderDao=(OrderDao) ac.getBean("orderDao");
        System.out.println(orderDao.getClass());
        orderDao.save();
    }
}
View Code

全部配置在bean.xml中

<!-- 配置OrderDao -->
        <bean id="userDao" class="e_AOP.myAOXml.UserDao"></bean>
        <!-- 配置UserDao -->
        <bean id="orderDao" class="e_AOP.myAOXml.OrderDao"></bean>
        <!-- 配置Aop -->
        <bean id="aop" class="e_AOP.myAOXml.Aop"></bean>
        <!-- Aop配置-->
        <aop:config>
            <!-- 定义一个切入点表达式:拦截哪些方法 -->
            <aop:pointcut expression="execution(* e_AOP.myAOXml.*.*(..))" id="pt"/>
            <!-- 切面 -->
            <aop:aspect ref="aop">
                <!-- 环绕通知 -->
                <aop:around method="around" pointcut-ref="pt"/>
                <!-- 前置通知: 在目标方法调用前执行 -->
                <aop:before method="begin" pointcut-ref="pt"/>
                <!-- 后置通知: -->
                <aop:after method="after" pointcut-ref="pt"/>
                <!-- 返回后通知 -->
                <aop:after-returning method="afterReturning" pointcut-ref="pt"/>
                <!-- 异常通知 -->
                <aop:after-throwing method="afterThrowing" pointcut-ref="pt"/>
            </aop:aspect>
        </aop:config>
View Code

切面类的所有配置

<!-- 切面类 -->
    <bean id="aop" class="cn.itcast.g_pointcut.Aop"></bean>
    
    <!-- Aop配置 -->
    <aop:config>
        
        <!-- 定义一个切入点表达式: 拦截哪些方法 -->
        <!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.*.*(..))" id="pt"/>-->
        
        <!-- 【拦截所有public方法】 -->
        <!--<aop:pointcut expression="execution(public * *(..))" id="pt"/>-->
        
        <!-- 【拦截所有save开头的方法 】 -->
        <!--<aop:pointcut expression="execution(* save*(..))" id="pt"/>-->
        
        <!-- 【拦截指定类的指定方法, 拦截时候一定要定位到方法】 -->
        <!--<aop:pointcut expression="execution(public * cn.itcast.g_pointcut.OrderDao.save(..))" id="pt"/>-->
        
        <!-- 【拦截指定类的所有方法】 -->
        <!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.*(..))" id="pt"/>-->
        
        <!-- 【拦截指定包,以及其自包下所有类的所有方法】 -->
        <!--<aop:pointcut expression="execution(* cn..*.*(..))" id="pt"/>-->
        
        <!-- 【多个表达式】 -->
        <!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) || execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>-->
        <!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) or execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>-->
        <!-- 下面2个且关系的,没有意义 -->
        <!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) &amp;&amp; execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>-->
        <!--<aop:pointcut expression="execution(* cn.itcast.g_pointcut.UserDao.save()) and execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>-->
        
        <!-- 【取非值】 -->
        <!--<aop:pointcut expression="!execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>-->
        <aop:pointcut expression=" not execution(* cn.itcast.g_pointcut.OrderDao.save())" id="pt"/>
        
        <!-- 切面 -->
        <aop:aspect ref="aop">
            <!-- 环绕通知 -->
            <aop:around method="around" pointcut-ref="pt"/>
        </aop:aspect>
    </aop:config>
View Code

4.Spring DAO

   类似于DbUtil

  1)配置xml导入JdbcTemplate对象

<!-- 1. 数据源对象: C3P0连接池 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql:///hib_demo"></property>
        <property name="user" value="root"></property>
        <property name="password" value="root"></property>
        <property name="initialPoolSize" value="3"></property>
        <property name="maxPoolSize" value="10"></property>
        <property name="maxStatements" value="100"></property>
        <property name="acquireIncrement" value="2"></property>
    </bean>
    
    <!-- 2. 创建JdbcTemplate对象 -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    
    <!-- dao 实例 -->
    <bean id="userDao" class="cn.itcast.h_jdbc.UserDao">
        <property name="jdbcTemplate" ref="jdbcTemplate"></property>
    </bean>
View Code

  2)使用这个JdbcTemplate对象

private JdbcTemplate jdbcTemplate;
    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    
    public void save() {
        String sql = "insert into t_dept(deptName) values('test');";
        jdbcTemplate.update(sql);
    }
    
    public Dept findById(int id) {
        String sql = "select * from t_dept where deptId=?";
        List<Dept> list = jdbcTemplate.query(sql,new MyResult(), id);
        return (list!=null && list.size()>0) ? list.get(0) : null;
    }
    
    public List<Dept> getAll() {
        String sql = "select * from t_dept";
        List<Dept> list = jdbcTemplate.query(sql, new MyResult());
        return list;
    }
    
    
    
    
    class MyResult implements RowMapper<Dept>{

        // 如何封装一行记录
        @Override
        public Dept mapRow(ResultSet rs, int index) throws SQLException {
            Dept dept = new Dept();
            dept.setDeptId(rs.getInt("deptId"));
            dept.setDeptName(rs.getString("deptName"));
            return dept;
        }
        
    }
View Code

 5.Spring事务管理

    1).事务管理分类

      1.编程式事务管理:是jdbc中conn.setAutoCommite(false) 

               Hibernate中是session.beginTransaction()

               细粒度的事务控制

      2.声明式事务管理:只需在配置文件中配置,实现了对事务控制最大解耦

                Jdbc:DataSourceTransactionManager

                Hibernate技术:HibernateTransactionManager

                粗粒度事务控制:只能给整个方法应用事务,因为aop拦截的是方法

    2)配置事务管理

      1.引入spring-aop相关4个jar文件;2.引入aop名称空间3.引入tx名称空间

      2.spring声明式事务管理配置:

          1)配置事务管理器类2)配置事务增强如何管理事务3)AOP配置拦截哪些方法应用上面的增强

<!-- #############5. Spring声明式事务管理配置############### -->
    <!-- 5.1 配置事务管理器类 -->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    
    <!-- 5.2 配置事务增强(如果管理事务?) -->
    <tx:advice id="txAdvice" transaction-manager="txManager">
        <tx:attributes>
            <tx:method name="get*" read-only="true"/>
            <tx:method name="find*" read-only="true"/>
            <tx:method name="*" read-only="false"/>
        </tx:attributes>
    </tx:advice>
    
    <!-- 5.3 Aop配置: 拦截哪些方法(切入点表表达式) + 应用上面的事务增强配置 -->
    <aop:config>
        <aop:pointcut expression="execution(* cn.itcast.a_tx.DeptService.*())" id="pt"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
    </aop:config>
View Code

    3)完整例子

1. DeptDao.java
public class DeptDao {
    
    // 容器注入JdbcTemplate对象
    private JdbcTemplate jdbcTemplate;
    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    public void save(Dept dept){
        String sql = "insert into t_dept (deptName) values(?)";
        jdbcTemplate.update(sql,dept.getDeptName());
    }
}

2. DeptService
public class DeptService {
    
    // 容器注入dao对象
    private DeptDao deptDao;
    public void setDeptDao(DeptDao deptDao) {
        this.deptDao = deptDao;
    }

    /*
     * 事务控制?
     */
    public void save(Dept dept){
        // 第一次调用
        deptDao.save(dept);
        
        int i = 1/0; // 异常: 整个Service.save()执行成功的要回滚
        
        // 第二次调用
        deptDao.save(dept);
    }
}
3. App 测试类
@Test
    public void testApp() throws Exception {
        //容器对象
        ApplicationContext ac = new ClassPathXmlApplicationContext("cn/itcast/a_tx/bean.xml");
        
        // 模拟数据
        Dept dept = new Dept();
        dept.setDeptName("测试: 开发部");
        
        DeptService deptService = (DeptService) ac.getBean("deptService");
        deptService.save(dept);
        
    } 
4. bean.xml  (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:p="http://www.springframework.org/schema/p"
    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">

    
    <!-- 1. 数据源对象: C3P0连接池 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql:///hib_demo"></property>
        <property name="user" value="root"></property>
        <property name="password" value="root"></property>
        <property name="initialPoolSize" value="3"></property>
        <property name="maxPoolSize" value="10"></property>
        <property name="maxStatements" value="100"></property>
        <property name="acquireIncrement" value="2"></property>
    </bean>
    
    <!-- 2. JdbcTemplate工具类实例 -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    
    <!-- 3. dao实例 -->
    <bean id="deptDao" class="cn.itcast.a_tx.DeptDao">
        <property name="jdbcTemplate" ref="jdbcTemplate"></property>
    </bean>
 
    <!-- 4. service实例 -->
    <bean id="deptService" class="cn.itcast.a_tx.DeptService">
        <property name="deptDao" ref="deptDao"></property>
    </bean>
    
    <!-- #############5. Spring声明式事务管理配置############### -->
    <!-- 5.1 配置事务管理器类 -->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    
    <!-- 5.2 配置事务增强(如果管理事务?) -->
    <tx:advice id="txAdvice" transaction-manager="txManager">
        <tx:attributes>
            <tx:method name="get*" read-only="true"/>
            <tx:method name="find*" read-only="true"/>
            <tx:method name="*" read-only="false"/>
        </tx:attributes>
    </tx:advice>
    
    <!-- 5.3 Aop配置: 拦截哪些方法(切入点表表达式) + 应用上面的事务增强配置 -->
    <aop:config>
        <aop:pointcut expression="execution(* cn.itcast.a_tx.DeptService.*())" id="pt"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
    </aop:config>
    
</beans>     
View Code

        3.注解方式实现事务管理

注解声明讲解:

事物传播行为介绍: 
@Transactional(propagation=Propagation.REQUIRED) 
如果有事务, 那么加入事务, 没有的话新建一个(默认情况下)
@Transactional(propagation=Propagation.NOT_SUPPORTED) 
容器不为这个方法开启事务
@Transactional(propagation=Propagation.REQUIRES_NEW) 
不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务
@Transactional(propagation=Propagation.MANDATORY) 
必须在一个已有的事务中执行,否则抛出异常
@Transactional(propagation=Propagation.NEVER) 
必须在一个没有的事务中执行,否则抛出异常(与Propagation.MANDATORY相反)
@Transactional(propagation=Propagation.SUPPORTS) 
如果其他bean调用这个方法,在其他bean中声明事务,那就用事务.如果其他bean没有声明事务,那就不用事务.

事物超时设置:
@Transactional(timeout=30) //默认是30秒

事务隔离级别:
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
读取未提交数据(会出现脏读, 不可重复读) 基本不使用
@Transactional(isolation = Isolation.READ_COMMITTED)
读取已提交数据(会出现不可重复读和幻读)
@Transactional(isolation = Isolation.REPEATABLE_READ)
可重复读(会出现幻读)
@Transactional(isolation = Isolation.SERIALIZABLE)
串行化

MYSQL: 默认为REPEATABLE_READ级别
SQLSERVER: 默认为READ_COMMITTED

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

@Transactional注解中常用参数说明

参 数 名 称

功 能 描 述

readOnly

该属性用于设置当前事务是否为只读事务,设置为true表示只读,false则表示可读写,默认值为false。例如:@Transactional(readOnly=true)

rollbackFor

该属性用于设置需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,则进行事务回滚。例如:

指定单一异常类:@Transactional(rollbackFor=RuntimeException.class)

指定多个异常类:@Transactional(rollbackFor={RuntimeException.class, Exception.class})

 续表)

参 数 名 称

功 能 描 述

rollbackForClassName

该属性用于设置需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,则进行事务回滚。例如:

指定单一异常类名称:@Transactional(rollbackForClassName="RuntimeException")

指定多个异常类名称:@Transactional(rollbackForClassName={"RuntimeException","Exception"})

noRollbackFor

该属性用于设置不需要进行回滚的异常类数组,当方法中抛出指定异常数组中的异常时,不进行事务回滚。例如:

指定单一异常类:@Transactional(noRollbackFor=RuntimeException.class)

指定多个异常类:@Transactional(noRollbackFor={RuntimeException.class, Exception.class})

noRollbackForClassName

该属性用于设置不需要进行回滚的异常类名称数组,当方法中抛出指定异常名称数组中的异常时,不进行事务回滚。例如:

指定单一异常类名称:@Transactional(noRollbackForClassName="RuntimeException")

指定多个异常类名称:

@Transactional(noRollbackForClassName={"RuntimeException","Exception"})

propagation

该属性用于设置事务的传播行为,具体取值可参考表6-7。

例如:@Transactional(propagation=Propagation.NOT_SUPPORTED,readOnly=true)

isolation

该属性用于设置底层数据库的事务隔离级别,事务隔离级别用于处理多事务并发的情况,通常使用数据库的默认隔离级别即可,基本不需要进行设置

timeout

该属性用于设置事务的超时秒数,默认值为-1表示永不超时

注意的几点:
1 @Transactional 只能被应用到public方法上, 对于其它非public的方法,如果标记了@Transactional也不会报错,但方法没有事务功能.

2用 spring 事务管理器,由spring来负责数据库的打开,提交,回滚.默认遇到运行期例外(throw new RuntimeException("注释");)会回滚,即遇到不受检查(unchecked)的例外时回滚;而遇到需要捕获的例外(throw new Exception("注释");)不会回滚,即遇到受检查的例外(就是非运行时抛出的异常,编译器会检查到的异常叫受检查例外或说受检查异常)时,需我们指定方式来让事务回滚 要想所有异常都回滚,要加上 @Transactional( rollbackFor={Exception.class,其它异常}) .如果让unchecked例外不回滚: @Transactional(notRollbackFor=RunTimeException.class)
如下:
@Transactional(rollbackFor=Exception.class) //指定回滚,遇到异常Exception时回滚
public void methodName() {
throw new Exception("注释");

}
@Transactional(noRollbackFor=Exception.class)//指定不回滚,遇到运行期例外(throw new RuntimeException("注释");)会回滚
public ItimDaoImpl getItemDaoImpl() {
throw new RuntimeException("注释");
}

3、@Transactional 注解应该只被应用到 public 可见度的方法上。 如果你在 protectedprivate 或者 package-visible 的方法上使用 @Transactional 注解,它也不会报错, 但是这个被注解的方法将不会展示已配置的事务设置。


4、@Transactional 注解可以被应用于接口定义和接口方法、类定义和类的 public 方法上。然而,请注意仅仅 @Transactional 注解的出现不足于开启事务行为,它仅仅 是一种元数据,能够被可以识别 @Transactional 注解和上述的配置适当的具有事务行为的beans所使用。上面的例子中,其实正是 <tx:annotation-driven/>元素的出现 开启 了事务行为。


5、Spring团队的建议是你在具体的类(或类的方法)上使用 @Transactional 注解,而不要使用在类所要实现的任何接口上。你当然可以在接口上使用 @Transactional 注解,但是这将只能当你设置了基于接口的代理时它才生效。因为注解是 不能继承 的,这就意味着如果你正在使用基于类的代理时,那么事务的设置将不能被基于类的代理所识别,而且对象也将不会被事务代理所包装(将被确认为严重的)。因 此,请接受Spring团队的建议并且在具体的类上使用 @Transactional 注解。
View Code

配置文件关于事务管理部分的改为

<!-- 事务管理器类 -->
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    
    <!-- 开启注解扫描 -->
    <context:component-scan base-package="cn.itcast.b_anno"></context:component-scan>
    
    <!-- 注解方式实现事务: 指定注解方式实现事务 -->
    <tx:annotation-driven transaction-manager="txManager"/>
View Code

调用事务的方法使用注解的用法

@Transactional(
            readOnly=false,
            timeout=-1,
            isolation=Isolation.DEFAULT,
            propagation=Propagation.REQUIRED
            )
    public void save(Dept dept){
        //第一次调用
        deptDao.save(dept);
    
        int i = 1/0; // 异常: 整个Service.save()执行成功的要回滚
        
        // 第二次调用
        deptDao.save(dept);
    }
View Code

      4.事务属性

@Transactional(
            readOnly = false,  // 读写事务
            timeout = -1,       // 事务的超时时间不限制
            noRollbackFor = ArithmeticException.class,  // 遇到数学异常不回滚
            isolation = Isolation.DEFAULT,              // 事务的隔离级别,数据库的默认
            propagation = Propagation.REQUIRED            // 事务的传播行为
    )
    public void save(Dept dept){
        deptDao.save(dept);
        int i = 1/0;
        deptDao.save(dept);
    }

事务传播行为:
    Propagation.REQUIRED
        指定当前的方法必须在事务的环境下执行;
        如果当前运行的方法,已经存在事务, 就会加入当前的事务;
    Propagation.REQUIRED_NEW
        指定当前的方法必须在事务的环境下执行;
        如果当前运行的方法,已经存在事务:  事务会挂起; 会始终开启一个新的事务,执行完后;  刚才挂起的事务才继续运行
View Code
原文地址:https://www.cnblogs.com/xiaoping1993/p/6922814.html