SpringBoot 第四章WEB(1)
资料
- 源码分析@EnableWebMvc使默认的SpringMvc自动配置失效
- WebMvcConfigurationSupport与WebMvcConfigurer的关系
- ctrl + o 获取所有的方法
- Thymeleaf循环时 带上序号
- ctrl + F9 重新编译
spring.thymeleaf.cache=false spring.mvc.hiddenmethod.filter.enabled=true xxxxxx.excludePathPatterns("/login","/","/user/login","/webjars/bootstrap/**");//登陆请求不能拦截
四、Web开发
简介
使用SpringBoot;
- 创建SpringBoot应用,选中我们需要的模块;
- SpringBoot已经默认将这些场景配置好了,只需要在配置文件中指定少量配置就可以运行起来
- 自己编写业务代码;
自动配置原理?
这个场景SpringBoot帮我们配置了什么?能不能修改?能修改哪些配置?能不能扩展?xxx
相对应的代码
xxxxAutoConfiguration:帮我们给容器中自动配置组件; xxxxProperties:配置类来封装配置文件的内容;
配置一些静态资源
需要了解一些静态资源的在springboot中的映射情况
WebMvcAutoConfiguration 的文件
WebMvcAuotConfiguration: @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { if (!this.resourceProperties.isAddMappings()) { logger.debug("Default resource handling disabled"); return; } Integer cachePeriod = this.resourceProperties.getCachePeriod(); if (!registry.hasMappingForPattern("/webjars/**")) { customizeResourceHandlerRegistration( registry.addResourceHandler("/webjars/**") .addResourceLocations( "classpath:/META-INF/resources/webjars/") .setCachePeriod(cachePeriod)); } String staticPathPattern = this.mvcProperties.getStaticPathPattern(); //静态资源文件夹映射 if (!registry.hasMappingForPattern(staticPathPattern)) { customizeResourceHandlerRegistration( registry.addResourceHandler(staticPathPattern) .addResourceLocations( this.resourceProperties.getStaticLocations()) .setCachePeriod(cachePeriod)); } } //配置欢迎页映射 @Bean public WelcomePageHandlerMapping welcomePageHandlerMapping( ResourceProperties resourceProperties) { return new WelcomePageHandlerMapping(resourceProperties.getWelcomePage(), this.mvcProperties.getStaticPathPattern()); } //配置喜欢的图标 @Configuration @ConditionalOnProperty(value = "spring.mvc.favicon.enabled", matchIfMissing = true) public static class FaviconConfiguration { private final ResourceProperties resourceProperties; public FaviconConfiguration(ResourceProperties resourceProperties) { this.resourceProperties = resourceProperties; } @Bean public SimpleUrlHandlerMapping faviconHandlerMapping() { SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping(); mapping.setOrder(Ordered.HIGHEST_PRECEDENCE + 1); //所有 **/favicon.ico mapping.setUrlMap(Collections.singletonMap("**/favicon.ico", faviconRequestHandler())); return mapping; } @Bean public ResourceHttpRequestHandler faviconRequestHandler() { ResourceHttpRequestHandler requestHandler = new ResourceHttpRequestHandler(); requestHandler .setLocations(this.resourceProperties.getFaviconLocations()); return requestHandler; } }
1)、所有 /webjars/** ,都去 classpath:/META-INF/resources/webjars/ 找资源;对应的类的路径下找
webjars:以jar包的方式引入静态资源;
http://www.webjars.org/<dependency> <groupId>org.webjars</groupId> <artifactId>jquery</artifactId> <version>3.4.1</version> </dependency>
http://localhost:8080/webjars/jquery/3.4.1/jquery.js 请求成功访问到js文件
2)、"/**" 访问当前项目的任何资源,都去(静态资源的文件夹)找映射
@ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false) public class ResourceProperties { private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/" }; "classpath:/META-INF/resources/", "classpath:/resources/", 需要新创建 "classpath:/static/", "classpath:/public/" 需要新创建 "/":当前项目的根路径
例如其中的index.html页面
private String staticPathPattern = "/**";
3)、欢迎页; 静态资源文件夹下的所有index.html页面;被"/**"映射;
localhost:8080/ 找index页面
4)、所有的 **/favicon.ico 都是在静态资源文件下找;5)、自定义路径下查找 其他默认的情况不会生效
spring.resources.static-locations=classpath:/hello,classpath:/static
3、模板引擎
JSP、Velocity、Freemarker、Thymeleaf
SpringBoot推荐的Thymeleaf:语法更简单,功能更强大;
系统的是3.0.11 layout暂未知 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>
找到thymeleafProperties 查看相关配置
@ConfigurationProperties(prefix = "spring.thymeleaf") public class ThymeleafProperties { private static final Charset DEFAULT_ENCODING = StandardCharsets.UTF_8; public static final String DEFAULT_PREFIX = "classpath:/templates/"; public static final String DEFAULT_SUFFIX = ".html";
只要我们把HTML页面放在classpath:/templates/,thymeleaf就能自动渲染;
@Controller public class TestController { @RequestMapping("/hello") //注意这里不能加responsebody 不然就是输出一串字符串 public String hello(){ return "hello"; } }
自动将hello 拼接为/template/hello.html 不支持jsp
4、语法
参考之前jsp的c: 做一个对比
导入thymeleaf的名称空间
<html lang="en" xmlns:th="http://www.thymeleaf.org">
效果
@Controller public class TestController { @RequestMapping("/hello") public String hello(Map<String,String>map){ map.put("hello","hello,thymeleaf"); return "hello"; } } 《!DOCTYPE html> 《html lang="en" xmlns:th="http://www.thymeleaf.org"> 《head> 《meta charset="UTF-8"> 《title>Title《/title> 《/head> 《body> 《h1 th:text="${hello}">欢迎来到该页面《/h1> 《/body> 《/html>
表达式 参考文献:https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html
Simple expressions:(表达式语法) Variable Expressions: ${...}:获取变量值;OGNL; 1)、获取对象的属性、调用方法 2)、使用内置的基本对象: #ctx : the context object. #vars: the context variables. #locale : the context locale. #request : (only in Web Contexts) the HttpServletRequest object. #response : (only in Web Contexts) the HttpServletResponse object. #session : (only in Web Contexts) the HttpSession object. #servletContext : (only in Web Contexts) the ServletContext object. ${session.foo} 3)、内置的一些工具对象: #execInfo : information about the template being processed. #messages : methods for obtaining externalized messages inside variables expressions, in the same way as they would be obtained using #{…} syntax. #uris : methods for escaping parts of URLs/URIs #conversions : methods for executing the configured conversion service (if any). #dates : methods for java.util.Date objects: formatting, component extraction, etc. #calendars : analogous to #dates , but for java.util.Calendar objects. #numbers : methods for formatting numeric objects. #strings : methods for String objects: contains, startsWith, prepending/appending, etc. #objects : methods for objects in general. #bools : methods for boolean evaluation. #arrays : methods for arrays. #lists : methods for lists. #sets : methods for sets. #maps : methods for maps. #aggregates : methods for creating aggregates on arrays or collections. #ids : methods for dealing with id attributes that might be repeated (for example, as a result of an iteration). Selection Variable Expressions: *{...}:选择表达式:和${}在功能上是一样; 补充:配合 th:object="${session.user}: <div th:object="${session.user}"> <p>Name: <span th:text="*{firstName}">Sebastian</span>.</p> <p>Surname: <span th:text="*{lastName}">Pepper</span>.</p> <p>Nationality: <span th:text="*{nationality}">Saturn</span>.</p> </div> 相当于 <div><p>${session.user,firstName}</p></div> Message Expressions: #{...}:获取国际化内容 Link URL Expressions: @{...}:定义URL; <th:href="@{...(orderId=${o.id})}"> 自动传入多个值 @{/order/process(execId=${execId},execType='FAST')} Fragment Expressions: ~{...}:片段引用表达式 <div th:insert="~{commons :: main}">...</div> Literals(字面量) Text literals: 'one text' , 'Another one!' ,… Number literals: 0 , 34 , 3.0 , 12.3 ,… Boolean literals: true , false Null literal: null Literal tokens: one , sometext , main ,… Text operations:(文本操作) String concatenation: + Literal substitutions: |The name is ${name}| Arithmetic operations:(数***算) Binary operators: + , - , * , / , % Minus sign (unary operator): - Boolean operations:(布尔运算) Binary operators: and , or Boolean negation (unary operator): ! , not Comparisons and equality:(比较运算) Comparators: > , < , >= , <= ( gt , lt , ge , le ) Equality operators: == , != ( eq , ne ) Conditional operators:条件运算(三元运算符) If-then: (if) ? (then) If-then-else: (if) ? (then) : (else) Default: (value) ?: (defaultvalue) Special tokens: No-Operation: _
用例
《!DOCTYPE html> 《html lang="en" xmlns:th="http://www.thymeleaf.org"> 《head> 《meta charset="UTF-8"> 《title>Title《/title> 《/head> 《body> 《div th:text="${hello}">《/div> 《hr> 《div th:text="html">《/div> 《div th:utext="html">《/div> 《hr> 《h4 th:each="user:${users}" th:text="${user}">《/h4> 《h4> 《span th:each="user:${users}">[[${user}]]《/span> 《/h4> 《/body> 《/html>
自动配置
Spring MVC auto-configuration
Spring Boot 自动配置好了SpringMVC
Inclusion of
ContentNegotiatingViewResolver
andBeanNameViewResolver
beans.- 自动配置了ViewResolver(视图解析器:根据方法的返回值得到视图对象(View),视图对象决定如何渲染(转发?还是 重定向?))
- ContentNegotiatingViewResolver:组合所有的视图解析器的;
WebMvcAutoConfiguration类中的方法 Mvc自动配置区域 || \/ 里面的方法 @Bean @ConditionalOnBean(ViewResolver.class) @ConditionalOnMissingBean(name = "viewResolver", value = ContentNegotiatingViewResolver.class) public ContentNegotiatingViewResolver viewResolver(BeanFactory beanFactory) { ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver(); resolver.setContentNegotiationManager(beanFactory.getBean(ContentNegotiationManager.class)); // ContentNegotiatingViewResolver uses all the other view resolvers to locate // a view so it should have a high precedence resolver.setOrder(Ordered.HIGHEST_PRECEDENCE); return resolver; } || \/ ContentNegotiatingViewResolver 来解析视图 如何解析视图 || \/ class ContentNegotiatingViewResolver { @Nullable public View resolveViewName(String viewName, Locale locale) throws Exception { //解析视图 ... //获取候选的视图 List<View> candidateViews = this.getCandidateViews(viewName, locale, requestedMediaTypes); //获取最优的视图 View bestView = this.getBestView(candidateViews, requestedMediaTypes, attrs); } || \/ 把所有的解析器拿来 一个一个解析 private List<View> getCandidateViews(String viewName, Locale locale, List<MediaType> requestedMediaTypes) throws Exception { List<View> candidateViews = new ArrayList(); if (this.viewResolvers != null) { Assert.state(this.contentNegotiationManager != null, "No ContentNegotiationManager set"); Iterator var5 = this.viewResolvers.iterator();//获取所有的解析器 挨个解析 ... } || \/ initServletContext从容器中获取所有的视图解析器 ,以下是解析器的获取 protected void initServletContext(ServletContext servletContext) { Collection<ViewResolver> matchingBeans = BeanFactoryUtils.beansOfTypeIncludingAncestors(this.obtainApplicationContext(), ViewResolver.class).values(); ViewResolver viewResolver; if (this.viewResolvers == null) { this.viewResolvers = new ArrayList(matchingBeans.size()); Iterator var3 = matchingBeans.iterator(); } ... }
- 如何定制:我们可以自己给容器中添加一个视图解析器;自动的将其组合进来;
@Bean //在配置类里面写 public ViewResolver getViewResolver(){ return new MyViewResolver(); } class MyViewResolver implements ViewResolver{ @Override public View resolveViewName(String s, Locale locale) throws Exception { System.out.println("MyviewResolver"); return null; } } || \/ 所有的请求会先到doDispatch 看看里面的视图解析器是什么 protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { ... }
DispatcherServlet中包含的组件 含视图解析器
Support for serving static resources, including support for WebJars (see below).静态资源文件夹路径,webjars
Static
index.html
support. 静态首页访问Custom
Favicon
support (see below). favicon.ico自动注册了 of
Converter
,GenericConverter
,Formatter
beans.- Converter:转换器; public String hello(User user):类型转换使用Converter
Formatter
格式化器; 2017.12.***te;
@Bean @ConditionalOnProperty(prefix = "spring.mvc", name = "date-format")//在文件中配置日期格式化的规则 public Formatter<Date> dateFormatter() { return new DateFormatter(this.mvcProperties.getDateFormat());//日期格式化组件 }
自己添加的格式化器转换器,我们只需要放在容器中即可
Support for
HttpMessageConverters
(see below).HttpMessageConverter:SpringMVC用来转换Http请求和响应的;User---Json;
HttpMessageConverters
是从容器中确定;获取所有的HttpMessageConverter;
自己给容器中添加HttpMessageConverter,只需要将自己的组件注册容器中(@Bean,@Component)
Automatic registration of
MessageCodesResolver
(see below).定义错误代码生成规则Automatic use of a
ConfigurableWebBindingInitializer
bean (see below).我们可以配置一个ConfigurableWebBindingInitializer来替换默认的;(添加到容器)
@Override protected ConfigurableWebBindingInitializer getConfigurableWebBindingInitializer( FormattingConversionService mvcConversionService, Validator mvcValidator) { try { //从容器中拿 return this.beanFactory.getBean(ConfigurableWebBindingInitializer.class); } catch (NoSuchBeanDefinitionException ex) { return super.getConfigurableWebBindingInitializer(mvcConversionService, mvcValidator); } }
初始化WebDataBinder;绑定器
请求数据=====JavaBean;
org.springframework.boot.autoconfigure.web:web的所有自动场景;
扩展SpringMVC
通过拦截器 来拦截hello请求
- 代码
<mvc:view-controller path="/hello" view-name="success"/> <mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/hello"/> <bean></bean> </mvc:interceptor> </mvc:interceptors>
现在没有了配置文件 springboot如何做---> 编写一个@Configuration 接口是WebMvcConfigurer//使用WebMvcConfigurer可以来扩展SpringMVC的功能 @Configuration public class MyMvcConfig implements WebMvcConfigurer { @Override public void addViewControllers(ViewControllerRegistry registry) { //浏览器发送 /hello请求来到 success registry.addViewController("/hello").setViewName("success"); } }
扩展mvc成功的原理
配置mvc需要经过WebMvcAutoConfiguration 来进行自动配置 ,是springmvc 的自动配置类
导入EnableWebMvcConfiguration,在做其他自动配置时会导入
@Configuration(proxyBeanMethods = false) //是一个配置类 @Import(EnableWebMvcConfiguration.class)//导入EnableWebMvcConfiguration @EnableConfigurationProperties({ WebMvcProperties.class, ResourceProperties.class }) @Order public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer { } || \/ @Configuration(proxyBeanMethods = false)//配置类 public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware { 实现RequestMappingHandlerAdapter、RequestMappingHandlerMapping等方法 } || \/ 获取所有的configurer @Configuration(proxyBeanMethods = false) public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport { @Autowired( required = false) //自动装配 方法参数从容器中获取configurers public void setConfigurers(List<configurer> configurers) { if (!CollectionUtils.isEmpty(configurers)) { this.configurers.addWebMvcConfigurers(configurers); } } } || \/ 其中一个实现案例 就是所有的configurer都加上addViewControllers public void addViewControllers(ViewControllerRegistry registry) { Iterator var2 = this.delegates.iterator(); while(var2.hasNext()) { WebMvcConfigurer delegate = (WebMvcConfigurer)var2.next(); delegate.addViewControllers(registry); } } 容器所有的WebMvcConfigure都会一起起作用 implements WebMvcConfigurer--> @Configuration包括自己写
效果:springmvc 的自动配置和我们的扩展配置都会起作用
全面接管springmvc
@EnableWebMvc:springboot对springmvc的自动配置不需要了,所有都是我们自己配的 静态资源失效 所有的SpringMVC的自动配置都失效
仅仅可以使用基本的设置,即DelegatingWebMvcConfiguration里设置的方法
即如果容器中存在WebMvcConfigurationSupport类,则springboot关于springmvc的自动配置类不会生效。
没有注解的效果
为什么会失效
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class) //<---//容器中没有这个组件的时候,这个自动配置类才生效ConditionalOnMissingBean @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10) @AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class }) public class WebMvcAutoConfiguration {} @Import({DelegatingWebMvcConfiguration.class}) public @interface EnableWebMvc { } DelegatingWebMvcConfiguration==包括=> private final WebMvcConfigurerComposite configurers = new WebMvcConfigurerComposite();是一个WebMvcConfigurer 实现回调
如何修改springboot的默认配置
模式
- SpringBoot在自动配置很多组件的时候,先看容器中有没有用户自己配置的(@Bean、@Component)如果有就用用户配置的,如果没有,才自动配置;如果有些组件可以有多个(ViewResolver)将用户配置的和自己默认的组合起来;
- 在SpringBoot中会有非常多的xxxConfigurer帮助我们进行扩展配置
- 在SpringBoot中会有很多的xxxCustomizer帮助我们进行定制配置
实验部分
- 修改web的首页 默认是去public找 我们需要将其改为template下找
第一种@Controller public class ControllerTest { @RequestMapping({"/", "/index.html"}) public String index() { return "index"; } }
第二种@Configuration public class MyMvcConfig implements WebMvcConfigurer { @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/index.html").setViewName("index"); registry.addViewController("/").setViewName("index"); } }
第三种@Configuration public class MyMvcConfig implements WebMvcConfigurer { @Bean public WebMvcConfigurer webMvcConfigurer(){ WebMvcConfigurer webMvcConfigurer = new WebMvcConfigurer() { @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/index.html").setViewName("index"); registry.addViewController("/").setViewName("index"); } }; return webMvcConfigurer; } }
通过th:src="@{/css..}" 会自动补上配置文件中的项目名 普通的src没法达到这一的效果
初始效果《!DOCTYPE html> 《html lang="en" xmlns:th="http://www.thymeleaf.org"> 《head> 《meta charset="UTF-8"> 《title>Title《/title> 《/head> 《link th:href="@{/css/myCss1.css}" type="text/css" rel="stylesheet"> 《link href="css/myCss2.css" rel="stylesheet" type="text/css"> 《body> 《p class="center1">hello,springboot th:src《/p> 《p class="center2">hello,springboot not -> th:src《/p> 《img th:src="@{/img/goIang.png}" th:height="50px"/> 《img src="img/goIang.png" height="50px"/> 《/body> 《/html> myCss1.css .center1 { text-align: center; color: red; font-size: 10px; } myCss2.css .center2 { text-align: center; color: red; font-size: 10px; }
加一个项目名server.servlet.context-path=/cznczai
即使加了路径 同样也可以访问
th:src 功能更加强大
国际化
1)、编写国际化配置文件;
2)、使用ResourceBundleMessageSource管理国际化资源文件
3)、在页面使用fmt:message取出国际化内容导入目标页面【对应资源不要加设置的目录名如static】
编写国际化配置文件i18n/xxxx.properties 【zh_CN】 【en_US】
idea会自动封装
导入国际化配置路径
在application.properties中配置 # 注册国际化配置文件 spring.messages.basename=i18n.login
效果
源码分析
public class MessageSourceAutoConfiguration { private static final Resource[] NO_RESOURCES = {}; @Bean @ConfigurationProperties(prefix = "spring.messages") public MessageSourceProperties messageSourceProperties() { return new MessageSourceProperties(); } protected static class ResourceBundleCondition extends SpringBootCondition { private static ConcurrentReferenceHashMap<String, ConditionOutcome> cache = new ConcurrentReferenceHashMap<>(); @Override //在环境中找到getProperty("spring.messages.basename", "messages"); public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { String basename = context.getEnvironment().getProperty("spring.messages.basename", "messages"); ConditionOutcome outcome = cache.get(basename); if (outcome == null) { outcome = getMatchOutcomeForBasename(context, basename); cache.put(basename, outcome); } return outcome; } } }
国际化Locale(区域信息对象);LocaleResolver(获取区域信息对象) 我们需要重新配置一个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; //返回国际化类型 } || \/ public class AcceptHeaderLocaleResolver implements LocaleResolver { public Locale resolveLocale(HttpServletRequest request) { Locale defaultLocale = this.getDefaultLocale(); 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 MyLocaleResolver implements LocaleResolver { @Override public Locale resolveLocale(HttpServletRequest request) { String encoding = request.getParameter("encoding"); Locale locale = null; if(StringUtils.isEmpty(encoding)){ locale = Locale.getDefault(); }else { String[] split = encoding.split("_"); locale = new Locale(split[0], split[1]); } return locale; } //不写 @Override public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) { } } @Configuration public class MyMvcConfig implements WebMvcConfigurer { @Bean public LocaleResolver localeResolver(){ return new MyLocaleResolver(); } }
效果
《!DOCTYPE html> 《html lang="en" xmlns:th="http://www.thymeleaf.org"> 《head> 《meta charset="UTF-8"> 《title>login《/title> 《!-- Bootstrap core CSS --> 《link th:href="@{/webjars/bootstrap/4.4.1/css/bootstrap.css}" rel="stylesheet" type="text/css"> 《!-- Custom styles for this template --> 《link th:href="@{/css/signin.css}" rel="stylesheet" type="text/css"> 《/head> 《body class="text-center"> 《form class="form-signin"> 《img class="mb-4" th:src="@{/img/bootstrap-solid.svg}" alt="no img" width="72" height="72"> 《h1 class="h3 mb-3 font-weight-normal" th:text="#{login.tip}">Please sign in《/h1> 《label class="sr-only" th:text="#{login.username}">Username《/label> 《input type="text" name="username" class="form-control" th:placeholder="#{login.username}" required="" autofocus=""> 《label for="inputPassword" class="sr-only" th:text="#{login.password}">Password《/label> 《input type="password" name="password id="inputPassword" class="form-control" th:placeholder="#{login.password}" required> 《div class="checkbox mb-3"> 《label> 《input type="checkbox" value="remember-me">[[#{login.remember}]] 《/label> 《/div> 《button class="btn btn-lg btn-primary btn-block" type="submit" th:text="#{login.sign}">Sign in《/button> 《p class="mt-5 mb-3 text-muted">© 2019-2020《/p> 《a class="btn btn-sm" th:href="@{/login(encoding='zh_CN')}">中文《/a> 《a class="btn btn-sm" th:href="@{/login(encoding='en_US')}">English《/a> 《/form> 《/body> 《/html>
登陆
进入页面后普通的连接什么样式都没有【因为是转发 因为相对路径发生改变 需要采用重定向】
@Controller public class InfoController { @RequestMapping("/user/login") public String login(@RequestParam("username") String username, @RequestParam("password") String password, Map<String, Object> map, HttpSession session) { System.out.println(username); System.out.println(password); session.setAttribute("loginUser","cznczai");//需要将登陆成功的用户放在session 方便后面检验 if (!StringUtils.isEmpty(username) && "123456".equals(password)) { return "redirect:/success"; } else { map.put("msg","用户名或密码错误"); return "login"; } } } 转发 变成了转发路径下的css文件见 《link href="css/myCss2.css" rel="stylesheet" type="text/css">
重定向后
设置错误信息提示
Map ---> request中 《p style="color:red" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}">《/p> 《!--如果msg为空 不显示-->
登陆拦截器
@Configuration public class MyMvcConfig implements WebMvcConfigurer { //注册拦截器 @Override public void addInterceptors(InterceptorRegistry registry) { //静态资源也不用拦截 springboot已经配置好了 registry.addInterceptor(new MyLoginInterceptor()).addPathPatterns("/**") .excludePathPatterns("/login","/user/login");//登陆请求不能拦截 } } //登陆检查 public class MyLoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Object loginUser = request.getSession().getAttribute("loginUser"); System.out.println("权限验证"); if(loginUser!=null){ return true; }else{ request.setAttribute("msg","没有权限,请登录"); request.getRequestDispatcher("/login").forward(request,response); return false; } } }
restful CRUD-员工列表
RestfulCRUD:CRUD满足Rest风格;URI: /资源名称/资源标识 HTTP请求方式区分对资源CRUD操作
普通CRUD(uri来区分操作) RestfulCRUD 查询 getEmp emp---GET 添加 addEmp?xxx emp---POST 修改 updateEmp?id=xxx&xxx=xx emp/{id}---PUT 删除 deleteEmp?id=1 emp/{id}---DELETE 实验的请求架构;
实验功能 请求URI 请求方式 查询所有员工 emps GET 查询某个员工(来到修改页面) emp/1 GET 来到添加页面 emp GET 添加员工 emp POST 来到修改页面(查出员工进行信息回显) emp/1 GET 修改员工 emp PUT 删除员工 emp/1 DELETE 回显值:th:value="" 放入book对象 进行回显 利用thymeleaf 来将add update页面结合在一起
添加修改的put post 修改
- SpringMVC中配置HiddenHttpMethodFilter;(SpringBoot自动配置好的)
- 页面创建一个post表单
- 创建一个input项,name="_method";值就是我们指定的请求方式
注册拦截器的代码需要修改
springboot2.0版本后配置拦截器会导致静态资源被拦截的解决方案 //注册拦截器 @Override public void addInterceptors(InterceptorRegistry registry) { //静态资源也不用拦截 springboot已经配置好了 registry.addInterceptor(new MyLoginInterceptor()).addPathPatterns("/**") .excludePathPatterns("/login","/","/user/login","/webjars/bootstrap/**");//登陆请求不能拦截 } //加入了webjars下的路径 不然会被拦截
list页面的代码
《table> 《tr> 《th>bookId《/th> 《th>bookName《/th> 《/tr> 《tr th:each="name:${bookList}"> 《td th:text="${nameStat.index+1}"> 《td th:text="${name}">《/td> 《td> 《form th:action="@{/book/}+${nameStat.index+1}" method="post"> 《input name="_method" value="delete" type="hidden"> 《button type="submit">Delete《/button> 《/form> 《/td> 《/tr> 《/table>
控制器
@Controller public class BookController { @Autowired private BookDao dao; @GetMapping("/books") public String getAllBookHandle(Model mv) { mv.addAttribute("bookList", dao.getAll()); System.out.println("getAllBookHandle..."); return "restfulTest"; } //Get不要带/{id} @RequestMapping(value="/book",method = RequestMethod.GET) public String getBookById(@RequestParam("id") Integer id, Model mv) { mv.addAttribute("bookList", dao.getNameById(id)); System.out.println("getBookById..." + id); return "restfulTest"; } @PutMapping("/book") public String updateBookById(Integer id, String name) { dao.updateBookById(id, name); System.out.println("updateBookById..." + id); return "redirect:/books"; } @DeleteMapping("/book/{id}") public String deleteBookById(@PathVariable("id") Integer id) { dao.deleteBookById(id); System.out.println("deleteBookById..." + id); return "redirect:/books"; } @PostMapping("/book") public String setBook(String name) { dao.addBook(name); System.out.println("setBook..."); return "redirect:/books"; } }
Dao
@Repository public class BookDao { private static HashMap<Integer, String> hm = new HashMap<Integer, String>(); static { hm.put(1, "西瓜书"); hm.put(2, "java编程思想"); hm.put(3, "算法导论"); hm.put(4, "算法"); } public List<String> getAll() { List<String> ls = new ArrayList<String>(); for (Map.Entry<Integer, String> entry : hm.entrySet()) { ls.add(entry.getValue()); } return ls; } public String getNameById(int id) { return hm.get(id); } public void deleteBookById(int id) { hm.remove(id); } public void updateBookById(int id, String name) { hm.replace(id,name); } public void addBook(String name) { hm.put(hm.size()+1, name); } }
请求页面
《a th:href="@{/books}">所有的书本《/a> 《br> 查找某id的书本 《form th:action="@{/book}" method="get"> 书的Id:《input type="text" name="id"/> 《input type="submit" value="提交"/> 《/form> 《br> 添加书本 《form th:action="@{/book}" method="post"> 书的名称:《input type="text" name="name"/> 《input type="submit" value="提交"/> 《/form> 《br> 修改某id的书本信息 《form th:action="@{/book}" method="post"> 《input name="_method" value="put" type="hidden"> 书的id:《input type="text" name="id"/> 书的名称:《input type="text" name="name"/> 《input type="submit" value="提交"/> 《/form>
CRUD-员工删除
《tr th:each="emp:${emps}"> 《td th:text="${emp.id}">《/td> 《td>[[${emp.lastName}]]《/td> 《td th:text="${emp.email}">《/td> 《td th:text="${emp.gender}==0?'女':'男'">《/td> 《td th:text="${emp.department.departmentName}">《/td> 《td th:text="${#dates.format(emp.birth, 'yyyy-MM-dd HH:mm')}">《/td> 《td> 《a class="btn btn-sm btn-primary" th:href="@{/emp/}+${emp.id}">编辑《/a> 《button th:attr="del_uri=@{/emp/}+${emp.id}" class="btn btn-sm btn-danger deleteBtn">删除《/button> 《/td> 《/tr> 《script> $(".deleteBtn").click(function(){ //删除当前员工的 $("#deleteEmpForm").attr("action",$(this).attr("del_uri")).submit(); return false; }); 《/script>