本文是作者原創(chuàng)文章狼纬,如需轉(zhuǎn)載晋柱,請(qǐng)先聯(lián)系
這幾天一直都在做spring+mybaits的整合項(xiàng)目砸讳,但是其中遇到很多問題琢融。所以現(xiàn)在開始重新學(xué)習(xí)一下spring,從最簡單的開始簿寂,下面演示一個(gè)demo漾抬。
工程目錄
本次demo的工程目錄如下:
注解:其中涂了紅色的請(qǐng)忽略,是做其他作用的常遂。包名為:justspring纳令,下面有兩個(gè)包分別是dao和service。dao用于模擬以后的數(shù)據(jù)庫操作克胳,service平绩,顧名思義是服務(wù)層。
這兩個(gè)包當(dāng)中各有兩個(gè)文件漠另,一個(gè)是接口(interface)一個(gè)是接口的實(shí)現(xiàn)(interfaceImpl)馒过。
詳細(xì)代碼(version 1.0)
dao(數(shù)據(jù)持久層)
//dao.java 文件名(下同)
package justspring.dao;
public interface Dao {
String sayHello();
}
//daoImpl.java
package justspring.dao;
public class DaoImpl implements Dao {
@Override
public String sayHello() {
return "hello by dao";
}
}
以上是dao層的代碼,因?yàn)檫@是一個(gè)demo酗钞,所以每個(gè)文件只有一個(gè)方法用于示范。在真實(shí)的環(huán)境當(dāng)中来累,dao層應(yīng)該是和數(shù)據(jù)庫打交道的砚作,dao只提供接口而具體的實(shí)現(xiàn)可以通過mybatis的sql映射來實(shí)現(xiàn),或者自己編寫數(shù)據(jù)庫的操作語句嘹锁。在這里葫录,因?yàn)槭莇emo所以采用了后者,并且沒有寫數(shù)據(jù)庫操作語句领猾。
service(服務(wù)層)
//service.java
package justspring.service;
public interface Service {
String sayHello();
String otherService();
}
//serviceImpl.java
package justspring.service;
import justspring.dao.Dao;
public class ServiceImpl implements Service {
private Dao dao;
public void setDao(Dao dao) {
this.dao = dao;
}
@Override
public String sayHello() {
return dao.sayHello();
}
@Override
public String otherService() {
return "other service";
}
}
服務(wù)米同,是用來承接控制層和數(shù)據(jù)層的骇扇,數(shù)據(jù)層取得的數(shù)據(jù)要在這里進(jìn)行各種邏輯操作。將service細(xì)分為兩小層:
- service 服務(wù)接口層
- serviceImpl 服務(wù)實(shí)現(xiàn)層
服務(wù)接口層很簡單面粮,就是將提供的服務(wù)逐一的列出來少孝,不管具體的實(shí)現(xiàn)。服務(wù)實(shí)現(xiàn)層熬苍,則是負(fù)責(zé)實(shí)現(xiàn)每一個(gè)服務(wù)稍走,這樣有利于后期的維護(hù)和升級(jí)。一般來說柴底,我們從數(shù)據(jù)庫查到的信息都是要拿來進(jìn)行各種邏輯操作的婿脸,這個(gè)操作的場所正是服務(wù)實(shí)現(xiàn)層。所以柄驻,等下會(huì)在服務(wù)實(shí)現(xiàn)層里面由spring注入dao接口狐树。既然如此為什么服務(wù)實(shí)現(xiàn)層不直接實(shí)現(xiàn)dao接口就好了呢?因?yàn)楹枧В嬲南到y(tǒng)里面還會(huì)有一些不依靠數(shù)據(jù)庫或者不同數(shù)據(jù)庫的操作抑钟,所以其他服務(wù)需要在服務(wù)接口層進(jìn)行聲明。
在這里答憔,最重要的就是這個(gè)serviceImpl了味赃。它負(fù)責(zé)實(shí)現(xiàn)服務(wù),我們可以看到這里面有一個(gè)Dao的對(duì)象虐拓,但是奇怪的是我們并沒有給它new任何的實(shí)例心俗。如果你對(duì)spring有一點(diǎn)了解的話就會(huì)知道,這里將會(huì)使用spring的依賴注入(Ioc)來實(shí)現(xiàn)了蓉驹。后面的spring的配置文件里會(huì)有說明城榛。
這里有兩個(gè)方法,其中的sayHello方法是使用了dao的方法态兴,otherService則可以想象成是一些不需要數(shù)據(jù)庫的服務(wù)狠持。
spring配置文件
相信熟悉spring的都會(huì)知道,spring需要有一個(gè)配置文件瞻润,主要就是用來配置bean的喘垂。所以,這個(gè)文件很重要绍撞,隨著spring的發(fā)展正勒,越來越多簡便的配置方法也出來了,很容易把人搞暈傻铣,我這次重新研究的重點(diǎn)就是想找到最簡便最清晰的配置方法章贞。但是,我還沒研究出來非洲,所以這里采用最原始的配置方法來演示鸭限。
//spring1.xml 這個(gè)名字大家可以隨便起
<?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">
<!--創(chuàng)建一個(gè)一個(gè)名字(可以用name或者id來表示蜕径,唯一標(biāo)識(shí),不可重復(fù))為dao的bean败京,class表示它的類(必須是類兜喻,不可以是接口或者抽象類)-->
<!--這個(gè)主要是用來等著被下面的bean使用的-->
<bean id="dao" class="justspring.dao.DaoImpl"/>
<!--創(chuàng)建一個(gè)名字為service,類型是ServiceImpl的bean喧枷,并且將這個(gè)bean注入到ServiceImpl.java的成員變量dao當(dāng)中-->
<!--property 當(dāng)中的name是成員變量的名字(需要保持一致)虹统,ref是指注入的是上面創(chuàng)建的bean-->
<bean id="service" class="justspring.service.ServiceImpl">
<property name="dao" ref="dao"/>
</bean>
</beans>
測試
做到這里就基本是萬事具備了,我們來測試一下是否真的成功了隧甚。為了不寫main函數(shù)那套這里用了Junit4來進(jìn)行车荔,需要在maven下添加一下依賴包。
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>testJustSpring</scope>
</dependency>
import justspring.service.Service;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class testJustSpring {
@Test
public void test() {
//引入spring的配置文件獲取上下文
ApplicationContext context = new ClassPathXmlApplicationContext("spring1.xml");
//通過上下文和bean的名字獲取service實(shí)例戚扳,就是剛才在配置文件下配置的第二個(gè)
//其實(shí)這里也可以通過依賴注入來實(shí)現(xiàn)忧便,不過需要寫setter代碼不太好看
Service service = (Service) context.getBean("service");
String string = service.sayHello();
System.out.println(string);
}
}
通過上面的代碼,我們可以得到的結(jié)果如下:
hello by dao
簡化spring配置過程
通過上面的配置帽借,成功地得到了想要的結(jié)果珠增。但是,如果bean很多的時(shí)候砍艾,我們都需要逐一的去配置就會(huì)顯得非常的麻煩蒂教。spring團(tuán)隊(duì)也一直致力于簡化.xml的配置步驟,這里簡述一下如何使用注解來簡化配置脆荷。
要想使用注解進(jìn)行xml配置凝垛,需要在xml當(dāng)中添加以下代碼:
<context:annotation-config/>
<context:component-scan base-package="justspring.service,justspring.dao"/>
以上第一行是開啟注解,第二行是掃描注解包蜓谋。如果有第二行代碼梦皮,第一行可以省略。
對(duì)于不同的層桃焕,spring提供了不同的注解剑肯。詳細(xì)的情況如下所示:
- dao層使用 @Repository,可以在其后添加("")來指定bean的名字(即name或者id)
- service層使用 @Service观堂,可以在其后添加("")來指定bean的名字(即name或者id)
- controller層使用 @Controller让网,可以在其后添加("")來指定bean的名字(即name或者id)
- 其他情況下可以使用 @Component,可以在其后添加("")來指定bean的名字(即name或者id)
注意师痕,以上注解都是在接口的實(shí)現(xiàn)類進(jìn)行寂祥。并且,如果沒有指定名字七兜,系統(tǒng)會(huì)默認(rèn)起名,規(guī)則是:取類名福扬,將第一個(gè)字母轉(zhuǎn)換為小寫腕铸。
使用以上的方法惜犀,我們可以完成將bean在xml的注冊。但是狠裹,如果在某一類當(dāng)中虽界,我們需要使用bean時(shí),怎么將xml當(dāng)中的bean注入到類當(dāng)中呢涛菠?這里要涉及到bean的自動(dòng)裝配策略莉御。相關(guān)的注釋主要有@Resource和@Autowired。這里不詳細(xì)講述他們的區(qū)別俗冻,需要看詳細(xì)介紹了可以看這篇文章礁叔,本文使用@Resource進(jìn)行∑。回到代碼當(dāng)中琅关,將上面的代碼做出如下的修改。
//DaoImpl.java
package justspring.dao;
import org.springframework.stereotype.Repository;
@Repository("dao") //添加了這行代碼
public class DaoImpl implements Dao {
@Override
public String sayHello() {
return "hello by dao";
}
}
//ServiceImpl.java
package justspring.service;
import justspring.dao.Dao;
import javax.annotation.Resource;
//因?yàn)檫@個(gè)工程里有Service類讥蔽,為了不發(fā)生歧義涣易,使用包名+類名
@org.springframework.stereotype.Service("service")
public class ServiceImpl implements Service {
@Resource //添加了這個(gè)注釋,將上面的dao注入到這里
private Dao dao;
@Override
public String sayHello() {
return dao.sayHello();
}
@Override
public String otherService() {
return "other service";
}
}
有了上面兩個(gè)代碼文件冶伞,我們的xml就解放出來了新症。不需要再進(jìn)行任何的bean注冊了,現(xiàn)在的xml應(yīng)該如下所示:
<?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">
<context:annotation-config/>
<context:component-scan base-package="justspring.service,justspring.dao"/>
</beans>
xml文件一下子干凈了很多响禽,省掉了配置的麻煩徒爹。測試類的代碼不作任何的變化,運(yùn)行后可以得到與之前一樣的結(jié)果金抡。測試類當(dāng)中的service還是要通過getBean來實(shí)現(xiàn)的瀑焦,我嘗試著用@Resource像注入dao那樣的方法來做,但是并沒有能夠成功梗肝。報(bào)錯(cuò)空指針榛瓮,這里接下來還需要認(rèn)真的研究一下。
詳細(xì)代碼(version 2.0)
前面已經(jīng)得到了一個(gè)完整的spring工程巫击,但是dao部分關(guān)于數(shù)據(jù)庫的還沒有實(shí)現(xiàn)禀晓,下面來完成怎么連接數(shù)據(jù)庫,并且從數(shù)據(jù)庫里面獲取到數(shù)據(jù)的操作坝锰。首先需要在maven下添加需要的依賴包粹懒,這里不一一列舉了,可以參看我這個(gè)系列的第一篇文章顷级。
Mybatis-Spring
Mybatis凫乖,是支持定制化 SQL、存儲(chǔ)過程以及高級(jí)映射的優(yōu)秀的持久層框架。MyBatis 避免了幾乎所有的 JDBC 代碼和手動(dòng)設(shè)置參數(shù)以及獲取結(jié)果集帽芽。MyBatis 可以對(duì)配置和原生Map使用簡單的 XML 或注解删掀,將接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java對(duì)象)映射成數(shù)據(jù)庫中的記錄。
Mybatis有一個(gè)中文的說明文檔导街,需要可以點(diǎn)擊這里查看
由于我們是在spring的框架下使用mybait披泪,所以除了配置mybatis外,還需要將它和spring框架結(jié)合在一起搬瑰。方便的是款票,已經(jīng)存在無縫連接它們的庫了,那就是Mybaits-Spring泽论。
Mybatis-Spring有一個(gè)中文的說明文檔艾少,需要可以點(diǎn)擊這里查看
注意,中文版不夠詳細(xì)佩厚,有能力還是請(qǐng)閱讀英文版
通過這個(gè)文檔姆钉,總結(jié)一下使用的方法。
安裝
使用整個(gè)模塊前需要引入必要的jar包抄瓦,或者添加依賴文件.
如果使用的是maven潮瓶,在pom.xml當(dāng)中添加如下:
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>x.x.x</version>
</dependency>
開始
要和spring一起使用mybatis,需要在spring的上下文當(dāng)中定義至少兩樣?xùn)|西钙姊。分別是:
- 一個(gè)SqlSessionFactory
- 至少一個(gè)數(shù)據(jù)映射器類
配置SqlSessionFactory
其中毯辅,第一點(diǎn)很容易配置,先把它干掉煞额。直接在spring的上下文配置就可以了思恐,注意,整合后膊毁,不需要單獨(dú)的mybatis配置文件胀莹,全部的配置內(nèi)容都可以在spring的上下文當(dāng)中進(jìn)行。
//spring1.xml 在上面已有的基礎(chǔ)上添加
//數(shù)據(jù)源配置婚温,需要根據(jù)個(gè)人情況填寫
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/數(shù)據(jù)庫名" />
<property name="username" value="用戶名" />
<property name="password" value="密碼" />
</bean>
//SqlSessionFactory配置描焰,注意此處有大坑(如果你使用Intellij這個(gè)ide的話請(qǐng)看后面的填坑指南)
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mapperLocations" value="classpath:mapper/*.xml"/>
</bean>
注意:上面的的文件不能照著填,需要將路徑理清楚栅螟,如果你自己在嘗試著寫的話荆秦,請(qǐng)把路徑換成你自己的。這里使用的是mysql數(shù)據(jù)庫力图,如果不是請(qǐng)按照實(shí)際情況填寫步绸。Intellij編譯工程的時(shí)候,會(huì)按照目錄的種類進(jìn)行編譯吃媒,xml文件在src的源碼目錄下的話不會(huì)編譯到target里面瓤介,需要單獨(dú)進(jìn)行設(shè)置吕喘。
配置數(shù)據(jù)映射器類
配置數(shù)據(jù)映射器的方法有很多,先簡要的敘述一下:
官方說明文檔在這里(這里不要去看中文的翻譯版刑桑,不單止內(nèi)容缺少兽泄,而且講得非常不清楚)
總結(jié)一下來說就是兩種方法:
- 利用xml來進(jìn)行顯示的逐一配置(如果mapper很多的話就會(huì)很麻煩)
- 利用mybatis-spring提供的自動(dòng)掃描機(jī)制(就像spring當(dāng)中掃描dao和service組件那樣)
有簡單的方法使用,相信沒有人希望用難的方法漾月,所以這里使用上述的第二種方法進(jìn)行,在spring1.xml(即spring的配置文件)當(dāng)中添加幾句代碼可以:
//spring1.xml 現(xiàn)在我的spring1.xml看起來是這樣的
<?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:mybatis="http://mybatis.org/schema/mybatis-spring"
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
http://mybatis.org/schema/mybatis-spring
http://mybatis.org/schema/mybatis-spring.xsd">
//添加了這一句和多引用了一個(gè)命名空間胃珍,請(qǐng)和上面的文件對(duì)比著看
<mybatis:scan base-package="justspring.dao" />
...省略相同的代碼
</beans>
添加數(shù)據(jù)庫查詢操作以及包裝服務(wù)層代碼
配置方面現(xiàn)在就都做好了梁肿,需要添加一些數(shù)據(jù)庫的操作代碼(寫在xml配置文件上),另外在dao和service提供一些接口并實(shí)現(xiàn)它觅彰。在dao真正操作數(shù)據(jù)庫的時(shí)候不需要我們自己寫實(shí)現(xiàn)類吩蔑,按照上面的配置,保證xml的名字和接口類的名字對(duì)應(yīng)一致填抬,框架會(huì)替我們做好實(shí)現(xiàn)烛芬。要實(shí)現(xiàn)操作數(shù)據(jù)庫,首先要有數(shù)據(jù)表飒责,我們這里建了一個(gè)用來測試赘娄,如下:
括號(hào)表示存在表里的數(shù)據(jù)
表名:person
字段1:LOG_NM("管理員0")
字段2:LOG_PW("123456")
//Dao.java
package justspring.dao;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
@Repository //沒有實(shí)現(xiàn)類DaoImpl,所以將這個(gè)注解放在了這里
public interface Dao {
// @Select("SELECT LOG_PW FROM person WHERE LOG_NM = #{name}")
//通過@Select注釋也可以不用寫xml直接進(jìn)行數(shù)據(jù)庫操作宏蛉,但是在比較復(fù)雜的查詢情況下還是需要xml的(官方的說法)遣臼,所以這里還是使用xml的方法
String getPwByUserNm(@Param("name")String name);
//@Param是一個(gè)注解,表示傳進(jìn)去的參數(shù)名字拾并,在xml當(dāng)中通過@Param括號(hào)里面的名字揍堰,可以獲取到傳進(jìn)去的參數(shù)
}
//Service.java
package justspring.service;
public interface Service {
String getPwByUserNm(String name);
}
//ServciceImpl.java
package justspring.service;
import justspring.dao.Dao;
import org.apache.ibatis.annotations.Param;
import javax.annotation.Resource;
@org.springframework.stereotype.Service("service")
public class ServiceImpl implements Service {
//同上,注入一個(gè)dao
@Resource
private Dao dao;
//實(shí)現(xiàn)通過用戶名獲取密碼的服務(wù)
@Override
public String getPwByUserNm(String name) {
return this.dao.getPwByUserNm(name);
}
}
//Dao.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
//命名空間必須指向接口類
<mapper namespace="justspring.dao.Dao">
<select id="getPwByUserNm" resultType="java.lang.String">
SELECT LOG_PW FROM person WHERE LOG_NM = #{name}
</select>
</mapper>
//testJustSpring.java
package justspring.test;
import justspring.service.Service;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import javax.annotation.Resource;
public class testJustSpring {
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("spring1.xml");
Service service = (Service) context.getBean("service");
String name = "管理員0";
String password = service.getPwByUserNm(name);
System.out.println(password);
}
}
以上嗅义,代碼已經(jīng)完全展示出來屏歹。但是前面說的大坑還沒有解決,為解決它先看一下正確的文件結(jié)構(gòu)應(yīng)該這樣設(shè)置:
請(qǐng)將文件結(jié)構(gòu)調(diào)整如上之碗!
請(qǐng)將文件結(jié)構(gòu)調(diào)整如上蝙眶!
請(qǐng)將文件結(jié)構(gòu)調(diào)整如上!
重要的事情說三遍继控,test的包可以不調(diào)整械馆,沒有影響。并且點(diǎn)擊工程根目錄武通,按下F12打開模塊設(shè)置霹崎,將resources目錄設(shè)置為resources root類型,點(diǎn)擊apply冶忱。重新編譯一次尾菇,應(yīng)該就能夠在.targe下看到Dao.xml文件了。詳情請(qǐng)看這里,非常感謝解決方案派诬。
執(zhí)行完這步操作劳淆,運(yùn)行一下就應(yīng)該可以看到:
123456