dtcms插件 宽禁带半导体 DHCP lua static tree angular material ip vue的钩子函数 electron教程 jq获取元素宽度 mysql默认密码 ie内核浏览器怎么设置 java三维数组 solidworks图库 python环境变量配置 python基础代码 java斐波那契数列 java集合 java基本数据结构 java怎么学 java遍历set 猫爪 免费家谱制作软件 js获取数组长度 苏拉玛起义的任务线 hzfs grep正则表达式 bin文件编辑器 dnf风神加点 linux解压 保留两位小数的函数 租房管理软件 召唤加点90刷图加点 远程桌面管理工具 php定时任务 求字符串长度的函数 cdr复制属性快捷键 xp系统修复工具 英语口语学习软件
当前位置: 首页 > 学习教程  > 编程语言

SSM之Spring系列(四)---- Spring三种方式实现账户的 CRUD 操作、Spring 整合 JUnit

2021/2/13 16:30:09 文章标签: 测试文章如有侵权请发送至邮箱809451989@qq.com投诉后文章立即删除

在上一篇文章我们对 Spring 基于注解的 IoC有了一定的了解,现在我们来看看一个简单的案例。这个案例将有三种方式实现,分别是XML,半注解,纯注解。看完案例之后就了解一下Spring 整合 JUnit。 文章目录案例:实现账户的 …

在上一篇文章我们对 Spring 基于注解的 IoC有了一定的了解,现在我们来看看一个简单的案例。这个案例将有三种方式实现,分别是XML,半注解,纯注解。看完案例之后就了解一下Spring 整合 JUnit。

文章目录

  • 案例:实现账户的 CRUD 操作
    • XML 方式
    • 半注解(XML + 注解)方式
    • 纯注解方式
  • Spring 整合 JUnit

案例:实现账户的 CRUD 操作

XML 方式

  • 导入相关依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>Spring_Day02_anno_ioc</artifactId>
    <version>1.0-SNAPSHOT</version>

    <packaging>jar</packaging>

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

        <dependency>
            <groupId>commons-dbutils</groupId>
            <artifactId>commons-dbutils</artifactId>
            <version>1.4</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.20</version>
        </dependency>

        <dependency>
            <groupId>c3p0</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.1.2</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.10</version>
        </dependency>
    </dependencies>
</project>

导入过程有错的,大家可以去看看我的这两篇文章。传送门1 传送门2

  • 创建数据库表并编写对应实体类


create table account(
	id int primary key auto_increment,
	name varchar(40),
	money float
)character set utf8 collate utf8_general_ci;

insert into account(name,money) values('张三',1000);
insert into account(name,money) values('李四',1000);
insert into account(name,money) values('一个Java小白',1000);
package com.cz.domain;

import javax.annotation.PreDestroy;
import java.io.Serializable;

/**
 * 账户的实体类
 */
public class Account implements Serializable {
    private Integer id;
    private String name;
    private Float money;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Float getMoney() {
        return money;
    }

    public void setMoney(Float money) {
        this.money = money;
    }

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", money=" + money +
                '}';
    }
}
  • 编写业务层接口和实现类

业务层接口:

package com.cz.service;

import com.cz.domain.Account;
import java.util.List;

/**
 * 账户业务层接口
 */
public interface AccountService {
    /**
     * 查询所有账户
     * @return
     */
    List<Account> findAllAccount();

    /**
     * 根据id查询账户
     * @param accountId
     * @return
     */
    Account findAccountById(Integer accountId);

    /**
     * 保存
     * @param account
     * @return
     */
    int saveAccount(Account account);

    /**
     * 更新
     * @param account
     * @return
     */
    int updateAccount(Account account);

    /**
     * 删除
     * @param accountId
     * @return
     */
    int removeAccount(Integer accountId);
}

实现类:

package com.cz.service.impl;

import com.cz.dao.AccountDao;
import com.cz.domain.Account;
import com.cz.service.AccountService;
import java.util.List;


/**
 * 账户的业务层实现类
 */
public class AccountServiceImpl implements AccountService {
    private AccountDao accountDao;

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

    public List<Account> findAllAccount() {
        return accountDao.findAllAccount();
    }

    public Account findAccountById(Integer accountId) {
        return accountDao.findAccountById(accountId);
    }

    public int saveAccount(Account account) {
        return accountDao.saveAccount(account);
    }

    public int updateAccount(Account account) {
        return accountDao.updateAccount(account);
    }

    public int removeAccount(Integer accountId) {
        return accountDao.removeAccount(accountId);
    }
}

  • 编写持久层接口和实现类

持久层接口:

package com.cz.dao;

import com.cz.domain.Account;
import java.util.List;

/**
 * 账户的持久层接口
 */
public interface AccountDao {

    /**
     * 查询所有账户
     * @return
     */
    List<Account> findAllAccount();

    /**
     * 根据id查询账户
     * @param accountId
     * @return
     */
    Account findAccountById(Integer accountId);

    /**
     * 保存
     * @param account
     * @return
     */
    int saveAccount(Account account);

    /**
     * 更新
     * @param account
     * @return
     */
    int updateAccount(Account account);

    /**
     * 删除
     * @param accountId
     * @return
     */
    int removeAccount(Integer accountId);
}

实现类:

package com.cz.dao.impl;

import com.cz.dao.AccountDao;
import com.cz.domain.Account;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;

import java.util.List;


/**
 * 账户的持久层实现类
 */

public class AccountDaoImpl implements AccountDao {

    private QueryRunner runner;

    public void setRunner(QueryRunner runner) {
        this.runner = runner;
    }

    public List<Account> findAllAccount() {
        try{
            return runner.query("select * from account",new BeanListHandler<Account>(Account.class));
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }

    public Account findAccountById(Integer accountId) {
        try{
            return runner.query("select * from account where id = ?",new BeanHandler<Account>(Account.class),accountId);
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }

    public int saveAccount(Account account) {
        try{
            return runner.update("insert into account(name,money)values(?,? )",account.getName(),account.getMoney());
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }

    public int updateAccount(Account account) {
        try{
            return runner.update("update account set name=?,money=? where id=?",account.getName(),account.getMoney(),account.getId());
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }

    public int removeAccount(Integer accountId) {
        try{
            return runner.update("delete from account  where id=?",accountId);
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }
}
  • 编写 Spring 配置文件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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--配置service-->
    <bean id="accountService" class="com.cz.service.impl.AccountServiceImpl">
        <!--注入dao-->
        <property name="accountDao" ref="accountDao"></property>
    </bean>
    <!-- 配置dao对象-->
    <bean id="accountDao" class="com.cz.dao.impl.AccountDaoImpl">
        <!--注入QueryRunner-->
        <property name="runner" ref="runner"></property>
    </bean>
    <!-- 配置QueryRunner-->
    <bean id="runner" class="org.apache.commons.dbutils.QueryRunner"  scope="prototype">
        <!--注入数据源-->
        <constructor-arg name="ds" ref="dataSource"></constructor-arg>
    </bean>

    <!-- 配置数据源-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!--注入连接数据库的必备信息-->
        <property name="driverClass" value="com.mysql.cj.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring?serverTimezone=UTC&amp;characterEncoding=utf-8"/>
        <property name="user" value="root"/>
        <property name="password" value="123456"/>
    </bean>
</beans>
  • 测试代码如下:
package com.cz.test;

import com.cz.domain.Account;
import com.cz.service.AccountService;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.List;

/**
 * 使用JUnit单元测试,测试我们的配置
 */
public class AccountServiceTest {

    private ApplicationContext ac;
    private AccountService accountService;

    @Before
    public void init(){
        //1.获取容器
        ac = new ClassPathXmlApplicationContext("bean.xml");
        //2.得到业务层service对象
        accountService = ac.getBean("accountService",AccountService.class);
    }

    @Test
    public void testFindAll(){
        //执行方法
        List<Account> accounts = accountService.findAllAccount();
        for (Account account : accounts){
            System.out.println(account);
        }
    }

    @Test
    public void testFindOne(){
        //执行方法
        Account account = accountService.findAccountById(1);
        System.out.println(account);
    }

    @Test
    public void testSave(){
        //执行方法
        Account account = new Account();
        account.setName("王五");
        account.setMoney(800000f);
        accountService.saveAccount(account);
        //下面这个方法也是可以的,下面的更新,删除也适用
//        int row = accountService.saveAccount(account);
//        Assert.assertEquals(1,row);
    }

    @Test
    public void testUpDate(){
        //执行方法
        Account account = accountService.findAccountById(3);
        account.setMoney(9000f);
        accountService.updateAccount(account);
    }

    @Test
    public void testDelete(){
        //执行方法
        accountService.removeAccount(4);
    }
}

通过上面的测试类,我们把容器的获取定义到类中去。但仍需要我们自己写代码来获取容器。能不能测试时直接就编写测试方法,而不需要手动编码来获取容器呢?

半注解(XML + 注解)方式

如果想使用注解进行配置,那么就可以用到上篇文章讲解到的注解传送门。

  • 业务层代码:

/**
 * 账户的业务层实现类
 */
@Service("accountService")
public class AccountServiceImpl implements AccountService {
    @Autowired
    private AccountDao accountDao;
     // 不需要 set 方法...
    // 业务方法跟上面一样...
  • 持久层代码:
**
 * 账户的持久层实现类
 */
@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {

    @Autowired
    private QueryRunner runner;
    // 不需要 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: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.cz"></context:component-scan>
    <!-- 配置QueryRunner-->
    <bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
        <!--注入数据源-->
        <constructor-arg name="ds" ref="dataSource"></constructor-arg>
    </bean>

    <!-- 配置数据源-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!--注入连接数据库的必备信息-->
        <property name="driverClass" value="com.mysql.cj.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring?serverTimezone=UTC&amp;characterEncoding=utf-8"/>
        <property name="user" value="root"/>
        <property name="password" value="7107883"/>
    </bean>
</beans>

在这个方法中,无法在 QueryRunner 和 ComboPooledDataSource 等第三方类上加注解,所以还是需要配置文件。

纯注解方式

  如果想要使用纯注解,那么我们就需要将配置文件中还存在的配置也使用注解配置,这时候就需要一些新的注解了。

@Configuration 注解

  • 作用
    • 用于指定当前类是一个Spring 配置类,当创建容器时会从该类上加载注解
  • 属性
    • value: 用于指定配置类的字节码
  • 注意
    • 获取容器时需要使用AnnotationApplicationContext(有 @Configuration 注解的类.class),写了这个之后可以不用写@Configuration注解
/**
 * 该类是一个配置类,他的作用和bean.xml是一样的
 */
@Configuration
public class SpringConfiguration {
  // ...  
}

我们已经把配置文件用类来代替了,但是如何配置创建容器时要扫描的包呢?请看下一个注解。

@ComponentScan 注解

  • 作用
    • 用于指定 Spring 在初始化容器时要扫描的包。
  • 属性
    • value / basePackages :两者都是用来指定要扫描的包。使用此注解等同于在xml中配置了:<context:component-scan base-package="cn.cz"/>
/**
 * 该类是一个配置类,他的作用和bean.xml是一样的
 */
@Configuration
@ComponentScan(basePackages = "com.cz")
public class SpringConfiguration {
}

我们已经配置好了要扫描的包,但是数据源和 QueryRunner对象如何从配置文件中移除呢?请看下一个注解。

@Bean

  • 作用
    • 该注解只能写在方法上,表明使用此方法创建一个对象,并且放入 Spring 容器
  • 属性
    • name :用于指定 Bean的id默认值是当前方法的名称
  • 细节
    • 当我们使用该注解配置方法时,如果方法有参数,那么 Spring 框架会去容器中查找有没有可用的 Bean 对象。查找的方式和@Autowired 的作用是一样
package config;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;

import javax.sql.DataSource;
import java.awt.color.ProfileDataException;
import java.beans.PropertyVetoException;
import java.util.Date;

/**
 * 该类是一个配置类,他的作用和bean.xml是一样的
 */
@Configuration
@ComponentScan(basePackages = "com.cz")
public class SpringConfiguration {

    /**
     * 用于创建一个QueryRunner对象并且放入 ioc 容器中,多例
     * @param dataSource
     * @return
     */
    @Bean(name = "runner")
    @Scope("prototype")
    public QueryRunner creatQueryRunner(DataSource dataSource){
        return new QueryRunner(dataSource);
    }

    /**
     * 创建一个数据源并且放入 ioc 容器中
     *
     * @return
     */
    @Bean(name = "dataSource")
    public DataSource createDataSource() {
        try {
            ComboPooledDataSource ds = new ComboPooledDataSource();
            ds.setDriverClass("com.mysql.cj.jdbc.Driver");
            ds.setJdbcUrl("jdbc:mysql://localhost:3306/spring?serverTimezone=UTC&amp;characterEncoding=utf-8");
            ds.setUser("root");
            ds.setPassword("7107883");
            return ds;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

到这里我们可以删除 bean.xml了。但是由于没有了配置文件,创建数据源的配置又都写死在类中了。如何把它们配置出来呢?请看下一个注解。

@PropertySource 注解

  • 作用
    • 用于加载 xxxxx.properties 文件中的配置
  • 属性
    • value[]:用于指定配置文件的名称和位置
  • 细节
    • 如果配置文件是在类路径下,需要写上classpath:

配置文件jdbcConfig.properties:

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring?serverTimezone=UTC&characterEncoding=utf-8
jdbc.user=root
jdbc.password=123456
package config;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.Scope;
import javax.sql.DataSource;

/**
 *Spring 连接 数据库的配置类
 */
@PropertySource("classpath:jdbcConfig.properties")
public class JdbcConfig {
    @Value("${jdbc.driver}")
    private String driver;

    @Value("${jdbc.url}")
    private String url;

    @Value("${jdbc.user}")
    private String user;

    @Value("${jdbc.password}")
    private String password;

    /**
     * 用于创建一个QueryRunner对象并且放入 ioc 容器中,多例
     *
     * @param dataSource
     * @return
     */
    @Bean(name = "runner")
    @Scope("prototype")
    public QueryRunner creatQueryRunner(DataSource dataSource) {
        return new QueryRunner(dataSource);
    }
    /**
     * 创建一个数据源并且放入 ioc 容器中
     *
     * @return
     */
    @Bean(name = "dataSource")
    public DataSource createDataSource() {
        try {
            ComboPooledDataSource ds = new ComboPooledDataSource();
            ds.setDriverClass(driver);
            ds.setJdbcUrl(url);
            ds.setUser(user);
            ds.setPassword(password);
            return ds;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

此时我们已经有了两个配置类,但是他们还没有关系。如何建立他们的关系呢?请看下一个注解。

@Import

  • 作用
    • 用于导入其他的配置类,被引入的配置类有无@Configuration注解均可
  • 属性
    • value[]:用于指定其他配置类的字节码
/**
 * 该类是一个配置类,他的作用和bean.xml是一样的
 */
@Configuration
@ComponentScan(basePackages = "com.cz")
@Import(JdbcConfig.class)
public class SpringConfiguration{

}

我们已经把要配置的都配置好了,但是新的问题产生了,由于没有配置文件了,如何获取容器呢?

ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);

Spring 整合 JUnit

  • 在原本的代码中,我们需要手动获取 IoC 容器,并获取 service 对象
		 //1.获取容器
        ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
        //2.得到业务层service对象
        accountService = ac.getBean("accountService", AccountService.class);
  • 但是我们是测试类,应该专注于测试功能,所以需要程序可以帮我们自己创建容器并且注入 service 对象。这时候就需要使用Spring 来整合 JUnit,步骤如下:
  1. 导入依赖
		<dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>

当我们使用spring 5.x版本的时候,要求junit的版本必须是4.12及以上

  1. 使用 @RunWith注解替换 JUnit 原本的运行器
@RunWith(SpringJUnit4ClassRunner.class)
public class AccountServiceTest {
	// ....
}

  1. 使用 @ContextConfiguration指定 Spring 配置文件或者配置类的位置
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)
public class AccountServiceTest {
}

@ContextConfiguration 注解

  • locations 属性:指定 XML 文件的位置,加上classpath:关键字表示在类路径下
  • classes 属性:指定注解类所在的位置
  1. 使用 @Autowired注入 service 对象,最终代码如下
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)
public class AccountServiceTest {

    @Autowired
    private AccountService service;

	// 测试方法...
}

本文链接: http://www.dtmao.cc/news_show_700049.shtml

附件下载

相关教程

    暂无相关的数据...

共有条评论 网友评论

验证码: 看不清楚?