Spring MVC概述
Spring MVC框架是一个开源的Java平台,为开发强大的基于Java的Web应用程序提供全面的基础架构支持非常容易和非常快速。
Spring web MVC框架提供了MVC(模型 - 视图 - 控制器)架构和用于开发灵活和松散耦合的Web应用程序的组件。
MVC模式导致应用程序的不同方面(输入逻辑,业务逻辑和UI逻辑)分离,同时提供这些元素之间的松散耦合。
-
模型(Model)封装了应用程序数据,通常它们将由POJO类组成。
-
视图(View)负责渲染模型数据,一般来说它生成客户端浏览器可以解释HTML输出。
-
控制器(Controller)负责处理用户请求并构建适当的模型,并将其传递给视图进行渲染。
项目源码详细介绍
该项目实现了智能解析用户输入语句的语义,输出结果以JSON格式展示。
源码下载:SpringMVCDemoNLI
1、 创建目录结构
创建一个Maven工程为:SpringMVCDemoNLI。下图为最终的目录结构:
2、 配置pom.xml
<?xml version="1.0"?>
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>com.demo</groupId>
<artifactId>SpringMVCDemoNLI</artifactId>
<packaging>war</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>SpringMVCDemoNLI Maven Webapp</name>
<url>http://maven.apache.org</url>
<properties>
<springframework.version>4.0.6.RELEASE</springframework.version>
<hibernate.validator.version>5.1.2.Final</hibernate.validator.version>
<javax.validation.version>1.1.0.Final</javax.validation.version>
<!-- 主要依赖库的版本定义 -->
<httpclient.version>4.5.3</httpclient.version>
<commons-lang3.version>3.5</commons-lang3.version>
<commons-codec.version>1.10</commons-codec.version>
<commons-logging.version>1.2</commons-logging.version>
<httpcore.version>4.4.6</httpcore.version>
<taglibs-standard.version>1.2.5</taglibs-standard.version>
<fastjson.version>1.2.34</fastjson.version>
</properties>
<dependencies>
<!-- Spring dependencies -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${springframework.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${springframework.version}</version>
</dependency>
<!-- httpclient -->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>${commons-codec.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>${commons-lang3.version}</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>${commons-logging.version}</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>${httpcore.version}</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>${httpclient.version}</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>${httpclient.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<!-- jsr303 validation dependencies -->
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>${javax.validation.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>${hibernate.validator.version}</version>
</dependency>
<!-- Servlet dependencies -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
<build>
<defaultGoal>compile</defaultGoal>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<finalName>SpringMVCDemoNLI</finalName>
</build>
</project>
3、创建POJO/域对象
保存用户提交的数据,并使用验证注释验证属性。
package com.demo.model;
import java.io.Serializable;
import javax.validation.constraints.Size;
public class CorpusInfo implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
@Size(min=2, max=30)
private String corpus;
public String getCorpus() {
return corpus;
}
public void setCorpus(String corpus) {
this.corpus = corpus;
}
@Override
public String toString() {
return "";
}
}
4、添加控制器
控制器处理GET和POST请求。
@Controller表明这个类是一个控制器在处理具有模式映射的@RequestMapping请求。这里使用 ‘/’, 它被作为默认的控制器。
方法inputCorpus注解为RequestMethod.GET服务默认是GET请求,并呈现包含空白表单的网页。
方法nliProcess注解为RequestMethod.POST服务处理表单提交POST请求。
@Valid要求spring来验证相关的对象(CorpusInfo)。
package com.demo.controller;
import javax.validation.Valid;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.demo.model.CorpusInfo;
import com.demo.util.NLIProcess;
@Controller
@RequestMapping("/")
public class DemoController {
private NLIProcess nli = new NLIProcess();
@RequestMapping(method = RequestMethod.GET)
public String inputCorpus(ModelMap model) {
CorpusInfo cinfo = new CorpusInfo();
model.addAttribute("cinfo", cinfo);
return "enroll";
}
@RequestMapping(method=RequestMethod.POST)
public String nliProcess(@Valid @ModelAttribute("cinfo") CorpusInfo cinfo,
BindingResult result, ModelMap model) {
if (result.hasErrors()) {
return "enroll";
}
model.addAttribute("answer", nli.getAnswer(cinfo.getCorpus()));
return "success";
}
}
nli.getAnswer(cinfo.getCorpus())实现语义解析,这里是使用的欧拉蜜开放平台智能语义解析。
package com.demo.util;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
public class NLIProcess {
private static final String url = "https://cn.olami.ai/cloudservice/api";
private static final String Appkey = "fcf20941682b494e9db8c23c14deeb74";
private static final String Appsecret = "1bcbbe1fb5924185a13c9b5f6d548ee0";
private static final String api = "nli";
private JSONObject process (String input) {
JSONObject NLIresult = new JSONObject();
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("appkey", Appkey));
params.add(new BasicNameValuePair("api", api));
long timestamp = Calendar.getInstance().getTimeInMillis();
params.add(new BasicNameValuePair("timestamp", String.valueOf(timestamp)));
params.add(new BasicNameValuePair("sign", generateSign(timestamp)));
JSONObject request = new JSONObject();
JSONObject data = new JSONObject();
try {
data.put("input_type", 0);
data.put("text", input);
request.put("data_type", "stt");
request.put("data", data);
} catch (JSONException e1) {
e1.printStackTrace();
return NLIresult;
}
params.add(new BasicNameValuePair("rq", request.toString()));
params.add(new BasicNameValuePair("cusid", "asdfghj"));
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpPost httppost = new HttpPost(url);
try {
httppost.setEntity(new UrlEncodedFormEntity(params, "UTF-8"));
CloseableHttpResponse response = httpclient.execute(httppost);
try {
HttpEntity entity = response.getEntity();
if (entity != null) {
String contnt = EntityUtils.toString(entity);
NLIresult = JSONObject.parseObject(contnt);
}
} finally {
response.close();
}
} catch (Exception e) {
e.printStackTrace();
return NLIresult;
} finally {
try {
httpclient.close();
} catch (Exception e) {
e.printStackTrace();
}
}
return NLIresult;
}
private String generateSign(long timestamp) {
String sign = Appsecret + "api=" + api + "appkey=" + Appkey + "timestamp=" + timestamp + Appsecret;
return MD5String(sign);
}
public String MD5String(String str) {
try {
MessageDigest msgDigest = MessageDigest.getInstance("MD5");
msgDigest.reset();
msgDigest.update(str.getBytes("UTF-8"));
byte[] byteArrary = msgDigest.digest();
StringBuffer md5StrBuff = new StringBuffer();
for (int i = 0; i < byteArrary.length; i++) {
String tmp = Integer.toHexString(0xFF & byteArrary[i]);
if (tmp.length() == 1) {
md5StrBuff.append(0).append(tmp);
} else {
md5StrBuff.append(tmp);
}
}
return md5StrBuff.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return null;
}
}
public String getAnswer(String corpus){
if(corpus != null && !corpus.equals("")){
JSONObject jsObj = process(corpus);
return formatJson(jsObj);
}
return "请输入正确的语料!";
}
private static String appendJson(String str, int count) {
String retStr = "<br>";
for (int i = 0; i < count; i++) {
retStr += str;
}
return retStr;
}
/**
* 将json转成便于阅读的格式
* @param oldJson
* @return
*/
public static String formatJson(JSONObject old) {
int i = 0;
String space = " ";
String formatJson = "";
int indentCount = 0;
Boolean isStr = false;
String currChar = "";
String oldJson = old.toString();
for (i = 0; i < oldJson.length(); i++) {
currChar = oldJson.substring(i, i + 1);
switch (currChar) {
case "{":
case "[":
if (!isStr) {
indentCount++;
formatJson += currChar + appendJson(space, indentCount);
} else {
formatJson += currChar;
}
break;
case "}":
case "]":
if (!isStr) {
indentCount--;
formatJson += appendJson(space, indentCount) + currChar;
} else {
formatJson += currChar;
}
break;
case ",":
if (!isStr) {
formatJson += "," + appendJson(space, indentCount);
} else {
formatJson += currChar;
}
break;
case ":":
if (!isStr) {
formatJson += ": ";
} else {
formatJson += currChar;
}
break;
case " ":
case "\n":
case "\t":
if (isStr) {
formatJson += currChar;
}
break;
case "\"":
if (i > 0 && !oldJson.substring(i - 1, i).equals("\\")) {
isStr = !isStr;
}
formatJson += currChar;
break;
default:
formatJson += currChar;
break;
}
}
return formatJson;
}
}
5、添加配置类
@Configuration指示该类包含注解为@Bean生产Bean管理是由Spring容器的一个或多个 bean 的方法。
@EnableWebMvc 等效于 mvc:annotation-driven 在XML文件中。
@ComponentScan 等效于 context:component-scan base-package=”…” 提供具有到哪里查找管理Spring beans/类。
方法ViewResolver 配置一个 ViewResolver 用来找出真正的视图。
方法 addResourceHandlers 配置 ResourceHandler 静态资源。例子中,将css文件放在 Web应用程序的 /static/css 目录中。
package com.demo.configuration;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.demo")
public class DemoConfiguration extends WebMvcConfigurerAdapter {
@Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
@Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("messages");
return messageSource;
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**").addResourceLocations("/static/");
}
}
方法messageSource 配置消息包,以支持[国际化]消息属性文件。Spring 将搜索应用程序类路径中一个名为messages.properties文件:
Size.cinfo.corpus=corpus must be between {2} and {1} characters long
6、添加视图(JSP页面)
添加两个JSP页面。第一个将包含一个表单,从用户接收输入,第二个是当表单输入验证成功时会显示成功消息给用户。
WEB-INF/views/enroll.jsp
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=Utf-8">
<title>智能语义解析——基于欧拉蜜开放平台</title>
<link href="<c:url value='/static/css/custom.css' />" rel="stylesheet"></link>
</head>
<body>
<div class="form-container">
<h1>智能语义理解</h1>
<form:form method="POST" modelAttribute="cinfo"
class="form-horizontal">
<div class="row">
<label class="form-group col-md-1" for="corpus">输入语句:</label>
<div class="form-group col-md-2">
<form:input type="text" path="corpus" id="corpus"
class="form-control input-sm" />
<div class="has-error">
<form:errors path="corpus" class="help-inline" />
</div>
</div>
<div class="form-group floatRight">
<input type="submit" value="send" class="btn btn-primary btn-sm">
</div>
</div>
<div class="row" style="color:#006600">
<label class="form-group col-md-1" for="corpus"><br>目前支持的语义解析领域有以下六个模块:</label>
</div>
<div class="row" style="color:#006600">
<label class="form-group col-md-1" for="corpus">新闻:</label>
<div class="form-group col-md-2">
<br>
app:news<br>
语料示例:<br>
今天的新闻<br>
有哪些娱乐新闻<br>
我要看新闻 <br>
新闻头条<br>
</div>
</div>
<div class="row" style="color:#006600">
<label class="form-group col-md-1" for="corpus">星座:</label>
<div class="form-group col-md-2">
<br>
app:horoname<br>
语料示例:<br>
天蝎座的运势<br>
天蝎座的本月运势<br>
帮我查查明日运势,天蝎座的<br>
</div>
</div>
<div class="row" style="color:#006600">
<label class="form-group col-md-1" for="corpus">公交:</label>
<div class="form-group col-md-2">
<br>
app:busline<br>
语料示例:<br>
615路的行车路线<br>
112路有哪些站点<br>
张江南环线停靠哪些站<br>
110路的公交路线<br>
</div>
</div>
<div class="row" style="color:#006600">
<label class="form-group col-md-1" for="corpus">成语:</label>
<div class="form-group col-md-2">
<br>
app:idioms<br>
语料示例:<br>
告诉我画龙点睛是什么意思<br>
画龙点睛的含义<br>
有没有哪个成语叫龙什么的<br>
带龙的成语有哪些<br>
</div>
</div>
<div class="row" style="color:#006600">
<label class="form-group col-md-1" for="corpus">笑话:</label>
<div class="form-group col-md-2">
<br>
app:joke<br>
语料示例:<br>
讲笑话<br>
给我讲个笑话吧<br>
来张趣图<br>
我要看笑话<br>
</div>
</div>
<div class="row" style="color:#006600">
<label class="form-group col-md-1" for="corpus">解梦:</label>
<div class="form-group col-md-2">
<br>
app:oneiromancy<br>
语料示例:<br>
给我查查梦到了黄金有什么寓意<br>
昨天做梦梦到黄金了<br>
梦到黄金了有什么说法<br>
我昨天做了一个关于黄金的梦<br>
</div>
</div>
</form:form>
</div>
</body>
</html>
WEB-INF/views/success.jsp
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>智能语义解析——基于欧拉蜜开放平台</title>
<link href="<c:url value='/static/css/custom.css' />" rel="stylesheet"></link>
</head>
<body>
<div class="success">
<div style="color:#006600">
news支持类别有:头条(importnews),娱乐(ent),体育(sports),军事(mil),金融(finance),财经(fortune),教育(edu),法治(fz),健康(health),社会(society)。<br>
horoname支持查询的运势有:今日运势、明日运势、本周运势、本月运势、今年运势。<br> <br>
app:模块名称。 <br> modifier:表示一句话的操作意图。 如语句 查看娱乐新闻 的意图为 查,
即用“query”来表示。 <br> slot:表示需要从语料中提取出来的关键字。 如语句 查看娱乐新闻 的意图已知道是
查,而查什么则是由slot来补充,即slot名字为type,值为从语料中提取出来的词语“娱乐”。<br>
更多详细介绍可参考:
<a href="https://cn.olami.ai/wiki/?mp=nli&content=nli_response_result.html">https://cn.olami.ai/wiki/?mp=nli&content=nli_response_result.html</a>
</div>
<br>
<br> ${answer}
</div>
</body>
</html>
static/css/custom.css
.form-container {
position:fiexd;
width:30%;
margin-left: 20px;
margin-top: 20px;
margin-bottom: 20px;
padding: 10px;
background-color: #E8E1E1;
border: 1px solid #ddd;
border-radius: 4px;
}
.form-group {
display: inline-block;
margin-bottom: 0;
vertical-align: middle;
}
.input-sm {
width: 400px;
height: 30px;
padding: 5px 10px;
font-size: 12px;
line-height: 1.5;
border-radius: 3px;
}
.floatRight{
float:right;
margin-right: 18px;
}
.row {
margin-right: 15px;
margin-left: 15px;
}
.col-md-1, .col-md-2 {
position: relative;
min-height: 1px;
padding-right: 5px;
padding-left: 5px;
}
.has-error{
color:red;
}
.success{
position:fiexd;
width:50%;
margin-left: 50px;
margin-top:20px;
padding: 20px;
background-color: #E8E1E1;
border: 1px solid #ddd;
border-radius: 4px;
}
7、添加初始化器类
package com.demo.configuration;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
public class DemoInitializer implements WebApplicationInitializer {
public void onStartup(ServletContext container) throws ServletException {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(DemoConfiguration.class);
ctx.setServletContext(container);
ServletRegistration.Dynamic servlet = container.addServlet(
"dispatcher", new DispatcherServlet(ctx));
servlet.setLoadOnStartup(1);
servlet.addMapping("/");
}
}
8、构建和部署应用程序
运行应用程序,访问URL:http://localhost:8080/SpringMVCDemoNLI
得到的初始页面如下图所示:
提交不正确输入时,会得到验证错误(在 message.properties 中用户定义的消息)
提交成功后跳转页面如下: