一、完全注解实现
1、说明
* spring的新注解:
* Configuration:
* 作用:指定当前类是一个配置类;
* ComponentScan:
* 作用:指定spring在创建容器时要扫描的包;
* 属性:
* value:它和basePackages的作用是一致的,都是指定spring在创建容器时要扫描的包;
* 备注:我们使用此注解就等同于在xml中配置:<context:component-scan base-package="com.zibo"/>
* Bean:
* 作用:用于把当前方法的返回值作为bean对象存入spring的ioc容器;
* 属性:name用于指定bean的id,当不写时,默认值为当前方法的名称;
* 细节:当我们使用注解配置方法时,如果方法有参数,spring框架去容器中查找有没有可用的bean对象;
* 查找的方式和Autowired注解的作用是一致的;
2、代码示例
SpringConfiguration类:
package com.zibo.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.beans.PropertyVetoException;
/**
* 该类是一个配置类,其作用于bean.xml相同
* spring的新注解:
* Configuration:
* 作用:指定当前类是一个配置类;
* ComponentScan:
* 作用:指定spring在创建容器时要扫描的包;
* 属性:
* value:它和basePackages的作用是一致的,都是指定spring在创建容器时要扫描的包;
* 备注:我们使用此注解就等同于在xml中配置:<context:component-scan base-package="com.zibo"/>
* Bean:
* 作用:用于把当前方法的返回值作为bean对象存入spring的ioc容器;
* 属性:name用于指定bean的id,当不写时,默认值为当前方法的名称;
* 细节:当我们使用注解配置方法时,如果方法有参数,spring框架去容器中查找有没有可用的bean对象;
* 查找的方式和Autowired注解的作用是一致的;
*/
@Configuration
@ComponentScan(basePackages = "com.zibo")//可省略为@ComponentScan("com.zibo")
public class SpringConfiguration {
//用于创建QueryRunner对象
@Bean(name = "runner")
@Scope("prototype")
public QueryRunner createQueryRunner(DataSource dataSource){
return new QueryRunner(dataSource);
}
//用于创建数据源对象
@Bean(name = "dataSource")
public DataSource createDataSource(){
try {
ComboPooledDataSource ds = new ComboPooledDataSource();
ds.setDriverClass("com.mysql.cj.jdbc.Driver");
ds.setJdbcUrl("jdbc:mysql://localhost:3306/zibo?serverTimezone=UTC");
ds.setUser("root");
ds.setPassword("zibo15239417242");
return ds;
} catch (PropertyVetoException e) {
throw new RuntimeException(e);
}
}
}
AccountServiceTest类:
package com.zibo.test;
import com.zibo.config.SpringConfiguration;
import com.zibo.domain.Account;
import com.zibo.service.IAccountService;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.List;
/**
* 使用junit单元测试进行测试
*/
public class AccountServiceTest {
private ApplicationContext ac;
private IAccountService as;
@Before
public void init(){
//1、获取容器
// ac = new ClassPathXmlApplicationContext("bean.xml");
ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
//2、得到业务层对象
as = ac.getBean("accountService", IAccountService.class);
}
@Before
public void end(){
// ac.close();
}
@Test
public void testFindAllAccount(){
//3、执行方法
List<Account> accounts = as.findAllAccount();
//4、遍历输出
for (Account account : accounts) {
System.out.println(account);
}
}
@Test
public void testFindAccountById(){
Account account = as.findAccountById(1);
System.out.println(account);
}
@Test
public void testSave(){
Account account = new Account();
account.setName("大哥");
account.setMoney(2000);
as.saveAccount(account);
}
@Test
public void testUpdate(){
Account account = new Account();
account.setId(3);
account.setName("二哥");
account.setMoney(3000);
as.updateAccount(account);
}
@Test
public void testDelete(){
as.deleteAccountById(1);
}
}
QueryRunnerTest类:
package com.zibo.test;
import com.zibo.config.SpringConfiguration;
import org.apache.commons.dbutils.QueryRunner;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class QueryRunnerTest {
@Test
public void testQueryRunnerTest(){
ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
QueryRunner runner1 = ac.getBean("runner", QueryRunner.class);
QueryRunner runner2 = ac.getBean("runner", QueryRunner.class);
System.out.println(runner1 == runner2);//true,由此可见,默认是单例,加注解@Scope("prototype")后边多例
}
}
文件结构图:
其他文件:
二、问题发现和解决
1、问题
截图:
说明:
配置信息写在代码里岂不是写死了,应该写在配置文件中;
优化后的代码:
SpringConfiguration配置类:
package com.zibo.config;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.springframework.context.annotation.*;
import javax.sql.DataSource;
import java.beans.PropertyVetoException;
/**
* 该类是一个配置类,其作用于bean.xml相同
* spring的新注解:
* Configuration:
* 作用:指定当前类是一个配置类;
* ComponentScan:
* 作用:指定spring在创建容器时要扫描的包;
* 属性:
* value:它和basePackages的作用是一致的,都是指定spring在创建容器时要扫描的包;
* 备注:我们使用此注解就等同于在xml中配置:<context:component-scan base-package="com.zibo"/>
* Bean:
* 作用:用于把当前方法的返回值作为bean对象存入spring的ioc容器;
* 属性:name用于指定bean的id,当不写时,默认值为当前方法的名称;
* 细节:当我们使用注解配置方法时,如果方法有参数,spring框架去容器中查找有没有可用的bean对象;
* 查找的方式和Autowired注解的作用是一致的;(可以手动指定@Qualifier("数据源名字"))
* Import:
* 作用:用于导入其他的配置类;
* 属性:value用于指定其他配置类的字节码,当我们使用Import注解的时候,有Import注解的类是父配置类,
* 导入的类是子配置类;
*
* PropertySource:
* 作用:用于指定properties文件的位置;
* 属性:value指定文件的名称和路径,关键字classpath表示类路径下
*/
@Configuration
@ComponentScan(basePackages = "com.zibo")//可省略为@ComponentScan("com.zibo")
@Import(JdbcConfig.class)
@PropertySource("classpath:jdbcConfig.properties")
public class SpringConfiguration {
}
JdbcConfig类:
package com.zibo.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.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import javax.sql.DataSource;
import java.beans.PropertyVetoException;
@Configuration
@ComponentScan(basePackages = "com.zibo")//可省略为@ComponentScan("com.zibo")
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(DataSource dataSource){
return new QueryRunner(dataSource);
}
//用于创建数据源对象
@Bean(name = "dataSource")
public DataSource createDataSource(){
try {
ComboPooledDataSource ds = new ComboPooledDataSource();
ds.setDriverClass(driver);
ds.setJdbcUrl(url);
ds.setUser(username);
ds.setPassword(password);
return ds;
} catch (PropertyVetoException e) {
throw new RuntimeException(e);
}
}
}
jdbcConfig.properties配置文件:
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/zibo?serverTimezone=UTC
jdbc.username=root
jdbc.password=*******
说明:
纯注解并没有比半注解省事,所以在自己选择时优选半注解;
2、 数据源匹配
图示:
3、spring整合junit问题分析和解决
问题分析:
应用程序的入口:
main方法;
junit单元测试中,没有main方法也能执行:
因为junit继承了一个main方法,这个main方法会判断当前测试类中有@Test注解的方法,并使其执行;
junit不管我们是否使用了spring框架:
不会读取配置文件/配置类来创建spring容器;
结论:
当测试放大执行时,没有Ioc容器,就算写了Autowired注解,也无法实现注入;
问题解决:
导入坐标:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
修改AccountServiceTest代码:
package com.zibo.test;
import com.zibo.config.SpringConfiguration;
import com.zibo.domain.Account;
import com.zibo.service.IAccountService;
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.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.List;
/**
* 使用junit单元测试进行测试
* spring整合junit配置:
* 1、导入spring整合junit的jar(坐标);
* 2、使用junit提供的注解,把原有的main方法替换成spring提供的@Runwith;
* 3、告知spring的运行器,spring的ioc创建是基于xml还是注解,并说明其位置,使用@ContextConfiguration;
* ContextConfiguration:
* locations:指定xml文件的位置,加上classpath关键字,表示在类路径下;
* classes:指定注解类所在的位置;
* 备注:当我们使用5.x版本的时候,junit的jar必须是4.12以上;
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)
public class AccountServiceTest {
// private ApplicationContext ac;
@Autowired
private IAccountService as;
@Before
public void init(){
//1、获取容器
// ac = new ClassPathXmlApplicationContext("bean.xml");
// ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
//2、得到业务层对象
// as = ac.getBean("accountService", IAccountService.class);
}
@Before
public void end(){
// ac.close();
}
@Test
public void testFindAllAccount(){
//3、执行方法
List<Account> accounts = as.findAllAccount();
//4、遍历输出
for (Account account : accounts) {
System.out.println(account);
}
}
@Test
public void testFindAccountById(){
Account account = as.findAccountById(1);
System.out.println(account);
}
@Test
public void testSave(){
Account account = new Account();
account.setName("大哥");
account.setMoney(2000);
as.saveAccount(account);
}
@Test
public void testUpdate(){
Account account = new Account();
account.setId(3);
account.setName("二哥");
account.setMoney(3000);
as.updateAccount(account);
}
@Test
public void testDelete(){
as.deleteAccountById(1);
}
}