创建第一个springboot项目
创建一个项目两种方式:
第一种方式:
去springboot官网)选中配置
第二种方式:
SpringBoot运行原理
1. pom.xml
父依赖
其中它主要是以来一个父项目,主要是管理项目的过滤及插件。
点进去,发现还有一个父依赖。
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
这里管理了SpringBoot应用中所有依赖版本的地方,SpringBoot版本控制中心。
所以之后在导入依赖的时候,我们就不需要写版本了,但是如果导入的包没有在依赖管理中就需要手动配置版本。
依赖管理中的名称都叫spring-boot-starter-xxx,
2. 主启动类
2.1 默认的启动类
package com.study.springboot1;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
//@SpringBootApplication 来标注一个主程序
//说明这是一个Spring Boot应用。
@SpringBootApplication
public class Springboot1Application {
public static void main(String[] args) {
//run()看着是一个方法,实际是启动一个服务
SpringApplication.run(Springboot1Application.class, args);
}
}
下面介绍一个这个启动类都做了什么
2.2 @SpringBootApplication
作用:标注在某个类上说明这个类是SpringBoot的主配置类,SpringBoot就应该运行这个类的main方法来启动SpringBoot应用。
进入这个注解可以看到上面还有很多其他注解。
@Target({
ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {
@Filter(
type = FilterType.CUSTOM,
classes = {
TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {
AutoConfigurationExcludeFilter.class}
)}
)
2.3 @ComponentScan
这个注解我们之前见过,就是扫描包。
作用:自动扫描并加载符合条件的组件或者Bean,讲这个Bean定义加载到IOC容器中。
2.4 @SpringBootConfiguration
作用:SpringBoot的配置类注解,标注在某个类上,表示这是一个SpringBoot的配置类;
继续看这个注释里面有什么:
@Target({
ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
这里@Configuration说明这是一个配置类,配置类就是对应的Spring的xml配置文件。
打开@Configuration注释
@Component
这里@Component,说明启动类本身也是Spring的一个组件,负责启动应用
@EnableAutoConfigur
作用:开启自动配置功能
打开这个注解:
@Target({
ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({
AutoConfigurationImportSelector.class})
首先看到@Import({AutoConfigurationImportSelector.class})
给容器导入组件,
@AutoConfigurationPackage
自动配置包
Spring Boot-YAML配置注入
1.配置文件:
//丢给spring管理,注册bean容器中
@Component
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
/*@ConfigurationProperties作用:
将配置我呢见中配置的每一个属性的值,映射到这个组件中。
告诉SpringBoot江本类中的所有属性和配置文件中相关的配置进行绑定。
参数prefix="dog",将配置文件中的person下面的所有属性一一对应
*/
@ConfigurationProperties(prefix = "dog")
public class Dog {
private String name;
private int age;
}
@Component//注册bean交给spring托管
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
@ConfigurationProperties(prefix = "person")
public class Person {
private String name;
private Integer age;
private Boolean happy;
private Date birthday;
private Map<String,Object> maps;
private List<Object> lists;
private Dog dog;
}
server:
port: 8081
dog:
name: 鸡蛋壳
age: 3
person:
age: 5
name: 苏卿
happy: true
birthday: 2020/1/4
maps: {
1: a,2: b}
lists: [q,b,c]
dog: {
name: d, age: 1 }
@SpringBootTest
class Springboot1ApplicationTests {
@Autowired
Person person;
@Test
void contextLoads() {
System.out.println(person.toString());
}
}
Spring Boot: Web开发静态资源处理
- 使用SpringBoot的步骤
员工管理系统
1. 准备工作
1.1 导入资源
将静态资源导入到resources文件夹里。
1.2 编写pojo实体类
员工表:
@Data
@NoArgsConstructor
public class Employee {
private Integer id;
private String lastName;
private String email;
private Integer gender; //性别 0 女, 1,男
private Department department;
private Date birth;
public Employee(Integer id, String lastName, String email, Integer gender, Department department) {
this.id = id;
this.lastName = lastName;
this.email = email;
this.gender = gender;
this.department = department;
this.birth = new Date();
}
}
部门表:
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Department {
private int id;
private String departmentName;
}
1.3 编写dao层
这里使用模拟数据库,并没有使用mysql数据库。
部门dao
@Repository 用于标注访问层(Dao层)的类。
与@Component注解相同,@Service和@Controller都予以理解为@Component注解的拓展,朱永都是在类上实例化bean,并把这个类交给spring容器进行管理。
@Repository
public class DepartmentDao {
private static Map<Integer, Department> departments = null;
static {
departments = new HashMap<Integer, Department>();
departments.put(101,new Department(101,"教学部"));
departments.put(102,new Department(102,"市场部"));
departments.put(103,new Department(103,"教研部"));
departments.put(104,new Department(104,"运营部"));
departments.put(105,new Department(105,"后勤部"));
}
public Collection<Department> findAll() {
return departments.values();
}
public Department findById(Integer id) {
return departments.get(id);
}
}
员工dao
@Repository
public class EmployeeDao {
private static Map<Integer, Employee> employees = null;
private final DepartmentDao departmentDao;
static {
employees = new HashMap<Integer,Employee>(); //创建一个部门表
employees.put(1001,new Employee( 1001,"AA","1622840727@qq.com",1,new Department(101,"教学部")));
employees.put(1002,new Employee( 1002,"BB","2622840727@qq.com",0,new Department(102,"市场部")));
employees.put(1003,new Employee( 1003,"CC","4622840727@qq.com",1,new Department(103,"教研部")));
employees.put(1004,new Employee( 1004,"DD","5628440727@qq.com",0,new Department(104,"运营部")));
employees.put(1005,new Employee( 1005,"FF","6022840727@qq.com",1,new Department(105,"后勤部")));
}
//逐渐自增
private static Integer initId=1006;
public EmployeeDao(DepartmentDao departmentDao) {
this.departmentDao = departmentDao;
}
//添加一个新员工:
public void addEmployee(Employee employee) {
if (employee.getId() == null){
employee.setId(initId++);
}
employee.setDepartment(departmentDao.findById(employee.getDepartment().getId()));
employees.put(employee.getId(), employee);
}
//查询全部员工
public Collection<Employee> getAllEmployees() {
return employees.values();
}
//通过ID查询员工
public Employee getEmployeeById(Integer id) {
return employees.get(id);
}
//删除员工
public void deleteEmployee(Integer id) {
employees.remove(id);
}
}
2. 首页实现:
2.1 引入Thymeleaf依赖
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-java8time</artifactId>
</dependency>
2.2 编写MyMVCConfig
@Configuration作用取代bean.xml配置文件注册bean对象
@Configuration
public class MyMVCConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
registry.addViewController("/index.html").setViewName("index");
registry.addViewController("/main.html").setViewName("dashboard");
registry.addViewController("/main").setViewName("dashboard");
}
}
这个可以对应理解为,在javaweb+jap中可以理解为servler-mapping
映射成"/"
<!-- web.xml -->
<servlet>
<servlet-name>indexServlet</servlet-name>
<servlet-class>com.example.IndexServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>indexServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
// IndexServlet.java
@WebServlet("/")
public class IndexServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.getRequestDispatcher("/index.jsp").forward(request, response);
}
}
在javamvc中可以理解成:使用@Controller注解定义一个控制器,并使用@RequestMapping注解来映射URL。
// IndexController.java
@Controller
public class IndexController {
@RequestMapping("/")
public String index() {
return "index"; // 返回视图名称,对应于 /WEB-INF/views/index.jsp
}
}
更改静态资源路径:
将所有静态资源都交给thymeleaf接管。@{}
例如:链接css文件时:<link th:href="@{/css/bootstrap.min.css}" rel="style
,用@{}
括起来。并在href
前面加上th:
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<title>Signin Template for Bootstrap</title>
<!-- Bootstrap core CSS -->
<link th:href="@{/css/bootstrap.min.css}" rel="stylesheet">
<!-- Custom styles for this template -->
<link th:href="@{/css/signin.css}" rel="stylesheet">
</head>
3. 页面国际化
首先需要将IDEA中的配置全部改成UTF-8
在
resources
资源文件下新建一个i18n
目录,用于存放国际化配置文件。创建
login.properties
文件,login_zh_CN.properties
中文文件,login_en_US.rpoperties
英文文件。然后会发现文件夹变了。然后需要安装
Resource Budle Editor
这个插件,才能更快更清晰的配置。当然不安装也可以。安装后是下面这个样子。点击资源包,就可以同时配三个,默认-英语-中文。当然不安装也可以直接设置:三个文件中都这样对应着设置即可。
配置生效:
我们需要在springboot配置文件中关联到这个文件,
在application.properties
文件中加入:
spring.messages.basename=i18n.login
然后我们还需要对Spring,中的l
在来自于前端点击配置的
<a class="btn btn-sm" th:href="@{/index.html(l='zh_CN')}">中文</a>
<a class="btn btn-sm" th:href="@{/index.html(l='en_US')}">English</a>
然后就是解析请求:
public class MyLocaleResolver implements LocaleResolver {
//解析请求
@Override
public Locale resolveLocale(HttpServletRequest request) {
// 获取请求中的语言参数
String language = request.getParameter("l");
Locale locale = Locale.getDefault();//如果没有就用默认的
//如果请求的链接携带了国际化的参数
if (!StringUtil.isNullOrEmpty(language)) {
String[] s = language.split("_");//分割
locale = new Locale(s[0], s[1]);
}
return locale;
}
@Override
public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
}
}
然后不要忘了将其配置到spring文件(MyMVCConfig.java
)中:
//自定义的国际化
@Bean
public LocaleResolver localeResolver() {
return new MyLocaleResolver();
}
4. 登录+拦截器
4.1 登录
禁用模板缓存
#禁用模板缓存
spring.thymeleaf.cache=false
首先肯定要先将表单中添加一个提交事件
<form class="form-signin" th:action="@{/user/login}"> …… </form>
编写对应的Controller
这里面的username和password对应着下面中的
name
标签提交都是携带name
标签<input type="text" name="username" class="form-control" th:placeholder="#{login.username}" required="" autofocus=""> <input type="password" name="password" class="form-control" th:placeholder="#{login.password}" required="">
@Controller public class LoginController { @RequestMapping("/user/login") public String login(@RequestParam("username") String username, @RequestParam("password") String password, Model model, HttpSession session) { //具体的业务: if (!StringUtil.isNullOrEmpty(username)&&"123456".equals(password)) { session.setAttribute("loginUser", username); return "redirect:/main"; }else { model.addAttribute("msg", "用户名错误或者密码错误"); return "index"; } } }
登录失败,将信息传送给前端。
<p style="color: red" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"></p>优化
优化,登陆后链接变化,使其重定向到首页
添加一个视图控制映射,加在自己的MyMVCConfig里面。
将Controller的代码改为重定向,就是这里,已经改过了。
if (!StringUtil.isNullOrEmpty(username)&&"123456".equals(password)) { session.setAttribute("loginUser", username); return "redirect:/main"; }else { model.addAttribute("msg", "用户名错误或者密码错误"); return "index"; }
4.2 登录拦截器
设置完上面,会发现一个问题:我们不登录,也可以直接访问到后台主页面,这是不允许的。所以我们添加拦截器。对没有登录的进行拦截。
在LoginController中添加Session.上面也已经写了。
session.setAttribute("loginUser", username);
自定义个一个拦截器:
public class LoginHandlerInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //登陆成功之后,应该有用户的session Object loginUser = request.getSession().getAttribute("loginUser"); if (loginUser == null) { request.setAttribute("msg","没有权限,请先登录"); request.getRequestDispatcher("/index.html").forward(request, response); return false; }else { return true; } } }
然后将拦截器配置到我们自己的SpringMVC配置类中(MyMVCConfig).
@Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginHandlerInterceptor()).addPathPatterns("/**").excludePathPatterns("/index.html","/", "/user/login","/css/**","/js/**","/img/**"); }
然后我们可以在后台获取用户登录的信息,这里我们选择只传递用户名
[[${session.loginUser}]]
5. 展示员工列表。
设置跳转:编写前端,
<a th:class="${active=='list.html'?'nav-link active':'nav-link'}" th:href="@{/emps}">员工管理</a>
编写Controller层对应的th:href="@{/emps}"
请求。
th:class="${active=='list.html'?'nav-link active':'nav-link'}"这个是在判断是否点击这个,如果点击这个显示蓝色,负责就正常颜色。为了凸显出我们在那个界面
@Controller
public class EmployeeController {
@Autowired
EmployeeDao employeeDao;
@Autowired
DepartmentDao departmentDao;
@RequestMapping("/emps")
public String lise(Model model) {
Collection<Employee> employees = employeeDao.getAllEmployees();
model.addAttribute("emps", employees);
return "emp/list";
}
}
公共元素抽离
我们看,每一个界面都有左边和上面这些公共元素,那么我们可以将其提取出来,封装一下,再拿来用时,直接插入
或者替换
即可。
例如我们将头部标签拿出来。
<!--顶部导航栏-->
<nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0" th:fragment="topbar">
<a class="navbar-brand col-sm-3 col-md-2 mr-0" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">[[${session.loginUser}]]</a> <!--$取EL表达式-->
<input class="form-control form-control-dark w-100" type="text" placeholder="Search" aria-label="Search">
<ul class="navbar-nav px-3">
<li class="nav-item text-nowrap">
<a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">注销</a>
</li>
</ul>
</nav>
然后我们在list页面引入,引入后就可以删除之前的了
<!--引入抽取的topbar-->
<!--模板名 : 会使用thymeleaf的前后缀配置规则进行解析 使用~{模板::标签名}-->
<!--顶部导航栏-->
<div th:replace="~{commons/commons::topbar}"></div>
th:insert="~{commons/commons::topbar}"
首先th:insert表示要插入:然后"~{}"这个是插入的表达式。
里面commons/commons,这个是公共元素在那个页面topbar这个是要插入那个块。
插入块那边用th:fragment="topbar"来标记。
5.3 员工信息页面展示:
<thead>
<tr>
<th>id</th>
<th>lastName</th>
<th>email</th>
<th>gender</th>
<th>department</th>
<th>birth</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr th:each="emp:${emps}">
<td th:text="${emp.getEmail()}"></td>
<td th:text="${emp.getId()}"></td>
<td th:text="${emp.getLastName()}"></td>
<td th:text="${emp.getGender()==0?'女':'男'}"></td>
<td th:text="${emp.department.getDepartmentName()}"></td>
<td th:text="${#dates.format(emp.getBirth(), 'yyyy-MM-dd HH:mm:ss')}"></td>
<td>
<a class="btn btn-sm btn-primary" th:href="@{/emp/} + ${emp.getId()}">编辑</a>
<a class="btn btn-sm btn-danger" th:href="@{/removeEmp/} + ${emp.getId()}">删除</a>
</td>
</tr>
</tbody>
6. 添加员工
前端表单
<h2><a class="btn btn-sm btn-success" th:href="@{/emp}">添加员工</a></h2>
编写对应的Controller
@GetMapping("/emp")
public String add(Model model) {
//查出所有部门的信息
Collection<Department> departments = departmentDao.findAll();
model.addAttribute("departments", departments);
return "emp/add";
}
添加前端页面
<div class="container-fluid">
<div class="row">
<!-- 侧边栏-->
<div th:replace="~{commons/commons::sidebar(active='list.html')}"></div>
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
<form th:action="@{/emp}" method="post">
<div class="form-group">
<label>姓名</label>
<input name="lastName" type="text" class="form-control" placeholder="姓名">
</div>
<div class="form-group">
<label>Email</label>
<input name="email" type="email" class="form-control" placeholder="Email">
</div>
<div class="form-group">
<label>性别</label><br/>
<div class="form-check form-check-inline">
<input name="gander" type="radio" class="form-check-input" value="1">
<label class="form-check-label">男</label>
</div>
<div class="form-check form-check-inline">
<input name="gander" type="radio" class="form-check-input" value="0">
<label class="form-check-label">女</label>
</div>
</div>
<div class="form-group">
<label>所属部门</label>
<select class="form-control" name="department.id">
<option th:each="dept:${departments}" th:text="${dept.getDepartmentName()}" th:value="${dept.getId()}"></option>
</select>
</div>
<div class="form-group">
<label>生日</label>
<input name="birth" type="text" class="form-control" placeholder="1975/01/01">
</div>
<button type="submit" class="btn btn-default">添加</button>
</form>
</main>
</div>
</div>
编写Controller层
注意这里都是"/emp"
但是一个是get
,一个是post
请求,是不同的。然后下面就是获取前端传过来的数据然后对模拟数据库进行操作,操作完,重定向到查询全部网站。
@PostMapping("/emp")
public String addEmp(Employee employee,Model model) {
employeeDao.addEmployee(employee);
return "redirect:/emps";
}
这是在前端中显示部门名称,而不是id,1,2,3,4,5这样用户不知道具体指代什么。
<select class="form-control" name="department.id">
<option th:each="dept:${departments}" th:text="${dept.getDepartmentName()}" th:value="${dept.getId()}"></option>
</select>
修改时间格式:在application.properies
中配置。从之前默认的的yyyy/MM/dd
spring.mvc.date-format=yyyy-MM-dd
修改员工信息
逻辑分析:
点击修改按钮应该先跳转到修改页面,
进入修改页面,原数据要显示在上面。修改完毕要返回列表页面。
- 前端设置跳转位置:
<a class="btn btn-sm btn-primary" th:href="@{/emp/} + ${emp.getId()}">编辑</a>
写Controller,在这里面,我们要通过id查到原数据,然后将原数据返回到修改页面,查部门消息是因为我们之后编辑部门时,能够显示出具体是什么部门,而不是单纯的id/
// 去员工修改页面 @GetMapping("/emp/{id}") public String toUpData(@PathVariable("id") Integer id, Model model){ //查出原本数据 Employee employee = employeeDao.getEmployeeById(id); model.addAttribute("emp", employee); //查所有的部门信息 Collection<Department> departments = departmentDao.findAll(); model.addAttribute("departments", departments); return "emp/update"; }
编写修改页面。
<div class="container-fluid"> <div class="row"> <!-- 侧边栏--> <div th:replace="~{commons/commons::sidebar(active='list.html')}"></div> <main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4"> <form th:action="@{/emp/update}" method="post"> <input type="hidden" name="id" th:value="${emp.getId()}"> <div class="form-group"> <label>姓名</label> <input th:value="${emp.getLastName()}" name="lastName" type="text" class="form-control" placeholder="姓名"> </div> <div class="form-group"> <label>Email</label> <input th:value="${emp.getEmail()}" name="email" type="email" class="form-control" placeholder="Email"> </div> <div class="form-group"> <label>性别</label><br/> <div class="form-check form-check-inline"> <input th:checked="${emp.getGender()==1}" name="gander" type="radio" class="form-check-input" value="1"> <label class="form-check-label">男</label> </div> <div class="form-check form-check-inline"> <input th:checked="${emp.getGender()==0}" name="gander" type="radio" class="form-check-input" value="0"> <label class="form-check-label">女</label> </div> </div> <div class="form-group"> <label>所属部门</label> <select class="form-control" name="department.id"> <option th:selected="${dept.getId()==emp.getDepartment().getId()}" th:each="dept:${departments}" th:text="${dept.getDepartmentName()}" th:value="${dept.getId()}"></option> </select> </div> <div class="form-group"> <label>生日</label> <input th:value="${#dates.format(emp.getBirth(), 'yyyy/MM/dd HH:mm')}" name="birth" type="text" class="form-control" placeholder="1975-01-01"> </div> <button type="submit" class="btn btn-default">修改</button> </form> </main> </div> </div>
编写修改的Controller
@PostMapping("/emp/update") public String updateEmp(Employee employee,Model model){ employeeDao.addEmployee(employee); return "redirect:/emps"; }
删除员工
list页面,提交地址:
<a class="btn btn-sm btn-danger" th:href="@{/removeEmp/} + ${emp.getId()}">删除</a>
编写Controller
//删除 @RequestMapping("/removeEmp/{id}") public String delete(@PathVariable("id") Integer id, Model model) { employeeDao.deleteEmployee(id); return "redirect:/emps"; }
404及注销
我们只需要命名为404.html
springboot就会自动帮我们使用了。
注销
注销请求地址
<a class="nav-link" th:href="@{/user/logout}">注销</a>
对应的Controller,这里去除
session
也行,直接invalidate
也行@RequestMapping("/user/logout") public String logout(HttpSession session) { session.removeAttribute("loginUser"); //session.invalidate(); return "redirect:/index.html"; }