接上篇《JAVA WEB快速入門之經過一個簡單的Spring項目瞭解Spring的核心(AOP、IOC)》,瞭解了Spring的核心(AOP、IOC)後,咱們再來學習與實踐Maven、SpringMVC、SpringJDBC(即:SSM中的S(Spring)S(SpringMVC)),暫不涉及ORM部份(即:M(Mybatis)),Mybatis將在下一篇文章中繼續給你們分享。我相信經過以前幾篇文章的學習與實踐,已基本熟悉了搭建JSP網站及把AOP IOC應用到項目中,已具有編寫JSP 普通WEB網站了,故從本文開始,一些以前講述過的步驟再也不一一說明,只講重點及結果。javascript
(提示:本文內容有點長,涉及的知識點也比較多,如果新手建議耐心看完!)css
1、瞭解Maven並基於Maven構建一個空的SpringMVC網站:html
1.1Maven是什麼?java
Maven 翻譯爲"專家"、"內行",是 Apache 下的一個純 Java 開發的開源項目。基於項目對象模型(縮寫:POM)概念,Maven利用一箇中央信息片段能管理一個項目的構建、報告和文檔等步驟。git
主要功能有:構建、文檔生成、報告、依賴、SCMs、發佈、分發、郵件列表github
Maven 提倡使用一個共同的標準目錄結構,Maven 使用約定優於配置的原則,你們儘量的遵照這樣的目錄結構,以下圖示(來源網絡):(假定 ${basedir} 表示工程目錄)web
Maven 有如下三個標準的生命週期:(每一個生命週期中都包含着一系列的階段(phase)。這些 phase 就至關於 Maven 提供的統一的接口,而後這些 phase 的實現由 Maven 的插件來完成。)
clean:項目清理的處理 、default(或 build):項目部署的處理、site:項目站點文檔建立的處理spring
詳細說明請參見:http://www.runoob.com/maven/maven-build-life-cycle.htmlsql
1.2搭建Maven環境express
1.2.1 從官網下載地址:http://maven.apache.org/download.cgi 中選擇對應版本點擊下載(開發通常以WINDOWS爲主,故下載apache-maven-x.x.x-bin.zip),下載後解壓到對應的目錄,而後配置以下環境變量:
新增變量名: MAVEN_HOME,變量值:maven解壓根目錄
編輯系統變量 Path,添加變量值:%MAVEN_HOME%\bin (WIN10系統直接添加一行)
配置完後,在cmd中輸入:mvn -v 若是正常輸出maven版本信息則表示OK;
1.2.2 設置Maven的本地倉庫的下載位置(默認是在系統盤(通常爲C):\Users\當前用戶名\.m2\repository),若是不改變則會致使系統盤分區容量不足,建議及時修改
打開%MAVEN_HOME%\conf 目錄下的settings.xml,修改localRepository元素的內容爲自定義的repository目錄,如:
<localRepository>E:/LocalMvnRepositories</localRepository>
而後設置IDE(eclipse或idea)MAVEN的存儲位置,以下是eclipse的修改截圖,idea同理
依次打開:windows->perferences->Maven(右邊列表節點)->User Settings,修改Global Settings 、User Settings的路徑(將settings.xml COPY到指定的目錄,而後這裏設置這個目錄),以下圖示:【若沒有maven選項可能須要手動安裝maven插件,詳見:http://www.javashuo.com/article/p-ubylvzeu-hz.html】
點擊update Settings按鈕完成更新,最後Apply便可
1.2.3 settings.xml中設置鏡像中央倉庫URL,以下:(阿里雲倉庫)
<mirror> <id>alimaven</id> <name>aliyun maven</name> <url>http://maven.aliyun.com/nexus/content/groups/public/</url> <mirrorOf>central</mirrorOf> </mirror> </mirrors>
1.2.4 使用maven命令建立一個空的maven webapp,在cmd中執行以下命令:(請先cd切換到指定的項目建立根目錄再執行以下命令)
mvn archetype:generate
而後根據每步提示進行交互處理,通常依次輸入:archetypeArtifactId(若是要搭建MVC,則選擇maven-archetype-webapp)、gourpId、artifactId、version、package,以下圖示:
固然若是不想一步步的按照嚮導來操做,能夠帶上完整參數來進行操做,例如:
mvn archetype:generate -DgroupId=cn.zuowenjun.java -DartifactId=mvnspringmvc2 -DarchetypeArtifactId=maven-archetype-webapp -DinteractiveMode=false
執行完成後會輸出build success字樣,就表示構建maven webapp項目成功,本地文件目錄就會生成相關的文件,若是須要使用IDE打開,能夠在eclipse中經過:File->Open Projects from File systm or archive->選擇import source路徑(命令生成的項目根目錄)->finish便可。
1.2.5 經過IDE(eclipse) maven插件來構建項目,操做步驟爲:
File->New->Maven Project ->嚮導界面(Create a simple Proejct不要勾選,其他項按需設置)->嚮導界面(Select an Archetype:選擇maven-archetype-webapp,以下圖示)->設置->finish便可
若是發現Archetype(即:項目原型模板,與VS中建立某個項目相似)的Version比較低,可使用右下角的"Add Archetype"添加最新的Archetype(如上圖示出現的1.4版本就是我加的),這裏咱們仍選擇1.0,下一步就出現以下圖示,設置相關信息便可
兩個踩坑點:(不管是用mvn命令行仍是maven插件建立的webapp項目存在兩個問題,須要處理)
1.構建WebApp項目資源目錄顯示不全,缺乏java等目錄,問題緣由是默認的項目是使用的JRE1.5,咱們只需改爲當前最新的版本便可(如:1.8),參考:https://blog.csdn.net/Sunny1994_/article/details/79058685
2.改完後可能還會報:Description Resource Path Location Type Java compiler level does not match the version of the installed Java project facet. mvnspringmvc Unknown Faceted Project Problem (Java Version Mismatch),這是因爲編譯的JDK版本與項目的JDK版本不一致形成的,經過:在項目上右鍵Properties-》Project Facets,在打開的Project Facets頁面中的Java下拉列表中,選擇相應版本,以下圖示:
3.報缺乏javax.servlet.http.HttpServlet父類,須要在pom文件中添加一下JAR包依賴,配置以下:(version請按須要配置)
<dependencies> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> </dependency> </dependencies>
解決後,最後再buid proejct如無報錯,說明空的maven project已建立OK。
1.3添加springMVC、SpringJDBC相關依賴、配置web.xml,實現SpringMVC項目開發環境
1.3.1在pom.xml中添加springMVC、SpringJDBC依賴,以下:(這裏將版本號單獨統一配置在properties中)
<properties> <spring.version>5.1.3.RELEASE</spring.version> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</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-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency> </dependencies>
以下是完整示例POM文件內容:(比較完整,涉及springMVC、springJDBC、數據驅動【這裏是sqlserver】、jsp視圖【JSTL、EL】)
<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>cn.zuowenjun.java</groupId> <artifactId>mvnspringmvc</artifactId> <packaging>war</packaging> <version>0.0.1-SNAPSHOT</version> <name>mvnspringmvc Maven Webapp</name> <url>http://maven.apache.org</url> <properties> <spring.version>5.1.3.RELEASE</spring.version> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <!-- JSP視圖所需依賴 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> </dependency> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.2</version> </dependency> <!-- JSP JSTL所需依賴 --> <dependency> <groupId>javax.servlet.jsp.jstl</groupId> <artifactId>jstl-api</artifactId> <version>1.2</version> <exclusions> <exclusion> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> </exclusion> <exclusion> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.glassfish.web</groupId> <artifactId>jstl-impl</artifactId> <version>1.2</version> <exclusions> <exclusion> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> </exclusion> <exclusion> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> </exclusion> <exclusion> <groupId>javax.servlet.jsp.jstl</groupId> <artifactId>jstl-api</artifactId> </exclusion> </exclusions> </dependency> <!-- springMVC所需依賴 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</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-webmvc</artifactId> <version>${spring.version}</version> </dependency> <!-- Spring JDBC【數據訪問】所需依賴 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>com.microsoft.sqlserver</groupId> <artifactId>mssql-jdbc</artifactId> <version>7.0.0.jre8</version> </dependency> </dependencies> <build> <finalName>mvnspringmvc</finalName> </build> </project>
1.3.2在web.xml中配置DispatcherServlet及ContextLoaderListener,以下:(以下完整的web.xml,有改造過,由於默認的web的聲明頭有問題,另外若是不配置contextConfigLocation,那麼springContext的配置文件默認路徑:[servlet-name(是DispatcherServlet配置的名稱)]-servlet.xml)
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> <display-name>Archetype Created Web Application</display-name> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <!-- Map all requests to the DispatcherServlet for handling --> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc-servlet.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- 解決中文請求亂碼問題 --> <filter> <filter-name>CharacterEncoding</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>CharacterEncoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- 定義默認首頁,歡迎頁 --> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <!-- 定義錯誤處理頁面,此處只定義404,其他的經過@ControllerAdvice+@ExceptionHandler來處理 --> <error-page> <error-code>404</error-code> <location>/WEB-INF/errors/notfound.jsp</location> </error-page> </web-app>
知識擴展說明:springMVC異常統一處理有多種方式,可參見:https://www.cnblogs.com/bloodhunter/p/4825279.html 、 https://www.cnblogs.com/junzi2099/p/7840294.html 、https://blog.csdn.net/butioy_org/article/details/78718405
1.3.3在src/main/resources中建立springmvc-servlet.xml(即:contextConfigLocation配置的路徑文件),這個是spring Context配置文件與上一篇介紹的Beans.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" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd"> <!-- 對包中的全部類進行掃描,以完成Bean建立和自動依賴注入的功能 ,多個包名用逗號分隔 --> <context:component-scan base-package="cn.zuowenjun.java.mvc"></context:component-scan> <mvc:annotation-driven> <!-- <mvc:argument-resolvers></mvc:argument-resolvers> --> <!-- <mvc:async-support></mvc:async-support> --> <!-- <mvc:message-converters></mvc:message-converters> --> <!-- <mvc:path-matching/> --> <!-- <mvc:return-value-handlers></mvc:return-value-handlers> --> </mvc:annotation-driven> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/" /> <property name="suffix" value=".jsp" /> </bean> <!-- 配置靜態資源,如html,圖片,js,css等,多個資源位置可用逗號分隔 --> <mvc:resources mapping="/pages/**" location="/WEB-INF/pages/" /> <!-- 可配置讀取外部配置文件,若是有配置jdbc.properties,則下面的dataSource的相關property可使用${xxx}佔位符,這裏不演示 --> <!--<context:property-placeholder location="classpath:jdbc.properties" /> --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.microsoft.sqlserver.jdbc.SQLServerDriver"></property> <property name="url" value="jdbc:sqlserver://ip:port;DatabaseName=testDB"></property> <property name="username" value="dbuser"></property> <property name="password" value="password"></property> </bean> <!-- 配置事務管理器 --> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <!-- 事務掃描開始(開啓@Tranctional) 此示例暫不啓用--> <!-- <tx:annotation-driven transaction-manager="txManager" /> --> </beans>
對以下配置做簡要說明:
context:component-scan:自動掃描包並將標記@Component(組件),@Service(服務),@Controller(控制器),@Repository(數據倉庫)的Bean註冊到Spring IOC容器中,這樣就無需手動在xml進行單獨配置了;詳見:https://www.cnblogs.com/fightingcoding/p/component-scan.html
mvc:annotation-driven:簡化配置自動註冊DefaultAnnotationHandlerMapping與AnnotationMethodHandlerAdapter兩個bean,這些是spring MVC爲@Controller分發請求所必須的。裏面也包含一些子元素的配置,詳見:https://starscream.iteye.com/blog/1098880
mvc:resources:配置處理靜態資源,配置的url路徑將只會由default默認的servlet來處理,而再也不由DispatcherServlet處理,詳見:http://www.javashuo.com/article/p-xbzyoyjy-ka.html
註冊Bean:InternalResourceViewResolver,視圖解析器,用於找到視圖文件;
註冊Bean:DriverManagerDataSource,指定SpringJDBC的數據源驅動相關鏈接信息;
註冊Bean:DataSourceTransactionManager,配置事務管理器,用於事務處理;
如上步驟都操做完後,就能夠Buid Project,若是構建成功,則說明SpringMVC項目環境已OK;能夠看到項目目錄以下圖示:
(其中那些包都是我爲後面寫代碼時提早建立的)
2、編寫SpringMVC示例代碼(演示:發博文,寫評論),瞭解SpringMVC及SpringJDBC:
2.1.定義Model:(在cn.zuowenjun.java.mvc.model包中分類定義:Post、PostComment 兩個數據模型)
//Post.java package cn.zuowenjun.java.mvc.model; import java.util.Date; public class Post { private int id; private String title; private String content; private String author; private Date createTime; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } } //PostComment.java package cn.zuowenjun.java.mvc.model; import java.util.Date; public class PostComment { private int id; private int postid; private String content; private String createby; private Date createTime; public int getId() { return id; } public void setId(int id) { this.id = id; } public int getPostid() { return postid; } public void setPostid(int postid) { this.postid = postid; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public String getCreateby() { return createby; } public void setCreateby(String createby) { this.createby = createby; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } }
很是簡單的POJO風格的兩個類,只有成對的getter、setter方法。
2.2定義Dao接口:(在cn.zuowenjun.java.mvc.dao包中分別定義:PostDao、PostCommentDao 兩個DAO接口,注意JAVA與C#的接口定義規範有所不一樣,C#中要求以I開頭,而JAVA中並無此要求,JAVA只要要求實現類以Impl後綴結尾)
//PostDao.java package cn.zuowenjun.java.mvc.dao; import java.util.Date; import java.util.List; import cn.zuowenjun.java.mvc.model.Post; public interface PostDao { Post get(int id); List<Post> getList(Date frmDate,Date toDate); int create(Post post); Boolean delete(int id); Boolean update(Post post); } //PostCommentDao.java package cn.zuowenjun.java.mvc.dao; import java.util.List; import cn.zuowenjun.java.mvc.model.PostComment; public interface PostCommentDao { PostComment get(int id); List<PostComment> getList(int postId); Boolean create(PostComment postCmmt); Boolean delete(int id); Boolean update(PostComment postCmmt); }
如上代碼所示,DAO接口主要定義了增、刪、改、查(查單個,查多個),這是大部份的常見場景,根據須要定義。
2.3實現Dao接口:(在cn.zuowenjun.java.mvc.dao.impl包中分別定義:BaseDao、PostDaoImpl、PostCommentDaoImpl,其中BaseDao是抽像類,主要是約束構造函數的入參及提供通用的獲取JdbcTemplate實例對象)
BaseDao抽象類定義:
package cn.zuowenjun.java.mvc.dao.impl; import javax.sql.DataSource; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; public abstract class BaseDao { private JdbcTemplate jdbcTemplateObj; private NamedParameterJdbcTemplate namedParamJdbcTemplate; public BaseDao(DataSource dataSource) { this.jdbcTemplateObj=new JdbcTemplate(dataSource); this.namedParamJdbcTemplate=new NamedParameterJdbcTemplate(dataSource); } protected JdbcTemplate getJdbcTemplate() { return this.jdbcTemplateObj; } protected NamedParameterJdbcTemplate getNamedParameterJdbcTemplate() { return this.namedParamJdbcTemplate; } }
BaseDao抽象類中分別實例化了兩個數據訪問對象:JdbcTemplate、NamedParameterJdbcTemplate,之因此這樣作是由於我會分別在PostDaoImpl演示使用NamedParameterJdbcTemplate來進行CRUD操做,PostCommentDaoImpl演示使用JdbcTemplate來進行CRUD操做。經過對比代碼來發現二者的區別以及優點。
PostDaoImpl類定義:
package cn.zuowenjun.java.mvc.dao.impl; import java.util.*; import javax.sql.DataSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DataAccessException; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.namedparam.*; import org.springframework.jdbc.support.*; import org.springframework.transaction.*; import org.springframework.transaction.support.*; import cn.zuowenjun.java.mvc.dao.*; import cn.zuowenjun.java.mvc.model.Post; public class PostDaoImpl extends BaseDao implements PostDao { private PlatformTransactionManager transactionManager; @Autowired public PostDaoImpl(DataSource dataSource,PlatformTransactionManager transactionManager) { super(dataSource); this.transactionManager=transactionManager; } @Override public Post get(int id) { String sql="select * from TA_TestPost where id=:id"; MapSqlParameterSource mapSqlParameterSource = new MapSqlParameterSource(); mapSqlParameterSource.addValue("id", id); return this.getNamedParameterJdbcTemplate().queryForObject(sql, mapSqlParameterSource,new BeanPropertyRowMapper<>(Post.class)); } @Override public List<Post> getList(Date frmDate, Date toDate) { String sql="select * from TA_TestPost where createTime between :frmDate to :toDate"; Map<String, Object> paramMap = new HashMap<>(); paramMap.put("frmDate", frmDate); paramMap.put("toDate", toDate); return this.getNamedParameterJdbcTemplate().query(sql, paramMap,new BeanPropertyRowMapper<>(Post.class)); } @Override public int create(Post post) { String sql="insert into TA_TestPost(title, content, author, createTime) values(:title,:content,:author,getdate())"; BeanPropertySqlParameterSource beanPropParam=new BeanPropertySqlParameterSource(post); KeyHolder keyHolder = new GeneratedKeyHolder(); int r= this.getNamedParameterJdbcTemplate().update(sql, beanPropParam, keyHolder); if(r>0) { System.out.println("create is ok!"); return keyHolder.getKey().intValue(); } else { System.out.println("create is failed!"); return -1; } } @Override public Boolean delete(int id) { TransactionDefinition def = new DefaultTransactionDefinition(); TransactionStatus status = transactionManager.getTransaction(def); try { Map<String, Object> paramMap = new HashMap<>(); paramMap.put("postid",id); NamedParameterJdbcTemplate namedJdbcTemplate=this.getNamedParameterJdbcTemplate(); namedJdbcTemplate.update("delete from TA_TestPost where id=:postid",paramMap); namedJdbcTemplate.update("delete from TA_TestPostComment where postid=:postid", paramMap); transactionManager.commit(status); System.out.println("delete is ok!"); return true; }catch(DataAccessException daEx) { System.out.printf("delete is failed,Exception:%s",daEx.getMessage()); transactionManager.rollback(status); return false; } } @Override public Boolean update(Post post) { String sql="update TA_TestPost set title=:title,content=:content,author=:author,createTime=getdate() where id=:id"; BeanPropertySqlParameterSource beanPropParam=new BeanPropertySqlParameterSource(post); int r= this.getNamedParameterJdbcTemplate().update(sql, beanPropParam); if(r>0) { System.out.println("update is ok!"); return true; } else { System.out.println("update is failed!"); return false; } } }
涉及知識要點說明:
1.NamedParameterJdbcTemplate支持的經常使用SQL參數類型有:MapSqlParameterSource(鍵值對),Map<String, ?>,BeanPropertySqlParameterSource(把一個Bean全部屬性映射成參數,推薦這種)
2.NamedParameterJdbcTemplate 使用的SQL語句中參數的命名規則爲:冒號+參數名,如: :content,有點相似C#中的ADO.NET的參數化類型(@content),只是前綴指示符不一樣而矣。
3.若執行新增一條記錄,且須要返回自增的ID,這時能夠經過傳入GeneratedKeyHolder的實例,最後使用該實例的變量獲取自增ID,如上面的create方法中的同樣,最終使用:keyHolder.getKey().intValue()獲取到自增ID;
4.查詢返回的結果若要映射爲實體對象(POJO、JavaBean),則須要自定義實現RowMapper<T>,而後傳入自定義的RowMapper的變量,固然可使用Spring JDBC 的默認實現類:BeanPropertyRowMapper(內部使用反射, 可能在某些場景下性能不佳),若是隻須要返回某列的值,則能夠直接指定映射的類型,如:String.class
5.若想查詢返回一個實體對象或實體對象列表,應該使用queryForObject、query的重載方法含RowMapper<T>的參數,注意:queryForList 只能返回某一列的值,不能直接返回實體對象列表,由於最後的一個參數Class<T> requiredType最終內部轉化爲了:SingleColumnRowMapper
6.使用事務須要PlatformTransactionManager、TransactionDefinition、TransactionStatus,如上面的delete方法;
PostCommentDaoImpl類定義:
package cn.zuowenjun.java.mvc.dao.impl; import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; import javax.sql.DataSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.RowMapper; import cn.zuowenjun.java.mvc.dao.PostCommentDao; import cn.zuowenjun.java.mvc.model.PostComment; import cn.zuowenjun.java.mvc.model.mapper.PostCommentMapper; public class PostCommentDaoImpl extends BaseDao implements PostCommentDao { @Autowired public PostCommentDaoImpl(DataSource dataSource) { super(dataSource); // TODO Auto-generated constructor stub } @Override public PostComment get(int id) { String sql = "select * from TA_TestPostComment where id=?"; // 第一種:基於定義好的實現了RowMapper的PostCommentMapper實例 // return this.getJdbcTemplate().queryForObject(sql,new Object[] {id},new // PostCommentMapper()); // 第二種:直接使用匿名內部類(能夠理解爲與C#的委託類型) // return this.getJdbcTemplate().queryForObject(sql,new Object[]{id},new RowMapper<PostComment>() { // // @Override // public PostComment mapRow(ResultSet rs, int rowNum) throws SQLException { // PostComment model=new PostComment(); // model.setId(rs.getInt("id")); // model.setPostid(rs.getInt("postid")); // model.setContent(rs.getString("content")); // model.setCreateby(rs.getString("createby")); // model.setCreateTime(rs.getDate("createtime")); // return model; // } // }); // 第三種:直接使用Lambda表達式,這個與C#lambda就比較像了,由於JAVA抄自C# return this.getJdbcTemplate().queryForObject(sql, new Object[] { id }, (rs, i) -> { PostComment model = new PostComment(); model.setId(rs.getInt("id")); model.setPostid(rs.getInt("postid")); model.setContent(rs.getString("content")); model.setCreateby(rs.getString("createby")); model.setCreateTime(rs.getDate("createtime")); return model; }); } @Override public List<PostComment> getList(int postId) { String sql = "select * from TA_TestPostComment where postid=?"; return this.getJdbcTemplate().query(sql, new Object[] { postId }, new PostCommentMapper()); } @Override public Boolean create(PostComment postCmmt) { String sql = "insert into TA_TestPostComment(id, postid, content, createby, createTime) values(?,?,?,?,?)"; int r = this.getJdbcTemplate().update(sql, new Object[] { postCmmt.getId(), postCmmt.getPostid(), postCmmt.getContent(), postCmmt.getCreateby(), postCmmt.getCreateTime() }); if (r > 0) { System.out.println("create is ok!"); return true; } else { System.out.println("create is failed!"); return false; } } @Override public Boolean delete(int id) { String sql = "delete from TA_TestPostComment where id=?"; int r = this.getJdbcTemplate().update(sql, new Object[] { id }); if (r > 0) { System.out.println("delete is ok!"); return true; } else { System.out.println("delete is failed!"); return false; } } @Override public Boolean update(PostComment postCmmt) { String sql = "update TA_TestPostComment set postid=?,content=?,createby=?,createTime=getdate() where id=?"; int r = this.getJdbcTemplate().update(sql, (pss) -> { pss.setInt(1, postCmmt.getPostid()); pss.setString(2, postCmmt.getContent()); pss.setString(3, postCmmt.getCreateby()); pss.setInt(4, postCmmt.getId()); }); if (r > 0) { System.out.println("update is ok!"); return true; } else { System.out.println("update is failed!"); return false; } } }
PostCommentMapper類定義:
package cn.zuowenjun.java.mvc.model.mapper; import java.sql.ResultSet; import java.sql.SQLException; import org.springframework.jdbc.core.RowMapper; import cn.zuowenjun.java.mvc.model.PostComment; public class PostCommentMapper implements RowMapper<PostComment> { @Override public PostComment mapRow(ResultSet rs, int rowNum) throws SQLException { PostComment model=new PostComment(); model.setId(rs.getInt("id")); model.setPostid(rs.getInt("postid")); model.setContent(rs.getString("content")); model.setCreateby(rs.getString("createby")); model.setCreateTime(rs.getDate("createtime")); return model; } }
涉及知識要點說明:
1.JdbcTemplate支持的常見SQL參數類型有:Object[](NamedParameterJdbcTemplate不直接支持,可經過getJdbcTemplate得到JdbcTemplate,而後繼續使用JdbcTemplate的CRUD用法),PreparedStatementCreator、PreparedStatementSetter
2.JdbcTemplate返回結果常見處理轉換類型有:RowMapper<T>、RowCallbackHandler、ResultSetExtractor<T> ,注意涉及數據索引都是從1開始
3.不管是NamedParameterJdbcTemplate 仍是JdbcTemplate的入參SQL參數類型、返回結果參數處理類型大部份都是函數式接口(標註了@FunctionalInterface),意味着咱們能夠直接使用匿名內部類或Lambda表達式來傳參,如上面代碼中的get方法,分別演示了使用PostCommentMapper、new RowMapper<PostComment>(){...}匿名內部類、(rs, i) -> {...}Lambda表達式,其它同理;
4.JdbcTemplate的SQL語句中的參數命名規則爲:?,與使用原生的JDBC 進行參數化查詢用法相同
NamedParameterJdbcTemplate 與JdbcTemplate 都實現JdbcOperations接口;
2.4定義Service接口:(在cn.zuowenjun.java.mvc.service包中分別定義了:PostService、PostCommentService、UserService 三個接口)
//PostService.java package cn.zuowenjun.java.mvc.service; import java.util.Date; import java.util.List; import cn.zuowenjun.java.mvc.model.Post; public interface PostService { Post get(int id); List<Post> getList(Date frmDate,Date toDate); List<Post> getAll(); int create(Post post); Boolean delete(int id); Boolean update(Post post); } //PostCommentService.java package cn.zuowenjun.java.mvc.service; import java.util.List; import cn.zuowenjun.java.mvc.model.PostComment; public interface PostCommentService { PostComment get(int id); List<PostComment> getList(int postId); Boolean create(PostComment postCmmt); Boolean delete(int id); Boolean update(PostComment postCmmt); } //UserService .java package cn.zuowenjun.java.mvc.service; public interface UserService { String login(String uid,String pwd); String logout(); String getLoginUserName(); }
如上代碼所示,接口中只是定義了業務所須要的方法,可能你們看到以爲與dao接口很相似(示例代碼中確實是相同的),但其實它們服務的對象不一樣,dao可能相比service更原子化,更單一 一些,dao只須要爲單個表提供數據讀寫服務,而service則是爲表現層(UI)提供真實的服務場景,而這些場景有多是複雜的,包含多個dao的數據源或操做多個dao,同時還承擔了業務數據的驗證邏輯處理轉換等功能,service層我理解是業務服務層(BLL),若是按照DDD來劃分,我認爲service層則是應用服務層或領域服務層
2.5實現Service接口:(在cn.zuowenjun.java.mvc.service.impl包中分別定義PostServiceImpl、PostCommentServiceImpl、UserServiceImpl類,它們實現了對應的service接口)
//PostServiceImpl.java package cn.zuowenjun.java.mvc.service.impl; import java.security.*; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import cn.zuowenjun.java.mvc.model.Post; import cn.zuowenjun.java.mvc.service.PostService; import cn.zuowenjun.java.mvc.dao.*; @Service public class PostServiceImpl implements PostService { @Autowired private PostDao postDao; @Override public Post get(int id) { return postDao.get(id); } @Override public List<Post> getList(Date frmDate, Date toDate) { long frmDateVal=frmDate.getTime(); long toDateVal=toDate.getTime(); if(frmDateVal>toDateVal) { throw new InvalidParameterException("開始時間需<=結束時間"); } return postDao.getList(frmDate, toDate); } @Override public List<Post> getAll() { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); try { return getList(sdf.parse("1900-1-1"), sdf.parse("2099-12-1")); } catch (ParseException e) { return null; } } @Override public int create(Post post) { String result=verifyModel(post,true); if(!result.isEmpty()) { throw new InvalidParameterException(result); } return postDao.create(post); } @Override public Boolean delete(int id) { return postDao.delete(id); } @Override public Boolean update(Post post) { String result=verifyModel(post,true); if(!result.isEmpty()) { throw new InvalidParameterException(result); } return postDao.update(post); } private String verifyModel(Post post,Boolean isNew) { StringBuilder errMsgBuilder=new StringBuilder(); if(!isNew && post.getId()<=0) { errMsgBuilder.append("ID不能爲空!"); } if(post.getTitle().trim().isEmpty()) { errMsgBuilder.append("標題不能爲空!"); } if(post.getContent().trim().isEmpty()) { errMsgBuilder.append("內容不能爲空!"); } if(post.getAuthor().trim().isEmpty()) { errMsgBuilder.append("做者不能爲空!"); } return errMsgBuilder.toString(); } } //PostCommentServiceImpl.java package cn.zuowenjun.java.mvc.service.impl; import java.security.InvalidParameterException; import java.util.Date; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import cn.zuowenjun.java.mvc.dao.PostCommentDao; import cn.zuowenjun.java.mvc.model.PostComment; import cn.zuowenjun.java.mvc.service.PostCommentService; @Service public class PostCommentServiceImpl implements PostCommentService { @Autowired private PostCommentDao postCommentDao; @Override public PostComment get(int id) { return postCommentDao.get(id); } @Override public List<PostComment> getList(int postId) { return postCommentDao.getList(postId); } @Override public Boolean create(PostComment postCmmt) { String result=verifyModel(postCmmt,true); if(!result.isEmpty()) { throw new InvalidParameterException(result); } postCmmt.setCreateTime(new Date()); return postCommentDao.create(postCmmt); } @Override public Boolean delete(int id) { return postCommentDao.delete(id); } @Override public Boolean update(PostComment postCmmt) { String result=verifyModel(postCmmt,false); if(!result.isEmpty()) { throw new InvalidParameterException(result); } return postCommentDao.update(postCmmt); } private String verifyModel(PostComment postCmmt,Boolean isNew) { StringBuilder errMsgBuilder=new StringBuilder(); if(!isNew && postCmmt.getId()<=0) { errMsgBuilder.append("ID不能爲空!"); } if(postCmmt.getPostid()<=0) { errMsgBuilder.append("文章ID不能爲空!"); } if(postCmmt.getContent().trim().isEmpty()) { errMsgBuilder.append("內容不能爲空!"); } if(postCmmt.getCreateby().trim().isEmpty()) { errMsgBuilder.append("回覆者不能爲空!"); } return errMsgBuilder.toString(); } } //UserServiceImpl.java package cn.zuowenjun.java.mvc.service.impl; import cn.zuowenjun.java.mvc.service.UserService; import java.util.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import org.springframework.stereotype.Service; import org.springframework.web.context.request.*; @Service public class UserServiceImpl implements UserService { @Override public String login(String uid, String pwd) { if(uid.isEmpty() || pwd.isEmpty()) { return "用戶名與密碼都不能爲空!"; } ResourceBundle userRes= ResourceBundle.getBundle("user"); String configUid= userRes.getString("user.userid"); String configPwd=userRes.getString("user.password"); if(configUid.equals(uid) && configPwd.equals(pwd)) { String configUName=userRes.getString("user.username"); HttpSession session= getRequest().getSession(); session.setAttribute("loginUid", uid); session.setAttribute("loginUname",configUName); return null; }else{ return "用戶名或密碼不正確!"; } } @Override public String logout() { try { getRequest().getSession().removeAttribute("loginUid"); return null; }catch(Exception ex) { return ex.getMessage(); } } @Override public String getLoginUserName() { Object loginUnameObj= getRequest().getSession().getAttribute("loginUname"); if(loginUnameObj==null) { return null; }else{ return (String)loginUnameObj; } } private HttpServletRequest getRequest() { HttpServletRequest request= ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest(); return request; } }
注意:登陸服務中我使用了user.properties屬性配置文件,username中的中文自動轉碼了。
user.userid=admin user.password=www.zuowenjun.cn.java user.username=\u68A6\u5728\u65C5\u9014
如上代碼所示,全部的service實現類都標註了@Service註解,以前的dao實現類也都標註了@Repository,目的是爲了實現spring容器的自動掃描並註冊到IOC容器中,正如我在上面講到的springmvc-servlet.xml配置文件中說的同樣;另外service實現類除了調用dao完成數據的操做,另外還有業務數據的校驗,好比代碼中的:verifyModel方法等,固然實際的大型項目中可能業務邏輯複雜得多,模型驗證也能夠經過註解來實現,與C#中在System.ComponentModel.DataAnnotations下的相關特性相似,你們有興趣能夠查閱相關資料;
2.6設計Controller及View:
2.6.1建立一個BlogController類,用於處理管理博文(查看、發表、修改、刪除)、評論(發表)的相關ACTION,以下:
package cn.zuowenjun.java.mvc.controller; import java.io.IOException; import java.security.InvalidParameterException; import java.text.*; import java.util.*; import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.propertyeditors.CustomDateEditor; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.ui.ModelMap; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.*; import org.springframework.web.context.request.WebRequest; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.support.RedirectAttributes; import cn.zuowenjun.java.mvc.model.Post; import cn.zuowenjun.java.mvc.model.PostComment; import cn.zuowenjun.java.mvc.service.*; /** * * @author zuowenjun.cn *refer-mavendepconfig:https://blog.csdn.net/qq_29227939/article/details/52063869 *refer-EL:http://www.cnblogs.com/dongfangshenhua/p/6731421.html */ @Controller @RequestMapping("/blog") public class BlogController { @Autowired private PostService postService; @Autowired private PostCommentService postCommentService; @RequestMapping() public ModelAndView list() { List<Post> postList= postService.getAll(); ModelAndView mv=new ModelAndView(); mv.addObject("posts",postList); mv.setViewName("bloglist"); return mv; } @RequestMapping(path="/querylist",method=RequestMethod.POST) public ModelAndView list(@RequestParam(required=true) Date frmDate,@RequestParam(required=true) Date toDate,ModelAndView mv) { List<Post> postList=postService.getList(frmDate, toDate); mv.setViewName("bloglist"); mv.addObject("posts",postList); return mv; } @RequestMapping("/post/{postid}") public String detail(@PathVariable String postid,ModelMap model) { int pid=Integer.parseInt(postid); model.put("post", postService.get(pid)); model.put("comments", postCommentService.getList(pid)); return "blogdetail"; } @RequestMapping(path="/savecomment",method=RequestMethod.POST) public String saveComment(@ModelAttribute() PostComment postComment,RedirectAttributes redirectAttr) { String resultMsg="評論保存成功"; if(!postCommentService.create(postComment)) { resultMsg="評論保存失敗,請稍後重試"; } redirectAttr.addFlashAttribute("msg", resultMsg); return "redirect:/blog/post/" + postComment.getPostid(); } @RequestMapping(path="/editpost/{postid}",method=RequestMethod.GET) public ModelAndView editPost(@PathVariable(required=true) int postid) { ModelAndView mv=new ModelAndView(); Post post=null; post=postService.get(postid); if(post==null) { throw new InvalidParameterException("無效的postid"); } mv.addObject("post", post); mv.setViewName("blogedit"); return mv; } @RequestMapping("/editpost") public String createPost(Map<String,Object> viewDataMap) { Post post=new Post(); viewDataMap.put("post", post); return "blogedit"; } @RequestMapping(path="/editpost",method=RequestMethod.POST) public String updatePost(@ModelAttribute("post") Post post,@RequestParam("doAction") String action,Model model, HttpServletResponse reponse) throws IOException { String result="保存成功!"; if(action.equals("delete")) { //刪除操做 if(!postService.delete(post.getId())){ result="刪除失敗,請重試!"; }else { String jsResult="<script>alert('刪除成功!');self.close();</script>"; reponse.setContentType("text/html;charset=utf-8"); reponse.getWriter().append(jsResult); return null; } } else { //編輯操做 if(post.getId()<=0) { //新增博文邏輯 int postId= postService.create(post); if(postId>0) { post.setId(postId); }else { result="保存失敗,請重試!"; } }else if(!postService.update(post)) { //更新博文邏輯 result="保存失敗,請重試!"; } } model.addAttribute("result", result); return "blogedit"; } @InitBinder public void initBinder(WebDataBinder binder, WebRequest request) { //轉換日期 DateFormat dateFormat=new SimpleDateFormat("yyyy-MM-dd"); binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));// CustomDateEditor爲自定義日期編輯器 } }
建立AccountController類,用於提供登陸、登出的途徑,代碼以下:
package cn.zuowenjun.java.mvc.controller; import java.io.IOException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.servlet.ModelAndView; import cn.zuowenjun.java.mvc.service.UserService; /** * seeparambind:http://www.cnblogs.com/xiaoxi/p/5695783.html * */ @Controller @RequestMapping("/account") public class AccountController { @Autowired private UserService userService; @RequestMapping("/signin") public String signIn() { return "signin"; } @RequestMapping(path="/signin",method=RequestMethod.POST) public ModelAndView signIn(@RequestParam(required=true) String uid,@RequestParam(required=true) String pwd) { String loginResult=userService.login(uid, pwd); ModelAndView mv= new ModelAndView(); if(loginResult==null || loginResult.isEmpty()) {//登陸成功跳轉 mv.setViewName("redirect:/blog"); } else { mv.setViewName("signin"); mv.addObject("message",loginResult==null || loginResult.isEmpty()?"登陸成功":"登陸失敗:" + loginResult); } return mv; } @RequestMapping("/signout") public void signOut(HttpServletRequest request, HttpServletResponse response) throws IOException { userService.logout(); response.sendRedirect(request.getContextPath() + "/account/signin"); } }
舒適提示:因爲是DEMO演示,故我在controller中儘量的使用不一樣的方式來完成各類邏輯,以便你們看到應用的效果,實際項目中不會是這樣的。
關於Controller類涉及以下知識點說明:
a.命名規範,統一使用資源名(通常是名詞)+controller結尾,如示例:BlogController,AccountController,儘可能符合REST的風格,同時類上標註:@Controller,以便告訴spring容器這是一個Controller的bean;
b.@RequestMapping:指定映射的請求路徑,與ASP.NET MVC的Route特性有異由同工之效,詳細用法可參考:https://www.iteye.com/news/32657/ 、http://www.javashuo.com/article/p-yykkggow-kb.html
c.Action獲取請求參數(按照ASP.NET MVC的說法就是:Model Binding),詳細用法可參考:http://www.javashuo.com/article/p-awvaufof-kc.html
d.Action指定視圖以及爲View指定Model數據(傳值給view):可使用ModelAndView、Model、ModelMap、Map<String,Object>、@ModelAttribute(做用於ACTION上)、HttpServletRequest(其實前面的幾種方法底層最終都是經過HttpServletRequest.setAttribute來實現的),注意除了能夠直接返回ModelAndView,由於它包含了setView的方法,其他的都應該返回String的viewName,能夠對照上面的示例代碼看一下,也可參見:https://blog.csdn.net/u012190514/article/details/80237885
e:Controller間或Action間的跳轉,使用:redirect:ACTION路徑,ACTION傳參多種方法詳見:http://www.javashuo.com/article/p-bgxgfiel-kz.html,,我主要想特別說明的是RedirectAttributes,它不用URL傳參,而是採用一次性session的模式,相似ASP.NET MVC中的TempData
2.6.2 建立相關UI視圖頁面(根據InternalResourceViewResolver配置的視圖解析位置及後綴名(/WEB-INF/jsp/、.jsp)在/WEB-INF/jsp/建立相關的jsp文件),分別有:signin.jsp(登陸)、bloglist.jsp(博客列表主頁)、blogdetail.jsp(博文詳情,評論)、blogedit.jsp(博文編輯,新增、修改、刪除),具體代碼以下:
登陸:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" isELIgnored="false" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>夢在旅途演示博客-登陸</title> <style type="text/css"> #loginbox{ width:300px; margin:100px auto 0 auto; padding:50px; border:5px groove gray; } #loginbox div{ margin:20px auto; } .txtcenter{ text-align:center; } </style> </head> <body> <form method="post"> <div id="loginbox"> <div><h3>歡迎,請登陸!</h3></div> <div>用戶ID:<input type="text" id="txtuid" name="uid" /></div> <div>密 碼:<input type="password" id="txtpwd" name="pwd" /></div> <div> <input type="submit" id="btnSubmit" value="登 錄" /> <input type="reset" id="btnReset" value="重置" /> </div> </div> </form> <div class="txtcenter"> <c:if test="${message!=null}"> <p>${message}</p> </c:if> </div> <p class="txtcenter">Copyright 2018 zuowj.cnblogs.com and zuowenjun.cn demo.</p> </body> </html>
博客列表主頁、博文詳情,評論:
<!-- bloglist.jsp --> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" isELIgnored="false"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>博客列表</title> </head> <body> <div style="text-align: right;"> <span>[ ${sessionScope.loginUid }(${sessionScope.loginUname }), <a href="${pageContext.request.contextPath}/account/signout">[退出]</a> ]</span> <a href="${pageContext.request.contextPath}/blog/editpost" target="_blank">[ +發表博文 ]</a> </div> <div> <form method="post" action="${pageContext.request.contextPath }/blog/querylist"> <fieldset> <legend>範圍查詢:</legend> <span>開始時間:</span> <input type="text" name="frmDate" value="${param.frmDate }" placeholder="yyyy-MM-dd" /> <span>結束時間:</span> <input type="text" name="toDate" placeholder="yyyy-MM-dd" value="${param.toDate}" /> <button id="btnquery">查詢</button> </fieldset> </form> </div> <c:choose> <c:when test="${posts!=null && posts.size()>0}"> <c:forEach items="${posts}" var="item"> <div> <h2> <a href="${pageContext.request.contextPath}/blog/post/${item.id }" target="_blank">${item.title}</a> <a href="${pageContext.request.contextPath}/blog/editpost/${item.id }" target="_blank" style="color:red;font-size:16px;">[修改]</a> </h2> <h4> 做者:${item.author },時間: <fmt:formatDate value="${item.createTime}" pattern="yyyy-MM-dd" /> </h4> <p>${item.content }</p> </div> <hr /> </c:forEach> </c:when> <c:otherwise> <p style="color:red;">沒有任何博客文章記錄!</p> </c:otherwise> </c:choose> </body> </html> <!-- blogedit.jsp --> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" isELIgnored="false"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>博文詳情:${post.title }</title> </head> <body> <div> <h2> ${post.title} </h2> <h4> 做者:${post.author }, 時間:<fmt:formatDate value="${post.createTime}" pattern="yyyy-MM-dd" /> </h4> <p>${post.content }</p> </div> <hr/> <div> <c:choose> <c:when test="${comments!=null && fn:length(comments)>0 }"> <c:forEach items="${ comments}" var="item"> <div style="margin:10px auto;"> <div style="border-bottom:solid 1px gray;margin-bottom:5px;"> ${item.createby } 回覆於:<fmt:formatDate value="${item.createTime}" pattern="yyyy-MM-dd HH:mm" /> </div> <div> ${item.content } </div> </div> </c:forEach> </c:when> <c:otherwise> <p>暫無相關評論!</p> </c:otherwise> </c:choose> <div> <form method="post" action="../savecomment"> <h3>發表新評論:</h3> <p>評論人:<input type="text" id="createby" name="createby" /></p> <p>評論內容:</p> <p><textarea rows="5" style="width:100%" id="content" name="content"></textarea> </p> <p> <button id="btnreply">提交評論</button> <input type="hidden" name="postid" value="${post.id }" /> </p> </form> <div> <c:if test="${msg!=null}"> <p>提交評論結果:${msg}</p> </c:if> </div> </div> </div> </body> </html>
博文編輯:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" isELIgnored="false" import="cn.zuowenjun.java.mvc.model.*" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%> <%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%> <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> <%-- ${post.id>0?"編輯"+ post.title:"新增博文" } --%> <% Post post=(Post)request.getAttribute("post"); if(post==null){ out.print("post is null!"); return; } if(post.getId()>0){ out.print("編輯"+ post.getTitle()); }else{ out.print("新增博文"); } %> </title> </head> <body> <form:form modelAttribute="post" method="POST" id="mainForm" action="${pageContext.request.contextPath }/blog/editpost"> <div>文章標題:</div> <div> <form:input path="title" /> </div> <div>做者:</div> <div> <form:input path="author" /> </div> <div>文章內容:</div> <div> <form:textarea path="content" rows="10" style="width:100%;" /> </div> <div> <button type="button" id="btnSave" data-action="update" onclick="javascript:doSubmit(this);">保存</button> <c:if test="${post.id>0 }"> <button type="button" id="btnDelete" data-action="delete" style="color:red;" onclick="javascript:doSubmit(this);">刪除</button> </c:if> <form:hidden path="id"/> <input type="hidden" name="doAction" id="_doAction" /> </div> </form:form> <c:if test="${result!=null && fn:length(result)>0 }"> <p>操做結果:${result}</p> </c:if> <script type="text/javascript"> function doSubmit(btn){ if(!confirm("你肯定要" + btn.innerText +"嗎?")){ return false; } var actionVal=btn.getAttribute("data-action"); //alert(actionVal); document.getElementById("_doAction").setAttribute("Value",actionVal); document.getElementById("mainForm").submit(); } </script> </body> </html>
jsp視圖涉及知識點說明:
a.JSP EL表達式:簡化JAVA代碼在前臺的使用,具體用法詳見:http://www.runoob.com/jsp/jsp-expression-language.html
b.JSP JSTL(標準標籤庫):是一個JSP標籤集合,它封裝了JSP應用的通用核心功能,支持通用的、結構化的任務,好比迭代,條件判斷,XML文檔操做,國際化標籤,SQL標籤等,具體用法詳見:http://www.runoob.com/jsp/jsp-jstl.html
我的認爲若是須要用好JSP視圖,就須要熟悉EL及JSTL這兩個利器,若是是採用先後端分離那就另當別論;
2.7.爲springMVC網站增長統一攔截器,實現統一的身份登陸狀態驗證
2.7.1定義一個實現了HandlerInterceptor接口的攔截器類:LoginValidationInterceptor,而後重寫preHandle方法,在裏面根據session來判斷登陸狀態,若沒有登陸則跳轉至登陸頁面,代碼以下:
package cn.zuowenjun.java.mvc.service.impl; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.HandlerInterceptor; public class LoginValidationInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String url = request.getRequestURI(); if(url.indexOf("/signin")>0) {//登陸頁面無需驗證登陸,放行 return true; } if(request.getSession().getAttribute("loginUid")==null) {//檢測到未登陸,轉到登陸頁面 response.sendRedirect(request.getContextPath() + "/account/signin"); return false; }else { return true; } } //其他postHandle、afterCompletion方法未重寫,直接使用默認實現,這與C#有區別(C#8.0中也會有默認實現) }
2.7.2在springmvc-servlet.xml中註冊攔截器,並指定攔截URL的匹配路徑,以下:
<!-- 配置攔截器 --> <mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**"/><!-- 攔截全部請求, /表示只攔截非JSP的請求,/*只攔截一級目錄,/**攔截全部目錄 --> <bean class="cn.zuowenjun.java.mvc.service.impl.LoginValidationInterceptor"></bean> </mvc:interceptor> </mvc:interceptors>
如上兩步即完成了攔截器的定義及配置,這樣當有請求過來後就會進入攔截器,固然也可使用JSP WEB中的filter過濾器來實現,二者的區別,詳見:https://blog.csdn.net/xiaoyaotan_111/article/details/53817918
2.8.爲springMVC網站增長自定義錯誤頁面
2.8.1統一處理錯誤有多種方法,如:@ExceptionHandler、@ResponseStatus、@ControllerAdvice+@ExceptionHandler,固然這些背後都是使用了默認的HandlerExceptionResolver的實現,詳見:http://www.cnblogs.com/xinzhao/p/4902295.html,本文采用@ControllerAdvice+@ExceptionHandler的方式來進行統一處理異常,代碼以下:
package cn.zuowenjun.java.mvc.service.impl; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import org.springframework.http.*; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.web.*; /** * * @author Zuowenjun *refer http://www.cnblogs.com/xinzhao/p/4902295.html */ @ControllerAdvice public class WebExceptionHandler { @ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler(HttpMessageNotReadableException.class) public void handleHttpMessageNotReadableException(HttpMessageNotReadableException e) { } /** * 405 - Method Not Allowed */ @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED) @ExceptionHandler(HttpRequestMethodNotSupportedException.class) public void handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) { } /** * 415 - Unsupported Media Type */ @ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE) @ExceptionHandler(HttpMediaTypeNotSupportedException.class) public void handleHttpMediaTypeNotSupportedException(Exception e) { } // /** // * 500 - Internal Server Error // */ // @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) // @ExceptionHandler(Exception.class) // public void handleException(Exception e) { // // } // 建立ModleAndView,將異常和請求的信息放入到Model中,指定視圖名字,並返回該ModleAndView @ExceptionHandler(Exception.class) public ModelAndView handleError(HttpServletRequest req, Exception exception) { ModelAndView mv = new ModelAndView(); mv.addObject("exception", exception); mv.addObject("url", req.getRequestURL()); mv.setViewName("error"); return mv; } }
對應的error.jsp 、notfound.jsp視圖頁面代碼以下:(其中notfound.jsp中從新設置了staus,目的是爲了兼容IE,若是是404,那麼IE將會顯示IE的默認404頁面)
<!--error.jsp--> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" isELIgnored="false" import="java.io.*,java.lang.*" %> <%! //獲取完整堆棧信息 String getStackTrace(Throwable throwable){ StringWriter stringWriter=new StringWriter(); PrintWriter printWriter=new PrintWriter(stringWriter); try { throwable.printStackTrace(printWriter); return stringWriter.toString(); }finally { printWriter.close(); } } %> <% Exception ex= (Exception)request.getAttribute("exception"); %> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>發生錯誤!</title> </head> <body> <h2>對不起,處理請求時發生錯誤!</h2> <p>錯誤信息:${exception.getMessage() }</p> <p>錯誤詳情:<%=getStackTrace(ex) %></p> <p>請求URL:${url}</p> <p><a href="javascript:history.back();">[返回]</a></p> </body> </html> <!--notfound.jsp--> <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" isELIgnored="false"%> <% response.setStatus(200); %> <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>404-頁面不存在!</title> </head> <body> <div style="width: 600px; margin-top: 100px;margin-left:auto;margin-right:auto;"> <p>你訪問的資源不存在,可能被外星人吃掉了!</p> <p>請求URL:<span id="rawUrl"></span></p> </div> <script type="text/javascript"> window.onload=function(){ document.getElementById("rawUrl").innerHTML=window.location.href; }; </script> </body> </html>
效果展現:
登陸:
博客主頁:
博文詳情及評論:
編輯博文:
當頁面出現錯誤時:
404頁面:
最後小結
1.本文內容涵蓋了:Maven、SpringMVC、SpringJDBC,相關的常見知識點都有在示例代碼中呈現出來了,有利於理解;
2.本文的springMVC Demo網站雖然簡單,但基本把相關的功能都完成了,你們能夠參照示例代碼進行學習與深刻,源代碼Git地址:https://github.com/zuowj/mvnspringmvc
3.springMVC打包WAR包的方式請參見:http://www.javashuo.com/article/p-txvnufux-ke.html,多項目部署到同一個tomcat的方法請參見:https://blog.csdn.net/dreamstar613/article/details/75282962/,若是出現打包或部署相關錯誤請根據錯誤描述自行百度,可能坑有點多;
好比:打包時可能報編譯JDK的問題或類型沒法解析問題,這時可能須要配置maven的編譯插件,詳見:http://www.javashuo.com/article/p-fvnbbukw-kg.html
若是報:Cannot change version of project facet Dynamic Web Module to 3.0.詳見:https://blog.csdn.net/xiongyouqiang/article/details/79130656
4.本文中對於一些關鍵的知識點都有說明,同時有與ASP.NET MVC 進行對照說明,以便JAVA,.NET開發者能夠相互快速瞭解與上手;
5.因爲目前都是使用先後端分離的模式,同時因爲目前都是使用ORM,且雖然目前的手動配置依賴相比以前的JSP WEB手動引入依賴包要方便,但仍是有點效率低下,故下一篇計劃講解: 基於springMVC實現Reset API,熟悉:Spring Boot+Mybatis+SpringMVC,敬請期待,謝謝!
注:文章如有不足之處歡迎評論交流,謝謝!(本文從開始寫到發表斷斷續續的差很少耗時了3周時間,原本計劃元旦前發出但因爲工做緣由未能及時總結,故拖到如今才發表)