SpringBoot 开发 Web 系统,快速入门指南!
myzbx 2025-06-30 18:50 45 浏览
01、背景介绍
在之前的文章中,我们简单的介绍了 SpringBoot 项目的创建过程,了解了 Spring Boot 开箱即用的特性,本篇文章接着上篇的内容继续介绍 Spring Boot 用于 web 工程开发时的其它特性。
废话不多说了,上代码!
02、应用实践
当将 SpringBoot 框架用于传统的 web 项目开发时,通常分为以下三个过程来实现。
- 第一步:连接数据库,实现对表进行 CRUD 操作
- 第二步:引入模板引擎来开发页面
- 第三步:使用一些常见的 web 特性来满足其它的功能开发
最后源码目录结构如下!
springboot-hello
├── src
│ └── main
│ ├── java
│ ├── com
│ ├── example
│ ├── springboot
│ ├── Application.java
│ ├── entity
│ ├── User.java
│ ├── service
│ ├── UserService.java
│ ├── web
│ ├── UserController
│ └── resources
│ ├── application.properties
│ ├── templates
│ └─── index.html
└── pom.xml
下面我们依次来看看相关的实践过程。
2.1、数据库操作
这里我们以 Mysql 数据库为例,采用 Spring 的 JdbcTemplate 模板来操作数据的的增删改查,过程如下。
2.1.1、准备数据库表
先创建tb_user表,包含属性id、name、age,可以通过执行下面的建表语句。
CREATE TABLE `tb_user` (
`id` bigint(20) unsigned NOT NULL,
`name` varchar(30) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
2.1.2、添加相关依赖包
在我们访问数据库的时候,需要先配置一个数据库驱动包,通常采用 JDBC 方式方式访问,需要在pom.xml中引入相关依赖包。
<!--spring jdbc-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!--mysql 驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
2.1.3、添加数据源配置
与此同时,还需要在application.properties文件中配置相关的数据源访问地址。
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
2.1.4、编写领域对象
根据数据库中创建的 User 表,创建对应的领域对象。
package com.example.springboot.entity;
public class User {
/**
* 用户ID
*/
private Long id;
/**
* 用户名称
*/
private String name;
/**
* 用户年龄
*/
private Integer age;
// set、get方法等...
}
2.1.5、编写数据访问对象
通过JdbcTemplate实现对tb_user表中的数据访问操作。
package com.example.springboot.service;
@Service
public class UserService {
@Autowired
private JdbcTemplate jdbcTemplate;
/**
* 查询用户
* @return
*/
public List<User> getAll() {
List<User> users = jdbcTemplate.query("select id, name, age from tb_user", (resultSet, i) -> {
User user = new User();
user.setId(resultSet.getLong("id"));
user.setName(resultSet.getString("name"));
user.setAge(resultSet.getInt("age"));
return user;
});
return users;
}
/**
* 通过ID查询用户
* @param id
* @return
*/
public User getById(Long id) {
User target = jdbcTemplate.queryForObject("select id, name, age from tb_user where id = ?", new BeanPropertyRowMapper<User>(User.class),id);;
return target;
}
/**
* 创建用户
* @param entity
* @return
*/
public int create(User entity){
return jdbcTemplate.update("insert into tb_user(id, name, age) values(?, ?, ?)", entity.getId(), entity.getName(), entity.getAge());
}
/**
* 修改用户
* @param entity
* @return
*/
public int updateById(User entity){
return jdbcTemplate.update("update tb_user set name = ?, age = ? where id = ? ", entity.getName(), entity.getAge(), entity.getId());
}
/**
* 删除用户
* @param id
* @return
*/
public int deleteById(Long id) {
return jdbcTemplate.update("delete from tb_user where id = ?", id);
}
}
2.1.6、编写单元测试用例
在src/test/java目录下,编写单元测试用例,验证代码中的增、删、改、查操作的正确性,包名与主目录中保持一致。
package com.example.springboot;
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserServiceTest {
@Autowired
private UserService userService;
@Test
public void test(){
// 插入5条数据
userService.create(new User(1L, "张三", 20));
userService.create(new User(2L, "李四", 21));
userService.create(new User(3L, "王五", 22));
// 查询全部数据
List<User> dbList1 = userService.getAll();
System.out.println("第一次全量查询结果:" + dbList1.toString());
// 修改数据
userService.updateById(new User(2L, "赵六", 21));
// 查询指定数据
User updateObj = userService.getById(2l);
System.out.println("查询[id=2]结果:" + updateObj.toString());
// 删除数据
userService.deleteById(2L);
userService.deleteById(3L);
// 查询全部数据
List<User> dbList2 = userService.getAll();
System.out.println("第二次全量查询结果:" + dbList2.toString());
}
}
单元测试,运行后的输出结果:
第一次全量查询结果:[User{id=1, name='张三', age=20}, User{id=2, name='李四', age=21}, User{id=3, name='王五', age=22}]
查询[id=2]结果:User{id=2, name='赵六', age=21}
第二次全量查询结果:[User{id=1, name='张三', age=20}]
此时操作数据库中的表数据,已经正常流通了。
上面介绍的JdbcTemplate只是最基本的几个操作,更多其他数据访问操作的使用可以参考:JdbcTemplate API
2.2、Thymeleaf 模板
在传统的 Java web 工程中,通常会采用 JSP 来编写页面并进行数据展示。而在 Spring Boot 框架中,推荐使用 Thymeleaf 模板引擎来开发 Web 页面。
Thymeleaf 是一款用于渲染 XML/XHTML/HTML5 内容的模板引擎,与 JSP、Velocity、FreeMarker 等类似,都可以轻易的与 Spring MVC 等 Web 框架进行集成作为 Web 应用的模板引擎。
下面我们一起来看下简单的页面集成应用。
2.2.1、添加相关依赖包
在 SpringBoot 项目中使用 Thymeleaf 时,只需要添加所需的模板引擎模块依赖包即可,内容如下。
<!--thymeleaf模板引擎-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
2.2.2、添加相关的配置参数
与此同时,还需要在application.properties文件中配置 thymeleaf 模版扫描路径,比如如下配置。
# thymelea模板配置
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.mode=HTML5
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.cache=false
spring.thymeleaf.enabled=true
其中spring.thymeleaf.prefix就是模板引擎扫描的路径。
2.2.3、创建页面模板
根据上一步映射的模板路径, 在模板路径
src/main/resources/templates下新建模板文件index.html,内容如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Home Page</title>
</head>
<body>
<h1>Hello !</h1>
<table>
<thead>
<tr>
<th>用户ID</td>
<th>用户名称</td>
<th>用户年龄</td>
</tr>
</thead>
<tbody>
<tr th:each="prod:${allUsers}">
<td th:text="${prod.id}">100</td>
<td th:text="${prod.name}">张三丰</td>
<td th:text="${prod.age}">99</td>
</tr>
</tbody>
</table>
</body>
</html>
2.2.4、编写页面请求对象
最后编写一个页面请求对象,用来处理路径的请求,将数据渲染到index页面上,具体实现如下:
@Controller
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/")
public String index(ModelMap map) {
// 查询所有的用户
List<User> users = userService.getAll();
map.addAttribute("allUsers", users);
return "index";
}
}
将上文中的三条数据插入到数据库,以便展示。
2.2.5、测试页面展示情况
最后将服务启动,在浏览器发起请求,地址为http://localhost:8080/,展示结果如下:
说明页面渲染正常,符合预期效果。
更多 Thymeleaf 的页面语法,可以访问 Thymeleaf 的官方文档来深入学习使用。
2.3、web 基本特性
除了以上功能,SpringBoot 还有几个常用特性功能,比如 SpringMVC 中的接口开发、过滤器、拦截器、aop 代理、异常处理等。
下面,我们一起简要的看看相关特性的用法。
2.3.1、接口开发
当与其它项目对接的时候,通常会采用 json 数据格式进行请求和返回,在传统的 SpringMVC 项目中,我们通常需要在每个接口方法上加@ResponseBody注解,以便数据以 json 格式返回给用户。
在 Spring Boot 框架中,我们只需要在接口类上添加@RestController注解,即可实现@Controller和@ResponseBody一样的效果。
示例如下。
@RestController
public class ApiController {
@Autowired
private UserService userService;
@GetMapping("/getUsers")
public List<User> getUsers() {
// 查询所有的用户
System.out.println("收到查询用户的请求");
List<User> users = userService.getAll();
return users;
}
}
将服务启动,访问
http://localhost:8080/getUsers,看看控制台输出结果。
可以看到,与预期一致。
如果是页面开发,只要使用@Controller注解即可,以免无法渲染数据。
2.3.2、过滤器
过滤器在 web 项目开发过程中经常会用到,比如用于收集调用日志、排除有 XSS 威胁的字符等,过滤器本质不属于 SpringBoot 自带的功能,而是 Servlet 提供的功能,SpringBoot 对此做了集成管理,实现方式也很简单。
首先创建一个过滤器实现类,示例如下。
public class LogFilter implements Filter {
@Override
public void init(javax.servlet.FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
System.out.println("日志过滤器,request url :"+request.getRequestURI());
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
}
}
然后将过滤器注册到 SpringBoot 中,示例如下。
@Configuration
public class FilterConfig {
/**
* 添加过滤器
* @return
*/
@Bean
public FilterRegistrationBean helloFilterRegistrationBean() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setName("logFilter"); // 指定过滤器名称
registration.setFilter(new LogFilter()); // 指定过滤器实现类
registration.setUrlPatterns(Collections.singleton("/*"));// 指定拦截路径
registration.addInitParameter("paramName", "paramValue");// 指定初始化参数
registration.setOrder(1);// 指定顺序
return registration;
}
}
将服务启动,访问
http://localhost:8080/getUsers,看看控制台输出结果。
日志过滤器,request url :/getUsers
收到查询用户的请求
说明过滤器已经正常工作了。
2.3.3、拦截器
拦截器在 web 项目开发过程中也经常会用到,比如用于用户权限的拦截等等。拦截器属于 SpringMVC 自带的功能,因此 SpringBoot 默认就支持,实现方式也很简单。
首先创建一个拦截器实现类,示例如下。
public class SignInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println("方法前拦截,request url:" + request.getRequestURI());
return HandlerInterceptor.super.preHandle(request, response, handler);
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("方法中拦截(不能拦截异常),request url:" + request.getRequestURI());
HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("方法后拦截(能拦截异常),request url:" + request.getRequestURI());
}
}
然后,将拦截器注册到拦截器链中。
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 将SignInterceptor添加到拦截器链中,并且指定拦截路径和拦截顺序
registry.addInterceptor(new SignInterceptor()).addPathPatterns("/*").order(1);
}
}
将服务启动,访问
http://localhost:8080/getUsers,看看控制台输出结果。
方法前拦截,request url:/getUsers
收到查询用户的请求
方法中拦截(不能拦截异常),request url:/getUsers
方法后拦截(能拦截异常),request url:/getUsers
可以发现,过滤器的执行顺序在拦截器之前。
其中拦截器中postHandle()和afterCompletion()方法,都可以实现对接口执行后进行拦截,两者不同点在于:
- postHandle()方法无法拦截异常;
- afterCompletion()方法可以拦截异常;
可以新增一个getUsersError()方法,增加运行时异常。
@GetMapping("/getUsersError")
public List<User> getUsersError() {
// 查询所有的用户
System.out.println("收到查询用户的请求");
if(1==1){
throw new NullPointerException("异常测试");
}
List<User> users = userService.getAll();
return users;
}
再次请求访问
http://localhost:8080/getUsersError,控制台输出结果如下。
方法前拦截,request url:/getUsersError
收到查询用户的请求
方法后拦截,request url:/getUsersError
当出现异常时,可见postHandle()方法,没有被执行。
2.3.4、aop 代理
aop 动态代理也是 web 项目开发过程中常用的功能特性,熟悉 Spring 的同学可能知道,Spring 的动态代理技术使用了 aspectj 框架的注解来实现切面技术,因此在使用的时候,需要添加相关的依赖包。
首先在pom.xml文件中添加 aspectj 依赖包,示例如下。
<!--添加 aspectj 依赖包 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
编写动态代理类,代理
com.example.springboot.web包下所有类的public方法。
@Order(1)
@Component
@Aspect
public class ControllerAspect {
/***
* 定义切入点
*/
@Pointcut("execution(public * com.example.springboot.web..*.*(..))")
public void methodAdvice(){}
/**
* 方法调用前通知
*/
@Before(value = "methodAdvice()")
public void before(JoinPoint joinPoint){
System.out.println("代理-> 来自Before通知,方法名称:" + joinPoint.getSignature().getName());
}
/**
* 方法调用后通知
*/
@After(value = "methodAdvice()")
public void after(JoinPoint joinPoint){
System.out.println("代理-> 来自After通知,方法名称:" + joinPoint.getSignature().getName());
}
/**
* 方法调用后通知,方法正常执行后,有返回值,会通知;如果抛异常,不会通知
*/
@AfterReturning(value = "methodAdvice()", returning = "returnVal")
public void afterReturning(JoinPoint joinPoint,Object returnVal){
System.out.println("代理-> 来自AfterReturning通知,方法名称:" + joinPoint.getSignature().getName() + ",返回值:" + returnVal.toString());
}
/**
* 方法环绕通知
*/
@Around(value = "methodAdvice()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("代理-> 来自Around环绕前置通知,方法名称:" + joinPoint.getSignature().getName());
Object returnValue = joinPoint.proceed();
System.out.println("代理-> 来自Around环绕后置通知,方法名称:" + joinPoint.getSignature().getName());
return returnValue;
}
/**
* 抛出异常通知
*/
@AfterThrowing(value = "methodAdvice()", throwing = "ex")
public void afterThrowing(JoinPoint joinPoint, Exception ex) {
System.out.println("代理-> 来自AfterThrowing通知,方法名称:" + joinPoint.getSignature().getName() + ",错误信息:" + ex.getMessage());
}
}
将服务启动,访问
http://localhost:8080/getUsers,控制台输出结果如下。
代理-> 来自Around环绕前置通知,方法名称:getUsers
代理-> 来自Before通知,方法名称:getUsers
收到查询用户的请求
代理-> 来自Around环绕后置通知,方法名称:getUsers
代理-> 来自After通知,方法名称:getUsers
代理-> 来自AfterReturning通知,方法名称:getUsers,返回值:[User{id=1, name='张三', age=20}, User{id=2, name='李四', age=21}, User{id=3, name='王五', age=22}]
访问
http://localhost:8080/getUsersError,控制台输出结果如下。
代理-> 来自Around环绕前置通知,方法名称:getUsersError
代理-> 来自Before通知,方法名称:getUsersError
收到查询用户的请求
代理-> 来自After通知,方法名称:getUsersError
代理-> 来自AfterThrowing通知,方法名称:getUsersError,错误信息:异常测试
可以很清晰的看到,当出现异常时AfterReturning()通知方法和Around环绕后置通知方法都不会执行,异常信息会进入到AfterThrowing() 通知方法中。
2.3.5、异常处理
Spring Boot 对异常处理也做了很多的支持,开发者可以通过@ExceptionHandler注解来全局代理异常信息,实现方式也很简单。
编写一个全局异常处理类,示例如下。
@ControllerAdvice
public class GlobalExceptionHandler {
/**
* 处理Exception异常
* @param ex
* @return
*/
@ExceptionHandler(value = Exception.class)
@ResponseBody
public Map exceptionHandler(Exception ex){
Map<String,Object> errorMap = new HashMap<>();
errorMap.put("code","500");
errorMap.put("message",ex.getMessage());
return errorMap;
}
}
将服务启动,访问
http://localhost:8080/getUsersError,控制台输出结果如下。
可以看到,异常请求被成功接管。
03、小结
本文主要围绕利用 SpringBoot 框架技术,开发一个传统的 web 项目时需要用到的技术点进行一次简单的知识总结,内容难免有所遗漏,如果有描述不对的地方,欢迎留言指出!
可能有的同学会提出这样的疑问,对于 web 特性中的过滤器、拦截器、aop 代理,如果目标的都是同一个方法,他们的执行顺序怎样的?
在实测过程中,从日志打印来看,执行顺序是过滤器 > 拦截器 > aop 代理,如果有多个相同类型的拦截器,依照顺序依次拦截。
相关推荐
- 如何设计一个优秀的电子商务产品详情页
-
加入人人都是产品经理【起点学院】产品经理实战训练营,BAT产品总监手把手带你学产品电子商务网站的产品详情页面无疑是设计师和开发人员关注的最重要的网页之一。产品详情页面是客户作出“加入购物车”决定的页面...
- 怎么在JS中使用Ajax进行异步请求?
-
大家好,今天我来分享一项JavaScript的实战技巧,即如何在JS中使用Ajax进行异步请求,让你的网页速度瞬间提升。Ajax是一种在不刷新整个网页的情况下与服务器进行数据交互的技术,可以实现异步加...
- 中小企业如何组建,管理团队_中小企业应当如何开展组织结构设计变革
-
前言写了太多关于产品的东西觉得应该换换口味.从码农到架构师,从前端到平面再到UI、UE,最后走向了产品这条不归路,其实以前一直再给你们讲.产品经理跟项目经理区别没有特别大,两个岗位之间有很...
- 前端监控 SDK 开发分享_前端监控系统 开源
-
一、前言随着前端的发展和被重视,慢慢的行业内对于前端监控系统的重视程度也在增加。这里不对为什么需要监控再做解释。那我们先直接说说需求。对于中小型公司来说,可以直接使用三方的监控,比如自己搭建一套免费的...
- Ajax 会被 fetch 取代吗?Axios 怎么办?
-
大家好,很高兴又见面了,我是"高级前端进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发!今天给大家带来的主题是ajax、fetch...
- 前端面试题《AJAX》_前端面试ajax考点汇总
-
1.什么是ajax?ajax作用是什么?AJAX=异步JavaScript和XML。AJAX是一种用于创建快速动态网页的技术。通过在后台与服务器进行少量数据交换,AJAX可以使网页实...
- Ajax 详细介绍_ajax
-
1、ajax是什么?asynchronousjavascriptandxml:异步的javascript和xml。ajax是用来改善用户体验的一种技术,其本质是利用浏览器内置的一个特殊的...
- 6款可替代dreamweaver的工具_替代powerdesigner的工具
-
dreamweaver对一个web前端工作者来说,再熟悉不过了,像我07年接触web前端开发就是用的dreamweaver,一直用到现在,身边的朋友有跟我推荐过各种更好用的可替代dreamweaver...
- 我敢保证,全网没有再比这更详细的Java知识点总结了,送你啊
-
接下来你看到的将是全网最详细的Java知识点总结,全文分为三大部分:Java基础、Java框架、Java+云数据小编将为大家仔细讲解每大部分里面的详细知识点,别眨眼,从小白到大佬、零基础到精通,你绝...
- 福斯《死侍》发布新剧照 "小贱贱"韦德被改造前造型曝光
-
时光网讯福斯出品的科幻片《死侍》今天发布新剧照,其中一张是较为罕见的死侍在被改造之前的剧照,其余两张剧照都是死侍在执行任务中的状态。据外媒推测,片方此时发布剧照,预计是为了给不久之后影片发布首款正式预...
- 2021年超详细的java学习路线总结—纯干货分享
-
本文整理了java开发的学习路线和相关的学习资源,非常适合零基础入门java的同学,希望大家在学习的时候,能够节省时间。纯干货,良心推荐!第一阶段:Java基础重点知识点:数据类型、核心语法、面向对象...
- 不用海淘,真黑五来到你身边:亚马逊15件热卖爆款推荐!
-
Fujifilm富士instaxMini8小黄人拍立得相机(黄色/蓝色)扫二维码进入购物页面黑五是入手一个轻巧可爱的拍立得相机的好时机,此款是mini8的小黄人特别版,除了颜色涂装成小黄人...
- 2025 年 Python 爬虫四大前沿技术:从异步到 AI
-
作为互联网大厂的后端Python爬虫开发,你是否也曾遇到过这些痛点:面对海量目标URL,单线程爬虫爬取一周还没完成任务;动态渲染的SPA页面,requests库返回的全是空白代码;好不容易...
- 最贱超级英雄《死侍》来了!_死侍超燃
-
死侍Deadpool(2016)导演:蒂姆·米勒编剧:略特·里斯/保罗·沃尼克主演:瑞恩·雷诺兹/莫蕾娜·巴卡林/吉娜·卡拉诺/艾德·斯克林/T·J·米勒类型:动作/...
- 停止javascript的ajax请求,取消axios请求,取消reactfetch请求
-
一、Ajax原生里可以通过XMLHttpRequest对象上的abort方法来中断ajax。注意abort方法不能阻止向服务器发送请求,只能停止当前ajax请求。停止javascript的ajax请求...
- 一周热门
- 最近发表
- 标签列表
-
- HTML 简介 (30)
- HTML 响应式设计 (31)
- HTML URL 编码 (32)
- HTML Web 服务器 (31)
- HTML 表单属性 (32)
- HTML 音频 (31)
- HTML5 支持 (33)
- HTML API (36)
- HTML 总结 (32)
- HTML 全局属性 (32)
- HTML 事件 (31)
- HTML 画布 (32)
- HTTP 方法 (30)
- 键盘快捷键 (30)
- CSS 语法 (35)
- CSS 轮廓宽度 (31)
- CSS 谷歌字体 (33)
- CSS 链接 (31)
- CSS 定位 (31)
- CSS 图片库 (32)
- CSS 图像精灵 (31)
- SVG 文本 (32)
- 时钟启动 (33)
- HTML 游戏 (34)
- JS Loop For (32)
