Java微服务_SpringBoot在Web应用中的知识点(四)

SpringBoot在Web应用中的知识点
 
本小记学习目标
  1. SpringBoot项目War包的生成与Tomcat发布
  2. https安全访问配置
  3. SpringBoot中数据验证
  4. 错误页面的配置
  5. 全局异常处理
  6. 文件上传功能
  7. 拦截器
  8. AOP拦截器
 
 
一、SpringBoot项目War包的生成与Tomcat发布
SpringBoot中默认支持Tomcat容器,当一个SpringBoot打包成为一个jar包并直接运行时会自动启动内部Tomcat容器,除了这种方式也可以把项目打包为war包,采用部署的形式,通过Tomcat进行发布处理。
 
如何把项目打成war包并在Tomcat中进行发布?
1.在xiaoxieboot项目上点击右键--->New--->Maven Module,建立一个新的Module:xiaoxieboot-web
 
2.在xiaoxieboot-web的项目中对pom.xml文件进行编辑
指定打包的方式为war
< packaging >war packaging >
新增相关的依赖
< dependencies >
       < dependency >
             < groupId > junit groupId >
             < artifactId > junit artifactId >
             < scope >test scope >
       dependency >
      
       < dependency >
             < groupId >org.springframework.boot groupId >
             < artifactId >spring-boot-starter-test artifactId >
             < scope >test scope >     
       dependency >
      
       < dependency >
             < groupId >org.springframework.boot groupId >
             < artifactId >spring-boot-starter-web artifactId >
       dependency >
      
       < dependency >
             < groupId >org.springframework.boot groupId >
             < artifactId >spring-boot- devtools artifactId >
       dependency >
  dependencies >
配置打包的方式
  < build >
       < plugins >
             < plugin >
                   < groupId >org.apache.maven.plugins groupId >
                   < artifactId > maven-war- plugin artifactId >
                   < configuration >
                         < warName >xiaoxieboot-web warName >
                   configuration >
             plugin >
       plugins >
  build >
 
3.我们需要更新Maven,项目上点击右键--->Maven--->Update Project...
 
4.手动新增WEB-INF/web.xml文件,如下图项目结构所示
Java微服务_SpringBoot在Web应用中的知识点(四)_第1张图片
对于web.xml要符合Tomcat容器的要求,内容如下
xml version= "1.0" encoding= "UTF-8" ?>
< web-app xmlns:xsi= " http://www.w3.org/2001/XMLSchema-instance " xmlns= " http://java.sun.com/xml/ns/javaee " xsi:schemaLocation= " http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd " id= "WebApp_ID" version= "2.5" />
 
5.新增SpringBoot程序启动类:com.xiaoxie.SpringBootStartApplication
package com.xiaoxie;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
@ SpringBootApplication
public class SpringBootStartApplication extends SpringBootServletInitializer {
       @Override
       protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
             return builder.sources(SpringBootStartApplication. class);
      }
      
       public static void main(String[] args) {
            SpringApplication. run(SpringBootStartApplication. class, args);
      }
      
}
注意:我们要以war包在Tomcat中运行,那么这个启动类必须要继承SpringBootServletInitializer类,同时要覆盖configure()方法。
 
6.新增一个Controller包并新增Controller类,com.xiaoxie.controller.TestController
package com.xiaoxie.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
       @GetMapping( "/test")
       public String test() {
             return "TEST!!!";
      }
}
 
7.把项目打包为war包,项目右键--->Run As--->Maven build...,在弹出的对话框中勾Skip Tests,Goals:中录入“clean package”
Java微服务_SpringBoot在Web应用中的知识点(四)_第2张图片
 
8.打包的过程在控制台会显示提示信息,如果正常打包完成,会在项目所在目录的target下生成一个war包,我们的war包为"xiaoxieboot-web.war"
 
9.把这个war包复制到Tomcat的webapps目录下,启动Tomcat,会自动对war包解压,同时在命令框提示信息中看到相应的启动信息
完成启动后访问:http://localhost:8080/xiaoxieboot-web/test
在页面中可以看到信息: TEST!!!
注意:访问时需要带上打包的名称xiaoxieboot-web(这个名称是在pom.xml中打war的配置中设置的)
 
二、https安全访问配置
SpringBoot启动时默认使用的是http通信,为了保证安全通常需要使用https来进行访问。
通常来说,https的访问是需要证书的,并且为了保证这个证书的安全,一定要在项止中使用CA进行认证。
 
1.使用JDK中的keytool命令生成证书 keystore.p12,命令如下:
keytool -genkey -alias mytomcat -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore D:/keystore.p12 -validity 3650 -dname "CN=Web Server,OU=Unit,O=Organization,L=City,S=State,C=US" -storepass P@ssw0rd
 
2.通过上面的命令则在D盘下生成了一个证书:keystore.p12
把这个证书复制到项目的src/main/resources/resources目录下
同时在这个目录下新增配置文件application.yml
server:
  port: 433 #https的端口设置为433
  ssl:
    key-store: classpath:keystore.p12  #keystore配置文件路径
    key-store-type: PKCS12  #keystore的类型为PKCS12
    key-alias: mytomcat #设置别名
    key-store-password: P@ssw0rd  #访问别名
 
3.在pom.xml的打包配置文件中对资源的访问添加p12类型,在内部下添加
< resources >
        < resource >
              < directory > src/main/resources directory >
              < includes >
                    < include >**/*.properties include >
                    < include >**/*. yml include >
                    < include >**/*. xml include >
                    < include >**/*. tld include >
                    < include >**/*.p12 include >
              includes >
              < filtering >false filtering >
        resource >
      resources>
 
4.新增一个配置类,以便于用户访问80端口的时候跳转到安全链接433端口上
package com.xiaoxie.config;
import org.apache.catalina.Context;
import org.apache.catalina.connector.Connector;
import org.apache.tomcat.util.descriptor.web.SecurityCollection;
import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class HttpConnectorConfig {
       public Connector initConnector() {
            Connector connector = new Connector( "org.apache.coyote.http11.Http11NioProtocol");
             connector.setScheme( "http");
             connector.setPort(80);
             connector.setSecure( false);
             connector.setRedirectPort(433);
             return connector;
      }
      
       @Bean
       public TomcatServletWebServerFactory servletContainerFactory() {
            TomcatServletWebServerFactory tswf = new TomcatServletWebServerFactory() {
                   protected void postProcessContext(Context context) {
                         //定义安全访问策略
                        SecurityConstraint securityConstraint = new SecurityConstraint();
                         securityConstraint.setUserConstraint( "CONFIDENTIAL");  //定义用户访问约束要求
                        SecurityCollection collection = new SecurityCollection();
                         collection.addPattern( "/*");   //匹配所有访问路径
                         securityConstraint.addCollection( collection);    //追加路径映射访问配置
                         context.addConstraint( securityConstraint);
                  }
            };
             tswf.addAdditionalTomcatConnectors(initConnector());
             return tswf;
      }
}
 
三、SpringBoot中数据验证
SpringBoot中的验证可以使用hibernate-vidator组件包实现验证处理,这个组件包支持的验证注解如下:
 
注解
描述
@Null
被注解的元素必须为null
@NotNull
被注解的元素不为null
@AssertTrue
被注解的元素必须为true
@Min(value)
被注解的元素必须为一个数值,且必须要大于等于指定的值
@Max(value)
被注解的元素必须为一个数值,且必须要小于等于指定的值
@DecimalMin(value)
被注解的元素必须为一个数值,且必须要大于等于指定的值
@DecimalMax(value)
被注解的元素必须为一个数值,且必须要小于等于指定的值
Size(max=,min=)
被注解的元素大小必须在指定的范围内
@Digits(integer,fraction)
被注解的元素必须是一个数字,其值 必须在可接受的范围内
@Past
被注解的元素必须是一个过去的日期
@Future
被注解的元素必须是一个将来的日期
@Pattern(regex=,flag=)
被注解的元素必须符合指定的正则表达式
@NotBlank(message=)
被注解的元素字符串非null,且长度必须大于0
@Email
被注解的元素必须是电子邮箱地址
@Length(min=,max=)
被注解的字符串的长度必须在指定范围内
@NotEmpty
被注解的字符串必须非空
@Range(min=,max=,message=)
被注解的元素必须在合适的范围内
1.在项目中新增错误提示信息的资源文件,ValidationMessages.properties    在src/main/resources目录下
注意:这里资源文件的名称必须是:ValidationMessages.properties,同时要注意使用ISO-8859-1的文件编码(SpringBoot 2.0 默认就是使用这个格式去读取资源文件的)
employee.no.notnull.error= 员工编号不可以为空
employee.no.email.error= 员工编号必须使用正确的邮箱
employee.name.notnull.error= 员工名称不可以为空
employee.post.notnull.error= 员工岗位不可以为空
employee.joinDate.past.error= 员工的入职日期不可以大于当前时间
employee.salary.digits.error= 员工的薪资必须是数值
 
2.新增一个vo类:com.xiaoxie.vo.Employee
package com.xiaoxie.vo;
import java.util.Date;
import javax.validation.constraints.Digits;
import javax.validation.constraints.Email;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Past;
public class Employee {
       @NotNull(message= "{employee.no.notnull.error}")
       @Email(message= "{employee.no.email.error}")
       private String no;       //员工编号
       @NotNull(message= "{employee.name.notnull.error}")
       private String name;     //员工姓名
       @NotNull(message= "{employee.post.notnull.error}")
       private String post;     //员工所在岗位
       @Past(message= "{employee.joinDate.past.error}")
       private Date joinDate;   //员工入职日期
       @Digits(integer=20,fraction=2,message= "{employee.salary.digits.error}")
       private Double salary;   //基本工资
      
      
       public String getNo() {
             return no;
      }
       public void setNo(String no) {
             this. no = no;
      }
       public String getName() {
             return name;
      }
       public void setName(String name) {
             this. name = name;
      }
       public String getPost() {
             return post;
      }
       public void setPost(String post) {
             this. post = post;
      }
       public Date getJoinDate() {
             return joinDate;
      }
       public void setJoinDate(Date joinDate) {
             this. joinDate = joinDate;
      }
       public Double getSalary() {
             return salary;
      }
       public void setSalary(Double salary) {
             this. salary = salary;
      }
}
从上面可以看到,在属性上添加了注解验证
 
3.新增一个Controller类,com.xiaoxie.controller.EmployeeController
package com.xiaoxie.controller;
import java.util.Date;
import java.text.SimpleDateFormat;
import java.util.Iterator;
import javax.validation.Valid;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.xiaoxie.vo.Employee;
@Controller
public class EmployeeController {
       @GetMapping( "/input_emp")
       public String input() {
             return "input_emp";
      }
      
       @PostMapping( "/save_emp")
       @ResponseBody
       public Object save( @Valid Employee emp,BindingResult result) {
             if( result.hasErrors()) {       //验证存在错误
                  Iterator iterator = result.getAllErrors().iterator();
                   while( iterator.hasNext()) {    //遍历所有错误
                        ObjectError error = iterator.next();
                         //在控制台打印错误信息
                        System. out.println( "错误信息--->Code:"+ error.getCode() + " ,message:" + error.getDefaultMessage());
                  }
                   return result.getAllErrors();
            } else {     //没有错误的情况
                   return emp;
            }
            
      }
      
       @InitBinder
       public void initBinder(WebDataBinder wdb) {
             //对日期格式化处理
            SimpleDateFormat sdf = new SimpleDateFormat( "yyyy-MM-dd");
             //注册一个日期格式化处理程序类
             wdb.registerCustomEditor(Date. class, new CustomDateEditor( sdf, true));
      }
}
对于方法input,它是跳转到Thymeleaf的模板页面input_emp.html,这时需要新增一包src/main/view/templates,并在其下新增模板页面input_emp.html
DOCTYPE html>
< html xmlns= " http://thymeleaf.org " >
< head >
< meta charset= "UTF-8" >
< title >新增一个员工 title >
head >
< body >
       < h2 >新增员工 h2 >
       < form th:action= "@{save_emp}" method= "post" >
            员工编码: < input type= "text" name= "no" />< span style=" color: gary;" >(请使用邮箱) span >
            员工姓名: < input type= "text" name= "name" />
            员工岗位: < input type= "text" name= "post" />
            入职日期: < input type= "text" name= "joinDate" />
            员工薪资: < input type= "text" name= "salary" />
             < input type= "submit" value= "提交" />
             < input type= "reset" value= "重置" />
       form >
body >
html >
在上面的页面中可以看到表单提交时提交到/save_emp,它对应到控制器类的save方法。
 
四、错误页面的配置
当我们程序出现错误时,为了给用户展现一个友好的错误提示页面,我们需要配置对应的错误信息提示页。错误提示页面一般来说属于静态页面。
1.在src/main/view/static目录下我们新增两个页面error-404.html、error-500.html
error-404.html
DOCTYPE html>
< html >
< head >
< meta charset= "UTF-8" >
< title >错误提示 title >
head >
< body >
       < h2 >404,页面消失了~~~ h2 >
body >
html >
error-500.html
DOCTYPE html>
< html >
< head >
< meta charset= "UTF-8" >
< title >错误提示 title >
head >
< body >
       < h2 >500,对不起让我先崩溃会~~ h2 >
body >
html >
2.新增错误页面的配置,com.xiaoxioe.Config.ErrorPageConfig
package com.xiaoxie.config;
import org.springframework.boot.web.server.ErrorPage;
import org.springframework.boot.web.server.ErrorPageRegistrar;
import org.springframework.boot.web.server.ErrorPageRegistry;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
@Configuration
public class ErrorPageConfig implements ErrorPageRegistrar{
       @Override
       public void registerErrorPages(ErrorPageRegistry registry) {
            ErrorPage error404 = new ErrorPage(HttpStatus. NOT_FOUND, "/error-404.html");
            ErrorPage error500 = new ErrorPage(HttpStatus. INTERNAL_SERVER_ERROR, "/error-500.html");
             registry.addErrorPages( error404, error500);
      }
}
通过上面的配置,当程序发生404,500错误时会跳转到指定的错误页面
 
五、全局异常处理
全局异常处理指的是针对程序中产生的Exception进行处理,当产生了异常后,可以跳转到指定的页面进行错误提示或者通过Restful的形式来返回错误信息。
1.建立一个全局的异常处理类,它可以处理所有的Exception异常,com.xiaoxie.advice.Exception
package com.xiaoxie.advice;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;
@ControllerAdvice  //控制层切面
public class ExceptionAdvice {
       public static final String DEFAULT_ERROR_VIEW = "error";     //默认错误显示页面error.html
      
       @ExceptionHandler
       public ModelAndView defaultErrorHandler(HttpServletRequest request,Exception e) {
            ModelAndView mav = new ModelAndView( DEFAULT_ERROR_VIEW);     //设置跳转路径
             //绑定相关数据
             mav.addObject( "exception", e);
             mav.addObject( "url", request.getRequestURL());
             return mav;
      }
}
 
2.上面默认的错误显示页面是error.html,所以在templates下新增error.html
DOCTYPE html>
< html xmlns= " http://thymeleaf.org " >
< head >
< meta charset= "UTF-8" >
< title >异常信息页面 title >
head >
< body >
       < p th:text= "${'错误访问路径:'+url}" />
       < p th:text= "${'错误信息:'+exception.message}" />
      
body >
html >
 
3.新增一个Controller,com.xiaoxie.controller.ExceptionController
package com.xiaoxie.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ExceptionController {
       @GetMapping( "/cal/{num}")
       public String cal( @PathVariable( "num") Integer num) {
             int result = 100/ num;
             return "100除以"+ num + "的结果是:" + result;
      }
}
 
通过上面我们定义了一个请求路径/cal/{num},这里的num必须是Integer
当请求的路径为/cal/n时,页面显示内容如下:
错误访问路径:https://localhost:433/cal/n
错误信息:Failed to convert value of type 'java.lang.String' to required type 'java.lang.Integer'; nested exception is java.lang.NumberFormatException: For input string: "n"
 
当请求路径为/cal/0时,页面显示内容如下:
错误访问路径:https://localhost:433/cal/0
错误信息:/ by zero
 
当请请路径为/cal/10时,页面显示内容如下:
100除以10的结果是:10
 
如果我们需要以Restful的形式回应异常信息,则可以把全局的异常处理切面类修改为如下
@RestControllerAdvice
public class ExceptionAdvice{
       @ExceptionHandler
       public Object defaultErrorHandler(HttpServletRequest request,Exception e) {
            ErrorInfo error = new ErrorInfo();
             error.setCode(HttpStatus. INTERNAL_SERVER_ERROR.value());
             error.setMessage( e.getMessage());
             error.setUrl( request.getRequestURL().toString());
             return error;
      }
 
ErrorInfo新定的一个vo类
package com.xiaoxie.vo;
public class ErrorInfo {
       private Integer code;
       private String message;
       private String url;
       public Integer getCode() {
             return code;
      }
       public void setCode(Integer code) {
             this. code = code;
      }
       public String getMessage() {
             return message;
      }
       public void setMessage(String message) {
             this. message = message;
      }
       public String getUrl() {
             return url;
      }
       public void setUrl(String url) {
             this. url = url;
      }
}
 
六、文件上传功能
SpringBoot本身是支持文件上传操作的,它采用了FileUpload组件实现文件的上传处理,在控制器中可以使用MultipartFile类进行接收处理。
1.新增一个控制器类 com.xiaoxie.controller.UploadController
package com.xiaoxie.controller;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
@Controller
public class UploadController {
      
       //跳转到upload.html
       @GetMapping( "/upload_pre")
       public String uploadPre() {
             return "upload";
      }
      
       @PostMapping( "/upload")
       @ResponseBody
       public Object upload(String name,MultipartFile photo) throws Exception {
            Map map = new HashMap();
             if( photo != null && photo.getSize() != 0) {
                   //这里说明有文件上传
                   map.put( "name-param", name);
                   map.put( "photo-name", photo.getName());
                   map.put( "content-type", photo.getContentType());
                   map.put( "photo-size", photo.getSize()); //按字节单位计算
                   //创建一个上传后保存的文件名称
                  String fileName = UUID. randomUUID() + "."+ photo.getContentType().substring( photo.getContentType().lastIndexOf( "/")+1);
                   //文件保存路径
                  String filePath = ((ServletRequestAttributes)RequestContextHolder. getRequestAttributes()).getRequest().getServletContext().getRealPath( "/") + fileName;
                   map.put( "photo-path", filePath);
                  File saveFile = new File( filePath);
                   //文件保存
                   photo.transferTo( saveFile);
                   return map;
            } else {
                   return "nothing";
            }
      }
}
这里有两个方法一个是uploadPre方法,它的作用是用来做访问的跳转转发到upload.html页面上去;第二个是upload方法,它接收一个post请求用来处理文件上传
注意:MaltipartFile这个类,它封装了上传文件的信息
2.在src/main/view/template下新增一个upload.html页面
DOCTYPE html>
< html  xmlns= " http://thymeleaf.org " >
< head >
< meta charset= "UTF-8" >
< title >上传文件 title >
head >
< body >
  < form th:action= "@{/upload}" method= "post" enctype= "multipart/form-data" >
      名称: < input type= "text" name= "name" />< br />
      图片: < input type= "file" name= "photo" />< br />
       < input type= "submit" value= "上传" />
  form >
body >
html >
 
在实际的开发过程中,一般是需要对用户上传文件的大小进行限制的,这样可以做到综合平衡服务器资源及用户需求。
可以在aplication.yml配置文件中新增上传限制
spring:
  servlet:
    multipart:
      enabled: true
      max-file-size: 10MB #设置单个文件的大小限制
      max-request-size: 20MB  #设置总体文件大小
      file-size-threshold: 512KB  #当上传文件达到指定配置量的时候,把文件内容写入磁盘
      location: / #临时目录
 
除了上面的在配置文件中对上件文件大小进行配置,也可以添加Bean的配置类进行配置
在com.xiaoxie.config包下添加配置类UploadConfig
package com.xiaoxie.config;
import javax.servlet.MultipartConfigElement;
import org.springframework.boot.web.servlet.MultipartConfigFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class UploadConfig {
       @Bean
       public MultipartConfigElement getMultipartConfig() {
            MultipartConfigFactory config = new MultipartConfigFactory();
             config.setMaxFileSize( "10MB");
             config.setMaxRequestSize( "100MB");
             config.setLocation( "/");
             return config.createMultipartConfig();
      }
}
注意:当配置aplication.yml配置文件和UploadConfig配置类都存在的时候,优先以配置类UploadConfig类中的配置来做校验
 
在实际开发中我们如果一次需要上传多个文件则需要使用MultipartHttpServletRequest进行文件接收
1.新增一个html的模板页面upload_m.html
DOCTYPE html>
< html  xmlns= " http://thymeleaf.org " >
< head >
< meta charset= "UTF-8" >
< title >多个上传文件 title >
head >
< body >
  < form th:action= "@{/upload_m}" method= "post" enctype= "multipart/form-data" >
      名称: < input type= "text" name= "name" />< br />
      图片1: < input type= "file" name= "photo" />< br />
      图片2: < input type= "file" name= "photo" />< br />
      图片3: < input type= "file" name= "photo" />< br />
       < input type= "submit" value= "上传" />
  form >
body >
html >
2.在UploadController类中新增两个Controller方法及一个文件的保存方法
//跳转到upload_m.html
       @GetMapping( "/upload_pre_m")
       public String uploadPrem() {
             return "upload_m";
      }
      
       @PostMapping( "/upload")
       @ResponseBody
       public Object upload(String name,MultipartFile photo) throws Exception {
            Map map = new HashMap();
             if( photo != null && photo.getSize() != 0) {
                   //这里说明有文件上传
                   map.put( "name-param", name);
                   map.put( "photo-name", photo.getName());
                   map.put( "content-type", photo.getContentType());
                   map.put( "photo-size", photo.getSize()); //按字节单位计算
                   //创建一个上传后保存的文件名称
                  String fileName = UUID. randomUUID() + "."+ photo.getContentType().substring( photo.getContentType().lastIndexOf( "/")+1);
                   //文件保存路径
                  String filePath = ((ServletRequestAttributes)RequestContextHolder. getRequestAttributes()).getRequest().getServletContext().getRealPath( "/") + fileName;
                   map.put( "photo-path", filePath);
                  File saveFile = new File( filePath);
                   //文件保存
                   photo.transferTo( saveFile);
                  
                   return map;
            } else {
                   return "nothing";
            }
      }
      
       @PostMapping( "/upload_m")
       @ResponseBody
       public Object uploadm(String name,HttpServletRequest request) {
            List result = new ArrayList();
             if( request instanceof MultipartHttpServletRequest) {
                  MultipartHttpServletRequest mrequest = (MultipartHttpServletRequest) request;
                  List files = mrequest.getFiles( "photo");
                  Iterator it = files.iterator();
                   while( it.hasNext()) {
                         //读取每一个上传的文件
                        MultipartFile photo = it.next();
                         //保存上传信息
                         try {
                               result.add(saveFile( photo));
                        } catch (Exception e) {
                               e.printStackTrace();
                        }
                  }
            }
             return result;
      }
       private String saveFile(MultipartFile file) throws Exception {
            String path = "nothing";
             if( file != null && file.getSize() > 0) {
                  String fileName = UUID. randomUUID() + "." + file.getContentType().substring( file.getContentType().lastIndexOf( "/")+1);
                   path = ((ServletRequestAttributes)RequestContextHolder. getRequestAttributes()).getRequest().getServletContext().getRealPath( "/") + fileName;
                  File saveFile = new File( path);
                   file.transferTo( saveFile);
            }
             return path;
      }
}
注意:当多个文件上传过程中有一个不成功时都不会完成保存文件的操作
 
七、拦截器
Web请求处理的过程中,拦截器是服务器端进行数据处理的最后一道关卡,在这里可以对所有用户请求的信息做拦截验证。
1.在程序中新增一个拦截器的类:com.xiaoxie.interceptor.Myinterceptor,这个类需要实现拦截器类HandlerInterceptor
package com.xiaoxie.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
//自定义拦截器实现拦截器接口
public class MyInterceptor implements HandlerInterceptor {
      
       private static final Logger log = LoggerFactory. getLogger(MyInterceptor. class);
       @Override
       public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
                   throws Exception {
             //在这里可以添加拦截器处理代码,在执行控制器之前执行,如果返回true则继续,返回false则不再继续请求
             return true;
      }
       @Override
       public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                  ModelAndView modelAndView) throws Exception {
             // 拦截器处理代码
             log.info( "MyInterceptor中postHandler=> " + modelAndView);
      }
       @Override
       public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
                   throws Exception {
             //拦截器处理代码
      }
}
2.如果需要拦截器生效需要新增配置类,对MVC做配置,并在其中添加自定义的拦截器及指定匹配的地址,在com.xoiaoxie.config包下新增类MyWebApplicationConfig类,继承WebMvcConfigurationSupport类
package com.xiaoxie.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import com.xiaoxie.interceptor.MyInterceptor;
@Configuration
public class MyWebApplicationConfig extends WebMvcConfigurationSupport {
       @ Override
       protected void addInterceptors(InterceptorRegistry registry) {
             registry.addInterceptor( new MyInterceptor()).addPathPatterns( "/**");   //匹配路
             super.addInterceptors( registry);
      }
      
}
 
八、AOP拦截器
AOP:面向切面编程。它的主要功能就是对业务层的方法调用进行拦截处理,SpringBoot默认是没有含AOP拦截器的,如果需要使用则要在项目中添加sping-boot-starter-aop依赖
在使用AOP之前需要在pom.xml中新增如下依赖
       < dependency >
             < groupId >org.springframework.boot groupId >
             < artifactId >spring-boot-starter- aop artifactId >
       dependency >
 
1.新增一个Service接口:com.xiaoxie.service.IinfoService
package com.xiaoxie.service;
public interface IinfoService {
       public String echo(String message);
}
2.新增service接口的实现类:com.xiaoxie.service.impl.InfoServiceImpl
package com.xiaoxie.service.impl;
import org.springframework.stereotype.Service;
import com.xiaoxie.service.IinfoService;
@Service
public class InfoServiceImpl implements IinfoService {
       @Override
       public String echo(String message) {
             return "【ECHO】" + message;
      }
}
3.新增一个切面类对业务代码进行拦截,com.xiaoxie.aspcet.ServiceAspect
package com.xiaoxie.aspect;
import java.util.Arrays;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class ServiceAspect {
       private static final Logger log = LoggerFactory. getLogger(ServiceAspect. class);
      
       @Around( "execution(* com.xiaoxie.service..*.*(..))")
       public Object arroundInvoke(ProceedingJoinPoint point) throws Throwable{
             log.info( "【serviceBefore】执行参数:"+Arrays. toString( point.getArgs()));
             //具体的业务调用
            Object obj = point.proceed( point.getArgs());
             //返回结果
             log.info( "【serviceAfter】返回结果:" + obj);
             return obj;
      }
}
4.编写测试类测试在业务层调用方法是的拦截是否生效,在src/test/java下新增类com.xiaoxie.test.InfoSeviceTest
package com.xiaoxie.test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import com.xiaoxie.SpringBootStartApplication;
import com.xiaoxie.service.IinfoService;
@SpringBootTest(classes=SpringBootStartApplication. class)
@RunWith(SpringJUnit4ClassRunner. class)
@WebAppConfiguration
public class InfoServiceTest {
       @Autowired
       private IinfoService infoService;
       @ Test
       public void testinfo() {
            System. out.println( infoService.echo( "SpringBoot-AOP拦截"));
      }
}
 
 
 

你可能感兴趣的:(Java,编程,spring,boot,java,spring,Java微服务)