页面设置
下载完bootstrap模板后,将html文件放在templates文件夹下,包含css,img,js文件的asserts文件夹放在static文件夹下。
我下载的模板首页页面:
国际化设置
国际化是指根据根据语言的不同,页面的语言也会跟着改变,比如在上例中显示语言是英文,此处我们可以修改为中文:
在设置国际化前需要在IDEA设置字符集为utf-8
在resources文件夹下新建i18n文件夹,并在其中新建login.properties文件,然后再新建login_zh_CN.properties文件,此时IDEA会自动将这两个文件放在一个Resource Bundle 'login'文件夹里。
新建一个en_US文件
点击login.properties文件,点击+符号新建login.tip:
我们希望将上述5个部分国际化:
然后在主配置文件application.properties加上:
spring.messages.basename=i18n.login
此处我们希望国际化index.html,则在其加上
<html lang="en" xmlns:th="http://www.thymeleaf.org">
通过#{}来加上对应配置
最后首页就变成之前我展示的样子。 然后我们希望点击页面的中文,English就会跳转到不同地区的页面,因此我们需要自定义一个解析器。
在MVC自动配置类WebMvcAutoConfiguration中,有一个解析地区的方法:
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(
prefix = "spring.mvc",
name = {"locale"}
)
public LocaleResolver localeResolver() {
// 若我们配置了本地区域化的信息,就返回用户配置的信息
if (this.mvcProperties.getLocaleResolver()==org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties.LocaleReslvr.FIXED) {
return new FixedLocaleResolver(this.mvcProperties.getLocale());
} else { //否则使用默认的地区解析器
AcceptHeaderLocaleResolver localeResolver = newAcceptHeaderLocaleResolver();
localeResolver.setDefaultLocale(this.mvcProperties.getLocale());
return localeResolver;
}
}
这是默认的地区解析器:
public class AcceptHeaderLocaleResolver implements LocaleResolver {
public Locale resolveLocale(HttpServletRequest request) {
//获取默认语言环境
Locale defaultLocale = this.getDefaultLocale();
//若默认环境非空且发来请求的头部字段Accept-Language为空,
//则使用默认的语言环境
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;
}
}
}
}
我们照搬着写一个自定义解析器:
public class MyLocalResolver implements LocaleResolver {
@Override
public Locale resolveLocale(HttpServletRequest httpServletRequest) {
String chooseLanguage = httpServletRequest.getParameter("language");
Locale locale = Locale.getDefault();
//StringUtils.isBlank(String str) 判断某字符串是否为空或长度为0或由空白符构成
//而isEmpty是判断str==null 或 str.length()==0
if(!StringUtils.isEmpty(chooseLanguage)) {
//zh_CN
String[] languages = chooseLanguage.split("_");
//参数为语言和环境
locale = new Locale(languages[0], languages[1]);
}
return locale;
}
@Override
public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) {}
}
然后将自定义的国际化组件添加到容器里
@Configuration
public class MyMVCConfig implements WebMvcConfigurer {
@Bean //向容器中注入组件MyLocalResolver
public LocaleResolver localeResolver() {
return new MyLocalResolver();
}
}
这是请求的链接:
运行点击中文,English即可切换到对应地区的页面
登陆设置
首先在登陆页面设置输入的账户名和密码对应的name值并设置提示信息:
然后是处理请求的Controller:
@Controller
public class UserController {
@RequestMapping("/user/login")
public String login(@RequestParam("uname") String uname, @RequestParam("password") String password,
Model model, HttpSession session) {
if(!StringUtils.isEmpty(uname) && "123".equals(password)) {
session.setAttribute("loginUser", uname);
/*此处我们自定义一个视图解析器处理对main.html的请求,
将其请求转至对dashboard.html页面的请求
实际上我们并没有main.html页面
这样是为了掩盖掉登陆后地址栏出现类似
http://localhost:8080/user/login?uname=123&password=123
的情况。(forward:/main.html掩盖不了输入的信息)
*/
return "redirect:/main.html";
}else {
model.addAttribute("msg", "用户名或密码错误");
return "index";
}
}
}
为了防止用户没有登陆却直接通过输入地址http://localhost:8080/main.html来访问页面,我们需要一个拦截器来检测访问者是否登陆:
public class LoginHandlerInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Object loginUser = request.getSession().getAttribute("loginUser");
if(loginUser == 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 addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
registry.addViewController("/index.html").setViewName("index");
System.out.println("addViewControllers");
registry.addViewController("/main.html").setViewName("dashboard");
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
//所有除了访问首页,登录,静态资源的请求都需经过拦截器
registry.addInterceptor(new LoginHandlerInterceptor())
.addPathPatterns("/**").excludePathPatterns("/index.html", "/", "/user/login",
"/asserts/*");
}
@Bean //向容器中注入自定义国际化组件MyLocalResolver
public LocaleResolver localeResolver() {
return new MyLocalResolver();
}
}
运行后无论是直接输入http://localhost:8080/dashboard.html还是http://localhost:8080/main.html,都会转至登陆页面并提示需要登陆:
登陆后即可跳转页面:
这是redirect发出的两次请求信息:
后台设置
此处我们以员工管理系统作为增删改查的示例。
我们可以通过lombok工具来省略bean类的get/set,构造函数。
首先导入依赖:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
然后在IDEA中添加相关插件并开启。
然后是两个bean类:
@Data //提供类所有属性的 get 和 set 方法,以及equals、canEqual、hashCode、toString 方法。
@NoArgsConstructor //无参构造
@AllArgsConstructor //有参构造
public class Department {
private Integer id;
private String name;
}
@Data
@NoArgsConstructor
public class Employee {
private Integer id;
private String lastName;
private String email;
private Integer sex;
private Department department;
private Date birth;
public Employee(Integer id, String lastName, String email, Integer sex, Department department) {
this.id = id;
this.lastName = lastName;
this.email = email;
this.sex = sex;
this.department = department;
this.birth = new Date(); //默认日期
}
}
dao层,此处我们通过HashMap模拟数据库里的两个表,不用数据库存储记录
@Repository
public class EmployeeDao {
// 模拟员工表
private static Map<Integer, Employee> employees = null;
@Autowired
private DepartmentDao departmentDao;
static {
employees = new HashMap<Integer, Employee>();
employees.put(1001, new Employee(1001, "AA", "323232@qq.com", 1, new Department(101, "教学部")));
employees.put(1002, new Employee(1002, "BB", "323232@qq.com", 0, new Department(101, "市场部")));
employees.put(1003, new Employee(1003, "CC", "323232@qq.com", 0, new Department(101, "运营部")));
employees.put(1004, new Employee(1004, "DD", "323232@qq.com", 1, new Department(101, "教研部")));
employees.put(1005, new Employee(1005, "EE", "323232@qq.com", 1, new Department(101, "后勤部")));
}
// 模拟主键自增
private static Integer selfIncreaseId = 1006;
//由于我们是将HashMap看作员工表,
// 因此添加/更新员工信息都是同一方法
public void save(Employee employee) {
if(employee.getId() == null) {
employee.setId(selfIncreaseId++);
}
//加入新员工记录前先设置其部门,部门信息是从新员工信息里获取的。
employee.setDepartment(departmentDao.getDepartmentById(employee.getDepartment().getId()));
// 模拟将新记录添加到员工表中
employees.put(employee.getId(), employee);
}
//查询全部员工信息
public Collection<Employee> getAll() { return employees.values();}
public Employee getById(Integer id) { return employees.get(id); }
public void deleteById(Integer id) { employees.remove(id); }
}
@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(101, "市场部"));
departments.put(103, new Department(101, "运营部"));
departments.put(104, new Department(101, "教研部"));
departments.put(105, new Department(101, "后勤部"));
}
public Collection<Department> getDepartments() { return departments.values(); }
public Department getDepartmentById(Integer id) { return departments.get(id); }
}
最后给出项目页面结构:
添加操作
首先在主配置文件application.properties中设置日期格式:
spring.mvc.date-format=yyyy-MM-dd HH:mm:ss
在index页面登陆后点击员工管理选项,发出请求
请求转至EmployeeController类的的toAddPage方法:
@Controller
public class EmployeeController {
// Controller层本应调用service层,此处我们直接调用dao层
@Autowired
EmployeeDao employeeDao;
@Autowired
DepartmentDao departmentDao;
@RequestMapping("/getEmps")
public String list(Model model) {
Collection<Employee> employees = employeeDao.getAll();
model.addAttribute("emps", employees);
return "emp/list";
}
@GetMapping("/addEmp")
public String toAddPage(Model model) {
Collection<Department> departments = departmentDao.getDepartments();
model.addAttribute("departments", departments);
return "emp/add";
}
@PostMapping("/addEmp")
public String addEmp(Employee employee) {
System.out.println(employee);
employeeDao.save(employee);
//(1)如果是forward,则第一次添加完员工信息后跳转至员工信息列表
// 每刷新一次页面就会添加一条记录。
//(2)无法 return "getEmps"
return "redirect:/getEmps";
}
}
然后转至添加页面:
填写完信息后提交,转至addEmp方法,然后跳转至emp文件夹下的list页面:
效果图
更新操作
在index页面登陆后,点击员工管理转至list页面,点击编辑按钮,
然后EmployeeController下的toUpdatePage方法处理请求
/*
跳转至更新页面
*/
@GetMapping("/emp/{id}")
public String toUpdatePage(@PathVariable("id")Integer id, Model model) {
Employee employee = employeeDao.getById(id);
System.out.println(employee);
model.addAttribute("emp", employee);
Collection<Department> departments = departmentDao.getDepartments();
model.addAttribute("departments", departments);
return "emp/update";
}
@PostMapping("/updateEmp")
public String updateEmp(Employee employee) {
System.out.println(employee);
employeeDao.save(employee);
return "redirect:/getEmps";
}
跳转至emp文件夹下的update页面
然后updateEmp处理并跳转至list页面。
效果图
删除操作
在list页面点击删除选项,EmployeeController处理该请求
@GetMapping("/delEmp/{id}")
public String deleteEmp(@PathVariable("id")Integer id) {
employeeDao.deleteById(id);
return "redirect:/getEmps";
}
最后转至list页面
效果图
其他操作
404页面
将类似404,505等需要显示错误的页面放在新建的error页面即可
注册
在UserController下通过invalidate 方法使 HttpSession 失效,然后转至登陆页面
@RequestMapping("/user")
@Controller
public class UserController {
@RequestMapping("/login")
public String login(@RequestParam("uname") String uname, @RequestParam("password") String password,
Model model, HttpSession session) {
if(!StringUtils.isEmpty(uname) && "123".equals(password)) {
session.setAttribute("loginUser", uname);
System.out.println("转发请求main.html");
return "redirect:/main.html";
}else {
model.addAttribute("msg", "用户名或密码错误");
return "index";
}
}
@RequestMapping("/logout")
public String logout(HttpSession session) {
session.invalidate();
return "redirect:/index.html";
}
}