一、完全註解實現
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);
}
}