Spring框架开发的三种模式

第一节 IoC的综合案例 - 纯xml开发

1.1综合案例描述

案例的需求

实现账户表的增删改查操作

案例的要求

选用基于XML的Spring和Mybatis整合配置实现。

  • 数据库表结构介绍
CREATE TABLE `account` (
`id`  int(11) NOT NULL ,
`name`  varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL ,
`money`  double NULL DEFAULT NULL ,
PRIMARY KEY (`id`)
)
ENGINE=InnoDB
DEFAULT CHARACTER SET=utf8 COLLATE=utf8_general_ci
ROW_FORMAT=COMPACT

1.2 案例的实现

1.2.1 创建工程导入坐标

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.1.9.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.1.9.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.2</version>
    </dependency>
    <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.20</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>2.0.1</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
</dependencies>

1.2.2 编写基础代码

/**
 * 账户的实体类
 */
public class Account {

    private Integer id;
    private String name;
    private Double money;

	//省略set,get,toString等方法
}
/**
 * 账户的业务层接口
 */
public interface AccountService {
    /**
     * 保存
     */
    void save(Account account);

    /**
     * 根据id删除
     */
    void delete(Integer id);

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

    /**
     * 根据id查询
     */
    Account findById(Integer id);

    /**
     * 根据名称查询账户
     */
    Account findByName(String name);

    /**
     * 查询所有
     */
    List<Account> findAll();
}

/**
 * 账户业务接口实现类
 */
public class AccountServiceImpl implements AccountService {

     // 依赖注入  通过依赖注入可以实现service调用dao的增删改查方法 通过方法进行增删改查的调用
    // 调用dao的时候要有接口所对应的SQL文件
    private AccountDao accountDao;

    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }

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

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

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

    @Override
    public Account findById(Integer id) {
        return accountDao.findById(id);
    }

    @Override
    public Account findByName(String name) {
        return accountDao.findByName(name);
    }

    @Override
    public List<Account> findAll() {
        return accountDao.findAll();
    }
}

/**
 * 账户持久层接口
 */
public interface AccountDao {
    /**
     * 保存
     */
    void save(Account account);

    /**
     * 根据id删除
     */
    void delete(Integer id);

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

    /**
     * 根据id查询
     */
    Account findById(Integer id);

    /**
     * 根据名称查询账户
     */
    Account findByName(String name);

    /**
     * 查询所有
     */
    List<Account> findAll();
}

1.2.3 编写mybatis的映射配置

<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.dao.AccountDao">

    <!--保存-->
    <insert id="save" parameterType="account">
        insert into account values(#{id},#{name},#{money})
    </insert>

    <!--根据id删除-->
    <delete id="delete" parameterType="int" >
        delete from account where id=#{id}
    </delete>

    <!--更新账户-->
    <update id="update" parameterType="account">
        update account set name=#{name},money=#{money} where id=#{id}
    </update>

    <!--根据id查询-->
    <select id="findById" parameterType="int" resultType="account">
        select * from account where id=#{id}
    </select>

    <!--根据名称查询账户-->
    <select id="findByName" parameterType="string" resultType="account">
        select * from account where name=#{name}
    </select>

    <!--查询所有-->
    <select id="findAll" resultType="account">
        select * from account
    </select>
</mapper>

1.2.4 连接数据库

jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/database?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
jdbc.username=root
jdbc.password=root
# 连接数据库

1.2.5 编写spring和mybatis整合配置

<?xml version="1.0" encoding="UTF-8"?>
<!-- Spring配置文件并导入约束 -->
<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 https://www.springframework.org/schema/context/spring-context.xsd">
    <!-- SpringContext spring核心配置文件 -->
    <!-- dao层内容 -->
    <!-- 1.连接数据库的配置文件 -->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <!-- 2.配置Druid数据源 -->
    <bean id="druidDataSource" 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>
    <!-- 3.配置SqlSessionFactory的对象 是为了让spring完成mybatis的对象创建的时候,可以知道通过SqlSessionFactoryBean就能帮助找到mybatis所用到的sqlsession -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- 数据源 -->
        <property name="dataSource" ref="druidDataSource"/>
        <!-- pojo的别名映射-->
        <property name="typeAliasesPackage" value="com.zhuxu.pojo"/>
    </bean>
    <!-- 4.配置dao层代码动态代理对象生成的扫描器 -->
    <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!-- 指定扫描的dao层的路径 -->
        <property name="basePackage" value="com.zhuxu.dao"/>
    </bean>
    <!-- 5.service层内容 spring的配置 -->
    <bean id="accountService" class="com.zhuxu.service.impl.AccountServiceImpl">
        <!-- 依赖注入 -->
        <property name="accountDao" ref="accountDao"/>
    </bean>
</beans>

1.2.6 测试

/*
测试类
 */
public class CRUDTest {
    @Test
    public void findByName(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("ApplicationContext.xml");
        AccountService accountService = applicationContext.getBean(AccountService.class);
        Account account = accountService.findByName("迪丽热巴");
        System.out.println("account = " + account);
    }

    @Test
    public void findAll(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("ApplicationContext.xml");
        AccountService accountService = applicationContext.getBean(AccountService.class);
        List<Account> accountList = accountService.findAll();
        for (Account account : accountList) {
            System.out.println("account = " + account);
        }
    }
}

第二节 注解开发

2.1 bean标签和注解的对应

  • bean标签对应注解@Component对应的是把xml配置文件中impl文件替换掉 替换如下的代码

  • 如:

    @Component("accountDao")
    public class AccountDaoImpl implements AccountDao {
        @Override
        public void saveAccount() {
            System.out.println("模拟保存账户");
        }
    }
    

    就不用在xml中写以下代码

    <!--配置accountService对象-->
    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"></bean>
    

    但是beans.xml配置没有指定接口实现类对象所以就需要开启springioc的注解扫描,扫描包中类的注解

    <context:component-scan base-package="com.itheima"></context:component-scan>
    
    • 注解属性value:bean标签的id属性
    • 不指定value属性,默认就是类名,首字母小写
    • 该注解衍生出了三个注解,@Controller(在servlet层),@Service(service层),@Repository(dao层 ),用法和@Componet一致,为了更加清晰的提现层的概念。
  • bean标签属性scope对应注解@Scope

    • 注解属性value:singleton,prototype
  • bean标签属性init-method对应注解@PostConstruct 构造方法执行完毕后执行

  • bean标签属性destroy-method对应注解@PreDestroy 对象销毁前执行的方法

  • service层

    public interface AccountService {
    
        //模拟保存账户
        void save();
    }
    
    //@Component("accountService")
    @Service("accountService")
    @Scope("prototype")
    public class AccountServiceImpl implements AccountService {
    
        private AccountDao accountDao;
    
        public void setAccountDao(AccountDao accountDao) {
            this.accountDao = accountDao;
        }
    
        @Override
        public void saveAccount() {
            accountDao.saveAccount();
        }
    }
    
  • dao层

    public interface AccountDao {
    
        //模拟保存账户
        void save();
    }
    
    //@Component("accountDao")
    @Repository("accountDao")
    public class AccountDaoImpl implements AccountDao {
        @Override
        public void saveAccount() {
            System.out.println("保存了账户");
        }
    
        private void init() {
            System.out.println("AccountDao对象初始化");
        }
    
        private void destroy() {
            System.out.println("AccountDao对象 销毁了");
        }
    }
    
  • 添加applicationContext配置文件命名空间

    <?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"
    >
        
        <!-- 开启spring的注解扫描,扫描包中类的注解-->
        <context:component-scan base-package="com.itheima"></context:component-scan>
    </beans>
    
  • 测试注解

    @Test
    public void testIOC(){
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        AccountService accountService = context.getBean(AccountService.class);
        AccountDao accountDao = context.getBean(AccountDao.class);
    
        System.out.println(accountService);
        System.out.println(accountDao);
    
        context.close();
    }
    

2.2 依赖注入注解

之前是私有化一对象,通过get或者set的方法进行依赖注入

   private AccountDao accountDao;

    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }
  • @Autowired注解(Spring框架提供)通过类型自动注入

    @Autowired
    private AccountDao accountDao;
    
  • 当接口的实现类不止一个时,无法通过类型自动注入了

    按照类型注入,如果无法确定唯一类型(接口有多个实现类),需要配合注解@Qualifier的使用

  • @Qualifier("id") 注解(Spring框架提供)

    • 按照id注入
  • @Resource注解(JDK提供)

    • 注解属性name:配置类的id
@Resource(name="accountDao")
private AccountDao accountDao;

代码演示

  • 业务层
@Service("accountService")
@Scope("singleton")
public class AccountServiceImpl implements AccountService {

     @Autowired
    //@Qualifier("accountDao2")
    //@Resource(name = "accountDao2")
    private AccountDao accountDao;

    @Override
    public void saveAccount(Account account) {
        accountDao.saveAccount(account);
    }
}
  • dao层
package com.itheima.dao.impl;

/**
 * 账户dao实现类
 */
@Component("accountDao")
//@Repository("accountDao")
@Scope("singleton")
public class AccountDaoImpl implements AccountDao {

    @Override
    public void saveAccount(Account account) {
        System.out.println("模拟转账");
    }

    @PostConstruct()
    public void init(){
        System.out.println("AccountDaoImpl 初始化...");
    }

    @PreDestroy
    public void destroy(){
        System.out.println("AccountDaoImpl 销毁...");
    }
}

2.3 Spring对Junit的支持

  • junit运行的时候底层使用了Runner对象,有一个默认使用的Runner对象。
  • Spring对junit的支持,其实是自己实现了一个Runner对象(按照junit runner的要求实现)
  • Spring对junit的支持的体现
    • 好处一:配置完之后,不需要我们手动的启动Spring
    • 好处二:可以在junit测试类中使用@AutoWired等方式注入对象,直接对其进行调用测试
  • 使用步骤
    • 引入spring-test.jar包
    • 配置测试类
  //Spring框架中的Runner对象, 替换Junit中的Runner对象 (更换启动器  或者叫更换执行对象)
  @RunWith(SpringJUnit4ClassRunner.class)

  //框架启动入口, xml配置文件启动(2选1)加载xml启动执行配置文件  可以加载多个配置文件
  @ContextConfiguration(locations = "classpath:beans.xml")
  //框架启动入口, 注解方式配置文件启动(2选1)  纯注解开发推荐使用
  //@ContextConfiguration(classes = SpringConfig.class)
  • 代码实现

    • pom.xml
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.9.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.9.RELEASE</version>
        </dependency>
    </dependencies>
    
    • 测试类
    package com.itheima;
    
    //Spring框架中的Runner对象, 替换Junit中的Runner对象
    @RunWith(SpringJUnit4ClassRunner.class)
    //框架启动入口, 注解方式配置文件启动
    //@ContextConfiguration(classes = SpringConfig.class);
    //框架启动入口, xml配置文件启动
    @ContextConfiguration(locations = "classpath:ApplicationContext.xml")
    public class AccountTest {
    
        //注入业务层接口
        @Autowired
        private AccountService service;
    
        @Test
        public void testIOC(){
    
            System.out.println("service = " + service);
            Account account = new Account();
            account.setName("小米");
            account.setMoney(888.0F);
            service.saveAccount(account);
        }
    }
    

第三节 IoC的综合案例(CRUD) - 半注解半xml开发

企业主流的开发方式

注意:往往第三方jar中的对象我们使用xml配置(比如druid数据库连接池、Mybatis的SQLSessionFactory),类似于service层和dao层的实现类,这属于我们自己写的代码,往往会使用注解,这就是半xml半注解的模式。

  • 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"
           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 https://www.springframework.org/schema/context/spring-context.xsd">
    
        <!--配置dao-->
        <!--配置properties文件的位置-->
        <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
        <!--配置数据源-->
        <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
            <property name="driverClassName" value="${jdbc.driverClassName}"></property>
            <property name="url" value="${jdbc.url}"></property>
            <property name="username" value="${jdbc.username}"></property>
            <property name="password" value="${jdbc.password}"></property>
        </bean>
        <!--配置mybatis的SqlSessionFactory工厂-->
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            <property name="dataSource" ref="druidDataSource"></property>
            <property name="typeAliasesPackage" value="com.itheima.pojo"></property>
        </bean>
        <!--配置创建dao代理实现类的扫描器-->
        <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <property name="basePackage" value="com.itheima.dao"></property>
        </bean>
    
        <!--配置service-->
        <!--让Spring开启注解扫描-->
        <context:component-scan base-package="com.itheima"></context:component-scan>
    </beans>
    
  • service层

    package com.itheima.service.impl;
    
    /**
     * 账户业务接口实现类
     */
    @Service("accountService")
    public class AccountServiceImpl implements AccountService {
    
        //依赖注入
        @Autowired
        private AccountDao accountDao;
    
        //增删改查方法 无修改, 笔记中代码省略
        
    }
    
  • 测试类

/*
测试类
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:ApplicationContext.xml"})
public class AccountServiceTest {

    @Autowired
    private AccountService accountService;

    //测试类方法省略
    @Test
    public void findAll() {
        List<Account> accountList = accountService.findAll();
        for (Account account : accountList) {
            System.out.println("account = " + account);
        }
    }
}

第四节 IoC的综合案例(CRUD) - 纯注解开发

  • @Configuration标识当前类是Spring的一个配置类

  • @ComponentScan替代xml中的<context:component-scan/>

  • @Import引入其他配置类,被引入的配置类可以不加@Configuration注解

  • @PropertySource:引入外部properties文件,注意加classpath:

  • @Value对成员变量赋值

  • @Bean将一个方法的返回值对象加入到Spring的容器当中管理

  • @Qualifier可以使用在方法参数上,表明对应的形参引入/注入的对象类型

4.1 SpringConfig框架启动配置类

只要在一个类上加@Configuration,那么这个类就是spring的配置类

/**
 * Spring框架的核心配置文件(注解配置)
 * @author Lucky
 * @date 2020/8/29
 */
// 标识当前类是spring的一个配置类
@Configuration
// 开启SpringIOC容器的注解扫描
/**
 * xml代码
 *   <!-- 扫描包,开启注解 -->
 *     <context:component-scan base-package="com.zhuxu" />
 * */

@ComponentScan({"com.zhuxu"})
// 加载properties配置文件
// @PropertySource({"classpath:jdbc.properties"})
public class SpringConfig {
    // 配置数据源
    /**
     * xml源码
     * <!-- 配置Druid数据源 -->
     *     <bean id="druidDataSource" 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>
     */
    @Bean("druidDataSource")
    public DataSource createDataSource(){
        DruidDataSource druidDataSource = new DruidDataSource();
        // 配置相关驱动信息(驱动、url、用户名、密码)
        druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
        druidDataSource.setUrl("jdbc:mysql://localhost:3306/ssm_lx");
        druidDataSource.setUsername("root");
        druidDataSource.setPassword("root");

        return  druidDataSource;

    }

    // 配置SqlSessionFactory对象
    // 参数DataSource dataSource,会自动从Spring IOC容器中找配置的Bean对象
    /**
     * 配置SqlSessionFactory对象xml源码
     * <!-- 配置SqlSessionFactory的对象 是为了让spring完成mybatis的对象创建的时候,可以知道通过SqlSessionFactoryBean就能帮助找到mybatis所用到的sqlsession
     * -->
     *     <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
     *         <!-- 数据源 -->
     *         <property name="dataSource" ref="druidDataSource"/>
     *         <!-- pojo的别名映射-->
     *         <property name="typeAliasesPackage" value="com.zhuxu.pojo"/>
     *     </bean>
     * */
    @Bean
    public SqlSessionFactoryBean createSqlSessionFactoryBean(@Qualifier("druidDataSource") DataSource dataSource){
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        // 配置相关信息(数据源,pojo别名)
        sqlSessionFactoryBean.setDataSource(dataSource);
        sqlSessionFactoryBean.setTypeAliasesPackage("com.zhuxu.pojo");

        return sqlSessionFactoryBean;
    }

    /**
     * xml代码
     *     <!-- 配置dao层代码动态代理对象生成的扫描器 -->
     *     <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
     *         <!-- 指定扫描的dao层的路径 -->
     *         <property name="basePackage" value="com.zhuxu.dao"/>
     *     </bean>
     * */
    // dao 扫描器
    @Bean
    public MapperScannerConfigurer createMapperScannerConfigurer(){
        MapperScannerConfigurer scannerConfigurer = new MapperScannerConfigurer();
        // 配置相关信息(指定扫描的dao层的路径)
        scannerConfigurer.setBasePackage("com.zhuxu.dao");
        return scannerConfigurer;
    }



}
  • 测试
/*
测试类
 */
@RunWith(SpringJUnit4ClassRunner.class)
//框架启动入口, 注解方式配置文件启动(2选1)
@ContextConfiguration(classes = SpringConfig.class)
public class CRUDTest {

    @Autowired
    private AccountService accountService;

    @Test
    public void findByName(){
        Account account = accountService.findByName("迪丽热巴");
        System.out.println("account = " + account);
    }

    @Test
    public void findAll(){
        List<Account> accountList = accountService.findAll();
        for (Account account : accountList) {
            System.out.println("account = " + account);
        }
    }
}

4.2 注解开发的SpringConfig配置优化

  • SpringConfig框架启动配置类

    /*
    作为Spring框架的主配置文件
     */
    @Configuration
    //开启Spring容器的注解扫描
    @ComponentScan({"com.itheima"})
    //导入子配置文件
    @Import({JDBCConfig.class, MybatisConfig.class})
    public class SpringConfig {
    
    }
    
  • JDBCConfig配置类

    //用于指定与数据库相关配置的配置文件
    @Configuration
    @PropertySource({"classpath:jdbc.properties"})
    public class JDBCConfig {
    
        @Value("${jdbc.driverClassName}")
        private String driverClassName;
        @Value("${jdbc.url}")
        private String url;
        @Value("${jdbc.username}")
        private String username;
        @Value("${jdbc.password}")
        private String password;
    
        //配置数据源
        @Bean("ataSource")
        public DataSource createDataSource(){
            //创建Druid数据源
            DruidDataSource druidDataSource = new DruidDataSource();
            //配置相关信息(Driver, url, username, password)
            druidDataSource.setDriverClassName(driverClassName);
            druidDataSource.setUrl(url);
            druidDataSource.setUsername(username);
            druidDataSource.setPassword(password);
    
            return druidDataSource;
        }
    }
    
  • Mybatis配置类

    //用于配置与Mybatis相关的配置
    @Configuration
    public class MybatisConfig {
        //配置SqlSessionFactoryBean对象
        @Bean("sqlSessionFactory")
        public SqlSessionFactoryBean createSqlSessionFactoryBean(DataSource ds){
            //创建SqlSessionFactoryBean 对象
            SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();
            //配置相关信息(数据源, pojo别名映射)
            sqlSessionFactory.setDataSource(ds);
            sqlSessionFactory.setTypeAliasesPackage("com.itheima.pojo");
    
            return sqlSessionFactory;
        }
    
        //配置dao的包扫描
        @Bean("scannerConfigurer")
        public MapperScannerConfigurer createMapperScannerConfigurer(){
            //创建MapperScannerConfigurer 对象
            MapperScannerConfigurer scannerConfigurer = new MapperScannerConfigurer();
            //配置相关信息(扫描的dao包, 找到了dao层的接口文件, 找到了SQL映射文件, 生成接口实现类对象并存到Spring容器中)
            scannerConfigurer.setBasePackage("com.itheima.dao");
    
            return scannerConfigurer;
        }
    }
    
  • 测试类

    /*
    测试类
     */
    @RunWith(SpringJUnit4ClassRunner.class)
    //加载的xml配置文件
    //@ContextConfiguration(locations = {"classpath:ApplicationContext.xml"})
    
    //加载的注解形式的Spring主配置文件
    @ContextConfiguration(classes = {SpringConfig.class})
    public class AccountServiceTest {
    
        @Autowired
        private AccountService accountService;
    	
        //其他方法省略
    
        @Test
        public void findAll() {
            List<Account> accountList = accountService.findAll();
            for (Account account : accountList) {
                System.out.println("account = " + account);
            }
        }
    }
    

第五节 案例:模拟转账

案例:模拟转账(并且模拟转账异常)

  • 汇款人账户减少一定的金额
  • 收款人账户增加一定的金额
  • 计算之后,更新数据库
  • 问题:模拟转账异常(人为制造异常,在两次update之间造了异常)

5.1 转账编码

1、引入依赖
<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.1.9.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.1.9.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.2</version>
    </dependency>
    <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.20</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>2.0.1</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.9.RELEASE</version>
    </dependency>
</dependencies>
2、业务层
/**
 * 账户的业务层接口
 */
public interface AccountService {
    /**
     * 转账
     */
    void transfer(String source, String target, double money);
    
    //其他方法省略
}
/**
 * 账户业务接口实现类
 */
@Service("accountService")
public class AccountServiceImpl implements AccountService {

    //依赖注入
    @Autowired
    private AccountDao accountDao;

    /*
    转账业务逻辑
    1.先查询账户信息
    2.修改账户信息
    3.持久化账户信息
     */
    @Override
    public void transfer(String source, String target, double money) {
        try {
            //开启事务

            // 转账业务逻辑
            //1 先查询账户信息
            Account sourceAccount = accountDao.findByName(source);
            Account targetAccount = accountDao.findByName(target);
            //2 修改账户信息
            sourceAccount.setMoney(sourceAccount.getMoney() - money);
            targetAccount.setMoney(targetAccount.getMoney() + money);
            //3 持久化账户信息
            accountDao.update(sourceAccount);
            //int i = 1/0;
            accountDao.update(targetAccount);

            //提交事务
        } catch (Exception e){
            e.printStackTrace();
            //回滚事务

        } finally {
            //关闭资源
        }
    }
    
    //其他方法省略
}
3、测试
/*
测试类
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:ApplicationContext.xml")
public class CRUDTest {

    @Autowired
    private AccountService accountService;

    @Test
    public void tranfer(){
        accountService.transfer("迪丽热巴","古力娜扎",1);
    }
}
4、发现问题
转账过程出现事务问题
原文地址:https://www.cnblogs.com/anke-z/p/13583009.html