一、组件版本
1)java 1.8.0_451 2)springboot 2.7.18 3)pagehelper-spring-boot-starter 1.4.5 4)mysql-connector-java
二、实现功能
1)实现需要登录才能查看其他页面 2)实现界面的增删改查 3)mybatis支持sql写mapper文件和xml文件两种方式 4)pagehelper实现分页功能
三、代码实现
1、代码架构
2、pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>springboot-login-news-curd</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.18</version>
</parent>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- SpringBoot Web 模块 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
<scope>provided</scope>
</dependency>
<!--mysql依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.25</version>
</dependency>
<!-- Thymeleaf for Frontend -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- PageHelper分页插件-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.5</version> <!-- 请根据需要选择合适的版本 -->
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName><!--修改编译出来的jar包名,仅为{artifactId}.jar-->
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<descriptorRefs>
<!--给jar包起的别名-->
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass>org.example.MyApplication</mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
3、控制器层
controller/UserController.java
package org.example.controller;
import org.example.pojo.User;
import org.example.service.UserService;
import org.example.util.SecurityUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
@Controller
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/login")
public String login() {
return "user/login"; // 返回登录页面
}
@PostMapping("/login")
public String login(@RequestParam("username") String username,
@RequestParam("password") String password,
RedirectAttributes redirectAttributes) {
User user = userService.getUserByUsername(username);
if (user != null && user.getPassword().equals(password)) {
// 登录成功,将用户信息存入会话
SecurityUtil.setLoginUser(user);
return "redirect:/news/"; // 跳转到新闻页面
} else {
// 登录失败,返回登录页面并提示错误
redirectAttributes.addFlashAttribute("error", "用户名或密码错误");
return "redirect:/user/login";
}
}
@GetMapping("/logout")
public String logout() {
// 登出,清除会话中的用户信息
SecurityUtil.removeLoginUser();
return "redirect:/user/login"; // 跳转到登录页面
}
}
controller/NewsController.java
package org.example.controller;
import org.example.pojo.News;
import org.example.service.NewsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Controller
@RequestMapping("/news")
public class NewsController {
@Autowired
private NewsService newsService;
@GetMapping("/")
public String list(Model model) {
List<News> newsList = newsService.getAllNews();
model.addAttribute("newsList", newsList);
return "news/list";
}
@GetMapping("/add")
public String add(Model model) {
model.addAttribute("news", new News());
return "news/add";
}
@PostMapping("/save")
public String save(@ModelAttribute News news) {
newsService.saveNews(news);
return "redirect:/news/";
}
@GetMapping("/edit/{id}")
public String edit(@PathVariable Integer id, Model model) {
News news = newsService.getNewsById(id);
model.addAttribute("news", news);
return "news/edit";
}
@PostMapping("/update")
public String update(@ModelAttribute News news) {
newsService.updateNews(news);
return "redirect:/news/";
}
@GetMapping("/delete/{id}")
public String delete(@PathVariable Integer id) {
newsService.deleteNews(id);
return "redirect:/news/";
}
}
4、config层
config/MyBatisConfig.java
package org.example.config;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import javax.sql.DataSource;
@Configuration
@MapperScan("org.example.mapper")
public class MyBatisConfig {
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
// sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
// .getResources("classpath:mapper/*.xml"));
return sessionFactory.getObject();
}
}
5、拦截器层
interceptor/LoginInterceptor.java
package org.example.interceptor;
import org.example.util.SecurityUtil;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 检查用户是否已经登录
if (!SecurityUtil.isLogin()) {
// 如果未登录,重定向到登录页面
response.sendRedirect(request.getContextPath() + "/user/login");
return false;
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
// 无需实现
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// 无需实现
}
}
6、mapper层
mapper/NewsMapper.java
package org.example.mapper;
import org.apache.ibatis.annotations.*;
import org.example.pojo.News;
import java.util.List;
/**
* 文章Mapper - sql都写在 mapper 文件中
*/
public interface NewsMapper {
@Select("SELECT * FROM news")
List<News> findAll();
@Select("SELECT * FROM news WHERE id = #{id}")
News findById(@Param("id") Integer id);
@Insert("INSERT INTO news (title, content) VALUES (#{title}, #{content})")
void insert(News news);
@Update("UPDATE news SET title = #{title}, content = #{content} WHERE id = #{id}")
void update(News news);
@Delete("DELETE FROM news WHERE id = #{id}")
void deleteById(@Param("id") Integer id);
}
7、service层
service/NewsService.java
package org.example.service;
import org.example.mapper.NewsMapper;
import org.example.pojo.News;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class NewsService {
@Autowired
private NewsMapper newsMapper;
public List<News> getAllNews() {
return newsMapper.findAll();
}
public News getNewsById(Integer id) {
return newsMapper.findById(id);
}
public void saveNews(News news) {
newsMapper.insert(news);
}
public void updateNews(News news) {
newsMapper.update(news);
}
public void deleteNews(Integer id) {
newsMapper.deleteById(id);
}
}
service/UserService.java
package org.example.service;
import org.example.pojo.User;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
@Service
public class UserService {
private static final Map<String, User> users = new HashMap<>();
static {
// 初始化一个固定的用户
User admin = new User();
admin.setId(1L);
admin.setUsername("admin");
admin.setPassword("admin");
users.put(admin.getUsername(), admin);
}
public User getUserByUsername(String username) {
return users.get(username); // 根据用户名获取用户信息
}
}
8、pojo层
pojo/news.java
package org.example.pojo;
import java.time.LocalDateTime;
public class News {
private Integer id;
private String title;
private String content;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;
// Getters and Setters
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public LocalDateTime getCreatedAt() {
return createdAt;
}
public void setCreatedAt(LocalDateTime createdAt) {
this.createdAt = createdAt;
}
public LocalDateTime getUpdatedAt() {
return updatedAt;
}
public void setUpdatedAt(LocalDateTime updatedAt) {
this.updatedAt = updatedAt;
}
}
pojo/User.java
package org.example.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Data
@Getter
@AllArgsConstructor
@NoArgsConstructor
public class User {
// Getters and Setters
private Long id;
private String username;
private String password;
}
9、util层
util/SecurityUtil.java
package org.example.util;
import javax.servlet.http.HttpSession;
import org.example.pojo.User;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
public class SecurityUtil {
private static final String LOGIN_USER = "loginUser";
public static void setLoginUser(User user) {
HttpSession session = getSession();
session.setAttribute(LOGIN_USER, user); // 将用户信息存入会话
}
public static User getLoginUser() {
HttpSession session = getSession();
return (User) session.getAttribute(LOGIN_USER); // 从会话中获取用户信息
}
public static boolean isLogin() {
return getLoginUser() != null; // 检查用户是否登录
}
public static void removeLoginUser() {
HttpSession session = getSession();
session.removeAttribute(LOGIN_USER); // 清除会话中的用户信息
}
private static HttpSession getSession() {
return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest().getSession();
}
}
10、主入口
MyApplication.java
package org.example;
import org.example.interceptor.LoginInterceptor;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.SpringApplication;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@SpringBootApplication
public class MyApplication implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 添加登录拦截器,拦截 /news/** 路径,但排除登录和登出路径
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/news/**") // 拦截新闻模块的所有请求
.excludePathPatterns("/user/login", "/user/logout"); // 排除登录和登出页面
}
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
11、配置文件
application.yml
spring:
profiles:
active: dev
application-dev.yml
server:
port: 8081
---
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/springboot-demo
username: root
password: abc123456
# 模版配置
thymeleaf:
mode: HTML5
cache: false
encoding: UTF-8
prefix: classpath:/templates/
suffix: .html
# mybatis 相关配置
mybatis:
#目的是为了省略resultType里的代码量
type-aliases-package: org.example.pojo
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# PageHelper 相关配置
pagehelper:
helperDialect: mysql
reasonable: true
12、前端模版文件
user/login.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Login</title>
</head>
<body>
<h1>Login</h1>
<form th:action="@{/user/login}" method="post">
<div><label>Username: <input type="text" name="username"></label></div>
<div><label>Password: <input type="password" name="password"></label></div>
<div><input type="submit" value="Login"></div>
</form>
<div th:if="${error}" style="color: red;">
<p th:text="${error}"></p>
</div>
</body>
</html>
news/list.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>News List</title>
</head>
<body>
<h1>News List</h1>
<a href="/news/add">Add News</a>
<a href="/user/logout">Logout</a>
<table border="1">
<thead>
<tr>
<th>ID</th>
<th>Title</th>
<th>Content</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr th:each="news : ${newsList}">
<td th:text="${news.id}"></td>
<td th:text="${news.title}"></td>
<td th:text="${news.content}"></td>
<td>
<a th:href="@{'/news/edit/' + ${news.id}}">Edit</a>
<a th:href="@{'/news/delete/' + ${news.id}}">Delete</a>
</td>
</tr>
</tbody>
</table>
</body>
</html>
news/add.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Add News</title>
</head>
<body>
<h1>Add News</h1>
<form th:action="@{/news/save}" method="post" th:object="${news}">
<label>Title:</label>
<input type="text" th:field="*{title}" />
<br />
<label>Content:</label>
<textarea th:field="*{content}"></textarea>
<br />
<button type="submit">Save</button>
</form>
</body>
</html>
news/edit.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Edit News</title>
</head>
<body>
<h1>Edit News</h1>
<form th:action="@{/news/update}" method="post" th:object="${news}">
<input type="hidden" th:field="*{id}" />
<label>Title:</label>
<input type="text" th:field="*{title}" />
<br />
<label>Content:</label>
<textarea th:field="*{content}"></textarea>
<br />
<button type="submit">Update</button>
</form>
</body>
</html>