侧边栏壁纸
博主头像
coydone博主等级

记录学习,分享生活的个人站点

  • 累计撰写 306 篇文章
  • 累计创建 51 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

Spring的IoC案例

coydone
2022-03-15 / 0 评论 / 0 点赞 / 340 阅读 / 13,471 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2022-04-10,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

dbutils+Spring完成单表的crud

准备

1、创建的数据库表。

CREATE TABLE s_account(
    id INT PRIMARY KEY AUTO_INCREMENT,
    NAME VARCHAR(40),
    money FLOAT
)CHARACTER SET utf8 COLLATE utf8_general_ci;

INSERT INTO s_account(NAME,money) VALUES('aaa',1000);
INSERT INTO s_account(NAME,money) VALUES('bbb',1000);
INSERT INTO s_account(NAME,money) VALUES('ccc',1000);

2、新建Maven工程,Jar包形式,在pom文件中导入坐标。

<?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>com.coydone</groupId>
    <artifactId>spring02_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>5.1.6</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.12</version>
        </dependency>
    </dependencies>
</project>

3、编写实体类

//账户的实体类
public class Account implements Serializable {
    private Integer id;
    private String name;
    private Float money;
    //省略有参、无参构造,getter()和setter()方法,toString()方法
}

4、编写dao接口即其实现类

//账户的持久层接口
public interface AccountDao {
    // 查询所有
    List<Account> findAll();
    //根据id查询
    Account findAccountById(Integer id);
    //保存
    void saveAccount(Account account);
    //更新
    void updateAccount(Account account);
    //根据id删除
    void deleteAccount(Integer id);
}
//账户的持久层实现类
public class AccountDaoImpl implements AccountDao {
    private QueryRunner qr;

    //定义setter()方法,便于在xml中注入QueryRunner
    public void setQr(QueryRunner qr) {
        this.qr = qr;
    }

    public List<Account> findAll() {
        try {
            return qr.query("select * from s_account",new BeanListHandler<Account>(Account.class));
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

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

    public void saveAccount(Account account) {
        try {
            qr.update("insert into s_account(name,money)values(?,?)",account.getName(),account.getMoney());
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    public void updateAccount(Account account) {
        try {
            qr.update("update s_account set name = ?,money = ? where id = ?",account.getName(),account.getMoney(),account.getId());
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    public void deleteAccount(Integer id) {
        try {
            qr.update("delete from s_account where id = ?",id
            );
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}

5、编写service层接口和实现类

service接口与dao层接口一样

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

    //定义setter()方法,便于xml形式注入
    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }

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

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

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

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

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

XML方式配置Spring的IoC

1、在resources下bean.xml中配置Spring的IoC

<?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.coydone.service.impl.AccountServiceImpl">
        <!-- 注入dao -->
        <property name="accountDao" ref="accountDao"></property>
    </bean>

    <!-- 配置Dao对象 -->
    <bean id="accountDao" class="com.coydone.dao.impl.AccountDaoImpl">
        <!-- 注入QueryRunner -->
        <property name="qr" ref="qr"></property>
    </bean>
    <!-- 配置QueryRunner 配置其为多例模式 -->
    <bean id="qr" 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.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/eesy_mybatis"></property>
        <property name="user" value="root"></property>
        <property name="password" value="root123"></property>
    </bean>
</beans>

2、编写测试类进行单元测试

package com.coydone.test;
//使用Junit单元测试,测试我们的配置
public class AccountServiceTest {
    @Test
    public void testFindAll(){
        //1.获取容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //2.得到业务层对象
        AccountService as = ac.getBean("accountService",AccountService.class);
        //3.执行方法
        List<Account> accounts = as.findAll();
        for (Account account : accounts) {
            System.out.println(account);
        }
    }

    @Test
    public void testFindOne(){
        //1.获取容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //2.得到业务层对象
        AccountService as = ac.getBean("accountService",AccountService.class);
        //3.执行方法
        Account account = as.findAccountById(1);
        System.out.println(account);
    }

    @Test
    public void testSave(){
        Account account = new Account();
        account.setName("add_test");
        account.setMoney(1234f);
        //1.获取容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //2.得到业务层对象
        AccountService as = ac.getBean("accountService",AccountService.class);
        //3.执行方法
        as.saveAccount(account);

    }
    @Test
    public void testUpdate(){
        //1.获取容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //2.得到业务层对象
        AccountService as = ac.getBean("accountService",AccountService.class);
        //3.执行方法
        Account account = as.findAccountById(4);
        account.setMoney(2345f);
        as.updateAccount(account);
    }
    @Test
    public void testDelete(){
        //1.获取容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //2.得到业务层对象
        AccountService as = ac.getBean("accountService",AccountService.class);
        //3.执行方法
        as.deleteAccount(4);
    }
}

注解改进IoC案例

1、在bean.xml中,将dao和service的配置删除

<!-- 配置service -->
<bean id="accountService" class="com.coydone.service.impl.AccountServiceImpl">
    <!-- 注入dao -->
    <property name="accountDao" ref="accountDao"></property>
</bean>

<!-- 配置Dao对象 -->
<bean id="accountDao" class="com.coydone.dao.impl.AccountDaoImpl">
    <!-- 注入QueryRunner -->
    <property name="qr" ref="qr"></property>
</bean>

2、将对象交给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
        http://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 告知spring在创建容器时要扫描的包 -->
    <context:component-scan base-package="com.coydone"></context:component-scan>

3、在dao实现类和service实现类中使用注解管理QueryRunner对象。此时不需要提供set方法。

// 账户的持久层实现类
@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {
    @Autowired
    private QueryRunner qr;
}
//账户的业务层实现类
@Service("accountService")
public class AccountServiceImpl implements AccountService {
    @Autowired
    private AccountDao accountDao;
}

使用配置类取代XML

Spring中的新注解

@Configuration

  • 作用:指定当前类是一个配置类。

  • 细节:当配置类作为AnnotationConfigApplicationContext对象创建的参数时,该注解可以不写。

@ComponentScan

  • 作用:用于通过注解指定Spring在创建容器时要扫描的包。

  • 属性:value,它和basePackages的作用一样,都是用于指定创建容器时要扫描的包,

  • 使用此注解就等同于在xml中配置了<context:component-scan base-package="com.coydone"></context:component-scan>

@Bean

  • 作用:用于把当前方法的返回值作为bean对象存入Spring的IoC容器中。

  • 属性:name:用于指定bean的id,当不写时,默认值是当前方法的名称。

  • 细节:当我们使用注解配置方法时,如果方法有参数,Spring框架会去容器中查找有没有可用的bean对象。

  • 查找的方式和@Autowired注解的作用是一样的。

@Import

  • 作用:用于导入其它的配置类。

  • 属性:value:用于指定其它配置类的字节码。

  • 当我们使用Import注解之后,有Import注解的类就是父配置类,而导入的都是子配置类。

@PropertySource

  • 作用:用于指定properties文件的位置。

  • 属性:value:指定文件的名称和路径。

  • 关键字:classpath:表示类路径下。

使用注解类改造代码

此时bean.xml中的配置还有:

<!-- 告知spring在创建容器时要扫描的包 -->
<context:component-scan base-package="com.coydone"></context:component-scan>
<!-- 配置QueryRunner -->
<bean id="qr" 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.jdbc.Driver"></property>
    <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/eesy_mybatis"></property>
    <property name="user" value="root"></property>
    <property name="password" value="root123"></property>
</bean>

通过配置类的形式取代:

package config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;
//该类是一个配置类,和bean.xml的作用一样
//主配置类
@Configuration
@ComponentScan("com.coydone")
@Import(JdbcConfig.class)
@PropertySource("classpath:jdbcConfig.properties")
public class SpringConfiguration {

}
package config;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;

import javax.sql.DataSource;

//和spring连接数据库相关的配置类
public class JdbcConfig {
    @Value("${jdbc.driver}")
    private String driver;

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

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

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

    //用于创建一个QueryRunner对象
    @Bean(name="runner")
    @Scope("prototype")
    public QueryRunner createQueryRunner(@Qualifier("ds1") DataSource dataSource){
        return new QueryRunner(dataSource);
    }

    //创建数据源对象
    @Bean(name="ds1")
    public DataSource createDataSource(){
        try {
            ComboPooledDataSource ds = new ComboPooledDataSource();
            ds.setDriverClass(driver);
            ds.setJdbcUrl(url);
            ds.setUser(username);
            ds.setPassword(password);
            return ds;
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }
}

在resources下创建jdbcConfig.properties

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/eesy_mybatis
jdbc.username=root
jdbc.password=root123

我们可以删除bean.xml文件进行测试,测试方法中的通过xml方式获取容器要改成通过注解方式获取容器。

//1.获取容器
//ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);

Spring整合Junit改进测试

应用程序的入口是main方法。Junit单元测试中,没有main方法也能执行,因为junit集成了一个main方法,该方法就会判断当前测试类中哪些方法有@Test注解,Junit就会让有Test注解的方法执行。

按照以前的方法添加一个@Before注解。可以处理冗余的代码。

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

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

而如果是测试工程师,那么我们Before中的代码只能由我们开发人员编写,测试人员可能不会。上面的例子,我们想使用Spring的注解。

而Junit不会管我们是否使用Spring框架,在执行测试方法时,Junit根本不知道我们是不是使用了Spring框架,所以也不会为我们读取配置文件/配置类创建Spring核心容器。

所以,当测试方法执行时,没有IoC容器,就算写了Autowired注解,也无法实现注入。

@Autowired
private AccountService as;

我们需要使用Spring整合Junit的依赖进行测试方法的改造。

1、导入Spring整合Junit的依赖。

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

2、使用Junit提供的Runwith注解把原有的main方法替换成Spring提供的。

3、告知Spring的运行器,Spring的IoC创建是基于xml还是注解的,并且说明位置。使用注解ContextConfiguration。属性有:

  • locations:指定xml文件的位置,加上classpath关键字,表示在类路径下。

  • classes:指定注解类所在的位置。

注意:当我们使用spring5.x版本的时候,要求Junit的Jar必须是4.12及以上。

import java.util.List;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)
public class AccountServiceTest {
    @Autowired
    private AccountService as;
    @Test
    public void testFindAll(){
        //3.执行方法
        List<Account> accounts = as.findAll();
        for (Account account : accounts) {
            System.out.println(account);
        }
    }
}
0

评论区