基于SpringBoot的Web前后端分离开发
SpringBoot主程序
@SpringBootApplication
这是一个SpringBoot启动类的注解。
可以完成一系列自动化配置。
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
开发一个URL
开发一个可以在浏览器直接访问的URL地址,也就是常说的API接口,前后端分离后一般都是RESTful风格
返回的数据是Json格式,因此有了@RequestBody注解
- @Controller
- @RequestMapping("url地址")
- @ResponseBody
@Controller 可以让你的类成为一个API接口类
@RequestMapping("url地址") 可以让你用一个方法开发出一个url接口
@ResponseBody 可以让你返回的数据为json格式
因此在这样的环境下,业内有了 controller 层,即建一个 controller 包来放置你的API类
@Controller
public class DemoController {
@RequestMapping("/demo")
@ResponseBody
public String print(){
System.out.println("钟小湖");
return "哈哈";
}
}
带参数的URL
例如Get请求
实现也很简单
直接在方法上加入参数即可
@Controller
public class DemoController {
@RequestMapping("/demo")
@ResponseBody
public String print(String name){
System.out.println("Hello," + name);
return name;
}
}
public String print(String name)
然后你也可以更花哨点使用别名对name进行映射,或者前端工程师已经规定好了参数名,你迫于无奈。
举个例子:请求体参数是nick,你想在后台转化为name
nick = name
这时候 @RequestParam 注解就发挥作用了
@Controller
public class DemoController {
@RequestMapping("/demo")
@ResponseBody
public String print(@RequestParam("nick") String name){
System.out.println("Hello," + name);
return name;
}
}
问题来了,RESTful 讲究把参数直接通过网址的形式传送
例如 localhost:8080/demo/乐心湖
这时候 @PathVariable 注解就出来了
@Controller
public class DemoController {
@RequestMapping("/demo/{name}")
@ResponseBody
public String print(@PathVariable String name){
System.out.println("Hello," + name);
return name;
}
}
重点: @RequestMapping("/demo/{name}") 中的 {} 符号
多个参数也是可以的
@RequestMapping("/demo/{id}/{name}")
@ResponseBody
public String print(@PathVariable String name, @PathVariable String id) {
System.out.println(id + name);
return id + name;
}
还有更高级的自定义
@PathVariable("映射的参数")
例如:
@PathVariable("name") String name
//这样就可以把地址上的name变成程序内部的name了。
//就是最基础的映射
//因为name本身结果也是name,所以SpringMvc会自动帮我们映射,这个映射是没有必要写的。
所以,很多人习惯性把映射也写上,以为这是必需要的。
事实上你只需确保你的参数名不要瞎改,很多多余工作我们也就省去了。
值得一提的是:这一切的注解和花哨操作都是基于两个接口
HttpServletRequest
HttpServletResponse
例如我们使用 @RequestParam 获取了请求头中的name参数
也可以用老大哥 HttpServletRequest 实现
@Controller
public class DemoController {
@RequestMapping("/demo")
@ResponseBody
public String print(HttpServletRequest request){
String name = request.getParameter("name");
System.out.println("Hello," + name);
return name;
}
}
例如我们使用 @ResponseBody + return 来返回数据
我们也可以用老大哥 HttpServletResponse
@Controller
public class DemoController {
@RequestMapping("/demo")
// @ResponseBody
public void print(HttpServletRequest request, HttpServletResponse response) throws IOException {
String name = request.getParameter("name");
System.out.println("Hello," + name);
response.getWriter().print("Hello " + name );
// return name;
}
}
当然你会发现 name 中文时会出现乱码,因此有了设置编码的操作
response.setContentType("application/json;charset=utf-8");
response.setCharacterEncoding("utf-8");
聪明的你一定知道要写在输出之前吧!
关于 HttpServletRequest 接口是非常非常非常常用的。
方法 | 用途 |
---|---|
getRequestURL() | 返回客户端发出请求时的完整URL。 |
getRequestURI() | 返回请求行中的参数部分。 |
getQueryString () | 方法返回请求行中的参数部分(参数名+值) |
getRemoteHost() | 返回发出请求的客户机的完整主机名。 |
getRemoteAddr() | 返回发出请求的客户机的IP地址。 |
getPathInfo() | 返回请求URL中的额外路径信息。额外路径信息是请求URL中的位于Servlet的路径之后和查询参数之前的内容,它以"/"开头。 |
getRemotePort() | 返回客户机所使用的网络端口号。 |
getLocalAddr() | 返回WEB服务器的IP地址。 |
getLocalName() | 返回WEB服务器的主机名。 |
getHeader(string name) | 以 String 的形式返回指定请求头的值。如果该请求不包含指定名称的头,则此方法返回 null。如果有多个具有相同名称的头,则此方法返回请求中的第一个头。头名称是不区分大小写的。可以将此方法与任何请求头一起使用 |
getParameter(String name) | 根据name获取请求参数(常用) |
可以自己测试使用。都是比较简单易用的方法。
业务封装
当你的代码多了,全部写在 controller 层就不雅观而已难看
所以有了 Service 层
Spring鼓励我们面向接口开发,因此这层Service我们一般会使用 接口+实现类的方式。
例如 我现在要对 response中文乱码的解决方案放到 Service
新建一个接口 DemoService
public interface DemoService {
void print(HttpServletResponse response);
}
接着去实现这个方法。
@Service
public class DemoServiceImpl implements DemoService {
@Override
public void print(HttpServletResponse response) {
response.setContentType("application/json;charset=utf-8");
response.setCharacterEncoding("utf-8");
}
}
你会注意到这里多了个 @Service 注解,他能够帮你将这个实现类托管到Spring工厂中,使其成为一个组件,日后你就不用new了。直接注入组件即可。
于是注入组件我们用到了一个 @AutoWired 注解
@Controller
public class DemoController {
@Autowired
private DemoService demoService;
}
你可以观察到这里注入的是接口,而非实现类。
这就是Spring的优雅之处,能够让你真正面向接口开发。你只管按规矩办事,其他交给Spring处理。
现在的接口层是下面这样的。
@Controller
public class DemoController {
@Autowired
private DemoService demoService;
@RequestMapping("/demo")
// @ResponseBody
public void print(HttpServletRequest request, HttpServletResponse response) throws IOException {
String name = request.getParameter("name");
System.out.println("Hello," + name);
demoService.print(response);
response.getWriter().print("Hello " + name );
// return name;
}
}
是不是依旧很优雅。
为了开发更加便捷,加上 RESTful 风格的大力推广。前后端分离的趋势难以阻挡,很多时候我们几乎都是返回json串。
因此有了一个新的注解
@RestController
它是 @ResponseBody + @Controller 的组合体。使用在类上,可以让你这个类的所有方法都返回json数据。
数据封装
当然Web开发离不开数据传输。
事实上,Map就可以自动装配这一点。
//@Controller
@RestController
public class DemoController {
@RequestMapping("/demo")
// @ResponseBody
public Map<String,Object> print() {
Map<String,Object> map = new HashMap<>();
map.put("id","9999");
map.put("name","乐心湖");
map.put("age","18");
return map;
}
}
多个map数据,我们就可以用List来收集。
//@Controller
@RestController
public class DemoController {
@RequestMapping("/demo")
// @ResponseBody
public ArrayList<Map<String, Object>> print() {
ArrayList<Map<String, Object>> mapArrayList = new ArrayList<>();
Map<String,Object> map1 = new HashMap<>();
Map<String,Object> map2 = new HashMap<>();
map1.put("id","9999");
map1.put("name","乐心湖");
map1.put("age","18");
map2.put("id","8888");
map2.put("name","钟小湖");
map2.put("age","19");
mapArrayList.add(map1);
mapArrayList.add(map2);
return mapArrayList;
}
}
这么优雅的数据,快点学起来吧。
数据难免要跟数据库打交道,那我们就去摸索一遍。
以 Mysql 为例,建一个 demo数据库
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for demo
-- ----------------------------
DROP TABLE IF EXISTS `demo`;
CREATE TABLE `demo` (
`id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`age` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
-- ----------------------------
-- Records of demo
-- ----------------------------
INSERT INTO `demo` VALUES ('8888', '钟小湖', '19');
INSERT INTO `demo` VALUES ('9999', '乐心湖', '18');
SET FOREIGN_KEY_CHECKS = 1;
怎么把数据对接到 SpringBoot 呢,这时候持久化框架就出现了。
在查询数据之前,你需要一个类,里面带着参数跟咱数据库字段要一样的吧?
因此我们讲这个类叫做实体映射类,用于取代无数据库时采用的map类型
因此早期我们使用 model 层来封装跟数据库一一对应的参数。
现在人们习惯使用 entity 层来代替它。
package com.example.demo.entity;
import lombok.Data;
@Data
public class Demo {
private String id;
private String name;
private String age;
}
这里有个 @Data 注解可能是伴随着你未来一生的。
他是 lombok 带来的,能够自动帮我们写 get set 方法
你需要在idea安装好lombok插件,然后在项目中引入依赖。
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.20</version>
</dependency>
这里顺便把 Mysql 连接的依赖给引入了。还有一个 mybatis-plus,这应该不用过多介绍吧。
于是我们需要去写数据库的配置
在 application.properties 下直接开整。
spring.datasource.url=jdbc:mysql://localhost:3306/demo?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
#数据库用户名
spring.datasource.username=root
#数据库密码
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#配置mybatis xml文件所在位置
mybatis-plus.mapper-locations=classpath:/mapper/*.xml
配置完成之后,我们就要去写sql语句了。
因此 Mybatis 规定了使用 Mapper 层做持久化操作。
@Mapper
public interface DemoMapper {
List<Demo> select();
}
对这个接口进行sql实现
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.example.demo.mapper.DemoMapper">
<select id="select" resultType="com.example.demo.entity.Demo">
SELECT * FROM demo
</select>
</mapper>
这样工作就基本完成了。
你可以在service层测试,也可以在controller层测试。
@RestController
public class DemoController {
@Autowired
private DemoMapper demoMapper;
@RequestMapping("/demo")
// @ResponseBody
public List<Demo> print() {
List<Demo> select = demoMapper.select();
return select;
}
}
访问/demo,效果跟原来map封装的数据一模一样。
最基本的开发流程大概就如此,你学会了吗?