前言
唉,第五章挺顺利,第六章利用Apache Tiles布局时不知道出了什么问题(百度是jdk版本问题——spring3.x需要jdk1.7,但我用的Spring4.0,所以没换jdk),哪怕回退操作也不能拯救一崩千里的工程 QQQAQQQ
甚是苦闷 QAQ
决定把每个操作都记下来
如果能对其他人有帮助就更好了~
– 框架小白,目前在Spring入门阶段(SpringMVC)
一、版本
- idea 2021.2 商业版
- jdk1.8
- tomcat 9
大部分参考的
https://www.cnblogs.com/wormday/p/8435617.html
《Spring in action》Spring实战第四版
用tomcat10出过问题
二、新建工程
1.配置文件
新建了一个Java空工程,框架后期可以加,命名spittr_1,位置F:\SpringMVC_projects\spittr_1
选择Add Framework Support…
选Java EE下的Web Application,Spring下的SpringMVC
配置tomcat,,,懒得写了,在Run —> Edit Configurations
配置完可能会有中文编码问题,回头出现了再改
在src下面新建包spittr
书上的web配置在Java类里写的,idea自动生成了XML配置文件,在/WEB-INF/web.xml,最后一项的 *.form 改成 /,即书上的将DispatcherServlet映射到“/”
在dispatcher-servlet.xml里,写上自动扫描组件的语句和jsp视图解析器(alt+enter真好用~):
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="spittr"/>
<!--指定视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 视图的路径 -->
<property name="prefix" value="/WEB-INF/view/"/>
<!-- 视图名称后缀 -->
<property name="suffix" value=".jsp"/>
<!-- Jstl -->
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"></property>
</bean>
</beans>
注:Spittr是一个博客应用,主要有两个领域概念:Spitter(应用的用户)和Spittle(用户发布的状态)。
2.编写基本的控制器
@Controller定义了一个组件bean,当收到对“/”的HTTP GET请求时,就会调用home()方法,返回的”home”被解析为实际的视图——“/WEB-INF/views/home.jsp”的JSP。:
改一下,/会指向默认的index.jsp,将路由改成了/home
HomeController类文件:
package spittr.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import static org.springframework.web.bind.annotation.RequestMethod.GET;
@Controller
@RequestMapping("/home")
public class HomeController {
@RequestMapping(method = GET)
public String home() {
return "home";
}
}
右键 - Add Framework Support…,选择Maven来管理依赖库,在pom.xml中添加:
<dependencies>
<!-- jstl 标签库 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
<scope>runtime</scope>
</dependency>
<!-- jsp 标准标签库 -->
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
</dependencies>
点一下maven更新库:
基本每一次添加完库都要,File - Project Structure,(shift+ctrl+alt+s)选中图中位置右键,选第一个,这样库会被加到/WEB-INF/lib里
这是jstl需要引入的库,第六章会说
先新建一个/WEB-INF/resources/style.css,备用
在/WEB-INF/view下写home.jsp文件:
标红的是还没在控制器写的路由,不管它
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page session="false" %>
<html>
<head/>
<title>Spitter</title>
<link rel="stylesheet"
type="text/css"
href="<c:url value="/WEB-INF/resources/style.css" />" >
</head>
<body>
<h1>Welcome to Spittr!</h1>
<a href="<c:url value="/spittles" />">Spittles</a> |
<a href="<c:url value="/spitter/register" />">Register</a>
</body>
</html>
检查一下没有其他占8080端口的,理论上这个时候应该是可以跑的了。。。
项目结构:
3.传递模型数据到视图
需要有一个页面展现最近提交的Spittle列表
Spittle类:属性包括消息内容、时间戳、Spittle发布时的经纬度。。。
package spittr;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import java.util.Date;
public class Spittle {
private final Long id;
private final String message;
private final Date time;
private double latitude;
private double longitude;
public Spittle(String message,Date time){
this(message,time,null,null);
}
public Spittle(String message,Date time,Double latitude,Double longitude){
this.id=null;
this.message=message;
this.time=time;
this.latitude=latitude;
this.longitude=longitude;
}
public long getId(){
return id;
}
public String getMessage(){
return message;
}
public Date getTime(){
return time;
}
public Double getLongitude(){
return longitude;
}
public Double getLatitude(){
return latitude;
}
@Override
public boolean equals(Object that){
return EqualsBuilder.reflectionEquals(this,that,"id","time");
}
@Override
public int hashCode(){
return HashCodeBuilder.reflectionHashCode(this,"id","time");
}
}
EqualsBuilder和HashCodeBuilder需要引入依赖:
<!-- Equals -->
<dependency>
<groupId>org.apache.maven.surefire</groupId>
<artifactId>surefire-booter</artifactId>
<version>2.22.2</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.8.1</version>
</dependency>
还需要定义一个数据访问的Repository(实现部分应该是数据库相关内容,这里简单定义为接口)- SpittleRepository接口:
package spittr.data;
import spittr.Spittle;
import java.util.List;
public interface SpittleRepository {
List<Spittle> findSpittles(long max, int count);
}
接口的实现放在SpittleRepositoryDAO类:
package spittr.data;
import org.springframework.stereotype.Repository;
import spittr.Spittle;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
@Repository
public class SpittleRepositoryDAO implements SpittleRepository {
public SpittleRepositoryDAO() {
}
@Override
public List<Spittle> findSpittles(long max, int count) {
List<Spittle> spittles = new ArrayList<Spittle>();
for (int i = 0; i < count; i++) {
spittles.add(new Spittle("Spittle-" + i, new Date(),0.0,0.0));
}
return spittles;
}
}
控制器SpittleController类:在模型中放入最新的spittle列表
package spittr.controller;
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 spittr.data.SpittleRepository;
@Controller
@RequestMapping("/spittles")
public class SpittleController {
private SpittleRepository spittleRepository;
@Autowired
public SpittleController(SpittleRepository spittleRepository) { //注入SpittleRepository
this.spittleRepository=spittleRepository;
}
@RequestMapping(method = RequestMethod.GET)
public String spittles(Model model){
model.addAttribute(spittleRepository.findSpittles(Long.MAX_VALUE,20));
return "spittles";
}
}
如果查看home.jsp会发现路由/spittles已经由标红变绿啦!
最后来一个呈现效果的spittles.jsp:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="s" uri="http://www.springframework.org/tags"%>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
<html>
<head>
<title>Spitter</title>
<link rel="stylesheet" type="text/css" href="<c:url value="/WEB-INF/resources/style.css" />" >
</head>
<body>
<div class="spittleForm">
<h1>Spit it out...</h1>
<form method="POST" name="spittleForm">
<input type="hidden" name="latitude">
<input type="hidden" name="longitude">
<textarea name="message" cols="80" rows="5"></textarea><br/>
<input type="submit" value="Add" />
</form>
</div>
<div class="listTitle">
<h1>Recent Spittles</h1>
<ul class="spittleList">
<c:forEach items="${spittleList}" var="spittle" >
<li id="spittle_<c:out value="spittle.id"/>">
<div class="spittleMessage"><c:out value="${spittle.message}" /></div>
<div>
<span class="spittleTime"><c:out value="${spittle.time}" /></span>
<span class="spittleLocation">(<c:out value="${spittle.latitude}" />, <c:out value="${spittle.longitude}" />)</span>
</div>
</li>
</c:forEach>
</ul>
<c:if test="${fn:length(spittleList) gt 20}">
<hr />
<s:url value="/spittles?count=${nextCount}" var="more_url" />
<a href="${more_url}">Show more</a>
</c:if>
</div>
</body>
</html>
好吧,这个是我直接从源码复制的,看起来它比书上的内容多了一个表单提交,不管它。。。
4.处理表单
5.3接受请求的输入,包括:
- 处理查询参数——要查看某一页Spittle列表
- 处理路径参数——要根据给定的ID来展现一个特定的Spittle记录
嗯试一试就行了不写了。。。进入5.4吧
实现用户注册功能,即实现/spitter/register路由
利用Spitter类来表示用户:
package spittr;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
public class Spitter {
private Long id;
private String username;
private String password;
private String firstName;
private String lastName;
private String email;
public Spitter() {}
public Spitter(String username, String password, String firstName, String lastName, String email) {
this(null, username, password, firstName, lastName, email);
}
public Spitter(Long id, String username, String password, String firstName, String lastName, String email) {
this.id = id;
this.username = username;
this.password = password;
this.firstName = firstName;
this.lastName = lastName;
this.email = email;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public boolean equals(Object that) {
return EqualsBuilder.reflectionEquals(this, that, "firstName", "lastName", "username", "password", "email");
}
@Override
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this, "firstName", "lastName", "username", "password", "email");
}
}
控制器SpitterController处理对/spitter/register的GET请求:
package spittr.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
@RequestMapping(value = "/spitter")
public class SpitterController {
@RequestMapping(value = "/register", method = RequestMethod.GET)
public String showRegisterForm() {
return "registerForm";
}
}
写registerForm.jsp:
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page session="false" %>
<html>
<head>
<title>Spittr</title>
<link rel="stylesheet" type="text/css" href="<c:url value="/WEB-INF/resources/style.css" />">
</head>
<body>
<h1>Register</h1>
<form method="post">
First Name:<input type="text" name="firstName" /><br>
Last Name:<input type="text" name="lastName" /><br>
UserName:<input type="text" name="username" /><br>
Password:<input type="password" name="password" /><br>
<input type="submit" value="Register">
</form>
</body>
</html>
这里form没有设置action属性,因此当表单提交时,它会提交到与展现时相同的URL路径上,即“/spitter/register”。这就意味着需要在服务器端处理该HTTP POST请求,因此在SpitterController中再添加一个方法来处理这个表单提交。
当处理注册表单的POST请求时,控制器接受表单数据并将其保存为Spitter对象,为了防止重复提交进行重定向到/spitter/username,重定向后显示个人的profile页面
SpitterController类中:
package spittr.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import spittr.Spitter;
import spittr.data.SpitterRepository;
@Controller
@RequestMapping(value = "/spitter")
public class SpitterController {
private SpitterRepository spitterRepository;
@Autowired
public SpitterController(SpitterRepository spitterRepository){
this.spitterRepository=spitterRepository;
}
@RequestMapping(value = "/register",method = RequestMethod.GET)
public String showRegisterForm(){
return "registerForm";
}
@RequestMapping(value = "/register",method = RequestMethod.POST)
public String processRegisterForm(Spitter spitter){
spitterRepository.save(spitter);
return "redirect:/spitter/"+spitter.getUsername();
}
@RequestMapping(value = "/{username}",method = RequestMethod.GET)
public String showSpitterProfile(
@PathVariable("username") String username,
Model model
){
Spitter spitter=spitterRepository.findByUsername(username);
model.addAttribute(spitter);
return "profile";
}
}
profile.jsp:
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page session="false" %>
<html>
<head>
<title>Spittr</title>
<link rel="stylesheet"
type="text/css"
href="<c:url value="/WEB-INF/resources/style.css" />" >
</head>
<body>
<h1>Your Profile</h1>
<c:out value="${spitter.username}" /><br/>
<c:out value="${spitter.firstName}" />,
<c:out value="${spitter.lastName}" />
</body>
</html>
提交表单之后,设想应该有一个将用户添加到数据库的处理,控制器中有SpitterRepository的接口,但是还没有实现它,这里以Map<Spitter,String>代替数据库了。。。
SpitterRepository接口:
package spittr.data;
import spittr.Spitter;
public interface SpitterRepository {
void save(Spitter spitter);
Spitter findByUsername(String username);
}
SpitterRepositoryDAO实现:
package spittr.data;
import org.springframework.stereotype.Repository;
import spittr.Spitter;
import java.util.HashMap;
import java.util.Map;
@Repository
public class SpitterRepositoryDAO implements SpitterRepository{
public SpitterRepositoryDAO() {
}
Map<String,Spitter> spitterMap=new HashMap<>();
@Override
public void save(Spitter spitter){
spitterMap.put(spitter.getUsername(),spitter);
System.out.println("Save success");
}
@Override
public Spitter findByUsername(String username){
return spitterMap.get(username);
}
}
5.校验器
校验表单:限制输入域值的长度,防止为空等
使用Spring对Java校验API(Java Validation API,又称JSR-303)的支持。
在Spitter类中,使用@NotNull和@Size注解:
private Long id;
@NotNull
@Size(min=5, max=16)
private String username;
@NotNull
@Size(min=5, max=25)
private String password;
@NotNull
@Size(min=2, max=30)
private String firstName;
@NotNull
@Size(min=2, max=30)
private String lastName;
@NotNull
@Email
private String email;
在pom.xml添加依赖:
<!-- 校验器 -->
<!-- https://mvnrepository.com/artifact/javax.validation/validation-api -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>1.1.0.Final</version>
</dependency>
<dependency>
<groupId>javax.el</groupId>
<artifactId>el-api</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.1.0.Final</version>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<version>3.3.2.Final</version>
</dependency>
<dependency>
<groupId>com.fasterxml</groupId>
<artifactId>classmate</artifactId>
<version>1.3.3</version>
</dependency>
在控制器中需要修改processRegisterForm()方法来应用校验功能:
@RequestMapping(value = "/register",method = RequestMethod.POST)
public String processRegisterForm(@Validated Spitter spitter, BindingResult result){ //校验Spitter输入
if(result.hasErrors()){
return "registerForm"; //如果校验出现错误,则重新返回表单
}
spitterRepository.save(spitter);
return "redirect:/spitter/"+spitter.getUsername();
}
理论上东西都实现完了。。。
一run定乾坤!
奈斯!
但是验证器没有起作用哈哈,第六章再说
溜了溜了
总结
下一章见!