总结

机会是留给有准备的人,大家在求职之前应该要明确自己的态度,熟悉求职流程,做好充分的准备,把一些可预见的事情做好。

对于应届毕业生来说,校招更适合你们,因为绝大部分都不会有工作经验,企业也不会有工作经验的需求。同时,你也不需要伪造高大上的实战经验,以此让自己的简历能够脱颖而出,反倒会让面试官有所怀疑。

*/
public interface UserDetailView extends UserSimpleView{};

public User() {

}

public User(String username, String password) {
    this.username = username;
    this.password = password;
}

public User(String id, String username, String password) {
    this.id = id;
    this.username = username;
    this.password = password;
}

public User(String id, String username, String password, Date birthDay) {
    this.id = id;
    this.username = username;
    this.password = password;
    this.birthDay = birthDay;
}

/**
 * 设定id只在简单视图显示
 * @return
 */
@JsonView(UserSimpleView.class)
public String getId() {
    return id;
}

public void setId(String id) {
    this.id = id;
}

/**
 * 设定用户名只在简单视图显示
 * @return
 */
@JsonView(UserSimpleView.class)
public String getUsername() {
    return username;
}

public void setUsername(String username) {
    this.username = username;
}

/**
 * 设定密码只在详情视图显示
 * @return
 */
@JsonView(UserDetailView.class)
public String getPassword() {
    return password;
}

public void setPassword(String password) {
    this.password = password;
}

/**
 * 设定生日只在简单视图显示
 * @return
 */
@JsonView(UserSimpleView.class)
public Date getBirthDay() {
    return birthDay;
}

public void setBirthDay(Date birthDay) {
    this.birthDay = birthDay;
}

@Override
public String toString() {
    return "User{" +
            "id='" + id + '\'' +
            ", username='" + username + '\'' +
            ", password='" + password + '\'' +
            ", birthDay=" + birthDay +
            '}';
}

}

#### (3)自定义校验注解


①、新建自定义的校验注解
package security.demo.validator;
import javax.validation.Constraint;
 import javax.validation.Payload;
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;@Target({ElementType.METHOD,ElementType.FIELD})
 @Retention(RetentionPolicy.RUNTIME)
 @Constraint(validatedBy = MyConstraintValidator.class)//表明这个注解具有校验作用,使用的校验逻辑由validatedBy指定的校验类来执行
 public @interface MyConstraint {
 /**
 * 校验需要的提示信息
 * @return
 */
 String message();
Class<?>[] groups() default {};

Class<? extends Payload>[] payload() default {};

}

②、新建自定义的校验逻辑类
package security.demo.validator;
import org.springframework.beans.factory.annotation.Autowired;
 import security.demo.service.HelloService;import javax.validation.ConstraintValidator;
 import javax.validation.ConstraintValidatorContext;/**
• 自定义的校验类需要实现ConstraintValidator接口
• MyConstraint:是需要验证的注解
• String:需要验证的字段类型,这样该注解就只能用在String类型的参数上,如果设置成Object,则任何类型都能使用该注解
 */
 public class MyConstraintValidator implements ConstraintValidator<MyConstraint,Object> {
@Autowired
 private HelloService helloService;
/**• 校验类初始化时执行
• @param constraintAnnotation
 */
 @Override
 public void initialize(MyConstraint constraintAnnotation) {
 System.out.println(“my custom validator init”);
 }/**
• @param o 需要校验的值
• @param constraintValidatorContext
• @return
 */
 @Override
 public boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) {
 helloService.greeting(“tom”);
 System.out.println(“需要校验的值:”+o);
 // return true;
 return false;
 }
 }③、将自定义的校验注解加到需要校验的字段上。
③、将自定义的校验注解加到需要校验的字段上。
/**
 * 用户名不能为空
 */
@MyConstraint(message = "用户名不能为空")
private String username;
### 


### 7、Restful API错误处理机制(3-6节内容)


#### (1)SpringBoot默认的错误处理机制


①: 浏览器端发出的请求,springboot如果处理时有错误,会由**BasicErrorController的errorHtml()方法**来处理,然后返回页面;


②: 非浏览器发出的请求(如PostMan等),springboot如果处理时有错误,会由**BasicErrorController的error()方法**来处理,然后返回JSON格式的错误信息;


#### (2)自定义异常处理


①: 浏览器端发出的请求,出现异常时返回的页面是在**src/main/resources/templates/error/**404.html配置,状态码是多少的就配置以该状态码为名称的页面,并在页面中设置返回的信息。


②: 非浏览器发出的请求(如PostMan等),springboot如果处理时有异常的情况处理方法:


步骤一:创建一个异常处理类
package security.demo.exception;
import java.io.Serializable;
/**
• 自定义的处理用户不存在的异常处理
 */
 public class UserNotExistException extends RuntimeException implements Serializable {
private static final long serialVersionUID = -4253671095865255861L;
private String id;
public UserNotExistException(String id) {
 super(“User Not Exist!”);
 this.id = id;
 }
public String getId() {
 return id;
 }
public void setId(String id) {
 this.id = id;
 }
 }
步骤二、创建一个异常的拦截处理器
package security.demo.web.controller;
import org.springframework.http.HttpStatus;
 import org.springframework.web.bind.annotation.ControllerAdvice;
 import org.springframework.web.bind.annotation.ExceptionHandler;
 import org.springframework.web.bind.annotation.ResponseBody;
 import org.springframework.web.bind.annotation.ResponseStatus;
 import security.demo.exception.UserNotExistException;import java.util.HashMap;
 import java.util.Map;/**
• ControllerAdvice:处理Controller抛出的异常
 */
 @ControllerAdvice
 public class ControllerExceptionHandler {
/**• 处理抛出来的UserNotExistException类型的异常
• @param exception
• @return
 */
 @ExceptionHandler(UserNotExistException.class)
 @ResponseBody
 @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
 public Map<String,Object> handlerUserNotExistException(UserNotExistException exception){
 Map<String,Object> result = new HashMap<>();
 result.put(“id”,exception.getId());
 result.put(“message”,exception.getMessage());
return result;
 }}
只要springboot方法里抛出**UserNotExistException**类型的异常就会由**ControllerExceptionHandler**配置的**handlerUserNotExistException()**方法来处理。


### 


### 8、Restful API的拦截(以拦截记录时间为例)(3-7、8节内容)


#### (1)过滤器(Filter)


**优点: filter里面是能够获取到(HttpServletRequest request)和响应(HttpServletResponse response),从request中也能获取到传入的参数信息;**


**缺点:无法知道是哪一个Controller类中的哪个方法被执行。**

Filter随web应用的启动而启动,只初始化一次,随web应用的停止而销毁。

1.启动服务器时加载过滤器的实例,并调用init()方法来初始化实例;

2.每一次请求时都只调用方法doFilter()进行处理;

3.停止服务器时调用destroy()方法,销毁实例。

方式一:实现Filter类(**有@Component注解**)

package security.demo.web.filter;

import org.springframework.stereotype.Component;
import javax.servlet.*;
 import java.io.IOException;
 import java.util.Date;/**
• @Component 这个注解的目的是将TimeFilter交给容器来管理。
• 时间过滤器
 */
 @Component
 public class TimeFilter implements Filter {
@Override
 public void init(FilterConfig filterConfig) throws ServletException {
 System.out.println(“时间过滤器初始化!”);
 }
@Override
 public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
 System.out.println(“时间过滤器开始执行过滤!”);
 // long startTime = new Date().getTime();
 long startTime = System.currentTimeMillis();
//执行下一个过滤器
 filterChain.doFilter(servletRequest,servletResponse);
 System.out.println("TimeFilter的耗时: "+(System.currentTimeMillis() - startTime));
 System.out.println("时间过滤器结束执行过滤!");

}

@Override
 public void destroy() {
 System.out.println(“销毁时间过滤器!”);
 }
 }
方式二:实现Filter((无**@Component注解**)


①先定义一个没有@Component注解的过滤器
package security.demo.web.filter;
import org.springframework.stereotype.Component;
import javax.servlet.*;
 import java.io.IOException;/**
• 没有@Component注解的过滤器
 */
 public class OtherTimeFilter implements Filter {
@Override
 public void init(FilterConfig filterConfig) throws ServletException {
 System.out.println(“OtherTimeFilter初始化!”);
 }
@Override
 public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
 System.out.println(“OtherTimeFilter开始执行过滤!”);
 long startTime = System.currentTimeMillis();
//执行下一个过滤器
 filterChain.doFilter(servletRequest,servletResponse);
 System.out.println("OtherTimeFilter的耗时: "+(System.currentTimeMillis() - startTime));
 System.out.println("OtherTimeFilter结束执行过滤!");
}
@Override
 public void destroy() {
 System.out.println(“销毁OtherTimeFilter!”);
 }
 }
②使用Filter配置类来管理没有@Component注解的过滤器
package security.demo.web.config;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import security.demo.web.filter.OtherTimeFilter;/**
• Filter:无法获取处理请求的方法信息
• springboot中filter的配置和顺序执行:
• 配置类
 */
 @Configuration
 public class WebFilterConfig {
/**• FilterRegistrationBean是用来注册Filter的类
• @return
 */
 @Bean
 public FilterRegistrationBean otherTimeFilter(){
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
 //将自定义的没有@Component注解的过滤器注册进来
 registrationBean.setFilter(new OtherTimeFilter());
 //设置过滤顺序
 registrationBean.setOrder(2);
 //设置过滤器的名字
 registrationBean.setName(“OtherTimeFilter”);
 //设置需要过滤的地址
 registrationBean.addUrlPatterns(“/*”);
 return registrationBean;}
}
#### 方式一和方式二的区别:


**方式二没有使用@Component注解,而是使用配置类来注册过滤器,这样的好处正如在过滤器注册时的设置一样清晰:**


**①:可以设置过滤器在过滤器链的位置(顺序);**


**②:可以设置过滤器在过滤器链中的名称;**


**③:可以设置过滤器拦截的规则;**


**而使用@Component注解的过滤器则无法更好的实现上述细节。**


#### (2)拦截器(Interceptor)


**优点:  可获请求(HttpServletRequest request)和响应(HttpServletResponse response)对象,也可获取方法所在类的类名及方法名信息****。**
MethodParameter[] methodParameters = ((HandlerMethod)handler).getMethodParameters();
    for (MethodParameter methodParameter : methodParameters) {
        String parameterName = methodParameter.getParameterName();
        // 只能获取参数的名称,不能获取到参数的值
        //System.out.println("parameterName: " + parameterName);
    }
**缺点:从handler对象中只能获取参数的名称,不能获取到参数的值,所以无法获取处理请求的方法里的形参信息,(从request中可以*间接*能获取到传入的参数信息)**。


**它比filter的执行优先级低。**


步骤一:需要先实现**HandlerInterceptor**
package security.demo.web.interceptor;
import org.springframework.stereotype.Component;
 import org.springframework.web.method.HandlerMethod;
 import org.springframework.web.servlet.HandlerInterceptor;
 import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;/**
• Interceptor:可获取处理请求的方法信息
 */
 @Component
 public class TimeInterceptor implements HandlerInterceptor {
/**• 处理请求的方法执行前运行此方法
• @param request
• @param response
• @param handler 处理请求的方法
• @return
• @throws Exception
 */
 @Override
 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
 System.out.println(“preHandle方法执行”);
 System.out.println("处理请求的方法的类名: "+((HandlerMethod)handler).getBean().getClass().getName());
 System.out.println("处理请求的方法名: "+((HandlerMethod)handler).getMethod().getName());
 request.setAttribute(“startTime”,System.currentTimeMillis());
return true;
 }/**
• 处理请求的方法执行后运行此方法
• @param request
• @param response
• @param handler
• @param modelAndView
• @throws Exception
 */
 @Override
 public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
 System.out.println(“postHandle方法执行”);
 Long startTime = (Long)request.getAttribute(“startTime”);
 System.out.println("Interceptor执行耗时: "+ (System.currentTimeMillis() - startTime));}
/**
• 处理请求的方法执行完毕后运行此方法
• @param request
• @param response
• @param handler
• @param exception
• @throws Exception
 */
 @Override
 public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception exception) throws Exception {
 System.out.println(“afterCompletion方法执行”);
 Long startTime = (Long)request.getAttribute(“startTime”);
 System.out.println("Interceptor执行耗时: "+ (System.currentTimeMillis() - startTime));
System.out.println(“exception is :”+exception);
 }
 }
步骤二:新建一个Web的配置类继承WebMvcConfigurationSupport
package security.demo.web.config;
import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.web.servlet.FilterRegistrationBean;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
 import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
 import security.demo.web.filter.OtherTimeFilter;
 import security.demo.web.interceptor.TimeInterceptor;/**
• Filter:无法获取处理请求的方法信息
• springboot中filter的配置和顺序执行:
• 配置类:配置过滤器和拦截器,如果同时配置了拦截器和过滤器,二者都会起作用
 */
 @Configuration
 public class WebConfig extends WebMvcConfigurationSupport {
@Autowired
 private TimeInterceptor timeInterceptor;
/**• FilterRegistrationBean是用来注册Filter的类
• @return
 */
 @Bean
 public FilterRegistrationBean otherTimeFilter(){
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
 //将自定义的没有@Component注解的过滤器注册进来
 registrationBean.setFilter(new OtherTimeFilter());
 //设置过滤顺序
 registrationBean.setOrder(2);
 //设置过滤器的名字
 registrationBean.setName(“OtherTimeFilter”);
 //设置需要过滤的地址
 registrationBean.addUrlPatterns(“/*”);
 return registrationBean;}
/**
• 注册拦截器
• @param registry
 */
 @Override
 protected void addInterceptors(InterceptorRegistry registry) {
 registry.addInterceptor(timeInterceptor);
 }
 }
#### (3)切片(Aspect)(3-8节内容) **(最常使用)**


**优点: 可以获取方法信息及方法里的参数信息。**


**缺点: 无法直接获取方法的请求及响应对象。**


**(通过下面代码可以间接获取方法的请求及响应对象)**
ServletRequestAttributes attribute = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
 HttpServletRequest request = attribute.getRequest();
 HttpServletResponse response = attribute.getResponse();
**常使用在日志,事务,请求参数安全验证等**
package security.demo.web.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
 import org.aspectj.lang.annotation.Around;
 import org.aspectj.lang.annotation.Aspect;
 import org.springframework.stereotype.Component;@Aspect
 @Component
 public class TimeAspect {/**
 * (* security.demo.web.controller.UserController.*(..)):
 * 第一个星号表示任何类型的返回值,第二个星号是UserController类里任何的方法
 * (..):表示方法里的任何参数
 * 整个的意思是作用在UserController的任何方法上,且不论方法的参数是什么,并且不管返回的是什么类型的方法上(即UserController的所有方法上)
 * ProceedingJoinPoint:ProceedingJoinPoint的对象包含了被拦截的方法的所有信息
 */
@Around("execution(* security.demo.web.controller.UserController.*(..))")
public Object handleControllerMethod(ProceedingJoinPoint pjp) throws Throwable {
    System.out.println("time aspect start!");
    //获取被拦截的方法
     Object proceed = pjp.proceed();
     //获取方法的参数的数组
     Object[] args = pjp.getArgs();
     for(Object arg : args){
         System.out.println("参数是: "+arg);
     }
    System.out.println("time aspect end!");

    return proceed;
}
/**
 * (* security.demo.web.controller.UserController.*(..)):
 * 第一个星号表示任何类型的返回值,第二个星号是UserController类里任何的方法
 * (..):表示方法里的任何参数
 * 整个的意思是作用在UserController的任何方法上,且不论方法的参数是什么,并且不管返回的是什么类型的方法上(即UserController的所有方法上)
 * ProceedingJoinPoint:ProceedingJoinPoint的对象包含了被拦截的方法的所有信息
 */
@Around("execution(* security.demo.web.controller.UserController.*(..))")
public Object handleControllerMethod(ProceedingJoinPoint pjp) throws Throwable {
    System.out.println("time aspect start!");
    //获取被拦截的方法
     Object proceed = pjp.proceed();
     //获取方法的参数的数组
     Object[] args = pjp.getArgs();
     for(Object arg : args){
         System.out.println("参数是: "+arg);
     }
    System.out.println("time aspect end!");

    return proceed;
}

}

**三者在程序中的执行顺序:**


①Filter  ②Interceptor  (③ControllerAdvice)  ④Aspect


**执行后的返回顺序:**


controller -> aspect -> controllerAdvice -> Interceptor -> Filter



![无]()


### 9、文件的上传下载


下载功能需要加入IO的jar包
<!-- 处理文件上传下载的IO依赖  -->
	<dependency>
		<groupId>commons-io</groupId>
		<artifactId>commons-io</artifactId>
		<version>2.5</version>
	</dependency>
代码:
package security.demo.web.controller;
import .IOUtils;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartFile;
 import security.demo.dto.FileInfo;import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.io.*;
 import java.util.UUID;@RestController
 @RequestMapping(“/file”)
 public class FileController {
/**
 * 文件上传
 * @param file
 * @return
 */
@PostMapping
public FileInfo update(MultipartFile file){
    //上传时参数的名字
    System.out.println(file.getName());
    //原始的文件名
    String fileOriginalName = file.getOriginalFilename();
    //文件的后缀名
    String fileSuffixName = fileOriginalName.substring(fileOriginalName.lastIndexOf("."));
    //文件的大小
    System.out.println(file.getSize());
    String folder = "自己指定一个路径";

    File uploadPath = new File(folder);
    if(!uploadPath.exists()){
        uploadPath.mkdirs();
    }

    File saveFilePath = new File(folder, UUID.randomUUID().toString()+fileSuffixName);
    try {
        file.transferTo(saveFilePath);
    } catch (IOException e) {
        e.printStackTrace();
    }

    FileInfo fileInfo = new FileInfo(saveFilePath.getAbsolutePath());
    return fileInfo;
}

/**
 * 文件下载,因为是写在响应response中的,所以是无返回值的
 * @param request
 * @param response
 * @param id
 */
@GetMapping("/{id}")
public void download(HttpServletRequest request, HttpServletResponse response, @PathVariable("id") String id) throws IOException {

        //输入流
        InputStream inputStream = new FileInputStream(new File("文件存储路径","文件的名字(包含后缀)"));
        //输出流
        OutputStream outputStream = response.getOutputStream();
        //设置响应中的内容类型为下载类型
        response.setContentType("application/x-download");
        //设置下载时文件的名字
        response.addHeader("Content-Disposition","attachment;filename=(设定的文件名(包含后缀名))");
        IOUtils.copy(inputStream,outputStream);

        outputStream.flush();
        //关闭流
        inputStream.close();
        outputStream.close();
}

}

### 10、异步处理REST服务


#### (1)使用Runnable异步处理Rest服务


在主线程中使用Callable来发起一个副线程来执行耗时的逻辑。(**需要注意的是副线程必须要由主线程发起调用,即主副线程之间必须要由关联**)
package security.demo.web.async;
import lombok.extern.slf4j.Slf4j;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RestController;import java.util.concurrent.Callable;
/**
• @author chenlc
 */
 @RestController
 @Slf4j
 public class AsyncController {
/**• 模拟同步请求处理
• @return
• @throws InterruptedException
 */
 @GetMapping(“/synchroOrder”)
 public String synchroOrder() throws InterruptedException {
 log.info(“主线程开始运行!”);
 Thread.sleep(1000);
 log.info(“主线程结束返回!”);
 return “success”;
 }/**
• 模拟异步请求处理
• 使用Runnable的Callable来异步处理Rest服务
• 副线程必须要由主线程发起调用,即主副线程之间必须要由关联
• @return
• @throws InterruptedException
 */
 @GetMapping(“/asyncOrder”)
 public Callable asyncOrder() throws InterruptedException {
 log.info(“主线程开始运行!”);
Callable result = new Callable(){
 @Override
 public String call() throws Exception {
 log.info(“副线程开始运行!”);
 Thread.sleep(1000);
 log.info(“副线程结束返回!”);
 return “success”;
 }
 };
 log.info(“主线程结束返回!”);
 return result;
 }}
#### (2)使用DeferredResult异步处理Rest服务


![无]()


①:模拟一个消息队列和应用2package security.demo.web.async;
import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Component;/**
• 模拟一个消息队列
 */
 @Component
 @Slf4j
 public class MockQueue {
/**• 下单
 */
 private String placeOrder;/**
• 完成订单
 */
 private String completeOrder;public String getPlaceOrder() {
 return placeOrder;
 }/**
• 模拟一个处理下单请求的应用
• @param placeOrder
• @throws InterruptedException
 */
 public void setPlaceOrder(String placeOrder){
 new Thread(() ->{
 log.info(“接到下单请求!”);
 //模拟处理下单请求
 // try {
 // Thread.sleep(1000);
 // } catch (InterruptedException e) {
 // e.printStackTrace();
 // }
 //模拟完成订单请求
blic String getPlaceOrder() {
 return placeOrder;
 }
/**
 * 模拟一个处理下单请求的应用
 * @param placeOrder
 * @throws InterruptedException
 */
public void setPlaceOrder(String placeOrder){
    new Thread(() ->{
        log.info("接到下单请求!");
        //模拟处理下单请求
// try {
 // Thread.sleep(1000);
 // } catch (InterruptedException e) {
 // e.printStackTrace();
 // }
 //模拟完成订单请求