配置项目环境及首页
准备工作
1、将我们学习 MyBatis的文件代码拷贝过来!
2、导入maven依赖
3、导入实体类
4、导入mapper及mapper.xml文件
5、配置资源问题、druid配置
6、导入我们的前端的素材
首页实现!
1、添加视图控制 第一种方式可以这样写
// 我们自己编写一个视图解析,路由!
@Override
public void addViewControllers(ViewControllerRegistry registry) {
// 视图跳转的控制!
registry.addViewController("/index").setViewName("login");
registry.addViewController("/").setViewName("login");
registry.addViewController("/index.html").setViewName("login");
}
2、控制器 第二种方式这样写
@RequestMapping({"/","/index.html","index"})
public String index(){
return "index";
}
正式开发项目之前,需要将所有页面的中链接的路径改为 thymeleaf 的路径配置!
1、配置页面支持 thymeleaf
2、所有的链接改为 @
3、将所有页面的链接都改为 @ {} 进行支持适配!
页面国际化
我们在平时工作或者开发一些网站的,尤其是国际网站,国际化就是必须要使用的了!
准备工作
首先需要保证编码没有问题:
创建i18n作为自己的国际化目录
1.首先在resource目录下面加上一个i18n的目录,然后点击这个目录右键选择NEW----
这样就是一个关于login登陆的国际化文件了,然后我们再去新建一个中文的propereties文件,首先点击i18n,然后右键选择NEW----->FILE 输入login_zh_CN.properties,然后这个i18n下面自动的就会生成一个关于login的resource bundle的目录,这样的话我们再新建英文的properties的时候,就可以这样做了
1.点击 Resource Bundle Login 然后NEW ---->Add Property Files to Resource Bundle
2.在打开的页面中,点击+号,输入en_US然后点击确定
然后我们开始配置我们的国际化页面的元素:
创建我们的国际化元素
1.首先点击中文的properties文件,然后点击这个文件左下角的resource bundle标签,使之变成另外一种编辑形式:
2.然后点击+号: 这个就是元素了,就是你的页面上你想要哪些元素的文字成为国际化的那么在这里你就需要i配置多少个这样的元素,右边的三个输入框中,第一个是默认的,第二个是英文的,第三个是中文的
然后我们去配置一下这个:
最终完成的结果:
如果是一整个完整的页面(文档页)文章量十分大的时候,没有必要做这些细节化的国际化操作!
thymeleaf配置国际化
还需要使项目识别我们的配置
去找MVCAutoConfigure 关于国际化资源配置的
messageSourceAutoConfigure
去再properties页面配置
spring.messages.basename=i18n.login
然后在前端页面去配置即可
th:text="#{login.tip}" login.tip就是实现了国际化的变量
原理: 就是请求头里面带着这些中英文的信息
通过点击按钮或者连接实现动态的中英文切换
以Dubbo官网为例,
中文:http://dubbo.apache.org/zh-cn/
英文:http://dubbo.apache.org/en-us/
是通过不同的链接来实现中英文切换的
在Spring中,国际化对象 Locale!在SpringBoot中,帮我们自动注入了国际化的组件!
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "spring.mvc", name = "locale")
public LocaleResolver localeResolver() { // 国际化对象解析LocaleResolver
// 如果用户配置了 就使用用户配置的,如果没有用户配置就使用默认的!
if (this.mvcProperties.getLocaleResolver() ==
WebMvcProperties.LocaleResolver.FIXED) {
return new FixedLocaleResolver(this.mvcProperties.getLocale());
}
// 接收请求头信息关于国际化的
AcceptHeaderLocaleResolver localeResolver = new
AcceptHeaderLocaleResolver();
localeResolver.setDefaultLocale(this.mvcProperties.getLocale());
return localeResolver;
}
在我们的接收请求解析中发现一个重写的方法:
@Override
public Locale resolveLocale(HttpServletRequest request) {
Locale defaultLocale = getDefaultLocale();
// 从 request 中获得 Accept-Language 语言信息
if (defaultLocale != null && request.getHeader("Accept-Language") == null) {
return defaultLocale;
}
// 得到请求中国际化的信息
Locale requestLocale = request.getLocale();
List<Locale> supportedLocales = getSupportedLocales();
if (supportedLocales.isEmpty() || supportedLocales.contains(requestLocale))
{
return requestLocale;
}!
Locale supportedLocale = findSupportedLocale(request, supportedLocales);
if (supportedLocale != null) {
return supportedLocale;
}
return (defaultLocale != null ? defaultLocale : requestLocale);
在sprinfboot中帮我们自动注入了国际化的组件(WEBMVCAutoConfigure里面的LocaleResolver)国际化解析
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(prefix = "spring.mvc", name = "locale")
public LocaleResolver localeResolver() {
//如果用户配置了,就使用用户配置的信息,如果没有就使用默认的信息
if (this.mvcProperties.getLocaleResolver() == WebMvcProperties.LocaleResolver.FIXED) {
return new FixedLocaleResolver(this.mvcProperties.getLocale());
}
AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
localeResolver.setDefaultLocale(this.mvcProperties.getLocale());
return localeResolver;
}
分析一下如何自定义一个国际化解析器
1、修改前端页面的链接
<!-- localhost:8080/index?l=zh_CN thymeleaf中传递参数不用使用? 使用()-->
<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>
2.处理这个请求! 我们是以参数的请求来进行处理的!我们去解析请求构造国际化对象即可!
package com.coding.handler;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.LocaleResolver;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Locale;
// 自己定义的一个国际化处理器
public class MyLocaleResolver implements LocaleResolver {
// 说白了还是 JavaWeb 的基本操作!
@Override
public Locale resolveLocale(HttpServletRequest request) {
System.out.println("=============================");
// 获取请求中的参数
String language = request.getParameter("l");
System.out.println("language===========>"+language);
Locale locale = Locale.getDefault(); // 如果我们没有配置就使用默认的!
if (!StringUtils.isEmpty(language)){
String[] split = language.split("_");
// 分析国家和地区
locale = new Locale(split[0],split[1]);
}
return locale;
}
@Override
public void setLocale(HttpServletRequest request, HttpServletResponse
response, Locale locale) {
}
}
3、注册到IOC中!
// 注册我们的国际化组件
// id localeResolver 只能为这个,应为SpringBoot回去扫描并是识别
/** 源码如下
public LocaleResolver getLocaleResolver() {
return this.localeResolver;
}
*/
// common-io 文件上传的类,用Spring去使用的时候,id是不能变得
@Bean
public LocaleResolver localeResolver(){
return new MyLocaleResolver();
}
4、重启项目,点击测试即可!
登录+拦截器
登录实现
1.修改登录的表单
th:action="@{/user/login}" method="POST"
2.只要涉及的页面的修改的我们需要禁用页面缓存,这样修改页面的时候就不要重新启动项目了
spring.thymeleaf.cache=false
3.创建自己的用户表
4.编写处理请求
// 作业:自己下去建立一个用户表即可!
//登录
@PostMapping("/user/login")
public String login(@RequestParam("username") String username,
@RequestParam("password") String password,
Model model, HttpSession session){
// 真实的操作应该去数据库中获取数据
if (!StringUtils.isEmpty(username) && "123456".equals(password)){
// 登录成功,放入Session
session.setAttribute("loginUser",username);
return "redirect:/main.html"; // 转发和重定向!
}else {
model.addAttribute("msg", "用户名和密码输入错误");
return "login";
}
}
拦截器
4.拦截器的实现 需要在登录的用户才能进入主页面,否则的话不能进入主页面
只修改前端页面的话不需要重新启动项目,只需要build项目一下即可,就是Ctrl+F9快捷键
编写拦截器
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class LoginInterceptor implements HandlerInterceptor {
// false 拦截
// true 通过放行
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse
response, Object handler) throws Exception {
Object user = request.getSession().getAttribute("loginUser");
if (user==null){ // 未登录,不放行,并且跳转到登录页!
request.setAttribute("msg","未登录,请先登录!");
// response.sendRedirect() ; 重定向
request.getRequestDispatcher("/index.html").forward(request,response); // 转发
return false;
}else {
// 放行
return true;
}
}
注册拦截器
// 注册拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 拦截器需要放行第一次登录的请求,否则就用户为null 了
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/index.html","/","index","/asserts/**","/user/login");
}
测试即可!
前端获取用户名展示
<!-- 直接使用也可以直接获取Thymeleaf的值,thymeleaf 中不能直接写 表达式 -->
<a class="navbar-brand col-sm-3 col-md-2 mr-0" style="color:white;">用户:
[[${session.loginUser}]]</a>
员工列表实现
Restful风格的API
/emp get
/emp post delete put
员工列表页面跳转
前端提交
<li class="nav-item">
<a class="nav-link" th:href="@{/emps}">员工管理</a>
</li>
后端处理
// @RequestMapping // 六种请求
@GetMapping("/emps")
public String list(Model model){
List<Employee> employees = employeeMapper.getEmployees();
model.addAttribute("emps",employees);
return "emp/list";
}
发现问题
跳转之后,页面存在公共的部分,我们应该抽取成模板 ,相当于Vue中的组件!
抽取公共部分,common文件夹中
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<!-- 存放页面模板 -->
<!--头部-->
<nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0"
th:fragment="topbar">
<!-- 直接使用也可以直接获取Thymeleaf的值,thymeleaf 中不能直接写 表达式 -->
<a class="navbar-brand col-sm-3 col-md-2 mr-0" style="color:white;">用户:
[[${session.loginUser}]]</a>
<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/#">Sign out</a>
</li>
</ul>
</nav>
<!--侧边栏-->
<nav class="col-md-2 d-none d-md-block bg-light sidebar" th:fragment="sidebar">
<div class="sidebar-sticky">
<ul class="nav flex-column">
<!-- 通过不同参数实现不同 class 样式 -->
<li class="nav-item">
<a th:class="${activeUrl=='main.html'?'nav-link active':'nav-
link'}"
th:href="@{/main.html}">
首页
</a>
</li>
<li class="nav-item">
<!-- 我需要知道当前是员工管理页面,通过参数来判断! -->
<a th:class="${activeUrl=='emps'?'nav-link active':'nav-link'}"
th:href="@{/emps}">员工管理
</a>
</li>
</ul>
</div>
</nav>
前端页面复用模块:
<!-- 头部-->
<div th:replace="~{common/bar::topbar}"></div>
<!--侧边栏-->
<div th:replace="~{common/bar::sidebar(activeUrl='emps')}"></div>
员工列表展示
修改前段页面样式
<!-- 主要内容区域 -->
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
<h2>
<!--添加员工-->
<button type="button" class="btn btn-success">添加员工</button>
</h2>
<div class="table-responsive">
<table class="table table-striped table-sm">
<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.getId()}"></td>
<td> [[${emp.getLastName()}]] </td>
<td th:text="${emp.getEmail()}"></td>
<td th:text="${emp.getGender()==0?'女':'男'}"></td>
<td th:text="${emp.getEDepartment().getDepartmentName()}">
</td>
<td th:text="${#dates.format(emp.getBirth(), 'yyyy-MM-dd
HH:mm')}"></td>
<td>
<button type="button" class="btn btn-sm btn-primary">编辑
</button>
<button type="button" class="btn btn-sm btn-danger">删除
</button>
</td>
</tr>
</tbody>
</table>
</div>
</main>
添加员工操作
拷贝list页面建一个 add.html,修改主页内容即可!
<!-- 主要内容区域 -->
<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>LastName</label>
<input type="text" class="form-control" name="lastName"
placeholder="coding">
</div>
<div class="form-group">
<label>Email</label>
<input type="text" class="form-control" name="email"
placeholder="24736743@qq.com">
</div>
<div class="form-group">
<label>Gender</label>
<!-- form -->
<div class="form-inline">
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="gender"
value="1">
<label>男</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="gender"
value="0">
<label>女</label>
</div>
</div>
</div>
<div class="form-group">
<label>Department</label>
<!-- 应该要从数据库中读取信息 -->
<select class="form-control" name="department">
<option th:each="department:${departments}"
th:text="${department.getDepartmentName()}"
th:value="${department.getId()}"></option>
</select>
</div>
<div class="form-group">
<label>Birth</label>
<input type="text" class="form-control" name="birth"
placeholder="2020/03/21">
</div>
<button type="submit" class="btn btn-primary">添加</button>
</form>
</main>
处理请求:
@PostMapping("/emp")
public String addEmp(Employee employee){
// TODO : 添加员工的逻辑
System.out.println(employee);
employeeMapper.save(employee);
// 添加完毕后应该返回首页,重定向url会变化,请求转发url不会变化
return "redirect:/emps";
}
注意:提交时间格式的问题!
# 前端提交的日期格式问题 (设置的细节问题,和前端提交的格式对应即可!)
spring.mvc.date-format=yyyy-MM-dd
修改员工实现
修改按钮的处理: 传入参数才可以的
<a th:href="@{/emp/updateEmployeeById/}+${emp.id}" type="button" class="btn btn-sm btn-primary">编辑</a>
1、新建一个修改页面 注意回显的格式和日期格式的处理
th:value="${#dates.format(employee.getBirth(),‘yyyy/MM/dd’)}"
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<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>Dashboard Template for Bootstrap</title>
<link th:href="@{/css/bootstrap.min.css}" rel="stylesheet">
<link th:href="@{/css/dashboard.css}" rel="stylesheet">
<style type="text/css">
/* Chart.js */
@-webkit-keyframes chartjs-render-animation {
from {
opacity: 0.99
}
to {
opacity: 1
}
}
@keyframes chartjs-render-animation {
from {
opacity: 0.99
}
to {
opacity: 1
}
}
.chartjs-render-monitor {
-webkit-animation: chartjs-render-animation 0.001s;
animation: chartjs-render-animation 0.001s;
}
</style>
</head>
<body>
<div th:replace="~{bar/topBar::topbar}"></div>
<div class="container-fluid">
<div class="row">
<div th:replace="~{bar/topBar::sidebar(activeUrl='emps')}"></div>
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
<!-- 组件中的name应该是和employee 对应的才对 -->
<form th:action="@{/emp}" method="post">
<input type="text" class="form-control" name="id"
style="width:250px" th:value="${employee.getId()}">
<div class="form-inline">
<label>名称:</label>
<input type="text" class="form-control" name="lastName"
placeholder="coding" style="width:250px" th:value="${employee.getLastName()}">
</div>
<div class="form-inline">
<label>邮箱:</label>
<input type="text" class="form-control" name="email"
placeholder="24736743@qq.com" style="width:250px" th:value="${employee.getEmail()}">
</div>
<div class="form-inline">
<label>性别:</label>
<!-- form -->
<div class="form-inline" style="width:250px">
<input class="form-check-input" type="radio" name="gender"
value="1" th:checked="${employee.getGender()}==1">
<label>男</label>
<input class="form-check-input" type="radio" name="gender"
value="0" th:value="${employee.getGender()}==0">
<label>女</label>
</div>
</div>
<div class="form-inline">
<label>部门:</label>
<select class="form-control" name="department" style="width:250px" >
<option th:each="department:${departments}" th:text="${department.getDepartmentName()}"
th:value="${department.getId()}" th:selected="${employee.getDepartment()==department.getId()}">
</option>
</select>
</div>
<div class="form-inline">
<label>生日:</label>
<input type="text" class="form-control" name="birth"
placeholder="2020/03/21" style="width:250px" th:value="${#dates.format(employee.getBirth(),'yyyy/MM/dd')}">
</div>
<button type="submit" class="btn btn-primary">添加</button>
</form>
</main>
</div>
</div>
<!-- Bootstrap core JavaScript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script type="text/javascript" th:src="@{/js/jquery.min.js}"></script>
<script type="text/javascript" th:src="@{/js/popper.min.js}"></script>
<script type="text/javascript" th:src="@{/js/bootstrap.min.js}"></script>
<!-- Icons -->
<script type="text/javascript" th:src="@{/js/feather.min.js}"></script>
<script>
feather.replace()
</script>
<!-- Graphs -->
<script type="text/javascript" th:src="@{/js/Chart.min.js}"></script>
<script>
var ctx = document.getElementById("myChart");
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
datasets: [{
data: [15339, 21345, 18483, 24003, 23489, 24092, 12034],
lineTension: 0,
backgroundColor: 'transparent',
borderColor: '#007bff',
borderWidth: 4,
pointBackgroundColor: '#007bff'
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: false
}
}]
},
legend: {
display: false,
}
}
});
</script>
</body>
</html>
3、测试:
@GetMapping("/emp/{id}")
public String updateEmployeeById(@PathVariable("id") int id,Model model){
List<Department> departments = departmentMapper.getDepartments();
model.addAttribute("departments",departments);
Employee employee = employeeMapper.getEmployeeById(id);
model.addAttribute("employee",employee);
return "emp/empUpdate";
};
删除员工注销
1、删除请求
2、处理请求
3、注销请求
4、注销测试
基本的错误处理方式
一步到位: 建立一个error文件夹,在里面放入对应的请求页面就可以!
404.html
500.html
4xx.html
5xx.html
上线网站
将项目打包到服务器上运行
首先我们需要有打包的那个插件:
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
2.修改pom.xml中的resource .将所有的代码全部都导出来
<resources>
<!-- 配置文件导出 -->
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/resource</directory>
<includes>
<include>**/*</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
3.然后在MAVEN试图中,首先点击clean ,然后再点击package
4.在clean的时候如果提示pom.xml出现问题的时候,
这时候有两种解决办法:
1.pom.xml文件里的jar前面的空格有问题,重新删除空格再换行即可解决
例如:jar是从其他地方复制过来的,画框的地方空格,可能有问题,但是pom文件是没有报错的,删除空格重新换行
2.从POM文件中,将对应的信息进行移除! 然后执行Maven操作:clean,将原先依赖重新填写到POM文件中,恢复正常,不再出现红色波浪线
配置数据库
创建和本地一样的数据库
运行项目(发现外网无法访问)
4、Java -jar xxxjar.jar
如果出现端口已经被使用的话,那么就需要更改端口了,然后重新打包运行
开启Liunx防火墙 9000 (依旧发现外网无法访问)
5、
在阿里云上配置安全组规则!开放 9000 段!
错误处理机制(拓展,有兴趣可以自己研究下错误页面定制!)
错误处理的自动配置 ErrorMvcAutoConfiguration,里面注册了如下的bean
DefaultErrorAttributes
BasicErrorController
ErrorPageCustomizer
DefaultErrorViewResolver
步骤:
1、如果系统一旦出现了 404. 500 错误,他就会被 ErrorPageCustomizer 进行处理!就会跳转到
/error 请求进行处理!
@Override
public void registerErrorPages(ErrorPageRegistry errorPageRegistry) {
ErrorPage errorPage = new ErrorPage(
this.dispatcherServletPath.getRelativePath(this.properties.getError().getPath()));
errorPageRegistry.addErrorPages(errorPage);
}
getPath就是下面的地址
@Value("${error.path:/error}")
private String path = "/error";
2、这个请求 BasicErrorController处理!若是是网页客户端请求就会返回页面,如果是api调用就会返
回 JSON 数据
@RequestMapping(produces = MediaType.TEXT_HTML_VALUE) // 如果是网页请求就返回视图
public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse
response) {
HttpStatus status = getStatus(request);
Map<String, Object> model = Collections
.unmodifiableMap(getErrorAttributes(request,
isIncludeStackTrace(request, MediaType.TEXT_HTML)));
response.setStatus(status.value());
// 错误的页面会被谁处理呢?
ModelAndView modelAndView = resolveErrorView(request, response, status,
model);
return (modelAndView != null) ? modelAndView : new ModelAndView("error",
model);
}
@RequestMapping // 其与的请求都会返回错误的状态
public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
HttpStatus status = getStatus(request);
if (status == HttpStatus.NO_CONTENT) {
return new ResponseEntity<>(status);
}
Map<String, Object> body = getErrorAttributes(request,
isIncludeStackTrace(request, MediaType.ALL));
return new ResponseEntity<>(body, status);
}