欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

JAVA WEB快速入门之从编写一个基于SpringMVC框架的网站了解Maven、SpringMVC、SpringJDBC

程序员文章站 2023-03-31 22:25:23
接上篇《JAVA WEB快速入门之通过一个简单的Spring项目了解Spring的核心(AOP、IOC)》,了解了Spring的核心(AOP、IOC)后,我们再来学习与实践Maven、SpringMVC、SpringJDBC(即:SSM中的S(Spring)S(SpringMVC)),暂不涉及ORM ......

接上篇《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网站了,故从本文开始,一些之前讲述过的步骤不再一一说明,只讲重点及结果。

(提示:本文内容有点长,涉及的知识点也比较多,若是新手建议耐心看完!)

 一、了解maven并基于maven构建一个空的springmvc网站:

 1.1maven是什么?

  maven 翻译为"专家"、"内行",是 apache 下的一个纯 java 开发的开源项目。基于项目对象模型(缩写:pom)概念,maven利用一个*信息片断能管理一个项目的构建、报告和文档等步骤。

  主要功能有:构建、文档生成、报告、依赖、scms、发布、分发、邮件列表

  maven 提倡使用一个共同的标准目录结构,maven 使用约定优于配置的原则,大家尽可能的遵守这样的目录结构,如下图示(来源网络):(假定 ${basedir} 表示工程目录)

  JAVA WEB快速入门之从编写一个基于SpringMVC框架的网站了解Maven、SpringMVC、SpringJDBC

  maven 有以下三个标准的生命周期:(每个生命周期中都包含着一系列的阶段(phase)。这些 phase 就相当于 maven 提供的统一的接口,然后这些 phase 的实现由 maven 的插件来完成。)
  clean:项目清理的处理 、default(或 build):项目部署的处理、site:项目站点文档创建的处理

  详细说明请参见:

1.2搭建maven环境

  1.2.1 从官网下载地址: 中选择对应版本点击下载(开发一般以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插件,详见:】

  JAVA WEB快速入门之从编写一个基于SpringMVC框架的网站了解Maven、SpringMVC、SpringJDBC

  点击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,如下图示:

  JAVA WEB快速入门之从编写一个基于SpringMVC框架的网站了解Maven、SpringMVC、SpringJDBC

   当然如果不想一步步的按照向导来操作,可以带上完整参数来进行操作,例如:

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即可

   JAVA WEB快速入门之从编写一个基于SpringMVC框架的网站了解Maven、SpringMVC、SpringJDBC

  如果发现archetype(即:项目原型模板,与vs中创建某个项目类似)的version比较低,可以使用右下角的"add archetype"添加最新的archetype(如上图示出现的1.4版本就是我加的),这里我们仍选择1.0,下一步就出现如下图示,设置相关信息即可

  JAVA WEB快速入门之从编写一个基于SpringMVC框架的网站了解Maven、SpringMVC、SpringJDBC

两个踩坑点:(不论是用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下拉列表中,选择相应版本,如下图示:

  JAVA WEB快速入门之从编写一个基于SpringMVC框架的网站了解Maven、SpringMVC、SpringJDBC

  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进行单独配置了;详见:

 mvc:annotation-driven:简化配置自动注册defaultannotationhandlermapping与annotationmethodhandleradapter两个bean,这些是spring mvc为@controller分发请求所必须的。里面也包含一些子元素的配置,详见:

 mvc:resources:配置处理静态资源,配置的url路径将只会由default默认的servlet来处理,而不再由dispatcherservlet处理,详见:

 注册bean:internalresourceviewresolver,视图解析器,用于找到视图文件;

 注册bean:drivermanagerdatasource,指定springjdbc的数据源驱动相关连接信息;

 注册bean:datasourcetransactionmanager,配置事务管理器,用于事务处理;

 如上步骤都操作完后,就可以buid project,如果构建成功,则说明springmvc项目环境已ok;可以看到项目目录如下图示:

JAVA WEB快速入门之从编写一个基于SpringMVC框架的网站了解Maven、SpringMVC、SpringJDBC(其中那些包都是我为后面写代码时提前创建的)

二、编写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特性有异由同工之效,详细用法可参考: 、

  c.action获取请求参数(按照asp.net mvc的说法就是:model binding),详细用法可参考:

  d.action指定视图以及为view指定model数据(传值给view):可以使用modelandview、model、modelmap、map<string,object>、@modelattribute(作用于action上)、httpservletrequest(其实前面的几种方法底层最终都是通过httpservletrequest.setattribute来实现的),注意除了可以直接返回modelandview,因为它包含了setview的方法,其余的都应该返回string的viewname,可以对照上面的示例代码看一下,也可参见:

  e:controller间或action间的跳转,使用:redirect:action路径,action传参多种方法详见:,,我主要想特别说明的是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代码在前台的使用,具体用法详见:

 b.jsp jstl(标准标签库):是一个jsp标签集合,它封装了jsp应用的通用核心功能,支持通用的、结构化的任务,比如迭代,条件判断,xml文档操作,国际化标签,sql标签等,具体用法详见:

 个人认为如果需要用好jsp视图,就需要熟悉el及jstl这两个利器,如果是采用前后端分离那就另当别论; 

2.7.为springmvc网站增加统一拦截器,实现统一的身份登录状态验证

2.7.1定义一个实现了handlerinterceptor接口的拦截器类:loginvalidationinterceptor,然后重写prehandle方法,在里面根据session来判断登录状态,若没有登录则跳转至登录页面,代码如下: