使用spring boot的开发流程:
自动配置原理?
请记住,饮水则思源,在每运用一个场景的时候,都要记住这是自动配置原理生效的。同时也要思考一下,spring-boot为我们配置了什么?能不能修改?有哪些配置可以修改?是否可以扩展等。
一定要记住:xxxAutoConfiguration帮我们在容器中自动的配置相关组件,而其xxxProperties封装了配置文件的内容。
查询类(ctrl+shift+n)WebMvcAutoConfiguration
查看其源码,与web相关的配置在此处都可以看到:
@ConfigurationProperties(
prefix = "spring.resources",
ignoreUnknownFields = false
)
public class ResourceProperties {
也就是说有关配置可以通过spring.resourcec进行设置。
再来看
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
} else {
Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
if (!registry.hasMappingForPattern("/webjars/**")) {
this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{"/webjars/**"}).addResourceLocations(new String[]{"classpath:/META-INF/resources/webjars/"}).setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
String staticPathPattern = this.mvcProperties.getStaticPathPattern();
if (!registry.hasMappingForPattern(staticPathPattern)) {
this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{staticPathPattern}).addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations())).setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
}
}
从该代码中我们可以看到,所有/webjars/**
都去classpath:/META-INF/resources/webjars
中找资源;
引入jquery:
<dependency>
<groupId>org.webjarsgroupId>
<artifactId>jqueryartifactId>
<version>3.3.0version>
dependency>
我们将下载到的jquery包展开可以看清楚目录结构,结合上面有关webjars的解释,我们可以尝试通过访问127.0.0.1:8081/webjars/jquery/3.3.0/jquery.js,发现可以成功的访问到该静态资源。也就是说,打包后的项目的确是可以访问到的。
还有一种配置资源的方式,"/**"访问当前项目的任何资源:
也就说,我们在上述文件夹上面放置静态资源,如果没有特别处理,系统默认都是可以访问到的。
继续查看该类的源码:
@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext) {
return new WelcomePageHandlerMapping(new TemplateAvailabilityProviders(applicationContext), applicationContext, this.getWelcomePage(), this.mvcProperties.getStaticPathPattern());
}
此处配置的是欢迎页的映射。他都是静态资源文件夹下的所有index.html文件。被/**
映射。
如localhost:8008则就是访问欢迎页面。
@Bean
public SimpleUrlHandlerMapping faviconHandlerMapping() {
SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
mapping.setOrder(-2147483647);
mapping.setUrlMap(Collections.singletonMap("**/favicon.ico", this.faviconRequestHandler()));
return mapping;
}
该处配置我们喜欢的图标,即浏览器表标签页左方的小图标。还是映射到了**.favicon.ico
,也就是我们的所有静态文件夹下面。
从上面可以看出,系统默认用一个staticLocations
作为映射路径,该路径默认为刚才所列出的默认路径,如果要配置自定义的资源路径,则可以:
spring.resources.static-locations=classpath:/hello,classpath:/hello2
就可以了,但要注意之前的默认值就不生效了。
市面上比较常用的有JSP、Velocity、FreeMarker、以及Spring boot推荐的Thymeleaf。
虽然模板引擎很多,但是其核心思想都是一样的,即按照一定的语法标准,将你的数据转化为实际的html页面,他们的区别只在于语法。
spring boot推荐的themeleaf语法比较简单,而且功能更强大。
注意,如果是1.x版本的spring boot此处若想使用3.x版本的thymeleaf的话,请在properties配置节配置其版本号以及布局版本号,以覆盖SB中默认的低版本thymeleaf。
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-thymeleafartifactId>
dependency>
查看spring-boot-autoconfigure
包可以查看到有关thymeleaf相关的配置信息ThymeleafAutoConfiguration
。其无非就是为thymeleaf添加相关的组件。我们主要关注的是其相关的配置规则:ThymeleafProperties。
@ConfigurationProperties(
prefix = "spring.thymeleaf"
)
public class ThymeleafProperties {
private static final Charset DEFAULT_ENCODING;
public static final String DEFAULT_PREFIX = "classpath:/templates/";
public static final String DEFAULT_SUFFIX = ".html";
private boolean checkTemplate = true;
private boolean checkTemplateLocation = true;
private String prefix = "classpath:/templates/";
private String suffix = ".html";
private String mode = "HTML";
private Charset encoding;
private boolean cache;
private Integer templateResolverOrder;
private String[] viewNames;
private String[] excludedViewNames;
private boolean enableSpringElCompiler;
private boolean renderHiddenMarkersBeforeCheckboxes;
private boolean enabled;
private final ThymeleafProperties.Servlet servlet;
private final ThymeleafProperties.Reactive reactive;
从默认规则里面我们不难看出很多东西其实已经无需修改,就按该默认配置进行业务代码编写就行了。也就是从配置中可以看出,我们的所有页面只需要放在classpath:/templates/
资源文件夹下面,并以.html
即为即可,根本无需其他多余的配置:
@RequestMapping("success")
public String success(){
return "success";
}
该代码交给thymeleaf渲染template/success.html
的文件。
详细的官方文件请点击:参考文档
<html xmlns:th="http://www.thymeleaf.org">
@RequestMapping("success")
public String success(Map<String, Object> map){
map.put("hello", "你好");
return "success";
}
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
this is success page.
<p th:text="${hello}">这里显示默认信息。p>
body>
html>
从输出结果可以看出,该页面完全可以独立于出来直接运行,其输出的结果不经过渲染只不过只会输出
这里是默认信息
这样的字段而已,做到了很好的前后端分离。
改变当前元素的文本内容等,例如:th:html属性
其值都可以替换原生的元素对应的值。
相关的介绍参考官方文档;
参考文档Iteration
部分。
该标签用于遍历数组数据。需要注意的是,该标签写在那个标签上,那个标签都会在每次遍历中生成一次。由此我们可以了解到,该标签经常会和表格行标签 以上所有的片段都可以组合使用,例如: 该表达式用于获取变量的值,使用的是OGNL语法: 和${…}的功能是一样的,不过有一个补充使用,即配合 其等价于: 也就是说,用 该表达式用于获取国际化内容。 定义url。 如果需要添加多个参数的话,用逗号分隔即可。 片段引用表达式。 参考文档 很多时候,我们想显示的文本其实是单纯的文本节点,完全不想使用html标签包裹,这时候需要怎么写呢。我们显然不能这样写: 正确的写法应该使用行内表达式 关于行内式有两种标签写法: 转义这个有点绕,不小心把自己也绕进去了。简单一点的记法:想要有html标签格式化文本,用 查看文档 Spring boot 自动配置好了Spring mvc,以下是SB对spring mvc的默认配置,这些配置组件都可以在 自动配置了ViewResoulver(视图解析器:根据方法的返回值得到视图对象(View),视图对象决定如何渲染…转发、重定向) Support for serving static resources, including support for WebJars (covered later in this document)): 静态资源文件夹路径,webjars. Automatic registration of Converter, GenericConverter, and Formatter beans. Support for Automatic registration of Static Custom Favicon support (covered later in this document). Automatic use of a SB对web的自动配置其实不止这个类中做了,其实还有其他的以***AutoConfiguration的类,都对web场景进行了一些配置。 模式:给容器中添加对应的自定义组件就可以了。 我们以前配置试图映射、拦截器都可以在springmvc.xml中进行配置,如下图所示: 那在spring boot中该怎么做呢? 编写一个配置类( 注意此处是1.x版本,2.x版本提示这个类准备移除了,我们不再需要继承 该方式即保留了spring boot的所有自动配置,也能用我们的扩展配置。可以查看如下了解原理: spring boot 会将所有的WebMvcConfiguration相关配置一起调用 这种情况下,我们一般是不想要SpringBoot的所有配置,所有的都是由我们自己来指定。只需要在自定义配置类中添加一个配置 注意其中的 我们再来看自动配置类的源码: 我们可以看到这一句 其生效方式刚好就是若容器中没有 使用SpringBoot一般不推荐SpringMVC全面接管(那就没必要用SpringBoot了) 添加entities包以及dao包,引入测试用例类以及静态资源,这里参考原视频教程配套代码。 我们已经将index.html放在了template文件夹下面,此时,我们为了将index.html映射到网站的首页,一般会写如下的控制方法: 该控制器方法可以将请求路径 再有,我们前面有讲到,springboot会将我们生成的所有组件添加到相应的组件族中,因此,我们这里也可以手动的创建一个WebMvcConfigurer,也同样生效(别忘了添加组件注解: 引入之后,则可以将之前的相对路径引入的静态资源替换掉了,找到index.html页面(改为login.html),使用 在springMVC中,我们需要: 步骤: 配置了组件 我们注意到这里 也就是说,我们的配置文件可以直接放在类路径下叫messages.properties即可,但是这样做显然不是很好,因此我们只需要修改 再次提醒,这里2.x版本. 1.x版本这里似乎是不一样的写法都行 部分源码如下: 现在我们想通过点击下方的 国家化Locale(区域信息对象);LocaleResolver获取区域信息对象,在 默认的区域信息解析器就是根据请求头带来的区域信息进行的国际化。 因此,我们完全可以自己写一个区域化信息解析器来自己处理请求信息。 别忘了添加该组件: 注意此处方法名必须写为 模板殷勤修改以后,若想实时生效,需要做如下操作: (1) 禁用模板引擎缓存(开发过程中建议关闭,生产环境可开启) (2)idea编译器的话,按ctrl + F9,重新编译一下即可。 #工具.工具方法 if标签的优先级比text高 通过进行登录检查,即实现SpringMVC中的 其中: 其中: 就等价于我们写一个Controller进行映射: 因此,如果只是单纯的映射跳转而没有其他的业务逻辑的话,推荐在这里直接配置即可。 首先,我们要编写一个拦截器组件: 注意: 其所有的方法都用了java8新关键字 添加拦截器组件到容器中,并配置相关的参数: 可以看到,该配置类实现 测试运行正常的登录,一切运行正常。搭配使用。
<h1>Product listh1>
<table>
<tr>
<th>NAMEth>
<th>PRICEth>
<th>IN STOCKth>
tr>
<tr th:each="prod : ${prods}">
<td th:text="${prod.name}">Onionstd>
<td th:text="${prod.price}">2.41td>
<td th:text="${prod.inStock}? #{true} : #{false}">yestd>
tr>
table>
表达式语法
'User is of type ' + (${user.isAdmin()} ? 'Administrator' : (${user.type} ?: 'Unknown'))
表达式语法详解
${…}
${session.user.id}
*{…}
使用,如下面的示例:<div th:object="${session.user}">
<p>Name: <span th:text="*{firstName}">Sebastianspan>.p>
<p>Surname: <span th:text="*{lastName}">Pepperspan>.p>
<p>Nationality: <span th:text="*{nationality}">Saturnspan>.p>
div>
<div>
<p>Name: <span th:text="${session.user.firstName}">Sebastianspan>.p>
<p>Surname: <span th:text="${session.user.lastName}">Pepperspan>.p>
<p>Nationality: <span th:text="${session.user.nationality}">Saturnspan>.p>
div>
*{}
可以省略公共的对象信息。当然,我们也可以在循环体内部混合使用这两种语法:<div th:object="${session.user}">
<p>Name: <span th:text="*{firstName}">Sebastianspan>.p>
<p>Surname: <span th:text="${session.user.lastName}">Pepperspan>.p>
<p>Nationality: <span th:text="*{nationality}">Saturnspan>.p>
div>
#{…}
@{…}
<a href="details.html"
th:href="@{http://localhost:8080/gtvg/order/details(orderId=${o.id})}">viewa>
<a href="details.html" th:href="@{/order/details(orderId=${o.id})}">viewa>
<a href="details.html" th:href="@{/order/{orderId}/details(orderId=${o.id})}">viewa>
@{/order/process(execId=${execId},execType='FAST')}
~{…}
<div th:insert="~{commons :: main}">...div>
行内表达式
Inlining
部分。my name is ${$name}.
my name is [[${name}]]!
[[...]]
等价于th:text
,会转义特殊字符,即按原样输出,标签会格式化内部文字;
[(...)]
等价于th:utext
,不会转义特殊字符,即按相应的标签格式输出,例如标签直接输出为
。
text
([[...]]
),想原样输出标签用utext
,。5.4 spring mvc自动配置
通过官方文档查阅
源码查阅
WebMvcConfigurationSupport
类中查看相应源码:
ContentNegotiatingViewResolver
: 组合所有的视图解析器。
HttpMessageConverters
(covered later in this document)
MessageCodesResolver
(covered later in this document).
index.html
support.
ConfigurableWebBindingInitializer
bean (covered later in this document).
5.5 修改SpringBoot的默认配置
5.6 扩展与全面接管SpringMVC
```xml
<mvc:view-controller path="/hello" view-name="success"/>
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/hello"/>
<bean>bean>
mvc:interceptor>
mvc:interceptors>
@Configuration
),是WebMvcConfigurerAdapter
类型;不能标注@EnableWebMvc
;
WebMvcConfigurerAdapter
,而是直接实现接口WebMvcConfigurer
来写配置类即可。@Configuration
public class MyConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
// 浏览器发送/restweb也会来到hello页面.
registry.addViewController("/restweb").setViewName("hello");
}
}
@import(EnableWebMvcConfiguration.class)
@Autowired(required = false)
public void setConfigurers(List<WebMvcConfigurer> configurers) {
if (!CollectionUtils.isEmpty(configurers)) {
this.configurers.addWebMvcConfigurers(configurers);
}
}
全面接管SpringMVC
@EnableWebMvc
即可,这样之后所有的SpringMVC的自动配置都失效了。@EnableWebMvc
原理:@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(DelegatingWebMvcConfiguration.class)
public @interface EnableWebMvc {
}
DelegatingWebMvcConfiguration
,其源码如下:@Configuration
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
@Configuration
@ConditionalOnWebApplication(
type = Type.SERVLET
)
@ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})
@ConditionalOnMissingBean({WebMvcConfigurationSupport.class})
@AutoConfigureOrder(-2147483638)
@AutoConfigureAfter({DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class})
public class WebMvcAutoConfiguration {
@ConditionalOnMissingBean({WebMvcConfigurationSupport.class})
WebMvcConfigurationSupport
,由于前面的注解导致该组件导入了进来,因此自动配置类就不生效了。导入的WebMvcConfigurationSupport
只是SpringMVC最基本的功能。
5.7 restweb制作
引入资源
添加thymeleaf场景包,springboot会自动启用该场景: <dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-thymeleafartifactId>
dependency>
配置首页映射
@RequestMapping({"/","index.html"})
public String index(){
return "index";
}
"/","index.html"
都映射为index.html的资源信息(thymeleaf渲染),还可以通过在配置类中配置映射的方式完成上述效果: @Override
public void addViewControllers(ViewControllerRegistry registry) {
// 浏览器发送/restweb也会来到hello页面.
registry.addViewController("/").setViewName("index");
// registry.addViewController("/index.html").setViewName("index");
}
@Bean
,将组件注册到容器中),上下两端代码完成的效果是一致的,当然也可以是互补的(映射不同的话): @Bean
public WebMvcConfigurer myWebMvcConfigurer(){
return new WebMvcConfigurer() {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/index").setViewName("index");
}
};
}
通过webjars引入其他静态资源
<dependency>
<groupId>org.webjarsgroupId>
<artifactId>jqueryartifactId>
<version>3.3.1-1version>
dependency>
<dependency>
<groupId>org.webjarsgroupId>
<artifactId>bootstrapartifactId>
<version>4.1.3version>
dependency>
th:xxx
修改路径引入:<link th:href="@{/webjars/bootstrap/4.1.3/css/bootstrap.css}" href="asserts/css/bootstrap.min.css" rel="stylesheet">
别忘了通过这个可以添加thymeleaf语法提示哦。
5.8 国际化
/resource
目录下新建i18n文件夹ResourcebundleMessageSource
管理国际化资源文件,在spring boot中,则不需要了(他已经帮我配置好了)。查看类MessageSourceAutoConfiguration
中已经有该组件的配置了。@Configuration
@ConditionalOnMissingBean(
value = {MessageSource.class},
search = SearchStrategy.CURRENT
)
@AutoConfigureOrder(-2147483648)
@Conditional({MessageSourceAutoConfiguration.ResourceBundleCondition.class})
@EnableConfigurationProperties
public class MessageSourceAutoConfiguration {
@Bean
@ConfigurationProperties(
prefix = "spring.messages"
)
public MessageSourceProperties messageSourceProperties() {
return new MessageSourceProperties();
}
@Bean
public MessageSource messageSource(MessageSourceProperties properties) {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
if (StringUtils.hasText(properties.getBasename())) {
messageSource.setBasenames(StringUtils.commaDelimitedListToStringArray(StringUtils.trimAllWhitespace(properties.getBasename())));
}
String basename = context.getEnvironment().getProperty("spring.messages.basename", "messages");
spring.messages.basename
配置即可自定义国际化内容的存放位置。spring.messages.basename=i18n/login
i18n.login
还记得学习thymeleaf的时候讲过一个获取国际化值的标签吗?没错,就是他:#{...}
<input type="text" class="form-control" th:placeholder="#{login.username}" placeholder="Username" required="" autofocus="">
区域信息对象
中文``English
两个按钮切换国际化页面该怎么做呢?WebMvcAutoConfiguration
:@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(
prefix = "spring.mvc",
name = {"locale"}
)
public LocaleResolver localeResolver() {
if (this.mvcProperties.getLocaleResolver() == org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties.LocaleResolver.FIXED) {
return new FixedLocaleResolver(this.mvcProperties.getLocale());
} else {
AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
localeResolver.setDefaultLocale(this.mvcProperties.getLocale());
return localeResolver;
}
}
Accept-Language: en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7
<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')}">Englisha>
@Bean
public LocaleResolver localeResolver(){
return new MyLocaleResolver();
}
localResolver
,因为Spring会默认将其作为Bean的名字,而SB的默认区域解析器会以此作为是否生成的条件。
5.9 拦截器
模板修改实时生效
spring.thymeleaf.cache=false
登录错误消息的提示
<p style="color:red" th:text="{msg}" th:if="${not #strings.isEmpty(msg)}">p>
重定向防止表单重复提交
return "redirect:/main.html"
拦截器
HandlerIntercepter
拦截方法.LoginController 登录逻辑检测
@Controller
public class LoginController {
@PostMapping(value = {"/user/login"})
public String login(@RequestParam("username")String username,
@RequestParam("password") String password,
HttpSession session,
Map<String, Object> map){
if(!StringUtils.isEmpty(username) && "123456".equals(password)){
session.setAttribute("loginUser","admin");
return "redirect:/index";
}else{
map.put("msg", "用户名或密码错误");
return "login";
}
}
}
@PostMapping(value = {"/user/login","login"})
等价于
@RequestMapping(value={"/user/login","login"}, method = RequestMethod.POST)
;@RequestParam("username")
强调该请求必须接受一个名为username的参数,否则会报400错误,并提示要求的参数不存在。index
给模板引擎解析,显然当前是转发user/login请求,如果我们刷新页面,会出现表单的重复提交,并且,如果index.html中如果写的是相对路径,则其路径会误认为user是一个项目名称,导致资源全部引入失败。因此,我们需要将其重定向。 return "redirect:/index";
视图解析器添加相关映射
@Override
public void addViewControllers(ViewControllerRegistry registry) {
// 浏览器发送/restweb也会来到hello页面.
registry.addViewController("/login").setViewName("login");
registry.addViewController("/").setViewName("index");
registry.addViewController("/index").setViewName("index");
}
registry.addViewController("/login").setViewName("login");
@RequestMapping(value="/login")
public String login(){
return "login";
}
配置拦截器
public class LoginHandlerInterupter implements HandlerInterceptor {
/**
* 其他的方法由于java8的default,我们可以不实现了,专心写这部分代码进行登录检查即可
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Object loginUser = request.getSession().getAttribute("loginUser");
// 若登录,放行请求
if(loginUser != null){
return true;
}
request.setAttribute("msg","未登录,请先登录");
System.out.println("this is is a preHandler method.");
request.getRequestDispatcher("/login").forward(request, response);
return false;
}
}
HandlerInterceptor
接口,通过查看该接口的源码可以发现: default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
default
修饰了所有的接口方法,因此,在我们的实现类中,无需实现所有的方法(如果不了解的话,请参考java8相关书籍——推荐 java8实战 ,大概就是default修饰的方法不需要实现类实现了,挑自己想覆盖的方法覆盖即可。)
添加拦截器组件到容器
/**
* 添加自定义拦截器到容器中,并配置相关参数
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 视频中老师有讲到spring boot已经对静态资源做了映射,不过他是针对1.x版本这样解释,但是我2.0版本这里是不行的,还是得放行静态资源,所以加了/assert和webjars这两个静态资源路径的放行,不然静态资源还是会被拦截下来。请注意一下。
registry.addInterceptor(new LoginHandlerInterupter()).addPathPatterns("/**")
. excludePathPatterns("/login","/user/login","/asserts/**","/webjars/**");
}
WebMvcConfigurer
的配置类中,目前其完全代码如下所示:
@Configuration
public class MyConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
// 浏览器发送/restweb也会来到hello页面.
registry.addViewController("/login").setViewName("login");
registry.addViewController("/").setViewName("index");
registry.addViewController("/index").setViewName("index");
}
/**
* 添加自定义拦截器到容器中,并配置相关参数
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
// SB已经对静态资源 *.css , *.jss进行了映射,我们可以不加管理
registry.addInterceptor(new LoginHandlerInterupter()).addPathPatterns("/**")
. excludePathPatterns("/login","/user/login","/asserts/**","/webjars/**");
}
/**
* 自定义本地解析器
* @return
*/
@Bean
public LocaleResolver localeResolver(){
return new MyLocaleResolver();
}
WebMvcConfigurer
接口,用于修改spring mvc相关的默认配置,比如这里添加了视图控制器、拦截器,另外则是一个之前为了做本地化配置的自定义本地化解析器。
addPathPatterns("/**")
表明该拦截器会拦截所有的请求.excludePathPatterns("/login","/user/login")
表明"/login","/user/login","/asserts/**","/webjars/**"
这四个请求,是特殊的、不拦截的,注意后两个是资源文件入口。测试运行
你可能感兴趣的:(Spring,Boot)