使用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());
}
}