准备工作
public class Department {
private Integer id;
private String dapartmentName;
//员工
public class Employee {
private Integer id;
private String lastName;
private String email;
private Integer gender;//0:女 1:男
private Department department;
private Date birth;
模拟数据库
//部门dao
@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(101,new Department(102,"市场部"));
departments.put(101,new Department(103,"教研部"));
departments.put(101,new Department(104,"运营部"));
departments.put(101,new Department(105,"后勤部"));
}
//获得所有部门信息
public Collection<Department> getDepartment(){
return departments.values();
}
//通过id获取到部门
public Department getDepartment(Integer id){
return departments.get(id);
}
}
//员工dao
@Repository
public class EmployeeDao {
//模拟数据库中的数据
private static Map<Integer, Employee> employees=null;
//员工所属部门
@Autowired
private DepartmentDao departmentDao;
static {
employees=new HashMap<Integer, Employee>();//创建一个部门表
employees.put(101,new Employee(101,"AA","2222461@qq.com",1,new Department(101,"教学部"),new Date()));
employees.put(102,new Employee(102,"BB","2222222@qq.com",0,new Department(102,"市场部"),new Date()));
employees.put(103,new Employee(103,"CC","2222222@qq.com",1,new Department(103,"教研部"),new Date()));
employees.put(104,new Employee(104,"DD","2222222@qq.com",0,new Department(104,"运营部"),new Date()));
employees.put(105,new Employee(105,"EE","2222222@qq.com",1,new Department(105,"后勤部"),new Date()));
}
//主键自增
private static Integer intId=10086;
//增加一个员工
private void save(Employee employee){
if (employee.getId() == null){
employee.setId(intId++);
}
employee.setDepartment(departmentDao.getDepartment(employee.getDepartment().getId()));
employees.put(employee.getId(),employee);
}
//查询全部员工信息
public Collection<Employee> getAll(){
return employees.values();
}
//通过id查询员工
public Employee getEmployeedById(Integer id){
return employees.get(id);
}
//删除员工
public void delete(Integer id){
employees.remove(id);
}
}
首页
- 首页配置,注意点,所有页面的静态资源都需要使用thymeleaf接管,使用@{/ } 相当于绝对路径
/相当于class目录根目录
server.servlet.context-path=/lt 相当于项目的虚拟目录
访问首页
方法一 不推荐使用
//访问这两个目录都能到达index.html 首页
@RequestMapping({"/","/index.html"})
public String index(){
return "index";
}
方法二 自定义视图解析器 推荐
@Configuration
public class MyMVCConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
registry.addViewController("/index.html").setViewName("index");
}
}
访问首页发现静态资源加载不进来,因为Thymeleaf模板之后要把引入的css改成Thymeleaf支持的格式
导入命名空间
xmlns:th="http://www.thymeleaf.org"
<link th:href="@{/css/bootstrap.min.css}" rel="stylesheet">
页面国际化
有的时候,我们的网站会去涉及中英文甚至多语言的切换,这时候我们就需要学习国际化了!
准备工作
先在IDEA中统一设置properties的编码问题!
编写国际化配置文件,抽取页面需要显示的国际化页面消息。我们可以去登录页面查看一下,哪些内容我们需要编写国际化的配置!
配置文件编写
1、我们在resources资源文件下新建一个i18n目录,存放国际化配置文件
2、建立一个login.properties文件,还有一个login_zh_CN.properties;发现IDEA自动识别了我们要做国际化操作;文件夹变了! 发现两个文件被合并了
3、我们可以在这上面去新建一个文件;
弹出如下页面:我们再添加一个英文的;
这样就快捷多了!
4、接下来,我们就来编写配置,我们可以看到idea下面有另外一个视图 可视化配置;
这个视图我们点击 + 号就可以直接添加属性了;我们新建一个login.tip,可以看到边上有三个文件框可以输入
第一个框代表默认
我们添加一下首页的内容!
然后依次添加其他页面内容即可!
名字随意起
然后去查看我们的配置文件;
login.properties :默认
login.btn=登录
login.password=密码
login.remember=记住我
login.tip=请登录
login.username=用户名
英文:
login.btn=Sign in
login.password=Password
login.remember=Remember me
login.tip=Please sign in
login.username=Username
中文:
ogin.btn=登录
login.password=密码
login.remember=记住我
login.tip=请登录
login.username=用户名
OK,配置文件步骤搞定!
配置文件生效探究
我们去看一下SpringBoot对国际化的自动配置!这里又涉及到一个类:MessageSourceAutoConfiguration
里面有一个方法,这里发现SpringBoot已经自动配置好了管理我们国际化资源文件的组件 ResourceBundleMessageSource;
// 获取 properties 传递过来的值进行判断
@Bean
public MessageSource messageSource(MessageSourceProperties properties) {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
if (StringUtils.hasText(properties.getBasename())) {
// 设置国际化文件的基础名(去掉语言国家代码的)
messageSource.setBasenames(
StringUtils.commaDelimitedListToStringArray(
StringUtils.trimAllWhitespace(properties.getBasename())));
}
if (properties.getEncoding() != null) {
messageSource.setDefaultEncoding(properties.getEncoding().name());
}
messageSource.setFallbackToSystemLocale(properties.isFallbackToSystemLocale());
Duration cacheDuration = properties.getCacheDuration();
if (cacheDuration != null) {
messageSource.setCacheMillis(cacheDuration.toMillis());
}
messageSource.setAlwaysUseMessageFormat(properties.isAlwaysUseMessageFormat());
messageSource.setUseCodeAsDefaultMessage(properties.isUseCodeAsDefaultMessage());
return messageSource;
}
我们真实 的情况是放在了i18n目录下,所以我们要去配置这个messages的路径;
spring.messages.basename=i18n.login
配置页面国际化值
去页面获取国际化的值,查看Thymeleaf的文档,找到message取值操作为:#{…}。我们去页面测试下:
IDEA还有提示,非常智能的!
我们可以去启动项目,访问一下,发现已经自动识别为中文的了!
但是我们想要更好!可以 根据按钮自动切换 中文英文!
配置国际化解析
在Spring中有一个国际化的Locale (区域信息对象);里面有一个叫做LocaleResolver (获取区域信息对象)的解析器!
我们去我们webmvc自动配置文件,寻找一下!看到SpringBoot默认配置:
@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;
}
AcceptHeaderLocaleResolver 这个类中有一个方法
public Locale resolveLocale(HttpServletRequest request) {
Locale defaultLocale = this.getDefaultLocale();
// 默认的就是根据请求头带来的区域信息获取Locale进行国际化
if (defaultLocale != null && request.getHeader("Accept-Language") == null) {
return defaultLocale;
} else {
Locale requestLocale = request.getLocale();
List<Locale> supportedLocales = this.getSupportedLocales();
if (!supportedLocales.isEmpty() && !supportedLocales.contains(requestLocale)) {
Locale supportedLocale = this.findSupportedLocale(request, supportedLocales);
if (supportedLocale != null) {
return supportedLocale;
} else {
return defaultLocale != null ? defaultLocale : requestLocale;
}
} else {
return requestLocale;
}
}
}
那假如我们现在想点击链接让我们的国际化资源生效,就需要让我们自己的Locale生效!
我们去自己写一个自己的LocaleResolver,可以在链接上携带区域信息!
修改一下前端页面的跳转连接: l是L
<!-- 这里传入参数不需要使用 ?使用 (key=value)-->
<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>
我们去写一个处理的组件类!
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 {
//解析请求
@Override
public Locale resolveLocale(HttpServletRequest request) {
//获取请求中的语言参数
String language = request.getParameter("l"); //l是L
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 httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) {
}
}
为了让我们的区域化信息能够生效,我们需要再配置一下这个组件!在我们自己的MvcConofig下添加bean;
@Bean
public LocaleResolver localeResolver(){
return new MyLocaleResolver();
}
我们重启项目,来访问一下,发现点击按钮可以实现成功切换!
注意:
- 我们需要配置i18n文件
- 我们如果需要在项目中按钮自动切换,我们需要自定义一个组件LocaleResolver
- 记得将自己写的组件配置到spring容器中 @Bean
- 国际化获取#{}
登录
发请求
<form class="form-signin" th:action="@{/user/login}" >
controller
@Controller
public class LoginController {
@RequestMapping("/user/login")
public String login(@RequestParam("username") String username,
@RequestParam("password") String password, Model model){
if (!StringUtils.isEmpty(username) && "123456".equals(password)){
//只是一个虚拟的地址 走解析器到真正的登录页面 掩藏真正的主页
return "redirect:/main.html";
}else {
model.addAttribute("msg","用户名或者密码错误");
return "index";
}
}
}
视图解析器
//如果登陆成功让他走解析器到main.html只是一个路径,为了地址栏保护主页的名称
registry.addViewController("/main.html").setViewName("dashboard");
登陆失败页面显示一句话
<!--如果msg的值为空,则不显示 thy模板的格式strings.isEmpty-->
<p style="color: red" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"></p>
发现问题
可以直接访问main页面进入到主页 不登录 需要拦截器
拦截器
//定义拦截器
public class LoginHandlerInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//登录成功后,session中有用户数据
Object user = request.getSession().getAttribute("loginUser");
if (user == null){
request.setAttribute("msg","没有登录,请先登录");
request.getRequestDispatcher("/index.html").forward(request,response);
return false;
}else {
return true;
}
}
}
配置拦截器
@Configuration
public class MyMVCConfig implements WebMvcConfigurer {
//添加自定义拦截器
@Override
public void addInterceptors(InterceptorRegistry registry) {
//new LoginHandlerInterceptor()自定义的拦截器名字
registry.addInterceptor(new LoginHandlerInterceptor()).
addPathPatterns("/**"). //拦截所有请求
excludePathPatterns("/index.html","/","/user/login","/css/**","/js/**","/img/**"); //放行登录页,登录请求,放行静态资源
}
}
controller
session.setAttribute("loginUser",username);
查询
提取公共页面
定一个一个公共页面在templates目录下创建一个commons目录在创建一个commons.html当作公共页面
顶部 和侧边栏都是公共的
-
th:fragment=“sidebar” 提取 名称自定义
-
<div th:replace="~{commons/commons::topbar}"></div> 第一个参数是公共页面的位置/名字::名称
高亮显示
<a th:class="${active=='list.html'?'nav-link active':'nav-link'}" th:href="@{/emps}">
nav-link active会高亮显示 收到参数为list.html高亮显示
<div th:replace="~{commons/commons::sidebar(active='main.html')}"></div>
前面是引入公共资源 () 括号是开始传参
传递参数使用括号传参
显示数据
后台 是Date类型 前端显示Fri Aug 21 08:56:49 CST 2020
这样展示
<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.id}"></td>
<td th:text="${emp.lastName}"></td>
<td th:text="${emp.email}"></td>
<td th:text="${emp.gender==0?'女':'男'}"></td>
<td th:text="${emp.department.getDapartmentName()}"></td> //对象类型
<td th:text="${#dates.format(emp.getBirth(),'yyyy-MM-dd HH:mm:ss')}"></td>
<td >
<button class="btn btn-sm btn-primary">编辑</button>
<button class="btn btn-sm btn-danger">删除</button>
</td>
</tr>
</tbody>
</table>
增加
springboot默认的时间格式是2020/8/21 写2020-8-21 提交就会报错
在配置文件中修改
#时间日期格式化
spring.mvc.date-format=yyyy-MM-dd
前端
<h2><a class="btn btn-sm btn-success" th:href="@{/emp}">添加员工</a></h2>
<form th:action="@{/emp}" method="post">
<div class="form-group">
<label >lastName</label>
<input type="text" name="lastName" class="form-control"placeholder="lt">
</div>
<div class="form-group">
<label >email</label>
<input type="email" name="email" class="form-control"placeholder="44565@qq.com">
</div>
<div class="form-group">
<label >gender</label>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="gender" value="1">
<label class="form-check-label">男</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="gender" value="0">
<label class="form-check-label">女</label>
</div>
</div>
<div class="form-group">
<label >department</label>
<!--name是department.id传过去一个id-->
<select class="form-control" name="department.id">
<!--这是一个特殊类型,所以我们需要提交的是一个属性 department.id-->
<option th:each="dept:${departments}"th:text="${dept.getDapartmentName()}" th:value="${dept.getId()}"></option>
</select>
</div>
<div class="form-group">
<label >birth</label>
<input type="text" name="birth" class="form-control" placeholder="lt">
</div>
<button type="submit" class="btn btn-default">添加</button>
</form>
controller
@GetMapping("/emp")
public String toAddpage(Model model){
//查询出部门的信息再页面展示选择
Collection<Department> departments = departmentDao.getDepartment();
System.out.println(departments.size());
model.addAttribute("departments",departments);
return "emp/add";
}
@PostMapping("/emp")
public String addEmp(Employee employee){
employeeDao.save(employee);
return "redirect:emps";
}
修改
参数这样传递
<a class="btn btn-sm btn-primary" th:href="@{/emp/}+${emp.getId()}">编辑</a>
controller
//修改
@GetMapping("/emp/{id}")
public String toUpdateEmp(@PathVariable("id")Integer id,Model model){
//查询原来的数据
Employee employeedById = employeeDao.getEmployeedById(id);
model.addAttribute("emp",employeedById);
//查询出部门的信息
Collection<Department> departments = departmentDao.getDepartment();
System.out.println(departments.size());
model.addAttribute("departments",departments);
return "emp/update";
}
//修改
@PostMapping("/updateEmp")
public String updateEmp(Employee employee){
employeeDao.save(employee);
return "redirect:emps";
}
<form th:action="@{/updateEmp}" method="post">
<!--隐藏于传过去id-->
<input type="hidden" name="id" th:value="${emp.getId()}">
<div class="form-group">
<label >lastName</label>
<input th:value="${emp.getId()}" type="text" name="lastName" class="form-control" placeholder="lt">
</div>
<div class="form-group">
<label >email</label>
<input th:value="${emp.getEmail()}" type="email" name="email" class="form-control" placeholder="44565@qq.com">
</div>
<div class="form-group">
<label >gender</label>
<div class="form-check form-check-inline">
<input th:checked="${emp.getGender()==1}" class="form-check-input" type="radio" name="gender" value="1">
<label class="form-check-label">男</label>
</div>
<div class="form-check form-check-inline">
<input th:checked="${emp.getGender()==0}" class="form-check-input" type="radio" name="gender" value="0">
<label class="form-check-label">女</label>
</div>
</div>
<div class="form-group">
<label >department</label>
<select class="form-control" name="department.id">
<!--查询出来Department展示-->
<option th:selected="${dept.getId()==emp.getDepartment().getId()}" th:each="dept:${departments}" th:text="${dept.getDapartmentName()}" th:value="${dept.getId()}"></option>
</select>
</div>
<div class="form-group">
<label >birth</label>
<!--显示日期-->
<input th:value="${#dates.format(emp.getBirth(),'yyyy-MM-dd HH:mm')}" type="text" name="birth" class="form-control" placeholder="lt">
</div>
<button type="submit" class="btn btn-default">修改</button>
</form>
删除
<a class="btn btn-sm btn-danger" th:href="@{/delemp/}+${emp.getId()}">删除</a>
controller
//删除
@RequestMapping("/delemp/{id}")
public String delempemP(@PathVariable("id") int id){
employeeDao.delete(id);
return "redirect:/emps";
}
404页面
在templates目录下创建个error目录 创建个404.html出现错误springboot自动找这个页面,位置一定要对
任何错误都是这样。
想要不走视图解析器 必须加上redirect
总结
前端:
- 模板 :别人写好的,我们拿来改成自己需要的
- 框架:组件自己手动组合拼接 Bootstrap,Layui,Semantic-ui
- 栅格系统
- 栅格系统