Java Web 后端技术(五)–Spring

概述:全栈式(full-stack)轻量级开源框架。
两大核心

  • IOC(Inverse Of Control:控制反转)
  • AOP(Aspect Oriented Programming:面向切面编程)
    优势:
  • 方便解耦合,简化开发。sping是一个容器,可以将所有对象创建和关系维护交给spring管理
  • AOP编程的支持。方便实现程序进行权限拦截,运行监控等功能
  • 声明式事务的支持。通过配置完成事务的管理,无需手动编程
  • 方便测试。支持Junit4支持
  • 方便集成各种优秀框架。内部提供各种优秀框架的直接支持

1.IOC

概述:一种设计思想,目的式指导设计出更加松耦合的程序。
获取方式:从spring容器中获取。

1.自定义IOC容器:
1. 创建java项目,导入自定义IOC相关坐标
2. 编写Dao接口和实现类
3. 编写Service接口和实现类
4. 编写beans.xml
5. 编写BeanFactory工具类
6. 编写测试代码

pom.xml相关IOC坐标

<dependencies>
    <dependency>
        <groupId>dom4j</groupId>
        <artifactId>dom4j</artifactId>
        <version>1.6.1</version>
    </dependency>
    <dependency>
        <groupId>jaxen</groupId>
        <artifactId>jaxen</artifactId>
        <version>1.1.6</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
</dependencies>

IUserDao

public interface IUserDao {

    public void save();
}

UserDaoImpl

public class UserDaoImpl implements IUserDao {

    public void save() {
        System.out.println("保存成功。。。");
    }
}

IUserService

public interface IUserService {

    public void save();
}

UserServiceImpl

public void save() {
    //调用DAO层方法,传统方式
    UserDaoImpl userDao = new UserDaoImpl();

    userDao.save();
}

beans.xml

<beans>
    <bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl"></bean>
</beans>

BeanFactory

public class BeanFactory {

    private static Map<String,Object> iocmap = new HashMap<String, Object>();

    //初始化对象实例
    static {

        //读取配置文件
        InputStream resourceAsStream = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");

        //解析xml
        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(resourceAsStream);

            //编写xpath表达式
            String xpath = "//bean";

            //获取到所有的bean标签
            List<Element> list = document.selectNodes(xpath);

            //遍历list集合并使用反射创建对象实例,并存到map集合中
            for (Element element : list) {
                String id = element.attributeValue("id");
                String className = element.attributeValue("class");
                //使用反射生成实例对象
                Object o = Class.forName(className).newInstance();
                //存到map中
                iocmap.put(id,o);
            }
        } catch (DocumentException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static Object getBean(String beanId){
        Object o = iocmap.get(beanId);
        return o;
    }
}

测试代码

@Test
    public void test1() {
 	IUserDao userDao = (IUserDao)BeanFactory.getBean("userDao");
        userDao.save();
    }
2.借助spring的IOC实现
1. 创建java项目,导入spring开发基本坐标 
2. 编写Dao接口和实现类 
3. 创建spring核心配置文件 
4. 在spring配置文件中配置 UserDaoImpl
5. 使用spring相关API获得Bean实例

spring基本坐标

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.1.5.RELEASE</version>
</dependency>

applicationContext.xml配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl"></bean>

</beans>

测试

@Test
public void test2(){

    //获取spring上下文对象,借助上下文对象获取ioc容器中的bean对象
    ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext(
"applicationContext.xml");

    //使用上下文对象从IOC容器中获取到bean对象
    IUserDao userDao = (IUserDao) classPathXmlApplicationContext.getBean("userDao");

    userDao.save();
}

ApplicationContext :代表应用上下文对象,获得spring中IOC容器的Bean对象

常用实现类:

1. ClassPathXmlApplicationContext  它是从**类的根路径下**加载配置文件 推荐使用这种。
2. FileSystemXmlApplicationContext  它是从**磁盘路径上**加载配置文件,配置文件可以在磁盘的任意位置。 
3. AnnotationConfigApplicationContext  当使用**注解配置容器对象**时,需要使用此类来创建 spring 容器。它用来读取注解。
3.spring配置文件

<bean id="" class="" init-method="" destroy-method=""></bean>用于配置对象交由Spring来创建。id为Bean实例在spring中的唯一标识;class为Bean的全类名。默认情况下它调用的是类中的无参构造函数,如果没有无参构造函数则不能创建成功。
scope 配置Bean标签的范围

取值范围

说明

singleton

默认值,单例的

prototype

多例的

request

WEB项目中,Spring创建一个Bean的对象,将对象存入到request域中

session

WEB项目中,Spring创建一个Bean的对象,将对象存入到session域中

globalsession

WEB项目中,应用在Portlet环境,如果没有Portlet环境那么globalSession 相当于 session

init-method: 指定类中的初始化方法名称;
destroy-method: 指定类中销毁方法名称;在容器销毁以后对象也销毁了,销毁方法来不及销毁。如果想要查看销毁方法则需要手动销毁。

4.Bean实例化三种方式

4.1无参构造方法实例化
它会根据默认无参构造方法来创建类对象,如果bean中没有默认无参构造函数,将会创建失败。

<bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl"/>

4.2工厂静态方法实例化
应用场景 :依赖的jar包中有个A类,A类中有个静态方法m1,m1方法的返回值是一个B对象。如果我们频繁使用B对象,此时我们可以将B对象的创建权交给spring的IOC容器,以后我们在使用B对象时,无需调用A类中的m1方法,直接从IOC容器获得。

<bean id="userDao" class="com.lagou.factory.StaticFactoryBean" factory-method="createUserDao" />

4.3工厂普通方法实例化
应用场景 :依赖的jar包中有个A类,A类中有个普通方法m1,m1方法的返回值是一个B对象。如果我们频繁使用B对象,此时我们可以将B对象的创建权交给spring的IOC容器,以后我们在使用B对象时,无需调用A类中的m1方法,直接从IOC容器获得。

<bean id="dynamicFactoryBean" class="com.lagou.factory.DynamicFactoryBean"/> 
<bean id="userDao" factory-bean="dynamicFactoryBean" factory-method="createUserDao"/>
5.Bean依赖注入

它是 Spring 框架核心 IOC 的具体实现
通过框架把持久层对象传入业务层,而不用我们自己去获取。
依赖注入方法:

1.构造方法注入:
UserServiceImpl

public class UserServiceImpl implements IUserService {

    private IUserDao userDao;

    public UserServiceImpl(IUserDao userDao){
        this.userDao = userDao;
    }
    
    public void save() {
        userDao.save();
    }
}

spring容器中调用有参构造注入

<bean id="userService" class="com.lagou.service.impl.UserServiceImpl">
        <!-- <constructor-arg index="0" type="com.lagou.dao.IUserDao" ref="userDao"></constructor-arg> -->
        <constructor-arg name="userDao" ref="userDao"></constructor-arg>
    </bean>

2.set方法注入

UserServiceImpl

public void setUserDao(IUserDao userDao){
        this.userDao = userDao;
    }

spring容器中调用set方式注入

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

数据类型注入
1.普通数据类型注入
UserDaoImpl

private String username;
    private Integer age;

    public void setUsername(String username) {
        this.username = username;
    }
    
    public void setAge(Integer age) {
        this.age = age;
    }
<bean id="userDao" class="com.lagou.dao.impl.UserDaoImpl" init-method="init" destroy-method="destory">
        <property name="username" value="子慕"></property>
        <property name="age" value="18"></property>
    </bean>

2.集合数据类型注入

集合数据类型包括list集合,set集合,array数组,map集合;配置方法都大同小异,以list集合为例。

UserDaoImpl

private List<Object> list;
 
 public void setList(List<Object> list) {
        this.list = list;
    }

applicationContext.xml配置

<!--user对象配置-->
    <bean id="user" class="com.lagou.domain.User">
        <property name="username" value="张三"></property>
        <property name="age" value="18"></property>
    </bean>
    
    <!--进行list集合数据类型的注入-->
    <property name="list">
            <list>
                <value>aaa</value>
                <ref bean="user"></ref>
            </list>
        </property>

properties配置注入

private Properties properties;

    public void setProperties(Properties properties) {
        this.properties = properties;
    }
<!--properties集合类型注入-->
        <property name="properties">
            <props>
                <prop key="k1">v1</prop>
                <prop key="k2">v2</prop>
            </props>
        </property>
6.配置文件模块化

实际开发中,Spring的配置内容非常多,这就导致Spring配置很繁杂且体积很大,所以,可以将部分
配置拆解到其他配置文件中,也就是所谓的配置文件模块化。
拆封方式

  • 按层级进行拆分:根据业务逻辑以及三层架构进行拆分
  • 按业务逻辑进行拆分:按照不同模块原则进行拆分

加载方式

  • 并列的多个配置文件
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml","applicationContext-dao.xml",...);
  • 主从配置文件
<import resource="applicationContext-xxx.xml"/>
7.springIOC注解开发

1.常用注解

注解

说明

@Component

使用在类上用于实例化Bean

@Controller

使用在web层类上用于实例化Bean

@Service

使用在service层类上用于实例化Bean

@Repository

使用在dao层类上用于实例化Bean

@Autowired

使用在字段上用于根据类型依赖注入

@Qualifier

结合@Autowired一起使用,根据名称进行依赖注入

@Resource

相当于@Autowired+@Qualifier,按照名称进行注入

@Value

注入普通属性

@Scope

标注Bean的作用范围

@PostConstruct

使用在方法上标注该方法是Bean的初始化方法

@PreDestroy

使用在方法上标注该方法是Bean的销毁方法

注: JDK11以后完全移除了javax扩展导致不能使用@resource注解

使用注解进行开发时,需要在applicationContext.xml中配置组件扫描,作用是指定哪个包及其子包下的Bean需要进行扫描以便识别使用注解配置的类、字段和方法。

<context:component-scan base-package="com.lagou"></context:component-scan>

2.spring新注解

注解

说明

@Configuration

用于指定当前类是一个Spring 配置类,当创建容器时会从该类上加载注解

@Bean

使用在方法上,标注将该方法的返回值存储到 Spring 容器中

@PropertySource

用于加载 properties 文件中的配置

@ComponentScan

用于指定 Spring 在初始化容器时要扫描的包

@Import

用于导入其他配置类

2.AOP

AOP 为 Aspect Oriented Programming 的缩写,意思为面向切面编程

AOP 是 OOP(面向对象编程) 的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

优点:

  • 在程序运行期间,在不修改源码的情况下对方法进行功能增强
  • 逻辑清晰,开发核心业务的时候,不必关注增强业务的代码
  • 减少重复代码,提高开发效率,便于后期维护
1.AOP底层实现

实际上,AOP 的底层是通过 Spring 提供的的动态代理技术实现的。在运行期间,Spring通过动态代理技术动态的生成代理对象,代理对象方法执行时进行增强功能的介入,在去调用目标对象的方法,从而完成功能的增强。

2.AOP相关术语

Spring 的 AOP 实现底层就是对上面的动态代理的代码进行了封装,封装后我们只需要对需要关注的
部分进行代码编写,并通过配置的方式完成指定目标的方法增强。

术语

描述

Target

代理的目标对象

Proxy

一个类被 AOP 织入增强后,就产生一个结果代理类

Joinpoint

在spring中,这些点指的是方法,因为 spring只支持方法类型的连接点

Pointcut

指我们要对哪些 Joinpoint 进行拦截的定义;真正被增强的方法

Advice

封装增强业务逻辑的方法;分类:前置通知、后置通知、异常通知、最终通知、环绕通知

Aspect

切入点和通知(引介)的结合

Weaving

指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织 入,而AspectJ采用编译期织入和类装载期织入

3.AOP开发明确事项

3.1 开发阶段

  1. 编写核心业务代码(目标类的目标方法) 切入点
  2. 把公用代码抽取出来,制作成通知(增强功能方法) 通知
  3. 在配置文件中,声明切入点与通知间的关系,即切面

3.2运行阶段(Spring框架控制)
Spring 框架监控切入点方法的执行。一旦监控到切入点方法被运行,使用代理机制,动态创建目标对象的代理对象,根据通知类别,在代理对象的对应位置,将通知对应的功能织入,完成完整的代码逻辑运行。

3.3底层代理实现
在 Spring 中,框架会根据目标类是否实现了接口来决定采用哪种动态代理的方式。

  • 当bean实现接口时,会用JDK代理模式
  • 当bean没有实现接口,用cglib实现( 可以强制使用cglib(在spring配置中加入<aop:aspectj-autoproxy proxyt-target-class=”true”/>)
4.基于XML的AOP开发
1. 创建java项目,导入AOP相关坐标 
2. 创建目标接口和目标实现类(定义切入点) 
3. 创建通知类及方法(定义通知) 
4. 将目标类和通知类对象创建权交给spring 
5. 在核心配置文件中配置织入关系,及切面

导入AOP相关坐标

<dependencies>
        <!--导入spring的context坐标,context依赖aop-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.5.RELEASE</version>
        </dependency>
        <!-- aspectj的织入(切点表达式需要用到该jar包) -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.13</version>
        </dependency>
        <!--spring整合junit-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.1.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>

目标接口

public interface AccountService{
    public void transfer();
}

目标接口实现类

public class AccountServiceImpl implements AccountService {
    public void transfer() {
        System.out.println("执行了...");
    }
}

通知类及方法

public class MyAdvice {

    public void before(){
        System.out.println("前置通知执行....");
    }
    
	public void afterReturnning(){
        System.out.println("后置通知执行....");
    }
}

SpringContext.xml

<!--目标类交给IOC容器-->
 <bean id="accountService" class="com.lagou.service.impl.AccountServiceImpl"></bean>
<!--通知类交给IOC容器-->
    <bean id="myAdvice" class="com.lagou.advice.MyAdvice"></bean>
<!--导入AOP命名空间-->
    <aop:config>
        <aop:pointcut id="myPointcut" expression="execution(* com.lagou..service.impl.AccountServiceImpl.*(..))"/>
        <aop:aspect ref="myAdvice">
            <aop:before method="before" pointcut="execution(public void com.lagou.service.impl.AccountServiceImpl.transfer())"/>
            <aop:after method="afterReturnning" pointcut-ref="myPointcut"/>
        </aop:aspect>
    </aop:config>
5.XML配置AOP详解

5.1切点表达式
execution([修饰符] 返回值类型 包名.类名.方法名(参数))

  1. 访问修饰符可以省略
  2. 返回值类型、包名、类名、方法名可以使用星号 * 代替,代表任意
  3. 包名与类名之间一个点 . 代表当前包下的类,两个点 … 表示当前包及其子包下的类
  4. 参数列表可以使用两个点 … 表示任意个数,任意类型的参数列表

5.2通知类型
<aop:通知类型 method=“通知类中方法名” pointcut=“切点表达式"></aop:通知类型>

标签

说明

aop:before

用于配置前置通知。指定增强的方法在切入点方法之前执行

aop:afterReturning

用于配置后置通知。指定增强的方法在切入点方法之后执行

aop:afterThrowing

用于配置异常通知。指定增强的方法出现异常后执行

aop:after

用于配置最终通知。无论切入点方法执行时是否有异常,都会执行

aop:around

用于配置环绕通知。开发者可以手动控制增强代码在什么时候执行

注意:通常情况下,环绕通知都是独立使用的

6.基于注解的AOP开发
1. 创建java项目,导入AOP相关坐标 
2. 创建目标接口和目标实现类(定义切入点) 
3. 创建通知类(定义通知) 
4. 将目标类和通知类对象创建权交给spring 
5. 在通知类中使用注解配置织入关系,升级为切面类 
6. 在配置文件中开启组件扫描和 AOP 的自动代理

将目标类和通知类对象创建权交给spring

@Service
public class AccountServiceImpl implements AccountService{}

@Component
public class MyAdvice{}

在通知类中使用注解配置织入关系,升级为切面类

@Component 
@Aspect 
public class MyAdvice { 
	@Before("execution(* com.lagou..*.*(..))") 			
	public void before() {
	 System.out.println("前置通知..."); 
 	} 
 }

在配置文件中开启组件扫描和AOP的自动代理

<!--组件扫描-->
    <context:component-scan base-package="com.lagou"/>
    <!--aop的自动代理-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
7.基于注解的AOP开发的通知类型

通知的配置语法:@通知注解(“切点表达式")

标签

说明

@Before

用于配置前置通知。指定增强的方法在切入点方法之前执行

@AfterReturning

用于配置后置通知。指定增强的方法在切入点方法之后执行

@AfterThrowing

用于配置异常通知。指定增强的方法出现异常后执行

@After

用于配置最终通知。无论切入点方法执行时是否有异常,都会执行

@Around

用于配置环绕通知。开发者可以手动控制增强代码在什么时候执行

3.JdbcTemplate

JdbcTemplate是spring框架中提供的一个模板对象,是对原始繁琐的Jdbc API对象的简单封装
核心对象JdbcTemplate jdbcTemplate = new JdbcTemplate(DataSource dataSource);核心方法

1.int update();执行增删改语句
2.List<T> query();查询多个
3.T queryForObject();查询一个
4.new BeanPropertyRowMapper<>();实现ORM映射封装
3.1spring整合jdbcTemplate环境准备

导入依赖坐标

<dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.15</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.13</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.1.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.1.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.1.5.RELEASE</version>
        </dependency>
    </dependencies>

创建Account类

public class Account {

    private Integer id;
    private String name;
    private Double money;
    
    //setter和getter
    }
3.2 dao层准备

AccountDao接口

public interface AccountDao {

    //查询所有账户
    public List<Account> findAll();

    //更具id查询账户
    public Account findById(Integer id);

    //添加账户
    public void save(Account account);

    //更新账户
    public void update(Account account);

    //根据id删除账户
    public void delete(Integer id);
}

AccountDaoImpl类

@Repository
public class AccountDaoImpl implements AccountDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    public List<Account> findAll() {
        String sql = "select * from account";
        List<Account> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Account>(Account.class));
        return list;
    }

    public Account findById(Integer id) {
        String sql = "select * from account where id = ?";
        Account account = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<Account>(Account.class), id);
        return account;
    }

    public void save(Account account) {
        String sql = "insert int account values(null,?,?)";
        jdbcTemplate.update(sql, account.getMoney(), account.getName());
    }


    public void update(Account account) {
        String sql = "update account set money = ? where name = ?";
        jdbcTemplate.update(sql, account.getMoney(), account.getName());

    }

    public void delete(Integer id) {

        String sql ="delete from account where id = ?";
        jdbcTemplate.update(sql,id);
    }
}
3.3service层准备

AccountService接口

public interface AccountService {

    //查询所有账户
    public List<Account> findAll();

    //更具id查询账户
    public Account findById(Integer id);

    //添加账户
    public void save(Account account);

    //更新账户
    public void update(Account account);

    //根据id删除账户
    public void delete(Integer id);
}

AccountServiceImpl类

@Service
public class AccountServiceImpl implements AccountService {
    @Autowired
    private AccountDao accountDao;

    public List<Account> findAll() {
        List<Account> all = accountDao.findAll();
        return all;
    }

    public Account findById(Integer id) {
        Account account = accountDao.findById(id);
        return account;
    }

    public void save(Account account) {
        accountDao.save(account);
    }

    public void update(Account account) {
        accountDao.update(account);
    }

    public void delete(Integer id) {
        accountDao.delete(id);
    }
}

ApplicaitonContext.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"
       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">

    <!--IOC注解扫描-->
    <context:component-scan base-package="com.lagou"/>

    <context:property-placeholder location="classpath:jdbc.properties"/>

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

    <!--jdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <constructor-arg name="dataSource" ref="dataSource"/>
    </bean>
</beans>

4.事务

Spring的事务控制可以分为编程式事务控制声明式事务控制

  • 编程式(了解): 开发者直接把事务的代码和业务代码耦合到一起,在实际开发中不用。
  • 声明式(重点): 开发者采用配置的方式来实现的事务控制,业务代码与事务代码实现解耦合,使用的AOP思想。
4.1基于xml的声明式事务控制

在 Spring 配置文件中声明式的处理事务来代替代码式的处理事务。底层采用AOP思想来实现的。

声明式事务控制明确事项

  • 核心业务代码(目标对象)
  • 事务增强代码(Spring已提供事务管理器)
  • 切面配置

步骤分析

  1. 引入tx命名空间
  2. 事务管理器通知配置
  3. 事务管理器AOP配置
<beans xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
	http://www.springframework.org/schema/tx 
	http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--通知增强--> 
<tx:advice id="txAdvice" transaction-manager="transactionManager"> 
	<!--定义事务的属性--> 
	<tx:attributes> 
		<tx:method name="*"/> </tx:attributes> 
	</tx:advice>
<!--aop配置--> 
<aop:config> 
	<!--切面配置--> 
	<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.lagou.serivce..*.*(..))"> </aop:advisor> 
</aop:config>

tx:method 事务参数的配置详解
<tx:method name="transfer" isolation="REPEATABLE_READ" propagation="REQUIRED" timeout="-1" read-only="false"/>

  • name: 切点方法名称
  • isolation:事务的隔离级别
  • propogation:事务的传播行为
  • timeout:超时时间
  • read-only:是否只读
4.2基于注解的声明式事务控制

常用注解步骤分析

  1. 修改service层,增加事务注解
  2. 修改spring核心配置文件,开启事务注解支持
@Service 
public class AccountServiceImpl implements AccountService { 

	@Autowired 
	private AccountDao accountDao;

	@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.REPEATABLE_READ, timeout = -1, readOnly = false) 
	@Override 
	public void transfer(String outUser, String inUser, Double money) { 
		accountDao.out(outUser, money);
 		int i = 1 / 0; 
 		accountDao.in(inUser, money); 
	 } 
 }
<!--事务的注解支持--> 
<tx:annotation-driven/>

5.web集成

5.1ApplicationContext应用上下文获取方式

应用上下文对象是通过 new ClasspathXmlApplicationContext(spring配置文件) 方式获取的,但是每次从容器中获得Bean时都要编写 new ClasspathXmlApplicationContext(spring配置文件) ,这样的弊端是配置文件加载多次,应用上下文对象创建多次。

解决思路分析:
在Web项目中,可以使用ServletContextListener监听Web应用的启动,我们可以在Web应用启动时,就加载Spring的配置文件,创建应用上下文对象ApplicationContext,在将其存储到最大的域servletContext域中,这样就可以在任意位置从域中获得应用上下文ApplicationContext对象了。

5.2 Spring提供获取应用上下文的工具

上面的分析不用手动实现,Spring提供了一个监听器ContextLoaderListener就是对上述功能的封装,该监听器内部加载Spring配置文件,创建应用上下文对象,并存储到ServletContext域中,提供了一个客户端工具WebApplicationContextUtils供使用者获得应用上下文对象。
操作

  • 在web.xml中配置ContextLoaderListener监听器(导入spring-web坐标)
  • 使用WebApplicationContextUtils获得应用上下文对象ApplicationContext

web坐标

<dependencies>
  	<dependency> 
 		<groupId>org.springframework</groupId>
  		<artifactId>spring-context</artifactId> 
 		<version>5.1.5.RELEASE</version> 
  	</dependency> 
 	<dependency> 	
  		<groupId>org.springframework</groupId> 
  		<artifactId>spring-web</artifactId> 
  		<version>5.1.5.RELEASE</version> 
  	</dependency>
  </dependencies>

ContextLoaderListener监听器

<!--全局参数--> 
<context-param> 
	<param-name>contextConfigLocation</param-name> 
	<param-value>classpath:applicationContext.xml</param-value> 
</context-param>
 <!--Spring的监听器--> 
 <listener> 
	<listener-class> 	
	org.springframework.web.context.ContextLoaderListener 
 	</listener-class> 
 </listener>