SpringBoot 企业级简化开发(一)https://developer.aliyun.com/article/1469559
SpringBoot web开发
jar:webapp在哪里
最大特点:自动装配
SpringBoot帮我们配置了什么,能不能进行修改,能修改那些东西,能不能拓展
- xxxxAutoConfiguration..向容器中自动配置组件
- XXXXProperties:实现自动配置类装配配置文件中自定义的内容!
要解决的问题:
- 导入静态资源,如何导入!
- 首页问题
- 模版引擎,thymeleaf
- 装配扩展SpringMvc
- 剩下的就只有增删改了
- 拦截器
- 扩展国际化
静态资源
@Override public void addResourceHandlers(ResourceHandlerRegistry registry) { if (!this.resourceProperties.isAddMappings()) { logger.debug("Default resource handling disabled"); return; } addResourceHandler(registry, "/webjars/", "classpath:/META-INF/resources/webjars/"); addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> { registration.addResourceLocations(this.resourceProperties.getStaticLocations()); if (this.servletContext != null) { ServletContextResource resource = new ServletContextResource(this.servletContext, SERVLET_LOCATION); registration.addResourceLocations(resource); } }); }
什么是webjars
一个网站是和maven仓库类似的导入依赖的网站
导入的依赖结构是
我们的静态资源路径方法中
addResourceHandler(registry, "/webjars/", "classpath:/META-INF/resources/webjars/");
就是去类路径下找到/META-INF/resources/webjars/下的文件,
例子:
访问http://localhost:8080/webjars/jquery/3.6.0/jquery.js
实测成功
总结:
- SpringBoot处理静态资源的方式
- webjars:http://localhost:8080/webjars/jquery/3.6.0/jquery.js
- pubilc:按优先级排序:resources,static,public,如何访问:/:
- http://localhost:8080/1.js
- 优先级:resources>static(默认)》public
首页如何定制
在web配置类中共有对首页的一系列处理
如何找得到资源下的index?
调用查找资源方法,找到index并且返回,没找到的话相对处理后返回空
SpringBoot页面跳转:
@Controller public class HelloController { @RequestMapping("/a") public String hello(){ return "index"; } }
注意:
- 在template目录下的所有页面,只能通过controller来跳转
- 需要模版引擎的支持
模版引擎:Thymeleaf
我们以前用jsp来展示数据,模版引擎的作用就是我们来写一个页面模版,比如一些值,表达式,tomcat支持jsp但是由于我们用的是嵌入式的tomcat,所以他现在默认是不支持jsp的
thymeleaf:
Thymeleaf 是适用于 Web 和独立环境的现代服务器端 Java 模板引擎,能够处理 HTML、XML、JavaScript、CSS 甚至纯文本。
Thymeleaf 的主要目标是提供一种优雅且高度可维护的模板创建方式。为了实现这一点,它建立在自然模板的概念之上,以不影响模板用作设计原型的方式将其逻辑注入模板文件。这改善了设计的沟通并弥合了设计和开发团队之间的差距。
Thymeleaf也已经从一开始就设计了Web标准记-尤其是HTML5 -允许您创建充分验证模板,如果这是一个需要你。
thymeleaf与mvc时讲到的视图解析器十分相似,
Springboot推荐使用模版引擎来简化开发,
引入依赖:
<dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf-spring5</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>
使用只需要导入依赖,我们将html放到templeats下就可以跳转了
注意:如果导入jar失败尝试回退版本,即可
thymeleaf基础语法:
<!--所有的html元素都可以被thymeleaf接管,如何接管? th:元素--> <div th:text="${msg}"></div>
表达式:
${x}
将返回x
存储在 Thymeleaf 上下文中或作为请求属性的变量。${param.x}
将返回一个名为(可能是多值的)的请求参数x
。${session.x}
将返回一个会话属性叫x
。${application.x}
将返回一个名为的servlet 上下文属性x
。
常用语法:
- 简单的表达:
- 变量表达式:
${...}
- 选择变量表达式:
*{...}
- 消息表达:
#{...}
- 链接 URL 表达式:
@{...}
- 片段表达式:
~{...}
- 文字
- 文本字面量:
'one text'
,'Another one!'
,... - 数字字面量:
0
,34
,3.0
,12.3
,... - 布尔文字:
true
,false
- 空字面量:
null
- 文字标记:
one
,sometext
,main
,...
- 文字操作:
- 字符串连接:
+
- 字面替换:
|The name is ${name}|
- 算术运算:
- 二元运算符:
+
,-
,*
,/
,%
- 减号(一元运算符):
-
- 布尔运算:
- 二元运算符:
and
,or
- 布尔否定(一元运算符):
!
,not
- 比较与相等:
- 比较器:
>
,<
,>=
,<=
(gt
,lt
,ge
,le
) - 等式运算符:
==
,!=
(eq
,ne
)
- 条件运算符:
- 如果-那么:
(if) ? (then)
- 如果-然后-其他:
(if) ? (then) : (else)
- 默认:
(value) ?: (defaultvalue)
常用代码示例:
controller:index
@Controller public class HelloController { @RequestMapping("/index") public String hello(Model model){ model.addAttribute("msg","<h1>hello SpringBoot</h1>"); model.addAttribute("users", Arrays.asList("hyc","lhy")); return "index"; } }
index.html
<!--所有的html元素都可以被thymeleaf接管,如何接管? th:元素--> <!--不转义--> <div th:text="${msg}"></div> <!--转义--> <div th:utext="${msg}"></div> <hr> <!--th:text显式数据--> <h3 th:each="user:${users}" th:text="${user}"></h3> <!--行内显式数据--> <h3 th:each="user:${users}" >[[${user}]]</h3> </body>
SpringBoot装配并且扩展SpringMvc
以视图解析器为例子:
原理:孙建平,真是我的好兄弟应该说是不是你的好办法真是我的好儿子我真的。
ContentNegotiatingViewResolver
类中有方法,getCandidateViews()
获取候选的视图,选择最好的视图返回,
官方文档是这样说的:
Spring Boot 为 Spring MVC 提供了自动配置,适用于大多数应用程序。
自动配置在 Spring 的默认值之上添加了以下功能:
- 包括
ContentNegotiatingViewResolver
和BeanNameViewResolver
bean。 - 支持提供静态资源,包括对 WebJars 的支持。
- 自动注册
Converter
,GenericConverter
和Formatter
bean类。 - 支持
HttpMessageConverters
。 - 的自动注册
MessageCodesResolver
。 - 静态
index.html
支持。 ConfigurableWebBindingInitializer
bean 的自动使用。
如果您想保留那些 Spring Boot MVC 自定义并进行更多(拦截器、格式化程序、视图控制器和其他功能),您可以添加自己@Configuration
的类型类,WebMvcConfigurer
但不添加 @EnableWebMvc
.
如果你想提供的定制情况RequestMappingHandlerMapping
,RequestMappingHandlerAdapter
或者ExceptionHandlerExceptionResolver
,仍然保持弹簧引导MVC自定义,你可以声明类型的豆WebMvcRegistrations
,并用它来提供这些组件的定制实例。
如果你想利用Spring MVC中的完全控制,你可以添加自己的@Configuration
注解为@EnableWebMvc
,或者添加自己的@Configuration
-annotatedDelegatingWebMvcConfiguration
中的Javadoc中所述@EnableWebMvc
。
如果我们想自定义视图解析器参考如下代码:
package com.hyc.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.View; import org.springframework.web.servlet.ViewResolver; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import java.util.Locale; //如果你想diy或者自定义动能,只需要写这个组件然后将它交给SpringBoot,SpringBoot就会帮我们自动装配 //拓展mvc @Configuration public class MyMvc implements WebMvcConfigurer { //public interface ViewResolver 实现了视图解析器的类我们就可以把他看成试图解析器 @Bean public MyViewResolver myViewResolver(){ return new MyViewResolver(); } //自定义了一个自己的视图解析器,只要我们自定义了试图解析器,SpringBoot就会帮我们自动装配 public static class MyViewResolver implements ViewResolver { @Override public View resolveViewName(String s, Locale locale) throws Exception { return null; } } }
装配原理小结:
- 芸芸之多的配置,原理其实是一样的,通过获取webmvc的自动配置原理分析,我们要学会一种方式,去在源码中得出结论,都是属于自己,一通百通
- Spring整个框架的底层有太多精妙的设计细节,阅读源码可以让我们对编写代码和理解原理的能力大大提升!
- 在自动配置很多组件的时候,SpringBoot会先查看容器中有没有用户自己配置的@bean,如果有就用用户配置的,如果没有就用默认配置,组件存在多个的时候,如视图解析器,就将用户配置的和自己默认的组合起来
员工管理系统
1、首页配置
1.首页配置,自己设置页面跳转
@Configuration public class MyMvcConfig implements WebMvcConfigurer { @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/").setViewName("index"); registry.addViewController("/").setViewName("index.html"); } }
2.首页设置,thymeleaf
xmlns:th="http://www.thymeleaf.org"
常用的thymeleaf文件头
注意点:所有的页面静态资源使用thymeleaf接管:url:@{}
页面国际化
- 我们需要配置i18n文件
- 我们如果需要在项目中进行按钮自动切换,我们需要自定义国际化组件
package com.hyc.managesystem.config; 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 myLocalresolver implements LocaleResolver { //解析请求 @Override public Locale resolveLocale(HttpServletRequest request) { // 获取请求的语言参数 String language = request.getParameter("l"); System.out.println(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) { } }
- 记得将自己写的组件配置到配置文件中
@Bean public LocaleResolver localeResolver(){ return new myLocalresolver(); }
- 使用#{}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0oH1S1RT-1648917634756)(C:/Users/Administrator/AppData/Roaming/Typora/typora-user-images/image-20210503093036786.png)]
位置如下
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 public class Department { private int id; //部门id private String departmentName; //部门名字 }
添加lombok依赖
<!--lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency>
1.3、编写dao层
这里我们模拟数据库,springboot和数据库的连接在后序课程中。
部门dao
package com.kuang.dao; import com.kuang.pojo.Department; import org.springframework.stereotype.Repository; import java.util.Collection; import java.util.HashMap; import java.util.Map; //部门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(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> getDepartments(){ return departments.values(); } //通过id得到部门 public Department getDepartmentById(Integer id){ return departments.get(id); } }
员工dao
package com.kuang.dao; import com.kuang.pojo.Department; import com.kuang.pojo.Employee; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; import java.util.Collection; import java.util.HashMap; import java.util.Map; //员工dao @Repository //被string托管 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","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 ininId = 1006; //增加一个员工 public void save(Employee employee){ if(employee.getId() == null){ employee.setId(ininId++); } employee.setDepartment(departmentDao.getDepartmentById(employee.getDepartment().getId())); employees.put(employee.getId(),employee); } //查询全部的员工 public Collection<Employee>getALL(){ return employees.values(); } //通过id查询员工 public Employee getEmployeeById(Integer id){ return employees.get(id); } //删除一个员通过id public void delete(Integer id){ employees.remove(id); } }
2、首页实现
2.1、引入Thymeleaf
pom.xml
导入依赖
<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
package com.kuang.config; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; //扩展使用SpringMVC @Configuration public class MyMvcConfig implements WebMvcConfigurer { @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/").setViewName("index"); registry.addViewController("/index.html").setViewName("index"); } }
更改静态资源路径
所有的静态资源都需要使用thymeleaf接管:@{}
application.properties 修改
# 关闭模板引擎的缓存 spring.thymeleaf.cache=false server.servlet.context-path=/kuang
2.3、测试首页
输入路径
http://localhost:8080/kuang/index.html
测试成功!
3、页面国际化
3.1、 File Encodings设置
先在IDEA中统一设置properties的编码问题!
编写国际化配置文件,抽取页面需要显示的国际化页面消息。我们可以去登录页面查看一下,哪些内容
我们需要编写国际化的配置!
3.2、配置文件编写
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
中文:
login.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,可以在链接上携带区域信息!
修改一下前端页面的跳转连接:
<!-- 这里传入参数不需要使用 ?使用 (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>
我们去写一个处理的组件类!
package com.kuang.component; 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"); 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(); }
我们重启项目,来访问一下,发现点击按钮可以实现成功切换!搞定收工!
注意点
SpringBoot 企业级简化开发(三)https://developer.aliyun.com/article/1469562