很久之前寫了一篇SSH搭建例子,由於工作原因已經轉到SpringMVC+Mybatis,就以之前SSH實現簡單登陸的例子,總結看看SpringMVC+Mybatis怎么實現。
Spring一開始是輕量級的框架,在SSH中,處於中間粘合劑的作用,核心作用是IoC(控制反轉)、DI(依賴注入),IoC和DI是同一個概念,只是以不同角度進行解釋。簡單的說,就是Spring幫助你管理Bean,只要寫好了配置文件或者Spring注解,那么Spring可以自動幫你創建Bean,不需要手動new。經過后來的發展出現的SpringMVC框架,與Struts一樣,同屬於MVC框架的一種,SpringMVC可以說和Spring是兩回事了,已經是一個比較重的框架了,我的理解是SpringMVC=Struts+Spring了。
Spring還有另外一個重要組成部分是AOP(Aspect-Oriented Programming 面向切面編程),目前主要使用在攔截器方面。實際上Struts也有攔截器,概念是類似的。
Hibernate、Mybatis、ibatis都是常用的持久層框架,它們都遵從ORM設計,可以簡單的認為Hibernate比較重,Mybatis、ibatis比較輕量,Mybatis又是由ibatis發展來的,所以Mybatis與ibatis非常相似,阿里用的就是ibatis(畢竟當時還沒有Mybatis),三者之間的區別網上可以搜一下,就不展開說了。
IDE集成開發工具我也從MyEclipse轉到IntelliJ IDEA了(版本是2017.1.3),工具上使用肯定有一些區別,但是寫代碼上是沒有區別的。
一、建立Maven SpringMVC項目
首先還是要下載安裝好JDK,目前JDK版本已經到9,也不用特意去下載J2EE的版本,就下載一個J2SE版本的就可以,我下載的是JDK8的,開發環境是Windows,所以下載了一個Windows x64的jdk-8u161-windows-x64.exe(下載地址 http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html),安裝好之后配置好環境變量,主要要設置JAVA_HOME和PATH兩個環境變量,CMD命令行敲java -version有版本信息展示就可以了。
另外為了更好的管理jar,我使用了maven(下載地址 https://maven.apache.org/download.cgi),網上搜索一下如何配置即可,主要配置MAVEN_HOME和PATH環境變量,在IDEA里還要在setting->Build,Execution,Deployment->Build Tools->Maven里設置Maven home directory、User setting file、Local repository。
使用IDEA創建maven SpringMVC項目的詳細過程請參考博文https://www.cnblogs.com/Sinte-Beuve/p/5730553.html,下面簡述步驟:
打開IDEA之后,File->new->Project,選擇如下maven-archetype-webapp
next,輸入GroupId和ArtifactId,next,next,輸入Project name
點擊finish,就創建好項目了,這時maven會首先去下載maven骨架,也就是項目組織結構配置文件,還有一些必要的jar包。
等maven下載完成之后,就得到了基本項目框架,參照上面博文,建立java目錄並標記source,建立相關pakage,項目框架就基本完成了,我的項目結構如下圖所示:
這時候,我們要利用maven加入springmvc、mybatis相關jar包,打開pom.xml,仿照如下添加properties和dependencies
<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/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.test</groupId> <artifactId>springmvctest</artifactId> <packaging>war</packaging> <version>1.0-SNAPSHOT</version> <name>springmvctest Maven Webapp</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <!-- spring版本號 --> <spring.version>4.1.6.RELEASE</spring.version> <!-- mybatis版本號 --> <mybatis.version>3.2.6</mybatis.version> <!-- log4j日志文件管理包版本 --> <slf4j.version>1.7.7</slf4j.version> <log4j.version>1.2.17</log4j.version> <!-- jackson包版本 --> <jackson.version>2.5.0</jackson.version> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <!--spring單元測試依賴 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> <scope>test</scope> </dependency> <!-- springMVC核心包 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <!-- spring核心包 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>4.0.9.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency> <!-- AOP begin --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.7.4</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.7.4</version> </dependency> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.1</version> </dependency> <!-- AOP end --> <!-- mybatis核心包 --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>${mybatis.version}</version> </dependency> <!--mybatis spring 插件 --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.2.2</version> </dependency> <!-- Mysql數據庫驅動包 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.34</version> </dependency> <!--servlet--> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.0.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.2</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> </dependencies> <build> <finalName>springmvctest</finalName> </build> </project>
如果后面還有需要下載的jar包,比如log4j等,可以去這個網站http://mvnrepository.com搜索相關jar包和版本,找到相關信息,也同樣寫一個dependency,maven就自動去下載了。
二、使用Spring
WEB-INF下的web.xml可以理解為整個項目的入口,無論是Struts還是Spring都是先從這個web.xml找到相關配置,再進入struts或是spring的框架中的。打開web.xml,修改為如下內容:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <servlet> <servlet-name>dispatcher</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dispatcher</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
以上就添加了Spring框架能力,DispatcherServlet顧名思義,就是轉發的意思,mapping的url-pattern寫的是/,說明所有請求都使用這個DispatcherServlet進行轉發。大家可以對比一下struts的配置文件,struts是使用filter的,而spring使用的是servlet。有了Dispatcher轉發,就需要再添加一個寫怎么樣轉發的配置文件dispatcher-servlet.xml,同樣在WEB-INF底下添加這個文件即可,dispatcher-servlet.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:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" 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://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <description>Spring Configuration</description> <!-- 靜態資源(js、image等)的訪問 --> <mvc:default-servlet-handler/> <!-- 配置注解驅動 可以將request參數與綁定到controller參數上 --> <mvc:annotation-driven /> <!-- 開啟組件自動掃描;使用Annotation自動注冊Bean,解決事物失效問題:在主容器中不掃描@Controller注解,在SpringMvc中只掃描@Controller注解。 --> <!-- base-package 如果多個,用“,”分隔 --> <context:component-scan base-package="com.test.dao,com.test.service,com.test.controller" /> <!-- 對模型視圖名稱的解析,即在模型視圖名稱添加前后綴(如果最后一個還是表示文件夾,則最后的斜杠不要漏了) 使用JSP--> <!-- 默認的視圖解析器 在上邊的解析錯誤時使用 (默認使用html)- --> <bean id="defaultViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/> <property name="prefix" value="/WEB-INF/view/"/><!--設置JSP文件的目錄位置--> <property name="suffix" value=".jsp"/> </bean> </beans>
<mvc:annotation-driven />這一句說明啟用自動掃描Spring注解,<context:component-scan base-package="com.test.dao,com.test.service,com.test.controller" />這句說明掃描的位置。defaultViewResolver這個bean主要是用來方便controller返回尋找jsp視圖,定義了jsp目錄位置是/WEB-INF/view/下面。
那么,這個時候,我們來先寫一個controller,名字就叫IndexController吧,用來處理Index主頁,其實就是登陸頁面。
package com.test.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; /** * Created by jeff on 2018/2/13. */ @Controller @RequestMapping("/") public class IndexController { @RequestMapping(value = "/", method = RequestMethod.GET) public String loginview(){ return "loginview"; } @RequestMapping(value = "/login", method = RequestMethod.GET) public String login(@ModelAttribute("username") String username, @ModelAttribute("password") String password){ if(username.equals("jeff") && password.equals("123")) return "index"; return "loginview"; } }
可以看到,Class IndexController上面有兩個注解,一個是@Controller,說明這個Class是一個Controller,另一個是@RequestMapping,相當於訪問路徑。
在IndexController定義了兩個方法,loginview和login,方法上面同樣有@RequestMapping注解,細分訪問路徑,還定義了它是GET還是POST方法。
在login方法的參數username和password前面還有@ModelAttribute注解,這是對應頁面元素的name的,用於獲取頁面元素的值。這里其實就是依賴注入了,我們沒有初始化username和passowrd,就直接使用了,其實就是Spring幫我們獲取並初始化設置了頁面對應的值進username和password里了。
那么,return "loginview"和return "index"又是什么意思呢?這兩個方法的返回值都是String類型,返回給誰呢?剛剛上面dispatcher-servlet.xml文件中,咱們定義了defaultViewResolver這個bean,就是用來處理這個String的,返回一個index,那么Spring就會去你定義好的/WEB-INF/view/下面,找對應的jsp文件。所以,應該可以猜到,我們需要在/WEB-INF/view/下面新建兩個jsp文件,一個叫loginview.jsp,一個叫index.jsp。
loginview.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html lang="zh-CN"> <body> <form method="get" action="/login"> 登錄名:<input name='username' type="text" /><br/> 密碼:<input name ='password' type="password" /><br/> <button type="submit" style="width: 70px; height: 20px;">確定</button> </form> </body> </html>
index.jsp
<html> <body> <h2>Hello World!</h2> </body> </html>
完成了這些,還不能運行起來,我們還有一個重要的東西還沒有,那就是tomcat或者jboss這些容器。初學者學習了幾個月,說不定其實還沒有了解什么是容器,容器是用來做什么的。首先,容器是指servlet容器,就是存放我們寫的servlet的。至於什么作用,其實大家從C寫面向過程過來的話,可以想一想,C里面必須有一個main函數,不然程序從哪里開始呢?學C++使用MFC框架時,我就發現沒有了main函數,覺得很奇怪,程序怎么知道從哪里進去呢?我們上面寫的代碼,配置文件,也沒有指定任何的main函數。其實main函數就是容器里的,可以想象成這個main函數一直在循環等待觸發事件,一旦收到點擊打開某個網頁之類的指令,那么容器就會去找相應的servlet進行處理。
也許您還會問:那什么又是servlet呢?簡單的說,就是實現了servlet接口的類或是繼承HttpServlet、GernericServlet的類都叫做servlet(通常繼承HttpServlet),可以覆寫doPost()、doGet()等方法。但是我們會發現,我們上面沒有一個類實現了servlet接口或繼承自HttpServlet、GernericServlet,那么http如何找到我們的Controller的呢?如上面提到的DispatcherServlet,它實現了servlet接口。這下大家應該明白,容器接收到http請求,找到web.xml看到配置了的DispatcherServlet,進入DispatcherServlet,由它轉到我們的Controller,進行后面的處理,Controller將處理結果返回給DispatcherServlet,然后通過doPost()或者doGet()返回頁面。
了解了這些,就去下載一個tomcat或者jboss容器,在idea里配置好就行了,我這里使用常用的tomcat,網上搜一下idea配置tomcat就可以了。
run之后,打開瀏覽器,輸入http://localhost:8080/就可以看到登陸頁面了
輸入jeff登錄名和123密碼,即可跳轉到index首頁,其他登陸名密碼則不會跳轉
三、使用Mybits
既然Mybatis是一個持久層框架,使用Mybatis之前,我們需要有一個數據庫,那么下載一個常用的開源免費的MySQL安裝配置好即可(https://dev.mysql.com/downloads/mysql/)。
安裝好之后,使用MySQLWorkbench連進去,進行創建數據庫、建表等操作。
create database springtest; use springtest; create table users ( id int PRIMARY KEY AUTO_INCREMENT, username varchar(20), password varchar(50) ) auto_increment = 1; insert into users(username,password) values('jeff','123');
相應的,我們可以在entity包下新建一個Users類,這個類就是常說的Model層的javaBean(但沒有實現序列化接口Serializable)
package com.test.entity; import org.springframework.stereotype.Repository; /** * Created by jeff on 2018/2/22. */
public class Users { private Integer id; private String username; private String password; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password;} }
這時候在項目中需要連接MySQL數據庫,在resources下添加jdbc.properties文件,內容如下:
jdbc_driverClassName=com.mysql.jdbc.Driver jdbc_url=jdbc:mysql://localhost:3306/springtest jdbc_username=root jdbc_password=888888
在dispatcher-servlet.xml增加以下內容:
... <!-- 配置數據源 --> <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations"> <list> <value>classpath:jdbc.properties</value> <!--要是有多個配置文件,只需在這里繼續添加即可 --> </list> </property> </bean> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName"> <value>${jdbc_driverClassName}</value> </property> <property name="url"> <value>${jdbc_url}</value> </property> <property name="username"> <value>${jdbc_username}</value> </property> <property name="password"> <value>${jdbc_password}</value> </property> </bean> <!-- 配置Mybatis的文件 ,mapperLocations配置**Mapper.xml文件位置,configLocation配置mybatis-config文件位置--> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="mapperLocations" value="classpath:mapper/*.xml"/> </bean> ...
propertyConfigurer這個bean定義了配置文件路徑,dataSource這個bean找到配置文件就自動注入property。
在jdbc.properties中配置了jdbc_driverClassName=com.mysql.jdbc.Driver,這就是咱們連接MySQL的jar包包含的數據庫驅動,我們在前面pom.xml已經寫了一個MySQL的dependency,已經將該jar包下載下來了,現在就可以直接使用了,如果使用別的數據庫,那么就換成別的驅動包即可,相應的jdbc_driverClassName就需要修改。
sqlSessionFactory這個工廠類bean是mybatis推薦的使用方式,mapperLocations定義了mybatis的mapper xml文件的位置,可以看到我這是定義在resources下mapper目錄下的。
所以,我們需要在mapper目錄下新建一個UsersMapper.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="com.test.mapper.UsersMapper"> <!--設置domain類和數據庫中表的字段一一對應,注意數據庫字段和domain類中的字段名稱不致,此處一定要!--> <resultMap id="BaseUsers" type="com.test.entity.Users"> <id column="id" property="id" jdbcType="INTEGER" /> <result column="username" property="username" jdbcType="VARCHAR" /> <result column="password" property="password" jdbcType="VARCHAR" /> </resultMap> <!-- 查詢所有記錄 --> <select id="selectAllUsers" resultMap="BaseUsers"> SELECT * FROM users </select> <!-- 查詢單個記錄 --> <select id="selectUsersByUsername" parameterType="java.lang.String" resultMap="BaseUsers"> SELECT * FROM users WHERE username=#{username} </select> </mapper>
這樣就定義了數據庫表與entity類的映射關系,所謂ORM,就是對象於數據庫表映射的意思。完成了這個,我們就需要寫dao、service接口類和實現類了
dao接口類
package com.test.dao; import com.test.entity.Users; /** * Created by jeff on 2018/2/22. */ public interface UsersDao { Users getUsersByUsername(String username); }
dao實現類
package com.test.dao.impl; import com.test.dao.UsersDao; import com.test.entity.Users; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.support.SqlSessionDaoSupport; import org.springframework.stereotype.Repository; import javax.annotation.Resource; /** * Created by jeff on 2018/2/22. */ @Repository public class UsersDaoImpl extends SqlSessionDaoSupport implements UsersDao { @Resource public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) { // TODO Auto-generated method stub super.setSqlSessionFactory(sqlSessionFactory); } @Override public Users getUsersByUsername(String username){ return this.getSqlSession().selectOne("com.test.mapper.UsersMapper.selectUsersByUsername", username); } }
service接口類
package com.test.service; import com.test.entity.Users; /** * Created by jeff on 2018/2/22. */ public interface UsersService { Users getUsersByUsername(String username); }
service實現類
package com.test.service.impl; import com.test.dao.UsersDao; import com.test.entity.Users; import com.test.service.UsersService; import org.springframework.stereotype.Service; import javax.annotation.Resource; /** * Created by jeff on 2018/2/22. */ @Service public class UsersServiceImpl implements UsersService { @Resource private UsersDao usersDao; @Override public Users getUsersByUsername(String username){ return usersDao.getUsersByUsername(username); } }
這時候IndexController需要修改為如下:
package com.test.controller; import com.test.entity.Users; import com.test.service.UsersService; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import javax.annotation.Resource; /** * Created by jeff on 2018/2/13. */ @Controller @RequestMapping("/") public class IndexController { @Resource private UsersService usersService; @RequestMapping(value = "/", method = RequestMethod.GET) public String loginview(){ return "loginview"; } @RequestMapping(value = "/login", method = RequestMethod.GET) public String login(@ModelAttribute("username") String username, @ModelAttribute("password") String password){ Users user = usersService.getUsersByUsername(username); if(user != null && user.getPassword().equals(password)) return "index"; return "loginview"; } }
這樣就完成了使用MyBatis連接數據庫查詢用戶信息跳轉index主頁的功能。
這里根據上面使用的Spring注解解釋一下,@Repository意思是倉庫、儲藏,這里就是持久層的意思,專門用來注解這是持久層bean,@Service意思就是服務層,專門用來注解服務層,還有一種注解是@Component,意思是組件,用來注解無法定義是服務層還是持久層的其他bean。這三個注解效果是一樣的,隨便換哪一個都是可以的,但為了方便閱讀和規范,請正確使用在對應的bean上。
@Controller的注解類似於struts的@Action注解,一般定義web層,直接與頁面交互。
@Resource注解標記了該屬性、方法需要被依賴注入。值得一提的是該注解並非Spring注解,而是J2EE的注解,只是Spring也支持使用該注解,Spring自己也實現了一個@AutoWired注解,兩個注解都能實現依賴注入,但使用起來有細微的區別,我通常使用@Resource方便一些。
最后,附上項目組織層級結構樹
由於知識水平有限,如有遺漏錯誤之處,請指正,非常感謝。