很多时候前端都需要调用后台服务实现交互功能,常见的数据交换格式多是JSON或XML,这里主要讲解Spring MVC为前端提供JSON格式的数据并实现与前台交互。RESTful则是一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。

一、JSON

1.1、概要

JSON(JavaScript Object Notation, JS 对象标记) 是一种轻量级的数据交换格式。它基于 ECMAScript (w3c制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。

在 JS 语言中,一切都是对象。因此,任何支持的类型都可以通过 JSON 来表示,例如字符串、数字、对象、数组等。但是对象和数组是比较特殊且常用的两种类型。

要实现从对象转换为 JSON 字符串,使用 JSON.stringify() 方法:
var json = JSON.stringify({a: 'Hello', b: 'World'}); //结果是 '{"a": "Hello", "b": "World"}'
要实现从 JSON 转换为对象,使用 JSON.parse() 方法:
var obj = JSON.parse('{"a": "Hello", "b": "World"}'); //结果是 {a: 'Hello', b: 'World'}

示例:

<!DOCTYPE html>
<html> <head>
<meta charset="UTF-8">
<title></title>
<!--[if lte IE 8]>
<script type="text/javascript" src="//res.wx.qq.com/a/wx_fed/webwx/res/json3.min.js"></script>
<![endif]-->
</head> <body>
<script type="text/javascript">
//js对象
var user = {
"name": "张学友",
"address": "中国香港"
};
//将对象转换成字符
var str = JSON.stringify(user);
alert(str);
//将字符串转换成json对象
var zxy = JSON.parse(str);
alert(zxy.name + "," + zxy.address);
</script>
</body> </html>

结果:

1.2、使用ModelAndView

修改pom.xml添加对jackson的依赖

        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.2</version>
</dependency>

在user控制器中添加一个action

    @RequestMapping(value = "/users")
public ModelAndView users(){
ModelAndView mav=new ModelAndView(new MappingJackson2JsonView());
mav.addObject(userService.queryAllUsers());
return mav;
}

运行结果:

1.3、使用@ResponseBody与Jackson

修改pom.xml添加对jackson的依赖

        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.2</version>
</dependency>

添加一个action,使用注解@ResponseBody,响应主体而不是路径

    @RequestMapping(value = "/userJson",produces = "application/json;charset=utf-8")
@ResponseBody
public String userJson(){
ObjectMapper mapper=new ObjectMapper();
try {
return mapper.writeValueAsString(userService.queryAllUsers());
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return null;
}

结果:

1.4、乱码问题

1.4.1、方法一在action上声明编码格式

@RequestMapping(path="/json",produces = "application/json;charset=UTF-8")

1.4.2、方法二修改Spring配置文件

上一种方法比较麻烦,如果项目中有许多action则每一个都要添加,可以通过Spring配置统一指定

<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg value="UTF-8"/>
</bean>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
<property name="failOnEmptyBeans" value="false"/>
</bean>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>

1.5、日期格式化问题

默认日期格式会变成一个数字,是1970年1月1日到当前日期的毫秒数:

Jackson 默认是转成timestamps形式

1.5.1、方法一注解字段

在实体字段上使用@JsonFormat注解格式化日期

@JsonFormat(locale="zh", timezone="GMT+8", pattern="yyyy-MM-dd HH:mm:ss")

代码:

    /**
* 出生日期
*/
@JsonFormat(locale="zh", timezone="GMT+8", pattern="yyyy-MM-dd HH:mm:ss")
private Date birthday;

结果:

1.5.2、方法二取消timestamps形式

如果只取消则会得到一个默认的日期格式,效果如下:

当然自定义输出格式是允许的

    @RequestMapping(value = "/userJson",produces = "application/json;charset=utf-8")
@ResponseBody
public String userJson(){
ObjectMapper mapper=new ObjectMapper();
//不使用时间差的方式
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); //自定义日期格式对象
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//指定日期格式
mapper.setDateFormat(sdf);
try {
return mapper.writeValueAsString(userService.queryAllUsers());
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return null;
}

运行结果:

1.6、工具类

工具类可以复用代码,提高开发效率,如上文中的序列化JSON:

package com.zhangguo.springmvc08.utils;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature; import java.text.SimpleDateFormat; /**
* JSON工具类,辅助类
* */
public class JsonUtil {
public static String getJson(Object object) {
return getJson(object,"yyyy-MM-dd HH:mm:ss");
} public static String getJson(Object object,String dateFormat) {
ObjectMapper mapper = new ObjectMapper();
//不使用时间差的方式
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
//自定义日期格式对象
SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
//指定日期格式
mapper.setDateFormat(sdf);
try {
return mapper.writeValueAsString(object);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return null;
}
}

调用:

    @RequestMapping(value = "/userJson",produces = "application/json;charset=utf-8")
@ResponseBody
public String userJson(){
return JsonUtil.getJson(userService.queryAllUsers(),"yyyy-MM-dd");
}

如对MySQL数据库的访问封装:

package com.zhangguo.springmvc08.utils; 

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map; public class JDBCUtil { public static String DRIVER="com.mysql.jdbc.Driver";
public static String URL="jdbc:mysql://127.0.0.1:3306/schoolmis?useUnicode=true&characterEncoding=UTF-8";
public static String USER_NAME="root";
public static String PASSWORD="pwd"; //加载驱动
static {
try {
Class.forName(DRIVER);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
} private JDBCUtil() { } /**
* 获得连接
*
* @return
*/
public static Connection getconnnection() {
Connection con = null;
try {
con = DriverManager.getConnection(URL, USER_NAME, PASSWORD);
} catch (SQLException e) {
e.printStackTrace();
}
return con;
} /**
* 关闭连接
*
* @param rs
* @param st
* @param con
*/
public static void close(ResultSet rs, Statement st, Connection con) {
try {
try {
if (rs != null) {
rs.close();
}
} finally {
try {
if (st != null) {
st.close();
}
} finally {
if (con != null)
con.close();
}
}
} catch (SQLException e) {
e.printStackTrace();
}
} /**
* 关闭连接
*
* @param rs
*/
public static void close(ResultSet rs) {
Statement st = null;
Connection con = null;
try {
try {
if (rs != null) {
st = rs.getStatement();
rs.close();
}
} finally {
try {
if (st != null) {
con = st.getConnection();
st.close();
}
} finally {
if (con != null) {
con.close();
}
}
}
} catch (SQLException e) {
e.printStackTrace();
}
} /**
* 关闭连接
*
* @param st
* @param con
*/
public static void close(Statement st, Connection con) {
try {
try {
if (st != null) {
st.close();
}
} finally {
if (con != null)
con.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
} /**
* insert/update/delete
* 增加/更新/删除
*
* @param sql 数据库语句
* @param args 可变参数(可以不带参数,可以带0-n个参数)
* @return
*/
public static int update(String sql, Object... args) {
int result = 0;
Connection con = getconnnection();
PreparedStatement ps = null;
try {
ps = con.prepareStatement(sql);
if (args != null) {
for (int i = 0; i < args.length; i++) {
ps.setObject((i + 1), args[i]);
}
}
result = ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(ps, con);
} return result;
} /**
* query, because need to manually close the resource, so not recommended
* for use it
*
* @param sql
* @param args
* @return ResultSet
*/
@Deprecated //注解
public static ResultSet query(String sql, Object... args) {
ResultSet result = null;
Connection con = getconnnection();
PreparedStatement ps = null;
try {
ps = con.prepareStatement(sql);
if (args != null) {
for (int i = 0; i < args.length; i++) {
ps.setObject((i + 1), args[i]);
}
}
result = ps.executeQuery();
} catch (SQLException e) {
e.printStackTrace();
}
return result;
} /**
* Query a single record
* 查询单个记录
* @param sql
* @param args
* @return Map<String,Object>
*/
public static Map<String, Object> queryForMap(String sql, Object... args) {
Map<String, Object> result = new HashMap<String, Object>();
List<Map<String, Object>> list = queryForList(sql, args);
if (list.size() > 0) {
result = list.get(0);
}
return result;
} /**
* Query a single record
* 查询单个记录返回强类型对象
* @param sql
* @param args
* @return <T> //泛型
*/
public static <T> T queryForObject(String sql, Class<T> clz, Object... args) {
T result = null;
List<T> list = queryForList(sql, clz, args);
if (list.size() > 0) {
result = list.get(0);
}
return result;
} /**
* Query a single record
*
* @param sql
* @param args
* @return List<Map<String,Object>>
*/
public static List<Map<String, Object>> queryForList(String sql, Object... args) {
List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
Connection con = null;
ResultSet rs = null;
PreparedStatement ps = null;
try {
con = getconnnection();
ps = con.prepareStatement(sql);
if (args != null) {
for (int i = 0; i < args.length; i++) {
ps.setObject((i + 1), args[i]);
}
}
rs = ps.executeQuery();
ResultSetMetaData rsmd = rs.getMetaData();
int columnCount = rsmd.getColumnCount();
while (rs.next()) {
Map<String, Object> map = new HashMap<String, Object>();
for (int i = 1; i <= columnCount; i++) {
map.put(rsmd.getColumnLabel(i), rs.getObject(i));
}
result.add(map);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
close(rs, ps, con);
}
return result;
} /**
* Query records
* 查询多个对象,返回强类型集合
* @param sql
* @param args
* @return List<T>
*/
public static <T> List<T> queryForList(String sql, Class<T> clz, Object... args) {
List<T> result = new ArrayList<T>();
Connection con = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
con = getconnnection();
ps = con.prepareStatement(sql);
if (args != null) {
for (int i = 0; i < args.length; i++) {
ps.setObject((i + 1), args[i]);
}
}
rs = ps.executeQuery();
ResultSetMetaData rsmd = rs.getMetaData();
int columnCount = rsmd.getColumnCount();
while (rs.next()) {
T obj = clz.newInstance();
for (int i = 1; i <= columnCount; i++) {
String columnName = rsmd.getColumnName(i);
String methodName = "set" + columnName.substring(0, 1).toUpperCase()
+ columnName.substring(1, columnName.length());
Method method[] = clz.getMethods();
for (Method meth : method) {
if (methodName.equals(meth.getName())) {
meth.invoke(obj, rs.getObject(i));
}
}
}
result.add(obj);
}
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} finally {
close(rs, ps, con);
}
return result;
}
}

1.7、数据持久化

上一章的示例中并没有直接访问数据库,数据以集合的形式存放在内存中,这里使用MySQL将数据存储到数据库中。该示例基于第8章的示例,请先熟悉第8章的内容《Spring MVC 学习总结(八)——Spring MVC概要与环境配置(IDEA+Maven+Tomcat7+JDK8、示例与视频)》

1.7.1、创建数据库与表

开启MySQL服务

打开管理工具Navicat

创建数据库

新建表

/*
Navicat MySQL Data Transfer Source Server : localhostMe
Source Server Version : 50506
Source Host : localhost:3306
Source Database : mvcdb Target Server Type : MYSQL
Target Server Version : 50506
File Encoding : 65001 Date: 2017-12-07 14:08:35
*/ SET FOREIGN_KEY_CHECKS=0; -- ----------------------------
-- Table structure for `user`
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '编号',
`name` varchar(32) NOT NULL COMMENT '姓名',
`birthday` datetime DEFAULT NULL COMMENT '生日',
`address` varchar(128) DEFAULT NULL COMMENT '地址',
`phone` varchar(11) DEFAULT NULL COMMENT '电话',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ----------------------------
-- Records of user
-- ----------------------------

1.7.2、添加测试数据

insert into user(name,birthday,address,phone)
select '张学友','1968-09-08','中国香港','' union
select '张惠妹','1969-01-05','中国北京','' union
select '张国立',SYSDATE(),'中国珠海','' select id,name,birthday,address,phone from user;

1.7.3、添加数据库驱动修改连接信息

在pom.xml中依赖MySQL驱动包

        <!--mysql驱动包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.35</version>
</dependency>

修改工具类的中数据库连接信息

    public static String DRIVER="com.mysql.jdbc.Driver";
public static String URL="jdbc:mysql://127.0.0.1:3306/mvcdb?useUnicode=true&characterEncoding=UTF-8";
public static String USER_NAME="root";
public static String PASSWORD="pwd";

1.7.4、新增UserDAOPro

新增UserDAOPro类,实现MySQL数据库访问,代码如下:

package com.zhangguo.springmvc08.dao;

import com.zhangguo.springmvc08.entity.User;
import com.zhangguo.springmvc08.utils.JDBCUtil;
import org.springframework.stereotype.Repository; import java.util.List; @Repository("mysql")
public class UserDAOPro implements IUserDAO {
public List<User> getAll() {
return JDBCUtil.queryForList("select id,name,birthday,address,phone from user", User.class);
} public User getUserById(int id) {
return JDBCUtil.queryForObject("select id,name,birthday,address,phone from user where id=?", User.class, id);
} public boolean add(User user) {
return JDBCUtil.update("insert into user(name,birthday,address,phone) values(?,?,?,?)", user.getName(), user.getBirthday(), user.getAddress(), user.getPhone()) > 0;
} public boolean delete(int id) {
return JDBCUtil.update("delete from user where id=?", id) > 0;
} public boolean update(User user) {
return JDBCUtil.update("update user set name=?,birthday=?,address=?,phone=? where id=?", user.getName(), user.getBirthday(), user.getAddress(), user.getPhone(), user.getId()) > 0;
}
}

1.7.5、修改用户业务类

因为系统中有两个类实现了IUserDAO,指定名称:

package com.zhangguo.springmvc08.service;

import com.zhangguo.springmvc08.dao.IUserDAO;
import com.zhangguo.springmvc08.dao.UserDAO;
import com.zhangguo.springmvc08.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service; import javax.annotation.Resource;
import java.util.List; /**用户业务*/
@Service
public class UserService { @Resource(name="mysql")
IUserDAO userdao; public List<User> queryAllUsers(){
return userdao.getAll();
} public User getUserById(int id){
return userdao.getUserById(id);
} public boolean deleteUser(int id){
return userdao.delete(id);
} public boolean addUser(User user){
return userdao.add(user);
} public boolean editUser(User user){
return userdao.update(user);
} }

1.7.6、彻底解决Spring MVC 中文乱码

添加用户后发现有乱码,调试发现发送到服务器的数据已经是乱码

1、页面编码

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>

2、URL中的乱码

改tomcat中server.xml中Connector的port=“8080”,加上一个 URIEncoding=”utf-8”

3、配置过滤器,指定所有请求的编码

修改web.xml,添加编码过滤器

    <filter>
<filter-name>encodingFilter</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>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

4、文件编码

将文件另存为utf-8格式

5、数据库编码

连接字符串指定编码格式

public static String URL="jdbc:mysql://127.0.0.1:3306/mvcdb?useUnicode=true&characterEncoding=UTF-8"

创建数据库时指定utf-8编码格式

最终运行结果正常:

二、RESTful

2.1、概要

REST(英文:Representational State Transfer,简称REST,表述性状态转移)描述了一个架构样式的网络系统,比如 web 应用程序。它首次出现在 2000 年 Roy Fielding 的博士论文中,他是 HTTP 规范的主要编写者之一。在目前主流的三种Web服务交互方案中,REST相比于SOAP(Simple Object Access protocol,简单对象访问协议)以及XML-RPC更加简单明了,无论是对URL的处理还是对Payload的编码,REST都倾向于用更加简单轻量的方法设计和实现。值得注意的是REST并没有一个明确的标准,而更像是一种设计的风格。

RESTful架构,就是目前最流行的一种互联网软件架构。它结构清晰、符合标准、易于理解、扩展方便,所以正得到越来越多网站的采用。

GET /tickets # 获取ticket列表
GET /tickets/12 # 查看某个具体的ticket
POST /tickets # 新建一个ticket
PUT /tickets/12 # 更新ticket 12.
DELETE /tickets/12 #删除ticekt 12

REST特点如下:

  • 基于HTTP协议
  • 是另一种服务架构
  • 传递是JSON、POX(Plain Old XML)而不是SOAP格式的数据
  • 充分利用HTTP谓词(Verb)
  • 侧重数据的传输,业务逻辑交给客户端自行处理

REST是一种分布式服务架构的风格约束,像Java、.Net(WCF、WebAPI)都有对该约束的实现,使URL变得更加有意义,更加简洁明了,如:

http://www.zhangguo.com/products/1 get请求 表示获得所有产品的第1个

http://www.zhangguo.com/products/product post请求 表示添加一个产品

http://www.zhangguo.com/products/1/price get请求 表示获得第1个产品的价格

http://www.zhangguo.com/products/1 delete请求 删除编号为1的产品

REST设计需要遵循的原则:

  • 网络上的所有事物都被抽象为资源(resource);
  • 每个资源对应一个唯一的资源标识符(resource identifier);
  • 通过通用的连接器接口(generic connector interface)对资源进行操作;
  • 对资源的各种操作不会改变资源标识符;
  • 所有的操作都是无状态的(stateless)

谓词
GET
表示查询操作,相当于Retrieve、Select操作
POST
表示插入操作,相当于Create,Insert操作
PUT
表示修改操作,相当于Update操作
DELETE
表示删除操作,相当于Delete操作

其它还有:

2.2、@RestController

Spring 4.0重要的一个新的改进是@RestController注解,它继承自@Controller注解。4.0之前的版本,Spring MVC的组件都使用@Controller来标识当前类是一个控制器servlet。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {
String value() default "";
}

使用这个注解,我们可以开发REST服务的时候不需要使用@Controller而专门的@RestController。

当你实现一个RESTful web services的时候,response将一直通过response body发送。为了简化开发,Spring 4.0提供了一个专门版本的controller。

添加了AsyncRestTemplate类,当开发REST客户端时允许非阻塞异步支持。

2.2.1、Hello World

默认控制器与Action

package com.zhangguo.springmvc08.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping; @Controller //声明为控制器
@RequestMapping(path = {"/","Home","First"}) //请求映射
public class HomeController {
@RequestMapping(path = "/index") //请求映射
public String index(Model model){
model.addAttribute("message","Hello Spring MVC!");
return "home/index";
} @RequestMapping(path = "/") //请求映射
public String first(Model model){
model.addAttribute("message","Hello Spring MVC,Welcome Page!");
return "home/index";
}
}

修改pom.xml添加jackson的引用

新增一个控制器,代码如下:

package com.zhangguo.springmvc08.controller;

import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController; @RestController
@RequestMapping(path="/users")
public class UsersController { @RequestMapping(path = "/{name}",method = RequestMethod.GET)
public String hello(@PathVariable String name){
return "Hello "+name;
} @RequestMapping(path = "/stu/{name}",method = RequestMethod.GET)
public Student student(@PathVariable String name){
return new Student("Hello "+name);
} } /**学生*/
class Student{
public Student(String name) {
this.name = name;
} private String name; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
}
}

运行结果:

从上面的示例可以看出,使用@RestController后返回的字符串不再是路径,如果返回的是对象则会直接序列化,可以是JSON或XML;如果返回的是对象类型则直接序列化成JSON格式,请注意添加对Jackson的依赖。

2.3、RESTful员工管理示例

假定要为员工(emp)提供对外的REST服务,接口如下:

/emps  get 获得所有的员工信息

/emps/1 get 获得编号为1的员工信息

/emps post 添加

/emps put 修改

/emps/1 delete 删除

2.3.1、获得所有的员工信息服务

/emps  get 获得所所有的员工信息

package com.zhangguo.springmvc08.controller;

import com.zhangguo.springmvc08.entity.User;
import com.zhangguo.springmvc08.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController; import java.util.List; @RestController
@RequestMapping(path = "/emps")
public class EmpController { @Autowired
UserService userService; @RequestMapping(path = "", method = RequestMethod.GET)
public List<User> getAllemps() {
return userService.queryAllUsers();
} }

结果:

2.3.2、获得指定编号的员工信息服务

/emps/1 get 获得编号为1的员工信息

代码:

    @RequestMapping(path = "/{id}", method = RequestMethod.GET)
public User getEmpById(@PathVariable int id) {
return userService.getUserById(id);
}

结果:

2.3.3、新增员工服务

/emps post 添加

代码:

    @RequestMapping(path = "", method = RequestMethod.POST)
public boolean addEmp(@RequestBody User user) {
return userService.addUser(user);
}

请求:

返回true

结果:

说明:参数中的json格式一定要使用标准格式,注意引号,注意Content-Type,默认的Content-Type类型是:application/x-www-form-urlencoded

因为我们使用json,则Content-Type的值应该为application/json;charset=utf-8

2.3.4、修改员工服务

/emps  修改 put请求

代码:

    @RequestMapping(path = "", method = RequestMethod.PUT)
public boolean updateEmp(@RequestBody User user) {
return userService.editUser(user);
}

测试:

结果:

2.3.3、删除员工服务

/emps/1 delete 删除

代码:

package com.zhangguo.springmvc08.controller;

import com.zhangguo.springmvc08.entity.User;
import com.zhangguo.springmvc08.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*; import java.util.List; @RestController
@RequestMapping(path = "/emps")
public class EmpController { @Autowired
UserService userService; @RequestMapping(path = "", method = RequestMethod.GET)
public List<User> getAllEmps() {
return userService.queryAllUsers();
} @RequestMapping(path = "/{id}", method = RequestMethod.GET)
public User getEmpById(@PathVariable int id) {
return userService.getUserById(id);
} @RequestMapping(path = "", method = RequestMethod.POST)
public boolean addEmp(@RequestBody User user) {
return userService.addUser(user);
} @RequestMapping(path = "", method = RequestMethod.PUT)
public boolean updateEmp(@RequestBody User user) {
return userService.editUser(user);
} @RequestMapping(path = "/{id}", method = RequestMethod.DELETE)
public AjaxState deleteEmpById(@PathVariable int id) {
Boolean result=userService.deleteUser(id);
return new AjaxState(result?"success":"error",id,result?"删除成功!":"删除失败");
} } class AjaxState{
public String state;
public Object data;
public String message; public AjaxState(String state, Object data, String message) {
this.state = state;
this.data = data;
this.message = message;
} public AjaxState(){}
}

测试:

结果:

已删除成功,delete请求不需要正文与get请求类似

2.4、AJAX客户端调用RESTful

ajax传送json格式数据,关键是指定contentType,data要是json格式

如果是restful接口,把type改成对应的post(增)、delete(删)、put(改)、get(查)即可

var post_data={"name":"test001","pass":"xxxx"};
$.ajax({
url: "http://192.168.10.111:8080/uc/login",
type: 'post',
contentType: "application/json; charset=utf-8",
data:JSON.stringify(post_data),
success:function (data) {
//调用成功
},
error: function(data, textStatus, errorThrown){
//调用失败
}
});

为了前端统一调用,修改后的控制器如下:

package com.zhangguo.springmvc08.controller;

import com.zhangguo.springmvc08.entity.User;
import com.zhangguo.springmvc08.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*; import java.util.List; @RestController
@RequestMapping(path = "/emps")
public class EmpController extends BaseController { @Autowired
UserService userService; @RequestMapping(path = "", method = RequestMethod.GET)
public AjaxState getAllEmps() {
List<User> users=userService.queryAllUsers();
boolean result=users!=null;
return new AjaxState(result?"success":"error",users,result?"获得数据成功!":"获得数据失败!");
} @RequestMapping(path = "/{id}", method = RequestMethod.GET)
public AjaxState getEmpById(@PathVariable int id) {
User user=userService.getUserById(id);
boolean result=user!=null;
return new AjaxState(result?"success":"error",user,result?"获得数据成功!":"获得数据失败!");
} @RequestMapping(path = "", method = RequestMethod.POST)
public AjaxState addEmp(@RequestBody User user) {
boolean result=userService.addUser(user);
return new AjaxState(result?"success":"error",user,result?"添加成功!":"添加失败");
} @RequestMapping(path = "", method = RequestMethod.PUT)
public AjaxState updateEmp(@RequestBody User user) {
boolean result=userService.editUser(user);
return new AjaxState(result?"success":"error",user,result?"修改成功!":"修改失败");
} @RequestMapping(path = "/{id}", method = RequestMethod.DELETE)
public AjaxState deleteEmpById(@PathVariable int id) {
Boolean result=userService.deleteUser(id);
return new AjaxState(result?"success":"error",id,result?"删除成功!":"删除失败");
} } class AjaxState{
public String state;
public Object data;
public String message; public AjaxState(String state, Object data, String message) {
this.state = state;
this.data = data;
this.message = message;
} public AjaxState(){}
}

2.4.1、用户列表

示例:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<title>Title</title>
</head>
<body>
<h2>员工管理</h2>
<table border="1" width="100%" id="tabEmps">
<tr>
<th>编号</th>
<th>姓名</th>
<th>生日</th>
<th>地址</th>
<th>电话</th>
<th>操作</th>
</tr>
</table>
<p> </p>
<p class="loading" style="display: none;">
<img src="img/loading.gif" align="absmiddle">努力加载中...
</p>
<p class="message"> </p>
<script src="js/jquery-1.11.3.min.js"></script>
<script>
// var data = {
// "state": "success",
// "data": {"id": 1, "name": "张学友", "birthday": -41500800000, "address": "中国香港", "phone": "18989890098"},
// "message": "获得数据成功!"
// }
var app = {
url: "http://localhost:8080/mvc08/emps",
init:function(){
this.binddata();
},
ajax: function (actionType, callback, path, data) {
$.ajax({
url: app.url + (path||""),
contentType: "application/json;charset=utf-8",
data: data || {},
type: actionType||"get",
dataType: "json",
success: function (data) {
if(data&&data.state=="success"){
app.info(data.message);
}else if(data&&data.state=="error"){
app.info(data.message);
}else{
app.info(data);
}
if(callback){
callback(data);
}
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
info(textStatus+errorThrown);
},
beforeSend: function () {
$(".loading").show(200);
}
,
complete: function () {
$(".loading").hide(200);
}
})
;
},
binddata: function () {
this.ajax("get",function(data){
$.each(data.data,function(index,emp){
var tr=$("<tr/>").appendTo("#tabEmps");
$("<td/>").text(emp.id).appendTo(tr);
$("<td/>").text(emp.name).appendTo(tr);
$("<td/>").text(emp.birthday).appendTo(tr);
$("<td/>").text(emp.address).appendTo(tr);
$("<td/>").text(emp.phone).appendTo(tr);
$("<td/>").html("<a>删除</a>").appendTo(tr);
});
});
},
info:function(msg){
$(".message")[0].innerHTML+=msg+"<br/>";
}
}; app.init();
</script>
</body>
</html>

结果:

2.4.2、新增用户

示例:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<title>Title</title>
</head>
<body>
<h2>员工管理</h2>
<table border="1" width="100%" id="tabEmps">
<tr>
<th>编号</th>
<th>姓名</th>
<th>生日</th>
<th>地址</th>
<th>电话</th>
<th>操作</th>
</tr>
</table>
<p class="loading" style="display: none;">
<img src="img/loading.gif" align="absmiddle">努力加载中...
</p>
<form id="formEmps">
<fieldset>
<legend>用户信息</legend>
<p>
<label for="name">姓名:</label>
<input name="name" id="name" type="text" required="required" maxlength="32"/>
</p>
<p>
<label for="birthday">生日:</label>
<input name="birthday" id="birthday" type="date" required="required" maxlength="8"/>
</p>
<p>
<label for="address">地址:</label>
<input name="address" id="address" type="text" required="required" maxlength="128"/>
</p>
<p>
<label for="phone">电话:</label>
<input name="phone" id="phone" type="text" required="required" maxlength="11"/>
</p>
<p>
<input id="id" type="hidden" name="id" value=""/>
<button type="button" id="btnSubmit">保存</button>
</p>
</fieldset>
</form>
<p class="message">
</p>
<script src="js/jquery-1.11.3.min.js"></script>
<script>
// var data = {
// "state": "success",
// "data": {"id": 1, "name": "张学友", "birthday": -41500800000, "address": "中国香港", "phone": "18989890098"},
// "message": "获得数据成功!"
// }
var app = {
url: "http://localhost:8080/mvc08/emps",
init: function () {
$("#btnSubmit").click(app.save);
this.binddata();
},
ajax: function (actionType, callback, path, data) {
$.ajax({
url: app.url + (path || ""),
contentType: "application/json;charset=utf-8",
data:JSON.stringify(data)||"{}",
type: actionType || "get",
dataType: "json",
success: function (data) {
if (data && data.state == "success") {
app.info(data.message);
} else if (data && data.state == "error") {
app.info(data.message);
} else {
app.info(data);
}
if (callback) {
callback(data);
}
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
app.info(textStatus + errorThrown);
},
beforeSend: function () {
$(".loading").show(200);
}
,
complete: function () {
$(".loading").hide(200);
}
})
;
},
binddata: function () {
$("#tabEmps tr:gt(0)").remove();
this.ajax("get", function (data) {
$.each(data.data, function (index, emp) {
var tr = $("<tr/>").appendTo("#tabEmps");
$("<td/>").text(emp.id).appendTo(tr);
$("<td/>").text(emp.name).appendTo(tr);
$("<td/>").text(emp.birthday).appendTo(tr);
$("<td/>").text(emp.address).appendTo(tr);
$("<td/>").text(emp.phone).appendTo(tr);
$("<td/>").html("<a>删除</a>").appendTo(tr);
});
});
},
getEmp:function(){
return {
"id":$("#id").val(),
"name":$("#name").val(),
"birthday":$("#birthday").val(),
"address":$("#address").val(),
"phone":$("#phone").val()
};
},
save:function(){
var emp=app.getEmp();
if(emp.id){
app.update(emp);
}else{
app.add(emp);
}
},
add:function(emp){
app.ajax("POST",function (data) {
app.binddata();
},"",emp);
},
update:function(emp){
app.ajax("Put",function (data) {
app.binddata();
},"",emp);
},
info: function (msg) {
$(".message")[0].innerHTML += msg + "<br/>";
}
}; app.init();
</script>
</body>
</html>

结果:

2.4.3、删除用户

示例:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<title>Title</title>
</head>
<body>
<h2>员工管理</h2>
<table border="1" width="100%" id="tabEmps">
<tr>
<th>编号</th>
<th>姓名</th>
<th>生日</th>
<th>地址</th>
<th>电话</th>
<th>操作</th>
</tr>
</table>
<p class="loading" style="display: none;">
<img src="img/loading.gif" align="absmiddle">努力加载中...
</p>
<form id="formEmps">
<fieldset>
<legend>用户信息</legend>
<p>
<label for="name">姓名:</label>
<input name="name" id="name" type="text" required="required" maxlength="32"/>
</p>
<p>
<label for="birthday">生日:</label>
<input name="birthday" id="birthday" type="date" required="required" maxlength="8"/>
</p>
<p>
<label for="address">地址:</label>
<input name="address" id="address" type="text" required="required" maxlength="128"/>
</p>
<p>
<label for="phone">电话:</label>
<input name="phone" id="phone" type="text" required="required" maxlength="11"/>
</p>
<p>
<input id="id" type="hidden" name="id" value=""/>
<button type="button" id="btnSubmit">保存</button>
</p>
</fieldset>
</form>
<p class="message">
</p>
<script src="js/jquery-1.11.3.min.js"></script>
<script>
// var data = {
// "state": "success",
// "data": {"id": 1, "name": "张学友", "birthday": -41500800000, "address": "中国香港", "phone": "18989890098"},
// "message": "获得数据成功!"
// }
var app = {
url: "http://localhost:8080/mvc08/emps",
init: function () {
$("#btnSubmit").click(app.save);
$("#tabEmps").on("click", ".del", app.delete);
this.binddata();
},
ajax: function (actionType, callback, path, data) {
$.ajax({
url: app.url + (path || ""),
contentType: "application/json;charset=utf-8",
data: JSON.stringify(data) || "{}",
type: actionType || "get",
dataType: "json",
success: function (data) {
if (data && data.state == "success") {
app.info(data.message);
} else if (data && data.state == "error") {
app.info(data.message);
} else {
app.info(data);
}
if (callback) {
callback(data);
}
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
app.info(textStatus + errorThrown);
},
beforeSend: function () {
$(".loading").show(200);
}
,
complete: function () {
$(".loading").hide(200);
}
})
;
},
binddata: function () {
$("#tabEmps tr:gt(0)").remove();
this.ajax("get", function (data) {
$.each(data.data, function (index, emp) {
var tr = $("<tr/>").data("emp", emp).appendTo("#tabEmps");
$("<td/>").text(emp.id).appendTo(tr);
$("<td/>").text(emp.name).appendTo(tr);
$("<td/>").text(emp.birthday).appendTo(tr);
$("<td/>").text(emp.address).appendTo(tr);
$("<td/>").text(emp.phone).appendTo(tr);
$("<td/>").html("<a class='del' href='#'>删除</a>").appendTo(tr);
});
});
},
getEmp: function () {
return {
"id": $("#id").val(),
"name": $("#name").val(),
"birthday": $("#birthday").val(),
"address": $("#address").val(),
"phone": $("#phone").val()
};
},
save: function () {
var emp = app.getEmp();
if (emp.id) {
app.update(emp);
} else {
app.add(emp);
}
},
add: function (emp) {
app.ajax("POST", function (data) {
app.binddata();
}, "", emp);
},
update: function (emp) {
app.ajax("Put", function (data) {
app.binddata();
}, "", emp);
},
delete: function () {
if (confirm("删除吗?")) {
var tr = $(this).closest("tr");
var emp = tr.data("emp");
app.ajax("DELETE", function (data) {
tr.remove();
}, "/" + emp.id);
}
},
info: function (msg) {
$(".message")[0].innerHTML += msg + "<br/>";
}
}; app.init();
</script>
</body>
</html>

结果:

2.4.4、更新数据

示例:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<title>Title</title>
</head>
<body>
<h2>员工管理</h2>
<table border="1" width="100%" id="tabEmps">
<tr>
<th>编号</th>
<th>姓名</th>
<th>生日</th>
<th>地址</th>
<th>电话</th>
<th>操作</th>
</tr>
</table>
<p class="loading" style="display: none;">
<img src="img/loading.gif" align="absmiddle">努力加载中...
</p>
<form id="formEmps">
<fieldset>
<legend>用户信息</legend>
<p>
<label for="name">姓名:</label>
<input name="name" id="name" type="text" required="required" maxlength="32"/>
</p>
<p>
<label for="birthday">生日:</label>
<input name="birthday" id="birthday" type="date" required="required" maxlength="8"/>
</p>
<p>
<label for="address">地址:</label>
<input name="address" id="address" type="text" required="required" maxlength="128"/>
</p>
<p>
<label for="phone">电话:</label>
<input name="phone" id="phone" type="text" required="required" maxlength="11"/>
</p>
<p>
<input id="id" type="hidden" name="id" value=""/>
<button type="button" id="btnSubmit">保存</button>
</p>
</fieldset>
</form>
<p class="message">
</p>
<script src="js/jquery-1.11.3.min.js"></script>
<script>
// var data = {
// "state": "success",
// "data": {"id": 1, "name": "张学友", "birthday": -41500800000, "address": "中国香港", "phone": "18989890098"},
// "message": "获得数据成功!"
// }
var app = {
url: "http://localhost:8080/mvc08/emps",
init: function () {
$("#btnSubmit").click(app.save);
$("#tabEmps").on("click", ".del", app.delete);
$("#tabEmps").on("click", ".edit", app.edit);
this.binddata();
},
ajax: function (actionType, callback, path, data) {
$.ajax({
url: app.url + (path || ""),
contentType: "application/json;charset=utf-8",
data: JSON.stringify(data) || "{}",
type: actionType || "get",
dataType: "json",
success: function (data) {
if (data && data.state == "success") {
app.info(data.message);
} else if (data && data.state == "error") {
app.info(data.message);
} else {
app.info(data);
}
if (callback) {
callback(data);
}
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
app.info(textStatus + errorThrown);
},
beforeSend: function () {
$(".loading").show(200);
}
,
complete: function () {
$(".loading").hide(200);
}
})
;
},
binddata: function () {
$("#tabEmps tr:gt(0)").remove();
this.ajax("get", function (data) {
$.each(data.data, function (index, emp) {
var tr = $("<tr/>").data("emp", emp).appendTo("#tabEmps");
$("<td/>").text(emp.id).appendTo(tr);
$("<td/>").text(emp.name).appendTo(tr);
$("<td/>").text(emp.birthday).appendTo(tr);
$("<td/>").text(emp.address).appendTo(tr);
$("<td/>").text(emp.phone).appendTo(tr);
$("<td/>").html("<a class='del' href='#'>删除</a> | <a class='edit' href='#'>编辑</a>").appendTo(tr);
});
});
},
getEmp: function () {
return {
"id": $("#id").val(),
"name": $("#name").val(),
"birthday": $("#birthday").val(),
"address": $("#address").val(),
"phone": $("#phone").val()
};
},
save: function () {
var emp = app.getEmp();
if (emp.id) {
$("#id").val("");
app.update(emp);
} else {
app.add(emp);
}
},
add: function (emp) {
app.ajax("POST", function (data) {
app.binddata();
}, "", emp);
},
update: function (emp) {
app.ajax("Put", function (data) {
app.binddata();
}, "", emp);
},
delete: function () {
if (confirm("删除吗?")) {
var tr = $(this).closest("tr");
var emp = tr.data("emp");
app.ajax("DELETE", function (data) {
tr.remove();
}, "/" + emp.id);
}
},
edit:function(){
var emp = $(this).closest("tr").data("emp");
$("#id").val(emp.id);
$("#name").val(emp.name);
$("#birthday").val(emp.birthday);
$("#address").val(emp.address);
$("#phone").val(emp.phone);
},
info: function (msg) {
$(".message")[0].innerHTML += msg + "<br/>";
}
}; app.init();
</script>
</body>
</html>

结果:

三、示例下载

https://git.coding.net/zhangguo5/SpringMVC08.git

四、视频

https://www.bilibili.com/video/av16991874/

五、作业

5.1、请练习上课示例

5.2、请使用Spring MVC对外提供商品(Product)的管理接口,如:

product/list 获得所有商品 get

product/1 获得编号为1的商品 get

product/delete/1 删除编号为1的商品 get

product/insert 新增商品 post

product/edit 编辑商品 post

使用AJAX调用发布的服务,实现如下功能,验证、搜索、多删除功能选作。

5.3、请完成一个前后台分离的汽车管理系统(CarSystem),需要管理汽车的(车牌、颜色、价格、出厂日期、排量),要求完成CRUD功能,界面美观大方。

a)、请使用MySQL数据库创建库与表(CarSystem)

b)、使用Spring MVC定义5个RESTful服务,注意路径格式,先用fiddler测试通过。

c)、定义car.html页面,使用jQuery插件中的ajax功能消费RESTful服务实现功能,反复测试。

六、工具下载

Fiddler2(汉化版) 链接: https://pan.baidu.com/s/1mhNTg1M 密码: qiib

Spring MVC 学习总结(九)——Spring MVC实现RESTful与JSON(Spring MVC为前端提供服务)的更多相关文章

  1. 我的Spring Boot学习记录(二):Tomcat Server以及Spring MVC的上下文问题

    Spring Boot版本: 2.0.0.RELEASE 这里需要引入依赖 spring-boot-starter-web 这里有可能有个人的误解,请抱着怀疑态度看. 建议: 感觉自己也会被绕晕,所以 ...

  2. 【Spring Boot学习之九】缓存支持

    环境 eclipse 4.7 jdk 1.8 Spring Boot 1.5.2 一.Spring Boot Cache以及整合EhCacheSpring从3.1开始定义了org.springfram ...

  3. ASP.NET MVC学习---(九)权限过滤机制(完结篇)

    相信对权限过滤大家伙都不陌生 用户要访问一个页面时 先对其权限进行判断并进行相应的处理动作 在webform中 最直接也是最原始的办法就是 在page_load事件中所有代码之前 先执行一个权限判断的 ...

  4. spring boot学习01【搭建环境、创建第一个spring boot项目】

    1.给eclipse安装spring boot插件 Eclipse中安装Spring工具套件(STS): Help -> Eclipse Marketplace... 在Search标签或者Po ...

  5. Spring Boot学习笔记 - 整合Swagger2自动生成RESTful API文档

    1.添加Swagger2依赖 在pom.xml中加入Swagger2的依赖 <!--swagger2--> <dependency> <groupId>io.spr ...

  6. Spring框架学习笔记(6)——阿里云服务器部署Spring Boot项目(jar包)

    最近接外包,需要部署服务器,便是参考了网上的几篇博文,成功在阿里云服务器成功部署了Spring Boot项目,特记下本篇笔记 Spring Boot项目打包 这里说一下部署的一些问题 1.mysql驱 ...

  7. spring boot 学习(一)——在idea建立第一个spring boot项目

    1.打开idea->点击file->点击new->点击project->点击Spring Initializr->点击default 2.这是springboot的启动类 ...

  8. 【ASP.NET MVC 学习笔记】- 19 REST和RESTful Web API

    本文参考:http://www.cnblogs.com/willick/p/3441432.html 1.目前使用Web服务的三种主流的方式是:远程过程调用(RPC),面向服务架构(SOA)以及表征性 ...

  9. Spring MVC学习总结

    Spring MVC学习总结 Spring MVC学习路(一) 下载配置文件 Spring MVC学习路(二) 设置配置文件 Spring MVC学习路(三) 编写第一个demo Spring MVC ...

  10. 转-Spring 注解学习手札(七) 补遗——@ResponseBody,@RequestBody,@PathVariable

    转-http://snowolf.iteye.com/blog/1628861/ Spring 注解学习手札(七) 补遗——@ResponseBody,@RequestBody,@PathVariab ...

随机推荐

  1. java笔记04: String的理解与运用

    一,“==”与equals() 运行以下代码,如何解释其输出结果? public class StringPool { public static void main(String args[]) { ...

  2. LeetCode 532. K-diff Pairs in an Array (在数组中相差k的配对)

    Given an array of integers and an integer k, you need to find the number of unique k-diff pairs in t ...

  3. iOS之 Auto Layout

    1. 动画 // 修改从 StoryBoard 绑定到类的约束的值 self.boxView.constant += 80 // 在动画闭包里对其父级进行 layoutIfNeeded() UIVie ...

  4. Django+Nginx+uWSGI部署

    一.介绍 Django的部署可以有多种方式,采用nginx+uwsgi的方式是最常见的一种方式.在这种方式中,将nginx作为服务器前端,接收WEB的所有请求,统一管理请求.nginx把所有静态请求自 ...

  5. struts2(四)之输入校验

    前言 这个本来是昨天就写好的,但是不知道为什么没有保存成功!但是今天起来再写一遍就当巩固一下知识吧. 一.输入校验概述 在以前我们写一个登录页面时,并没有限制用户的输入,不管用户输入什么,我们都存入数 ...

  6. C++图形编程之graphics.h头文件

    graphics.h是Turbo C的针对DOS下的一个C语言图形库,如果要用的话应该用TC的编译器来编译,但是如果需要在vc及vs环境中使用graphics.h的功能,则可以选择下载EasyX图形库 ...

  7. sersync实现数据实时同步

    1.1 第一个里程碑:安装sersync软件 1.1.1 将软件上传到服务器当中并解压 1.上传软件到服务器上 rz -E 为了便于管理上传位置统一设置为 /server/tools 中 2.解压软件 ...

  8. 2017上海QCon之旅总结(下)

    本来这个公众号的交流消息中间件相关的技术的.十月去上海参加了QCon,第一次参加这样的技术会议,感受挺多的,所以整理一下自己的一些想法接公众号和大家交流一下. 三天的内容还挺多的,之前已经有上篇和中篇 ...

  9. php正则表达式,在抓取内容进行匹配的时候表现不稳定

    最近做了一个 抓取内容的程序,使用php的正则表达式对抓取的内容进行匹配,当进行大量匹配运算的时候,发现偶尔会出现匹配失败的情况.检查不出任何原因. 匹配失败导致匹配结果为空,最终导致写入数据库失败. ...

  10. [深度学习]实现一个博弈型的AI,从五子棋开始(1)

    好久没有写过博客了,多久,大概8年???最近重新把写作这事儿捡起来……最近在折腾AI,写个AI相关的给团队的小伙伴们看吧. 搞了这么多年的机器学习,从分类到聚类,从朴素贝叶斯到SVM,从神经网络到深度 ...