SpringBoot (5) 整合web开发技术

    科技2024-05-24  68

    SpringBoot (5) 整合web开发技术

    1.Json解析方案

    HttpMessageConverter 消息转换工具

    有两方面功能:

    1.将服务端的对象序列化成字符串

    2.将前端传过来的json字符串反序列化成java对象SpringMvc配置了Jackson和Gson的HttpMessageConverter,springboot中又做了自动化配置,如果用户使用两者,只需要添加相应的依赖即可,不需要额外配置。具体可查看源码JacksonHttpMessageConvertersConfiguration和GsonAutoConfiguration。

    使用Gson作为web的json解析方案

    pom.xml配置如下

    <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot<</groupId> <artifactId>spring-boot-starter-json</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> </dependency>

    使用fastjson作为web的json解析方案

    springboot对fastjson没有实现自动化配置,因此使用fastjson相对麻烦些。

    <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot<</groupId> <artifactId>spring-boot-starter-json</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.70</version> </dependency>

    编写配置类

    @Configuration public class WebMvcConfig { @Bean FastJsonHttpMessageConverter fastJsonHttpMessageConverter(){ FastJsonHttpMessageConverter converter=new FastJsonHttpMessageConverter(); return converter; } }

    2.静态资源访问

    2.1 ssm中静态资源配置

    <mvc: resource mapping="/js/**" location="/js/">

    2.2 springboot 中的静态资源配置

    springboot 提供了自动化的配置。参考源码WebMvcAutoConfigration.java、ResourceProperties.java

    private static final String[] CLASSPATH_RESOURCE_LOCATIONS = new String[]{"classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/"};

    静态资源默认位置(5个,有优先级 从高到低如下):

    1.classpath:/META-INF/resources/

    2classpath:./resources/

    3.classpath:/static/

    4.classpath:/public/

    5./(斜杠指向webapp下面)

    2.3 springboot 自定义静态资源配置位置

    有两种方式

    1.在application.properties中

    ##相当于<mvc: resource mapping="/feiyu/**" location="/feiyu/">中的location spring.resources.static-locations=classpath:/feiyu/ ##相当于<mvc: resource mapping="/feiyu/**" location="/feiyu/">中的mapping spring.mvc.static-path-pattern=/feiyu

    2.java配置方式

    @Configuration public class WebConfig implements WebMvcConfigurer { /** * 添加静态资源文件,外部可以直接访问地址 * @param registry */ @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { //如下配置则能可以访问src/main/resources/feiyu下面的文件 registry.addResourceHandler("/feiyu/**").addResourceLocations("classpath:/feiyu/"); } }

    3.SpringBoot文件上传

    源码:MultipartResolver.java 使用idea快捷键ctrl+H 发现MultipartResolver有两个实现类 StandardServletMultipartResolver和CommonsMultipartResolver(支持低版本的servlert)。springboot中使用的是StandardServletMultipartResolver,在springmvc中该类不支持serverlet 2.5,在springboot是支持的。

    3.1 单文件上传

    controller代码如下: @RestController public class FileUploadController { SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd"); //上传必须是post请求 @PostMapping("/upload") public String upload(MultipartFile file, HttpServletRequest req) throws IOException { String format = sdf.format(new Date()); String realPath = "E:/test/" + format; File folder = new File(realPath); if(!folder.exists()){ folder.mkdir(); } String oldName=file.getOriginalFilename(); String newName = UUID.randomUUID().toString() + oldName.substring(oldName.lastIndexOf(".")); // file.transferTo(new File(folder,newName)); String p=folder.getAbsolutePath()+File.separator+newName; file.transferTo(new File(p)); String url=req.getScheme()+"://"+req.getServerName()+":"+req.getServerPort()+"/img/"+format+"/"+newName; return url; } }

    2 .html代码

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="/upload" method="post" enctype="multipart/form-data"> <input type="file" name="file"> <input type="submit" value="提交"> </form> </body> </html> 配置类

    添加静态资源映射,以便能使用url访问上传的文件

    @Configuration public class WebConfig implements WebMvcConfigurer { /** * 添加静态资源文件,外部可以直接访问地址 * @param registry */ @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/img/**").addResourceLocations("file:E:/test/"); } } application.properties

    配置属性 例如 允许上传的最大文件大小

    #允许上传文件上限 spring.servlet.multipart.max-file-size=1KB

    运行项目,浏览器访问http://localhost:8080/index.html

    选择文件,提交,上传成功后返回下面链接

    http://localhost:8080/img/2020-09-21/26603170-ddc3-45c9-b0e1-53030a8596e7.jpg

    使用该链接可以查看刚刚上传的图片。

    3.2多文件上传

    1.controller代码

    @PostMapping("/uploads") public String uploads(MultipartFile[] files, HttpServletRequest req) throws IOException { String format = sdf.format(new Date()); String realPath = "E:/test/" + format; File folder = new File(realPath); if(!folder.exists()){ folder.mkdir(); } for (MultipartFile file : files) { String oldName=file.getOriginalFilename(); String newName = UUID.randomUUID().toString() + oldName.substring(oldName.lastIndexOf(".")); // file.transferTo(new File(folder,newName)); String p=folder.getAbsolutePath()+File.separator+newName; file.transferTo(new File(p)); String url=req.getScheme()+"://"+req.getServerName()+":"+req.getServerPort()+"/img/"+format+"/"+newName; System.out.println(url); } return "success"; }

    2.html代码

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="/uploads" method="post" enctype="multipart/form-data"> <input type="file" name="files" multiple> <input type="submit" value="提交"> </form> </body> </html>

    3.3 ajax文件上传

    借助jquery来实现文件上传

    1.html代码

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="jquery3.3.1.js"></script> </head> <body> <div id="result"></div> <input type="file" id="file"> <input type="button" value="上传" onclick="uploadFile()"> <input type="file" id="files" multiple> <input type="button" value="批量上传" onclick="uploadFiles()"> <script> function uploadFile() { var file=$("#file")[0].files[0]; var formData=new FormData(); formData.append("file",file); $.ajax({ type:'post', url:'/upload', processData:false,//processData默认为true(该方法为jQuery独有的)默认情况下会将发送的数据序列化以适应默认的内容类型application/x-www-form-urlencoded contentType:false, data:formData, success:function (msg) { $("#result").html(msg); } }) } function uploadFiles() { var files=$("#files")[0].files; var formData=new FormData(); for (var i=0;i<files.length;i++) { formData.append("files",files[i]); } //formData.append("files",files); //这样写后台收不到 $.ajax({ type:'post', url:'/uploads', processData:false,//processData默认为true(该方法为jQuery独有的)默认情况下会将发送的数据序列化以适应默认的内容类型application/x-www-form-urlencoded contentType:false, data:formData, success:function (msg) { $("#result").html(msg); } }) } </script> </body> </html>

    2.controller代码同3.1和3.2

    4.@ControllerAdvice用法

    该注解是spring注解,不是springboot专有的。

    这里讲三个用法。

    1.处理全局异常。

    2.预设全局数据。

    3.请求参数预处理。

    4.1 处理全局异常

    @ControllerAdvice public class MyCustomException { @ExceptionHandler(MaxUploadSizeExceededException.class) public void myException(MaxUploadSizeExceededException e, HttpServletResponse req) throws IOException { req.setContentType("text/html;charset=utf-8"); PrintWriter out=req.getWriter(); out.write("上传文件大小超出上限"); out.flush(); out.close(); } }

    此时使用3.1文件上传,选择超出范围的文件后,页面输出“ 上传文件大小超出上限”。

    4.2 预设全局数据

    @ControllerAdvice配合 @ModelAttribute使用。该数据可以在任意Controller里获得。

    import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ModelAttribute; import java.util.HashMap; import java.util.Map; @ControllerAdvice public class GlobalData { @ModelAttribute(value="info") public Map<String,Object> mydata(){ Map<String,Object> map=new HashMap(); map.put("name","feiyu"); return map; } }

    编写controller

    @RestController public class HelloController { @GetMapping("/hello") public String hello(Model m){ Map<String, Object> map = m.asMap(); Map info = (Map) map.get("info"); return (String) info.get("name"); } }

    4.3 请求参数预处理

    @RestController public class BookController { @PostMapping("/book") public void addBook(@ModelAttribute("b") Book b, @ModelAttribute("a") Author a){ System.out.println(b); System.out.println(a); } } @ControllerAdvice public class GlobalData { @ModelAttribute(value="info") public Map<String,Object> mydata(){ Map<String,Object> map=new HashMap(); map.put("name","feiyu"); return map; } @InitBinder("a") public void initA(WebDataBinder binder){ binder.setFieldDefaultPrefix("a.");//参数匹配前缀是a. } @InitBinder("b") public void initB(WebDataBinder binder){ binder.setFieldDefaultPrefix("b.");//参数匹配前缀是b. } }

    postman访问,数据能正确的映射到对象里。

    5.springboot 处理异常

    5.1 默认异常处理

    5.1.1

    在resource/static 新建error文件夹,创建404.html和500.html

    404.html如下:

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>404</h1> </body> </html>

    编写controller,制造一个异常

    @RestController public class HelloController { @GetMapping public String hello(){ int i=1/0; return "hello"; } }

    访问http://localhost:8080/hello,跳转到404.html

    5.1.2 4开头顶异常会跳转到4xx.html页面

    支持模糊匹配,创建4xx.html5

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>4xx</h1> </body> </html>

    404.html和4xx.html页面同时存在优先使用404.html

    5.1.3 templates下新建error

    在error下面新建4xx.html

    引入theamleaf依赖

    优先使用templates下的错误页面,优先使用具体的html。

    templates.error.404.html>templates.error.4xx.html>static.error.404.html>static.error.4xx.html

    自动配置源码在ErrorMvcAutoConfiguration.java和DefaultErrorViewResolver.java中。

    5.2 自定义异常数据

    编写 5xx.html

    <!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>template 5xx</h1> <table BORDER="1"> <tr> <td>path</td> <td th:text="${path}"></td> <!--默认的--> </tr> <tr> <td>timestamp</td> <td th:text="${timestamp}"></td><!--默认的--> </tr> <tr> <td>message</td> <td th:text="${message}"></td><!--默认的--> </tr> <tr> <td>status</td> <td th:text="${status}"></td><!--默认的--> </tr> <tr> <td>myError</td> <!--自定义异常--> <<td th:text="${myError}"></td> </tr> </table> </body> </html>

    定义继承DefaultErrorAttributes的类,重写getErrorAttributes方法

    @Component public class MyErrorAttribute extends DefaultErrorAttributes { @Override public Map<String, Object> getErrorAttributes(WebRequest webRequest, boolean includeStackTrace) { Map<String, Object> map = super.getErrorAttributes(webRequest, includeStackTrace); map.put("myError","自定义异常"); return map; } }

    5.3自定义异常视图

    /** * 自定义异常视图 */ @Component public class MyErrorViewResovler extends DefaultErrorViewResolver { public MyErrorViewResovler(ApplicationContext applicationContext, ResourceProperties resourceProperties) { super(applicationContext, resourceProperties); } @Override public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) { ModelAndView mv=new ModelAndView(); mv.setViewName("feiyu"); mv.addAllObjects(model);//此model不可修改 return mv; } }

    发生异常会跳转feiyu.html

    6.CORS实现跨域

    同源策略:相同的协议、域名、端口号。

    CORS: 跨域资源共享

    传统方案是JSONP,CORS是另外一种方案。比前者更现代。

    6.1 方法级别配置

    @GetMapping("/hello") @CrossOrigin(origins = "http://localhost:8081") public String hello(){ return "hello"; }

    允许来自http://localhost:8081的请求。@CrossOrigin也可以放在类名上

    6.2 全局配置

    @Configuration public class WebMvcConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { //允许 http://localhost:8081 registry.addMapping("/**").allowedOrigins("http://localhost:8081") .allowedHeaders("*") //包含请求头 .allowedMethods("*") //get post put delete 等 .maxAge(30*1000)//探测请求的最大时间 ; } }

    7.注册拦截器

    7.1定义拦截器

    public class MyInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("preHandle"); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("postHandle"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("afterCompletion"); } }

    7.2 配置拦截器

    @Configuration public class WebMvcConfig implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(myInterceptor()).addPathPatterns("/**"); } @Bean MyInterceptor myInterceptor(){ return new MyInterceptor(); } }

    8.系统启动任务

    在系统启动的时候执行的任务

    8.1 CommandLineRunner(第一种方式)

    @Component @Order(99)//指定优先级 public class MyCommandLineRunner implements CommandLineRunner { @Override public void run(String... args) throws Exception { System.out.println("MyCommandLineRunner>>>>"+ Arrays.toString(args)); } }

    args参数 可以在使用java -jar 启动项目时指定

    例如 java -jar commondlinerunner-0.0.1-SNAPSHOT.jar feiyu java 指定2个参数 feiyu和java

    8.2 ApplicationRunner( 第二种方式)

    @Component @Order(99) public class MyApplicationRunner implements ApplicationRunner { @Override public void run(ApplicationArguments args) throws Exception { String[] sourceArgs = args.getSourceArgs();//获取所有参数 System.out.println(Arrays.toString(sourceArgs)); List<String> nonOptionArgs = args.getNonOptionArgs(); System.out.println(nonOptionArgs); Set<String> optionNames = args.getOptionNames(); System.out.println(optionNames); for(String key :optionNames){ List<String> optionValues1 = args.getOptionValues(key); System.out.println(optionValues1); } } }

    打包后 运行命令

    java -jar applicationrunner-0.0.1-SNAPSHOT.jar --name=feiyu --address=china 男

    运行结果

    [--name=feiyu, --address=china, 男] [男] [address, name] [china] [feiyu]

    两种方式主要是支持的参数不同

    9. 整合servlet、Filter、Listener

    9.1 定义servlet 、filter、listener

    @WebServlet(urlPatterns = "/myservlet") public class MyServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { System.out.println("MyServlet"); } } @WebFilter(urlPatterns = "/*") public class MyFilter implements Filter { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("MyFilter"); filterChain.doFilter(servletRequest, servletResponse); } } @WebListener public class MyRequestListener implements ServletRequestListener { @Override public void requestDestroyed(ServletRequestEvent sre) { System.out.println("requestDestroyed"); } @Override public void requestInitialized(ServletRequestEvent sre) { System.out.println("requestInitialized"); } }

    9.2 配置

    @SpringBootApplication @ServletComponentScan(basePackages = "com.feiyuxuy") public class ServletApplication { public static void main(String[] args) { SpringApplication.run(ServletApplication.class, args); } }

    10 .路径映射

    一些简单的访问不需要通过controller访问页面模板,通过路径映射访问也可以

    @Configuration public class WebMvcConfig implements WebMvcConfigurer { @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/hello").setViewName("hello"); } }

    访问http://localhost:8080/hello即可找到hello.html,不需要编写controller

    11.使用类型转换器 进行参数转换

    @Component public class DateConverter implements Converter<String,Date> { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); @Override public Date convert(String source) { if (source != null && !"".equals(source)) { try { return sdf.parse(source); } catch (ParseException e) { e.printStackTrace(); } } return null; } }

    12.自定义欢迎页

    @Controller public class HelloController { @GetMapping("/index") public String hello() { return "index"; } }

    访问http://localhost:8080会自动转去执行 http://localhost:8080/index

    13.自定义 favicon

    https://tool.lu/favicon 使用该网站制作ico文件,将文件放在resource或者static下面即可

    14.出去自动化配置

    @SpringBootApplication(exclude = ErrorMvcAutoConfiguration.class) public class WelcomeApplication { public static void main(String[] args) { SpringApplication.run(WelcomeApplication.class, args); } }

    第二种方式在application.properties里配置

    spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration

    15.使用AOP

    15.1 pom.xml

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

    15.2 定义切面

    @Component @Aspect public class LogComponet { @Pointcut("execution(* com.feiyuxuy.aop.service.*.*(..))") public void pc1(){ //定义拦截规则 } @Before(value = "pc1()") public void before(JoinPoint jp) { //前置通知 String name = jp.getSignature().getName(); System.out.println("before--" + name); } @After(value = "pc1()") public void after(JoinPoint jp) {//后置通知 String name = jp.getSignature().getName(); System.out.println("after--" + name); } @AfterReturning(value = "pc1()", returning = "result") public void afterReturning(JoinPoint jp, Object result) {//返回通知 有返回值时 String name = jp.getSignature().getName(); System.out.println("afterReturning----" + name + "-----" + result); } @AfterThrowing(value = "pc1()",throwing = "e") public void afterThrowing(JoinPoint jp,Exception e) {//异常通知 String name = jp.getSignature().getName(); System.out.println("afterThrowing---"+name+"----"+e.getMessage()); } @Around("pc1()") public Object around(ProceedingJoinPoint pjp) throws Throwable {//环绕通知 可替换上面四种 System.out.println("前置。。。"); Object proceed = pjp.proceed(); System.out.println("后置。。。"); System.out.println(proceed.toString()); return "feiyu"; } }

    源码地址:https://gitee.com/xuyou1028/spring-boot-tutorial

    Processed: 0.012, SQL: 9