前面一篇文章花大量內容,重點學習了 Spring入門 的一些思想,以及簡單的學習了 IOC基礎 以及基於基於 XML 的配置方式,你們應該清楚,XML與註解經常是如影隨行的,他們就像一對雙胞胎,但兄弟兩個的想法都是一致的,那就是幫助開發者實現想要的功能,咱們所說的IOC 技術,無疑是爲了下降程序間的耦合,那麼,今天就來聊一聊,基於註解的IOC配置,固然爲了你們有對比學習,兩種配置同時講解,同時我把例舉得儘可能完整一些,就來完成一個對單表進行 CURD 的案例java
說明:因爲我這裏建立的是一個Maven項目,因此在這裏修改 pom.xml 添加一些必要的依賴座標就能夠mysql
若是建立時沒有使用依賴的朋友,去下載咱們所須要的 jar 包導入就能夠了spring
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.2.RELEASE</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>commons-dbutils</groupId> <artifactId>commons-dbutils</artifactId> <version>1.4</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> </dependencies>
簡單看一下,spring核心的一些依賴,以及數據庫相關的依賴等就都導入進來了sql
<div align="center">
<img src="https://user-gold-cdn.xitu.io/2020/2/24/1707618a159a64af?w=535&h=368&f=png&s=152529" style="zoom:80%">
</div>數據庫
-- ---------------------------- -- Table structure for account -- ---------------------------- CREATE TABLE `account` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(32), `balance` float, PRIMARY KEY (`id`) )
沒什麼好說的,對應着咱們的表創出實體apache
public class Account implements Serializable { private Integer id; private String name; private Float balance; ......補充 get set toString 方法
public interface AccountService { void add(Account account); void delete(Integer accpuntId); void update(Account account); List<Account> findAll(); Account findById(Integer accountId); }
public class AccountServiceImpl implements AccountService { private AccountDao accountDao; public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } public void add(Account account) { accountDao.addAccount(account); } public void delete(Integer accpuntId) { accountDao.deleteAccount(accpuntId); } public void update(Account account) { accountDao.updateAccount(account); } public List<Account> findAll() { return accountDao.findAllAccount(); } public Account findById(Integer accountId) { return accountDao.findAccountById(accountId); } }
public interface AccountDao { void addAccount(Account account); void deleteAccount(Integer accountId); void updateAccount(Account account); List<Account> findAllAccount(); Account findAccountById(Integer accountId); }
因爲今天要完成的是一個增刪改查的操做,因此咱們引入了 DBUtils 這樣一個操做數據庫的工具,它的做用就是封裝代碼,達到簡化 JDBC 操做的目的,因爲之後整合 SSM 框架的時候,持久層的事情就能夠交給 MyBatis 來作,而今天咱們重點仍是講解 Spring 中的知識,因此這部分會用就能夠了,重點看 XML 與 註解 兩種配置方式安全
用到的內容基本講解:微信
QueryRunner 提供對 sql 語句進行操做的 API (insert delete update)session
ResultSetHander 接口,定義了查詢後,如何封裝結果集(僅提供了咱們用到的)框架
public class AccountDaoImpl implements AccountDao { private QueryRunner runner; public void setRunner(QueryRunner runner) { this.runner = runner; } public void addAccount(Account account) { try { runner.update("insert into account(name,balance)values(?,?)", account.getName(), account.getBalance()); } catch (Exception e) { throw new RuntimeException(e); } } public void updateAccount(Account account) { try { runner.update("update account set name=?,balance=? where id=?", account.getName(), account.getBalance(), account.getId()); } catch (Exception e) { throw new RuntimeException(e); } } public void deleteAccount(Integer accountId) { try { runner.update("delete from account where id=?", accountId); } catch (Exception e) { throw new RuntimeException(e); } } public List<Account> findAllAccount() { try { return runner.query("select * from account", new BeanListHandler<Account>(Account.class)); } catch (Exception e) { throw new RuntimeException(e); } } public Account findAccountById(Integer accountId) { try { return runner.query("select * from account where id = ? ", new BeanHandler<Account>(Account.class), accountId); } catch (Exception e) { throw new RuntimeException(e); } } }
在這裏有兩基本的方式,一是經過構造函數注入,另外一種就是經過Set注入,實際上所作的就是,使用類的構造函數或者Set給成員變量進行賦值,但特別的是,這裏是經過配置,使用 Spring 框架進行注入首先就是頭部的依賴信息,順便提一句,固然咱們能夠去官網查找貼過來
先把針對上面功能的具體配置代碼貼出來
<?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="cn.ideal.service.impl.AccountServiceImpl"> <property name="accountDao" ref="accountDao"></property> </bean> <!--配置Dao--> <bean id="accountDao" class="cn.ideal.dao.impl.AccountDaoImpl"> <property name="runner" ref="runner"></property> </bean> <!--配置 QueryRunner--> <bean id="runner" class="org.apache.commons.dbutils.QueryRunner"> <!--注入數據源--> <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/ideal_spring"></property> <property name="user" value="root"></property> <property name="password" value="root99"></property> </bean> </beans>
分析一下:
配置 Bean 標籤的時候,咱們見到了兩種形式 property、constructor-arg 也就是對應着 set 方式 與構造函形式,先說一下比較常見的 set 方式,用上面的代碼中距離:
顧名思義,就是經過去找你給出對應的 Set 方法,而後對成員變量進行賦值,先看下類中的代碼
public class AccountServiceImpl implements AccountService { //成員 private AccountDao accountDao; //Set方法 public void setAccountDao(AccountDao accountDao) { this.accountDao = accountDao; } ...... 下面是增刪改查的方法 }
這是 bean.xml 中的配置
<!--配置Service--> <bean id="accountService" class="cn.ideal.service.impl.AccountServiceImpl"> <property name="accountDao" ref="accountDao"></property> </bean>
而後 property 配置的過程當中,有一些屬性須要說一下
固然,之後可能會見到一種方式就是 使用 p名稱空間注入數據 (本質仍是set)
頭部中須要修改引入這一句
xmlns:p="http://www.springframework.org/schema/p"
我直接拿之前文章中的一個例子:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:p="http://www.springframework.org/schema/p" 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"> <bean id="accountService" class="cn.ideal.service.impl.AccountServiceImpl" p:name="湯姆" p:age="21" p:birthday-ref="nowdt"/> <bean id="nowdt" class="java.util.Date"></bean> </beans>
下面就是使用構造函數的一種方式,這一種的前提就是:類中必須提供一個和參數列表相對應的構造函數
因爲咱們選擇的是 DBUtils 這樣一個工具,而它爲咱們提供了兩種構造函數,即帶參和無參,因此咱們能夠在其中注入數據源,也可使得每一條語句都獨立事務
還有一點須要說明的就是:咱們下面的數據源使用了 c3p0 這只是一種選擇方式,並非必定的,是由於使用 DBUtils 的時候須要手動傳遞一個 Connection 對象
<!--配置 QueryRunner--> <bean id="runner" class="org.apache.commons.dbutils.QueryRunner"> <!--注入數據源--> <constructor-arg name="ds" ref="dataSource"></constructor-arg> </bean>
來講一下所涉及到的標籤:
constructor-arg(放在 bean 標籤內) 再說一說其中的一些屬性值
給誰賦值:
賦什麼值:
爲了演示這些方式,咱們在一個實體成員中將常見的一些集合都寫出來,而後補充其 set 方法
private String[] strs; private List<String> list; private Set<String> set; private Map<String,String> map; private Properties props;
在配置中也是很簡單的,只須要按照下列格式寫標籤就能夠了,因爲增刪改查中並無涉及到集合的相關信息,這裏就是簡單提一下,能夠本身測試一下
<bean id="accountService" class="cn.ideal.service.impl.AccountServiceImpl"> <property name="strs"> <array> <value>張三</value> <value>李四</value> <value>王五</value> </array> </property> <property name="list"> <list> <value>張三</value> <value>李四</value> <value>王五</value> </list> </property> <property name="set"> <set> <value>張三</value> <value>李四</value> <value>王五</value> </set> </property> <property name="map"> <map> <entry key="name" value="張三"></entry> <entry key="age" value="21"></entry> </map> </property> <property name="props"> <props> <prop key="name">張三</prop> <prop key="age">21</prop> </props> </property> </bean>
public class AccountServiceTest { private ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml"); private AccountService as = ac.getBean("accountService", AccountService.class); @Test public void testAdd(){ Account account = new Account(); account.setName("jack"); account.setBalance(1000f); as.add(account); } @Test public void testUpdate(){ Account account = as.findById(4); account.setName("傑克"); account.setBalance(1500f); as.update(account); } @Test public void testFindAll(){ List<Account> list = as.findAll(); for(Account account : list) { System.out.println(account); } } @Test public void testDelete(){ as.delete(4); } }
添加,修改(包含了查詢指定id),刪除
<div align="center">
<img src="https://user-gold-cdn.xitu.io/2020/2/24/1707618a27d84ba4?w=259&h=153&f=png&s=5014" style="zoom:80%"> <img src="https://user-gold-cdn.xitu.io/2020/2/24/1707618a841155ed?w=266&h=156&f=png&s=5381" style="zoom:80%"> <img src="https://user-gold-cdn.xitu.io/2020/2/24/1707618adeefa5e7?w=263&h=136&f=png&s=4654" style="zoom:80%">
</div>
查詢全部
<div align="center">
<img src="https://user-gold-cdn.xitu.io/2020/2/24/1707618aeedd590b?w=437&h=90&f=png&s=31302" style="zoom:80%">
</div>
首先,咱們先將上面的例子使用註解來實現一下,再來具體的講解:
首先須要爲 Dao 和 Service 的實現類中 添加註解
@Service("accountService") public class AccountServiceImpl implements AccountService { @Autowired private AccountDao accountDao; 下面的原封不動 }
@Repository("accountDao") public class AccountDaoImpl implements AccountDao { @Autowired private QueryRunner runner; 下面的原封不動 }
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" 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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--開啓掃描--> <context:component-scan base-package="cn.ideal"></context:component-scan> <!--配置 QueryRunner--> <bean id="runner" class="org.apache.commons.dbutils.QueryRunner"> <!--注入數據源--> <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/ideal_spring"></property> <property name="user" value="root"></property> <property name="password" value="root99"></property> </bean> </beans>
到這裏,一個最基本的註解改造就完成了,你們能夠用前面的測試類進行一下測試
下面咱們正式說一下註解配置相關的知識
@Component
<bean id="" class=""
>@Controller @Service @Repository
對於建立對象的註解,Spring 還提供了三種更加明確的說法,做用是徹底相同的,可是針對不一樣的場景起了不一樣的叫法罷了
<property name="" ref="">
或者 <property name="" value="">
容器中有一個惟一的 bean 對象類型和注入的變量類型一致,則注入成功
@Autowired private AccountDao accountDao; @Repository("accountDao") public class AccountDaoImpl implements AccountDao {......}
@Autowired private AccountDao accountDaoA; @Repository("accountDaoA") public class AccountDaoImplA implements AccountDao {......} @Repository("accountDaoB") public class AccountDaoImplB implements AccountDao {......}
它有時候必須配合別的註解使用,有沒有一個標籤能夠解決這個問題呢?答案就是 @Resource
前面三個都是用來注入其餘的 bean 類型的數據,下面來講一說,基本類型以及String的實現
(特別說明:集合類型的注入只能經過 XML 來實現)
<bean id="" class="" scope
至關於:<bean id="" class="" init-method="" destroy-method="" />
通常來講,咱們兩種配置方式都是有人使用的,不過我我的更習慣使用註解的方式
XML
註解:
XML配置 | 註解配置 | |
---|---|---|
建立對象 | <bean id="" class="" > |
@Controller @Service @Repository@Component |
指定名稱 | 經過 id 或者 name 值指定 | @Controller("指定的名稱") |
注入數據 | <property name="" ref=""> |
@Autowired @Qualifier @Resource @Value |
做用範圍 | <bean id="" class="" scope> |
@Scope |
生命週期 | <bean id="" class="" init-method="" destroy-method=""/> |
@PostConstruct @PreDestroy |
爲何要補充新註解呢? 在咱們使用註解時,在書寫代碼時,簡化了不少,可是咱們在 bean.xml 文件中 仍然須要 開啓掃描、 進行配置QueryRunner 以及 數據源,如何完全擺脫 xml 配置全面使用註解呢?
這也就是咱們將要補充的幾個新註解,做用就是讓咱們全面使用註解進行開發
private ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
依舊使用上方的 CURD 的案例代碼進行修改,首先與cn同級建立了一個名爲 config 的包,而後編寫一個名爲 SpringConfiguration 的類,固然實際上這兩個名字無所謂的,添加註解
@Configuration public class SpringConfiguration { }
@Configuration 至關於已經幫咱們把 bean.xml 文件創立好了,按照咱們往常的步驟,應該指定掃描的包了,這也就是咱們這個註解的做用
<!--開啓掃描--> <context:component-scan base-package="cn.ideal"></context:component-scan>
具體使用:
@Configuration @ComponentScan("cn.ideal") public class SpringConfiguration { }
寫好了配置類,以及指定了掃描的包,下面該作的就是配置 QueryRunner 以及數據源了,在 XML 中咱們會經過書寫 bean 標籤來配置,而 Spring 爲咱們提供了 @Bean 這個註解來替代原來的標籤
具體使用:
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 javax.sql.DataSource; public class JdbcConfig { /** * 建立一個 QueryRunner對象 * @param dataSource * @return */ @Bean(name = "runner") public QueryRunner creatQueryRunner(DataSource dataSource){ return new QueryRunner(dataSource); } /** * 建立數據源,而且存入spring * @return */ @Bean(name = "dataSource") public DataSource createDataSource() { try { ComboPooledDataSource ds = new ComboPooledDataSource(); ds.setUser("root"); ds.setPassword("1234"); ds.setDriverClass("com.mysql.jdbc.Driver"); ds.setJdbcUrl("jdbc:mysql:///spring_day02"); return ds; } catch (Exception e) { throw new RuntimeException(e); } } }
上面在建立數據源的時候,都是直接把配置信息寫死了,若是想要使用 properties 進行內容的配置,在這時候就須要,使用 @PropertySource 這個註解
@Configuration @ComponentScan("cn.ideal") @PropertySource("classpath:jdbcConfig.properties") public class SpringConfiguration { }
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對象 * @param dataSource * @return */ @Bean(name = "runner") public QueryRunner creatQueryRunner(DataSource dataSource){ return new QueryRunner(dataSource); } /** * 建立數據源,而且存入spring * @return */ @Bean(name = "dataSource") public DataSource createDataSource() { try { ComboPooledDataSource ds = new ComboPooledDataSource(); ds.setUser(username); ds.setPassword(password); ds.setDriverClass(driver); ds.setJdbcUrl(url); return ds; } catch (Exception e) { throw new RuntimeException(e); } } }
這樣看來一個 JdbcConfig 就基本寫好了,咱們在其中配置了 QueryRunner 對象,以及數據源,這個時候,實際上咱們原先的 bean.xml 就能夠刪掉了,可是咱們雖然寫好了 JdbcConfig 可是如何將兩個配置文件聯繫起來呢?這也就是這個註解的做用
@Configuration @ComponentScan("cn.ideal") @Import(JdbcConfig.class) @PropertySource("classpath:jdbcConfig.properties") public class SpringConfiguration { }
修改獲取容器的方式後,就能夠進行測試了
private ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class); private AccountService as = ac.getBean("accountService", AccountService.class);
因爲咱們須要經過上面測試中兩行代獲取到容器,爲了避免每一次都寫這兩行代碼,因此咱們在前面將其定義在了成員位置,可是有沒有辦法能夠省掉這個步驟呢?
也就是說,咱們想要程序自動建立容器,可是原來的 junit 很顯然是實現不了的,由於它並不會知道咱們是否使用了 spring ,不過 junit 提供了一個註解讓咱們替換它的運行器,轉而由 spring 提供
首先須要導入 jar 包 或者說導入依賴座標
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.0.2.RELEASE</version> </dependency>
使用 @RunWith 註解替換原有運行器 而後使用 @ContextConfiguration 指定 spring 配置文件的位置,而後使用 @Autowired 給變量注入數據
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = SpringConfiguration.class) public class AccountServiceTest { @Autowired private AccountService as; }
到這裏,這篇文章就結束了,這篇文章主要講解的內容接着咱們上一篇文章講解,上一篇更多的是從傳統按部就班的提到 Spring,更多的是幫助你們理解爲何用Spring,而這篇文章是基於上一篇的基礎,更多的提到的是如何去用 Spring,因此看起來更像是一個工具書同樣的文章,並無提到不少的思想原理,而是幫助你們快速的上手用 XML 以及註解的方式,固然大神天然不少,不敢說什麼有技術,但總歸是但願能給不太熟悉 spring 的朋友一些幫助,或者臨時當作一篇工具文來查找,再次感謝你們的訪問與贊,謝謝朋友們的支持!再次感謝!
若是文章中有什麼不足,歡迎你們留言交流,感謝朋友們的支持!
若是能幫到你的話,那就來關注我吧!若是您更喜歡微信文章的閱讀方式,能夠關注個人公衆號
在這裏的咱們素不相識,卻都在爲了本身的夢而努力 ❤一個堅持推送原創開發技術文章的公衆號:理想二旬不止