Java之spring学习注解Ioc

使用spring的Ioc的实现账户的CRUD

需求和技术要求

需求

实现账户的CRUD操作

技术要求

使用spring的IoC实现对象的管理
使用DBAssit作为持久层解决方案
使用c3p3数据源

准备

在这里插入图片描述

创建项目,并引入依赖

1、创建一个maven项目,不使用骨架
2、编写pom.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>com.oceanstar</groupId>
    <artifactId>spring_04</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>org.springframework</groupId>
            <artifactId>spring-test</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>

实现

创建数据库并编写实体类

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('aaa',1000);
insert into account(name,money) values('bbb',1000);
insert into account(name,money) values('ccc',1000);
package com.oceanstar.domain;

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.oceanstar.dao;

import com.oceanstar.domain.Account;

import java.util.List;

/*
* 持久层接口定义
* */
public interface IAccountDao {
    /**
     * 查询所有
     * @return
     */
    List<Account> findAllAccount();

    /**
     * 查询一个
     * @return
     */
    Account findAccountById(Integer acccountId);

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

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


    /**
     * 删除
     * @param acccountId
     */
    void deleteAccount(Integer acccountId);
}

package com.oceanstar.dao.impl;

import com.oceanstar.dao.IAccountDao;
import com.oceanstar.domain.Account;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.springframework.stereotype.Component;

import java.sql.SQLException;
import java.util.List;

/*
 * 持久层接口实现
 * */
public class AccountDaoImpl implements IAccountDao {
    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 (SQLException e){
            throw new RuntimeException(e);
        }

    }

    public Account findAccountById(Integer acccountId) {
        try {
            return runner.query("SELECT * FROM account where id = ?",
                    new BeanHandler<Account>(Account.class), acccountId);
        }catch (SQLException e){
            throw new RuntimeException(e);
        }
    }


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

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

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

编写业务层代码

package com.oceanstar.service;

import com.oceanstar.domain.Account;

import java.util.List;

/*
* 业务实现接口
* */
public interface IAccountService {

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

    /**
     * 查询一个
     * @return
     */
    Account findAccountById(Integer acccountId);

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

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


    /**
     * 删除
     * @param acccountId
     */
    void deleteAccount(Integer acccountId);
}

package com.oceanstar.service.impl;

import com.oceanstar.dao.IAccountDao;
import com.oceanstar.domain.Account;
import com.oceanstar.service.IAccountService;

import java.util.List;

/*
* 业务实现类
* */
public class AccountServiceImpl implements IAccountService {
    private IAccountDao iAccountDao;

    public void setAccountDao(IAccountDao iAccountDao) {
        this.iAccountDao = iAccountDao;
    }

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

    public Account findAccountById(Integer acccountId) {
        return iAccountDao.findAccountById(acccountId);
    }

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

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

    public void deleteAccount(Integer acccountId) {
        iAccountDao.deleteAccount(acccountId);
    }
}

编写配置文件

要是是要引入这个类

<?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">

</beans>

具体内容如下

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

    <!--配置dao对象-->
    <bean id="accountDao" class="com.oceanstar.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.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test?serverTimezone=UTC"></property>
        <property name="user" value="root"></property>
        <property name="password" value="123456"></property>
    </bean>


</beans>

测试

编写测试类

package com.oceanstar.test;

import com.oceanstar.domain.Account;
import com.oceanstar.service.IAccountService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.List;

public class AccountServiceTest {

    @Test
    public void testFindAll() {
        //1、获取容器
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
        //2、得到业务层对象
        IAccountService accountService = (IAccountService) applicationContext.getBean("accountService");
        //3、执行方法
        List<Account>accounts = accountService.findAllAccount();
        for (Account account : accounts){
            System.out.println( account.toString());;
        }
    }

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


    @Test
    public void testSave() {
        //1、获取容器
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
        //2、得到业务层对象
        IAccountService accountService = (IAccountService) applicationContext.getBean("accountService");

        Account account = new Account();
        account.setName("oceanstar");
        account.setMoney(111111111f);
        accountService.saveAccount(account);
    }

    @Test
    public void testUpdate(){
        //1、获取容器
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
        //2、得到业务层对象
        IAccountService accountService =  applicationContext.getBean("accountService", IAccountService.class);
        //3、执行方法
        Account account = accountService.findAccountById(1);
        account.setMoney(20000f);
        accountService.updateAccount(account);

    }

    @Test
    public void testDelete(){
        //1、获取容器
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
        //2、得到业务层对象
        IAccountService accountService =  applicationContext.getBean("accountService", IAccountService.class);
        //3、执行方法
        accountService.deleteAccount(1);
    }
}

分析

通过上面的测试类,可以看出,每个测试方法都重新获取了一次spring的核心容器,造成了不必要的重复代码,应该避免发生

        //1、获取容器
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
        //2、得到业务层对象
        IAccountService accountService = (IAccountService) applicationContext.getBean("accountService");

基于注解的IOC配置

明确

  • 注解配置和xml配置要实现的功能都是一样的,就是要降低程序间的耦合,只是实现的形式不一样。

  • 我们可以将上面的基于xml配置内容改为使用注解实现

常用注解

用于创建对象的

相当于:

<bean id="" class="">
@Component
  • 作用:把当前类对象存入spring容器中
  • 属性:value,指定bean的id。如果不指定value属性默认bean的id时当前类的类名(首字母小写)
@Controller,@Service,@Repository

这三个注解都是@Component的衍生注解,他们的作用以及属性是一模一样的。只是提供了更加明确的语义化。
@Controller:用于表现层的注解。
@Service:用于业务层的注解
@Repository:用于持久层的注解

用于注入数据的

相当于

<property name="" ref="">
<property name="" value="">
@Autowired
  • 作用:自动按照类型注入。
    如果没有

  • 出现位置:可以是成功变量上,也可以是方法商。

当使用注解注入属性时,set方法可以省略。它只能注入其他bean类型,当有多个类型匹配时,使用要注入的对象变量名称作为bean的id,在spring容器查找,找到了也可以注入成功,找不到就报错。

如果没有bean对象和要注入的变量类型匹配,直接报错

只要容器中有唯一的一个bean对象和要注入的变量类型匹配,就可以注入成功。
在这里插入图片描述
如果容器中有多个bean对象和要注入的变量类型匹配,先按照类型找到匹配的对象,然后使用变量名称来匹配
在这里插入图片描述

@Qualifier
  • 作用:在自动按照类型注入的基础上,再按照名称注入。它在给字段注入时不能独立使用,必须和@Autowire一起使用。但是给方法参数注入时,可以独立使用
  • 属性:value,指定bean的id。
@Resource
  • 作用:直接按照bean的id注入。它也只能注入其他bean类型。
  • 属性:指定bean的id

以上的三个注解都只能注入其他bean类型的数据,而基本类型和string类型无法使用上述注解实现。
另外:集合类型的注入只能通过xml来实现。

 @Autowired //java.lang.NullPointerException
    @Qualifier("accountDao")

    上面两个注解相当于
    @Resource(name = "accountDao")
@Value
  • 作用:注入基本数据类。他可以使用spring中spel表达式。

指定改变作用范围的。

相当于

<bean id="" class="" scope="">
@Scope
  • 作用:指定bean的作用范围。默认是单例的
  • 属性:value,取值singleton, prototype,request session, golbalsession

和生命周期相关的

相当于

<bean id="" class="" init-method="" destory-method="">
@PostConstruct
  • 作用:指定初始化方法
@PreDestory
  • 作用:用于执行销毁方法

关于spring注解和xml的选择问题

  • 注解的优势:配置简单,维护方便

  • xmk的优势:修改时,不用改源码。不涉及重新编译和不是。

  • spring管理bean方式比较
    在这里插入图片描述

环境搭建

使用@Component注解配置管理的资源

package com.oceanstar.service.impl;

import com.oceanstar.dao.IAccountDao;
import com.oceanstar.domain.Account;
import com.oceanstar.service.IAccountService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.List;

/*
* 业务实现类
* */
@Service("accountService")
//@Scope("singleton")
public class AccountServiceImpl implements IAccountService {

//    @Autowired //java.lang.NullPointerException
//    @Qualifier("accountDao")
    @Resource(name = "accountDao")
    private IAccountDao iAccountDao;


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

package com.oceanstar.dao.impl;

import com.oceanstar.dao.IAccountDao;
import com.oceanstar.domain.Account;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import java.sql.SQLException;
import java.util.List;

/*
 * 持久层接口实现
 * */
@Repository("accountDao")
public class AccountDaoImpl implements IAccountDao {

    @Autowired
    private QueryRunner runner;



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

    }

    public Account findAccountById(Integer acccountId) {
        try {
            return runner.query("SELECT * FROM account where id = ?",
                    new BeanHandler<Account>(Account.class), acccountId);
        }catch (SQLException e){
            throw new RuntimeException(e);
        }
    }
}

创建spring的xml配置文件并开启对注解的支持

基于注解整合时,导入约束时需要导入一个context名称空间下的约束

<?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">


    <!--告知sprring在创建容器时要扫描的包-->
    <context:component-scan base-package="com.oceanstar"></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.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test?serverTimezone=UTC"></property>
        <property name="user" value="root"></property>
        <property name="password" value="123456"></property>
    </bean>


</beans>

spring管理对象细节

基于注解的spring Ioc配置中,bean对象的特点和基于xml配置是一模一样的。

待改造的问题

我们之所以现在离不开xml配置文件,是因为我们需要告警spring框架在读取配置文件创建容器时,要告诉扫描哪个包里面的注解来创建对象·,并存入文件中。

另外,数据源和JdbcTemplate的配置也需要注解实现。

工程目录

在这里插入图片描述

新注解说明

@Configuration
  • 作用:指定当前类是一个配置类
  • 细节:当配置类作为AnnotationConfigApplicationContext对象创建的参数时,该注解可以不写。
package config;

import org.springframework.context.annotation.Configuration;

/*
* 该类时一个配置类,它的作用和bean.xml是一样的。
* */
@Configuration
public class SpringConfiguration {
}

下一步是告知需要扫描哪个包

@ComponentScan
  • 作用:通过注解指定spring在创建容器时要扫描的包。
package config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

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

下一步移除数据源和JdbcTemplate对象

@Bean
  • 作用:用于把当前方法的返回值作为bean对象存入spring的ioc容器中。
  • 属性: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 javax.sql.DataSource;
import java.beans.PropertyVetoException;

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

    /*
    * 用于创建一个QueryRunner对象
    * @param dataSource
    * @return
    * */
    @Bean(name = "runner")
    @Scope("prototype")
    public QueryRunner ceateQueryRunner(DataSource dataSource){
        return new QueryRunner(dataSource);
    }
    /*
     * 用于创建一个数据源对象
     * @param dataSource
     * @return
     * */
    @Bean(name = "dataSource")
    public DataSource createDataSource(){
        try {
            ComboPooledDataSource dataSource = new ComboPooledDataSource();
            dataSource.setDriverClass("com.mysql.jdbc.Driver");
            dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test?serverTimezone=UTC");
            dataSource.setUser("root");
            dataSource.setPassword("123456");
            return dataSource;
        } catch (PropertyVetoException e) {
            throw  new RuntimeException(e);
        }
    }
}

好了,现在bean.xml已经没有什么用了,但是我们当前是通过xml来获取容器的,但是现在我们如何通过注解来获取容器了?

补充:测试runner是单例还是多例

package com.oceanstar.test;

import config.SpringConfiguration;
import org.apache.commons.dbutils.QueryRunner;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/*
* 测试query是否单例
* */
public class QueryRunnerTest {

    @Test
    public void testQueryQunner(){
        //1、获取容器
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfiguration.class);
        //2、得到queryQunner对象
        QueryRunner queryRunner = applicationContext.getBean("runner", QueryRunner.class);
        QueryRunner queryRunner1 = applicationContext.getBean("runner", QueryRunner.class);

        System.out.println(queryRunner == queryRunner1);
    }
}

通过注解获取容器

修改测试文件

    @Test
    public void testFindAll() {
        //1、获取容器
      //  ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfiguration.class);
        //2、得到业务层对象
        IAccountService accountService = (IAccountService) applicationContext.getBean("accountService");

//        //3、执行方法
        List<Account>accounts = accountService.findAllAccount();
        for (Account account : accounts){
            System.out.println( account.toString());;
        }
    }

以此修改,我们就可以删除bean.xml了。

再次修改

@Configuration
  • 细节:当配置类作为AnnotationConfigApplicationContext对象创建的参数时,该注解可以不写。
    在这里插入图片描述
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.Configuration;
import org.springframework.context.annotation.Scope;

import javax.sql.DataSource;
import java.beans.PropertyVetoException;

@Configuration
public class JdbcConfiguration {
    /*
     * 用于创建一个QueryRunner对象
     * @param dataSource
     * @return
     * */
    @Bean(name = "runner")
    @Scope("prototype")
    public QueryRunner ceateQueryRunner(DataSource dataSource){
        return new QueryRunner(dataSource);
    }
    /*
     * 用于创建一个数据源对象
     * @param dataSource
     * @return
     * */
    @Bean(name = "dataSource")
    public DataSource createDataSource(){
        try {
            ComboPooledDataSource dataSource = new ComboPooledDataSource();
            dataSource.setDriverClass("com.mysql.jdbc.Driver");
            dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test?serverTimezone=UTC");
            dataSource.setUser("root");
            dataSource.setPassword("123456");
            return dataSource;
        } catch (PropertyVetoException e) {
            throw  new RuntimeException(e);
        }
    }
}

package config;


import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;




/*
* 该类时一个配置类,它的作用和bean.xml是一样的。
* */
@Configuration
@ComponentScan(basePackages = {"com.oceanstar", "config"})
public class SpringConfiguration {

}

如果想要去掉Configuration, 则测试类中修改为【不推荐】

 ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfiguration.class, JdbcConfiguration.class);
@Import
  • 作用:用于导入其他配置类
  • 属性:value,用于指定其他配置类的字节码
    当我们使用import的注解之后,有import注解的类就是父配置类,而导入的都是子配置类。
/*
* 该类时一个配置类,它的作用和bean.xml是一样的。
* */
@ComponentScan(basePackages = {"com.oceanstar"})
@Import(JdbcConfiguration.class)
public class SpringConfiguration {

}

public class JdbcConfiguration {
    /*
     * 用于创建一个QueryRunner对象
     * @param dataSource
     * @return
     * */
    @Bean(name = "runner")
    @Scope("prototype")
    public QueryRunner ceateQueryRunner(DataSource dataSource){
        return new QueryRunner(dataSource);
    }
}
public class AccountServiceTest {

    @Test
    public void testFindAll() {
        //1、获取容器
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfiguration.class);
        //2、得到业务层对象
        IAccountService accountService = (IAccountService) applicationContext.getBean("accountService");

//        //3、执行方法
        List<Account>accounts = accountService.findAllAccount();
        for (Account account : accounts){
            System.out.println( account.toString());;
        }
    }
 }

再次修改

在这里插入图片描述
上面的写死了,是不太好的。我们需要修改他

工程目录改为:
在这里插入图片描述

@PropertySource
  • 作用:用于加载.properties文件中的配置。
  • 属性:value[],用于指定properties文件位置。如果是在类路径下,需要协商classpath。

通过AnnotationConfigApplicationContext读取SpringConfiguration,通过@Import(JdbcConfiguration.class)建立SpringConfiguration和JdbcConfiguration的父子配置关系。

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;
import java.beans.PropertyVetoException;


@PropertySource("classpath:jdbc.properties")
public class JdbcConfiguration {

    @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对象
     * @param dataSource
     * @return
     * */
    @Bean(name = "runner")
    @Scope("prototype")
    public QueryRunner ceateQueryRunner(DataSource dataSource){
        return new QueryRunner(dataSource);
    }
    /*
     * 用于创建一个数据源对象
     * @param dataSource
     * @return
     * */
    @Bean(name = "dataSource")
    public DataSource createDataSource(){
        try {
            ComboPooledDataSource dataSource = new ComboPooledDataSource();
            dataSource.setDriverClass(driver);
            dataSource.setJdbcUrl(url);
            dataSource.setUser(username);
            dataSource.setPassword(password);
            return dataSource;
        } catch (PropertyVetoException e) {
            throw  new RuntimeException(e);
        }
    }
}

dbc.properties内容

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test?serverTimezone=UTC
jdbc.username=root
jdbc.password=123456

srping整个Juint

测试类中的问题

在测试类中,每个测试方法都有一下两行代码:

这个代码的作用是获取容器,如果不写的话,直接会提示空指针异常。

解决方法一

package com.oceanstar.test;

import com.oceanstar.domain.Account;
import com.oceanstar.service.IAccountService;
import config.SpringConfiguration;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;


import java.util.List;

public class AccountServiceTest {
    private ApplicationContext applicationContext;
    private IAccountService accountService;

    @Before
    public void init(){
        //1、获取容器
        applicationContext = new AnnotationConfigApplicationContext(SpringConfiguration.class);
        //2、得到业务层对象
        accountService = (IAccountService) applicationContext.getBean("accountService");
    }
    @Test
    public void testFindAll() {
//        //3、执行方法
        List<Account>accounts = accountService.findAllAccount();
        for (Account account : accounts){
            System.out.println( account.toString());;
        }
    }

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


    @Test
    public void testSave() {
        Account account = new Account();
        account.setName("aaa");
        account.setMoney(111111111f);
        accountService.saveAccount(account);
    }

    @Test
    public void testUpdate(){
        //3、执行方法
        Account account = accountService.findAccountById(5);
        account.setMoney(20000f);
        accountService.updateAccount(account);

    }

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

解决方法二

思路分析:

1、应用程序的入口:

  • main方法

2、junit单元测试中,没有main方法也能执行

  • junit集成了一个main方法
  • 该方法会判断当前测试类中哪些方法有@Test注解
  • juint就让有Test注解的方法执行

3、juint不会管我们是否采用spring框架

  • juint根本不知道我们是不是使用了spring框架
  • 所以也就不会为我们读取配置文件/配置类吵架呢spring容器

4、由上面可知:

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

配置步骤

引入junit包的jar坐标
     <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
使用@RunWith注解替换原有运行器

使用juint提供的一个注解把原有的main方法替换了,替换成spring提供的@RunWith

@RunWith(SpringJUnit4ClassRunner.class)
public class AccountServiceTest {

}    
使用@ContextConfiguration指定spring配置文件的位置

告知spring的运行器,spring和ioc创建是基于xml还是注解的,

  • locations属性:指定配置文件的位置。如果是类路径下,需要用classpath:表明
  • classes属性:用于指定注解的类。
@ContextConfiguration(locations = "classpath:bean.xml")
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)
public class AccountServiceTest {
}
使用@Autowired给测试类中的变量注入数据
package com.oceanstar.test;

import com.oceanstar.domain.Account;
import com.oceanstar.service.IAccountService;
import config.SpringConfiguration;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;


import java.util.List;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)
public class AccountServiceTest {
    @Autowired
    private ApplicationContext applicationContext;
    @Autowired
    private IAccountService accountService;

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

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



}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值