0

springboot原理实战(14)--springbootweb

 1 year ago
source link: https://blog.51cto.com/u_12486320/5448745
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

springboot原理实战(14)--springbootweb

原创

本文章的大纲如下:

springboot原理实战(14)--springbootweb_ide

第一部分:

一、请求method

springboot的请求注解是​​@RequestMapping(value = "/user/home",method= RequestMethod.GET)​​​ spring4.3以后可以使用: ​​@GetMapping("/user/home2")​​,​​@PostMapping("/user/create")​​等语义化表示get,post等请求方法。

springboot原理实战(14)--springbootweb_html_02

二、传递参数的3种方式

1.RequestParam

​@RequestParam 默认必填,可以设置非必填,和默认值​​:

/**
* @RequestParam 注解默认是参数必须提供值
* @param username
* @param password
* @return
*/
@PostMapping("/user/create")
@ResponseBody
public String create(@RequestParam(value = "username",defaultValue = "admin") String username, @RequestParam(value="password",required = false) String password){
return "user create, username=" + username + ",password="+password;
}

2.url方式 @PathVariable

/**
* 获取url的方式/user/1 ,/user/2
* @param id
* @return
*/
@GetMapping("/user/{id}")
@ResponseBody
public String display(@PathVariable("id") String id){
return "user display is=" + id;
}

3.HttpServletRequest方式

@ResponseBody
@GetMapping("/user/ip")
public String edit(HttpServletRequest req){
return "user edit " + req.getRemoteHost();
}

三、@RestController

这个注解相当于@Controller+@RequestBody,用在视图访问层,表示当前controller的方法的返回值并可以直接用于body测试.返回json格式数据:

springboot原理实战(14)--springbootweb_spring boot_03

四、springboot的数据渲染到jsp页面

需要的步骤:

1.配置文件:

application.properties加入2个配置项,执行页面路径和后缀

spring.mvc.view.prefix=/WEB-INF/jsp/
spring.mvc.view.suffix=.jsp

2.加入 tomcat-embed-jasper的依赖

pom.xml中:

<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<!-- <version>9.0.27</version>-->
</dependency>
<!--servlet依赖jar-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
</dependency>
<!--Jstl标签依赖的jar包start-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>

build下加入resoures

<resources>
<!-- <resource>-->
<!-- <directory>src/main/java</directory>-->
<!-- <includes>-->
<!-- <include>**/*.xml</include>-->
<!-- </includes>-->
<!-- </resource>-->
<!-- <resource>-->
<!-- <directory>src/main/resources</directory>-->
<!-- <includes>-->
<!-- <include>**/*.*</include>-->
<!-- </includes>-->
<!-- </resource>-->
<resource>
<directory>src/main/webapp</directory>
<targetPath>META-INF/resources</targetPath>
<includes>
<include>**/*.*</include>
</includes>
</resource>
</resources>
</build>

3.测试:

@Controller
public class LoginController {

@PostMapping("/login")
public String login(@RequestParam(value = "username") String username, @RequestParam(value = "password") String password) {
if (username.equals(password)) {
return "ok";
}
return "fail";
}

/**
* 给jsp传参数,类似req.setAttribute
* @param model
* @return
*/
@GetMapping("/login")
public String loginIndex(Model model){
model.addAttribute("username", "root");
return "login";
}
}

login.jsp页面:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>this is login page</h1>
<p>username is ${username}</p>
</body>
</html>

运行入口函数

@SpringBootApplication
public class Demo11Application {

public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Demo11Application.class, args);
}

}

运行结果,显示已经数据已经渲染到jsp页面:

springboot原理实战(14)--springbootweb_html_04

五、springboot渲染数据到freemarker页面:

在springboot中使用freemarker的步骤

1. 在pom.xml里面加入依赖

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>

2.配置文件修改freemarker的位置和后缀

看配置信息可以看源码: ​​FreeMarkerProperties​​:

springboot原理实战(14)--springbootweb_spring_05

我们在application.properties中修改配置信息:

spring.freemarker.templateLoaderPath=classpath:/ftl/
spring.freemarker.suffix=.ftl

3.测试:

@Controller
public class AccountController {

@RequestMapping("/reg")
public String req(Model model){
model.addAttribute("username", "root");
return "reg";
}
}

页面代码:

springboot原理实战(14)--springbootweb_ide_06

运行入口函数:

@SpringBootApplication
public class Demo11Application {

public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Demo11Application.class, args);
}

}

查看页面,显示已经渲染到HTML页面:

springboot原理实战(14)--springbootweb_spring boot_07

六、更换web容器

pringboot 容器默认使用的是tomcat作为外部容器, 可以更换为jetty。
具体操作如下:

1.排除tomcat,加入jetty的依赖

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- 排除tomcat-->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>

2.测试:

运行入口函数:

@SpringBootApplication
public class Demo11Application {

public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(Demo11Application.class, args);
}

}

运行结果,看打印结果,已显示为jetty:

springboot原理实战(14)--springbootweb_ide_08

源码地址:
​​ ​https://github.com/hufanglei/springboot-v-study/tree/springboot-web1​


一、请求静态资源:

1. 放在webapp下 加resources.xml 可以访问到

做个案例,测试下:

  • 在webapp下准备一个图片和一个user.html
springboot原理实战(14)--springbootweb_html_09
pom上加入资源
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/webapp</directory>
<!--注意此次必须要放在此目录下才能被访问到 -->
<targetPath>META-INF/resources</targetPath>
<includes>
<include>**/**</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>**/*</include>
</includes>
</resource>
</resources>

</build>
  • 入口函数启动:
@SpringBootApplication
public class Demo12Application {

public static void main(String[] args) {
SpringApplication.run(Demo12Application.class, args);
}

}
  • 查看效果,显示图片和页面都可以访问到:
springboot原理实战(14)--springbootweb_spring_10

2. springboot默认的静态资源的路径是:

  • “classpath:/META-INF/resources/”,
  • “classpath:/resources/”,
  • “classpath:/static/”,
  • “classpath:/public/” };

我们可以在ResourceProperties源码中找到这个原因:

springboot原理实战(14)--springbootweb_html_11

在public下准备个文件看下能否访问到:

springboot原理实战(14)--springbootweb_ide_12

运行入口函数:看下页面,显示已经访问到public下的html页面和css。

springboot原理实战(14)--springbootweb_spring_13

3.可以通过spring.resources.static-locations 配置项修改默认静态资源路径

我们指定html文件下为我们的默认静态资源路径,并准备个login.html,看下能否访问:

springboot原理实战(14)--springbootweb_html_14
springboot原理实战(14)--springbootweb_spring_15

运行,看下页面效果:

springboot原理实战(14)--springbootweb_spring_16

结果显示已经访问到html下的页面。修改默认的静态资源路径成功。

二、sevlet,filter,listener的2种配置方式:

1.springboot如何使用servlet

有2种方式,一种是用sevlet3加入口函数注解扫码方式,一种是配置类注入servlet方式:
现在动手试试:

① 方法一:(servlet3可以使用这种方法)
  • 1)编写servlet,然后加上响应的注解@WebServlet
  • 2)需要启用@ServletComonentScan注解
@WebServlet("/user.do")
public class BookServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

public BookServlet() {
System.out.println("===servet BookServlet========");
}

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().println("book servlet");
}

}
@ServletComponentScan
@SpringBootApplication
public class Demo12Application {
public static void main(String[] args) {
SpringApplication.run(Demo12Application.class, args);
}
}

运行入口,访问BookServlet 映射的user.do路径:

springboot原理实战(14)--springbootweb_html_17

说明servlet已经访问成功。

② 方法2:(servlet2.5及以下版本可以使用这种方法)
  • 1) 书写servlet无需对位映射,不用加@WebServlet注解
  • 2)书写个配置类,使用@Bean注入,注入的话,需要调用ServletRegistrationBean的构造方法。
    代码如下:
    servlet:
public class BookServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

public BookServlet() {
System.out.println("===servet BookServlet========");
}

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().println("book servlet");
}
}
@SpringBootConfiguration
public class ServletConfigration {

@Bean
public ServletRegistrationBean createBookServlet(){
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new BookServlet(), "/book.do");
return servletRegistrationBean;
}
}
@SpringBootApplication
public class Demo12Application {

public static void main(String[] args) {
SpringApplication.run(Demo12Application.class, args);
}

}

启动并测试:结果显示ok:

springboot原理实战(14)--springbootweb_spring boot_18

2.springboot如何使用过滤器

使用的方式和servet差不多,一种是@WebFilter("/*")注解方式,另一种是也是通过配置类:
不过生成filter的类是 fileter -》 FilterRegistrationBean。

方式1通过@WebFilter("/*")+入口@ServletComponentScan注解实现过滤器
@WebFilter("/*")
public class LogFilter implements Filter {


@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("==LogFilter =init===");
}

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
System.out.println("==LogFilter =filter ==="+ request.getRemoteHost());
chain.doFilter(request,response);
}

@Override
public void destroy() {
System.out.println("=====destroy======");
}
}
@ServletComponentScan
@SpringBootApplication
public class Demo12Application {

public static void main(String[] args) {
SpringApplication.run(Demo12Application.class, args);
}

}

启动入口函数,访问刚才的servlet,看下控制台打印情况:

springboot原理实战(14)--springbootweb_html_19

显示过滤器已经被注入了。

方式2:配置类+注入bean,
public class EchoFilter implements Filter {

@Override
public void init(FilterConfig filterConfig) throws ServletException {

}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
System.out.println("spring boot web filter "+ req.getRequestURI());
chain.doFilter(request,response);
}
@Override
public void destroy() {

}
}
@SpringBootConfiguration
public class ServletConfigration {

@Bean
public FilterRegistrationBean createFilterRegistrationBean(){
FilterRegistrationBean filter = new FilterRegistrationBean();
filter.setFilter(new EchoFilter());
filter.setUrlPatterns(Arrays.asList("/book.do"));
return filter;
}
}
@SpringBootApplication
public class Demo12Application {

public static void main(String[] args) {
SpringApplication.run(Demo12Application.class, args);
}

}
springboot原理实战(14)--springbootweb_spring boot_20

运行结果:显示过滤器已经起作用了了。

springboot原理实战(14)--springbootweb_html_21

3.springboot如何使用监听器

1)方式1:@WebListener+@ServletComponentScan
@WebListener
public class MyListener implements ServletContextListener {

@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("StartUpListener app startup at " + LocalDateTime.now().toString());
}

@Override
public void contextDestroyed(ServletContextEvent sce) {

}
}
@ServletComponentScan
@SpringBootApplication
public class Demo12Application {

public static void main(String[] args) {
SpringApplication.run(Demo12Application.class, args);
}

}
springboot原理实战(14)--springbootweb_html_22
2)方式2:配置类+@bean+ServletListenerRegistrationBean构造函数注入
public class StartUpListener implements ServletContextListener {

@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("StartUpListener app startup at " + LocalDateTime.now().toString());
}

@Override
public void contextDestroyed(ServletContextEvent sce) {

}
}
@SpringBootConfiguration
public class ServletConfigration {

@Bean
public ServletListenerRegistrationBean createServletListenerRegistrationBean(){
ServletListenerRegistrationBean listener = new ServletListenerRegistrationBean(new StartUpListener());
return listener;
}
}
@SpringBootApplication
public class Demo12Application {

public static void main(String[] args) {
SpringApplication.run(Demo12Application.class, args);
}

}
springboot原理实战(14)--springbootweb_html_23

源码地址:

 ​https://github.com/hufanglei/springboot-v-study/tree/demo12​


1.拦截器:

拦截器的使用步骤

①写一个拦截器,实现HandlerInterceptor接口

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class LogHandlerInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("=========preHandle===============" + handler);
return true;
}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("=========postHandle===============" + handler);
}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("=========afterCompletion===============" + handler);
}
}
  • preHandle: controller执行之前调用
  • postHandle: controller执行之后,且页面渲染之前调用
  • afterCompletion: 页面渲染之后调用,一般用于资源清理操作

②写一个类,实现WebMvcConfigurer接口,重写addInterceptors方法(已经过时),并调用registry.addInterceptor把上一步的拦截器加进去

@SpringBootConfiguration
public class WebConfiguration implements WebMvcConfigurer {

// @Override
// public void addResourceHandlers(ResourceHandlerRegistry registry) {
// registry.addResourceHandler("/web/**").addResourceLocations("classpath:/web/");
// }

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LogHandlerInterceptor());
}
}

写个controller

@RestController
public class UserController {

@GetMapping("/user/home")
public String home(){
return "user home";
}

@GetMapping("/user/help")
public String help() {
throw new IllegalArgumentException("args is empty");
}
}
@SpringBootApplication
public class Demo13Application {
public static void main(String[] args) {
SpringApplication.run(Demo13Application.class, args);
}
}

页面测试:

springboot原理实战(14)--springbootweb_ide_24

看下控制台:

springboot原理实战(14)--springbootweb_spring boot_25

显示已经拦截controller。

2.异常处理:

1)默认异常处理逻辑ErrorMvcAutoConfiguration

当我们访问一个不存在的url时候会出现如下页面:

springboot原理实战(14)--springbootweb_spring boot_26

看下ErrorMvcAutoConfiguration源码:

springboot原理实战(14)--springbootweb_spring_27
springboot原理实战(14)--springbootweb_spring boot_28

排除springboot默认显示的异常页方式1:

可以看到找到就是渲染的这段代码。

我们可以关闭这个默认异常页配置:

springboot原理实战(14)--springbootweb_ide_29

看了源码再去修改applicaiton.properties

server.error.whitelabel.enabled=false

重新运行:

springboot原理实战(14)--springbootweb_html_30

输入一个无效的url,就没有默认的异常处理页了。

排除springboot默认显示的异常页方式2:

也可以直接在入口函数上,用exclude去排除自动注册的这个默认异常注册类:

@SpringBootApplication(exclude = ErrorMvcAutoConfiguration.class)
public class Demo13Application {

public static void main(String[] args) {
SpringApplication.run(Demo13Application.class, args);
}
}

2).自处理异常

定义一个controller。手动抛出异常:

①在当前controller类使用@ExceptionHandler处理异常

@RestController
public class BookController {

// @ExceptionHandler(value = FileNotFoundException.class)
@ExceptionHandler(value = FileNotFoundException.class)
public String error(Exception e){
return "file not found exception==== " + e.getMessage();
}

@GetMapping("/book/error1")
public String list() throws FileNotFoundException {
throw new FileNotFoundException("file not found");
}

@GetMapping("/book/error2")
public String error2() throws ClassNotFoundException {
throw new ClassNotFoundException("file not found");
}

@GetMapping("/user/null")
public String empty() {
throw new NullPointerException("args is empty");
}
}

我们看到访问list,error2,empty的方法都会抛出异常,同时定义一个处理异常的方法error:

@ExceptionHandler(value = FileNotFoundException.class)
public String error(Exception e){
return "file not found exception==== " + e.getMessage();
}

如果匹配到 @ExceptionHandler中value对应的异常,就可以在error方法中处理异常,或者跳转到错误页面。

我们访问/user/null:

springboot原理实战(14)--springbootweb_spring boot_31

​@GetMapping("/user/null") public String empty() { throw new NullPointerException("args is empty"); } }​​​ 对应的是空指针异常,而​​@ExceptionHandler(value = FileNotFoundException.class)​​是文件找不到的异常,所以controller没给我们处理。然后访问@GetMapping("/book/error1")

springboot原理实战(14)--springbootweb_spring boot_32

看到已经帮我们处理了。

如果想要@ExceptionHandler当前类全部的方法,就可以扩大异常范围,改成exception.class就可了。

@ExceptionHandler(value = Exception.class)
public String error(Exception e){
return " exception==== " + e.getMessage();
}

②全局处理异常的2种方式:

  • a.方式1:使用ErrorPageRegistrar的接口

写一个类,实现ErrorPageRegistrar接口,实现registerErrorPages,在该方法里面,添加具体的错误处理逻辑

@Component
public class CommonErrorRegistry implements ErrorPageRegistrar {
@Override
public void registerErrorPages(ErrorPageRegistry registry) {
ErrorPage e404 = new ErrorPage(HttpStatus.NOT_FOUND, "/404.html");
ErrorPage e500 = new ErrorPage(INTERNAL_SERVER_ERROR, "/500.html");
ErrorPage argsPage= new ErrorPage(IllegalArgumentException.class, "/args.html");
registry.addErrorPages(e404, e500,argsPage);
}
}
  • b.方式2:@ControllerAdvice
    具体做法:

-1.写一个类,需要加上@ControllerAdvice注解
-2.写一个异常处理方法,需要加上@Exception(value=Exception.class)这个注解,在该方法上处理异常,

@ControllerAdvice
public class GloabalExceptinHandler {

@ExceptionHandler(value = Exception.class)
@ResponseBody
public String errorHandler(Exception e){
return "gloabal error " + e.getClass().getName()+ " =="+ e.getMessage();
}

@ExceptionHandler(value = NullPointerException.class)
@ResponseBody
public String errorHandler2(Exception e){
return "gloabal error2 " + e.getClass().getName()+ " =="+ e.getMessage();
}
}

这2种方法都可以跳转到自定义的页面,或者抛出异常。

看下这个ErrorPage类的源码:

springboot原理实战(14)--springbootweb_ide_33

可以看到,errorpage可以通过请求响应状态码控制跳转页面,可以根据具体异常控制跳转页。

演示代码git地址:
​​ ​https://github.com/hufanglei/springboot-v-study/tree/springbootweb3​


以上就说了springbootweb的大体内容,基础springboot2的。关于请求方法参数,数据渲染到jsp页面和freemarker页面的配置方式,替换内置tomcat的方法,请求静态页面的设置,过滤器,监听器,servlet的2种注册方式,拦截器的自定义,异常处理的类上的处理,和全局处理的2种方式。


个人微信公号:

搜索: 怒放de每一天

不定时推送相关文章,期待和大家一起成长!!

springboot原理实战(14)--springbootweb_spring_34

  • 收藏
  • 评论
  • 分享
  • 举报

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK