文章曾经收录在 Github.com/niumoo/JavaNotes ,更有 Java 程序员所须要把握的外围常识,欢送Star和指教。
欢送关注我的公众号,文章每周更新。
1. 拦截器
Springboot 中的 Interceptor 拦截器也就是 mvc 中的拦截器,只是省去了 xml 配置局部。并没有实质的不同,都是通过实现 HandlerInterceptor 中几个办法实现。几个办法的作用一一如下。
- preHandle
进入 Habdler 办法之前执行,个别用于身份认证受权等。 - postHandle
进入 Handler 办法之后返回 modelAndView 之前执行,个别用于塞入公共模型数据等。 - afterCompletion
最初解决,个别用于日志收集,对立后续解决等。
<!-- more -->
1.1 引入依赖
<!-- Spring Boot web 开发整合 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <artifactId>spring-boot-starter-json</artifactId> <groupId>org.springframework.boot</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- 阿里 fastjson --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.47</version> </dependency> <!-- Lombok 工具 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!-- 导入配置文件处理器,在配置springboot相干文件时候会有提醒 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> <!-- 单元测试 --> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>RELEASE</version> <scope>compile</scope> </dependency>
1.2 编写拦截器
package net.codingme.boot.config;import lombok.extern.slf4j.Slf4j;import org.springframework.web.servlet.HandlerInterceptor;import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;/** * <p> * 拦截器 * * @Author niujinpeng * @Date 2019/1/6 16:54 */@Slf4jpublic class LogHandlerInterceptor implements HandlerInterceptor { /** * 申请办法执行之前 * 返回true则通过 * * @param request * @param response * @param handler * @return */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { StringBuffer requestURL = request.getRequestURL(); log.info("preHandle申请URL:" + requestURL.toString()); return true; } /** * 返回modelAndView之前执行 * @param request * @param response * @param handler * @param modelAndView */ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) { log.info("postHandle返回modelAndView之前"); } /** * 执行Handler实现执行此办法 * @param request * @param response * @param handler * @param ex */ @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { log.info("afterCompletion执行完申请办法齐全返回之后"); }}
1.3 配置拦截器
省去了 XML 中的拦截器配置局部后,应用 springboot 举荐的形式配置自定义拦截器。
package net.codingme.boot.config;import com.alibaba.fastjson.serializer.SerializerFeature;import com.alibaba.fastjson.support.config.FastJsonConfig;import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;import org.springframework.context.annotation.Configuration;import org.springframework.http.MediaType;import org.springframework.http.converter.HttpMessageConverter;import org.springframework.web.servlet.config.annotation.InterceptorRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;import java.util.ArrayList;import java.util.List;/** * <p> * 1.应用FastJSON * 2.配置工夫格式化 * 3.解决中文乱码 * 4.增加自定义拦截器 * * @Author niujinpeng * @Date 2018/12/13 15:35 */@Configurationpublic class WebMvcConfig implements WebMvcConfigurer { /** * 自定义JSON转换器 * * @param converters */ @Override public void configureMessageConverters(List<HttpMessageConverter<?>> converters) { FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter(); FastJsonConfig fastJsonConfig = new FastJsonConfig(); fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat); //日期格式化 fastJsonConfig.setDateFormat("yyyy-MM-dd HH:mm:ss"); //解决中文乱码问题 List<MediaType> fastMediaTypes = new ArrayList<>(); fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8); converter.setSupportedMediaTypes(fastMediaTypes); converter.setFastJsonConfig(fastJsonConfig); converters.add(converter); } /** * 增加自定义拦截器 * .addPathPatterns("/**") 拦挡的申请门路 * .excludePathPatterns("/user"); 排除的申请门路 * * @param registry */ @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LogHandlerInterceptor()) .addPathPatterns("/**") .excludePathPatterns("/user"); }}
2 切面编程
- AOP:面向切面(方面)编程,扩大性能不批改源代码实现
- AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码
AOP底层应用动静代理实现
- 有接口状况应用动静代理创立接口实现类代理对象
- 没有接口状况应用动静代理创立类的子类代理对象
import lombok.extern.slf4j.Slf4j;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.annotation.*;import org.springframework.core.annotation.Order;import org.springframework.stereotype.Component;import org.springframework.web.context.request.RequestContextHolder;import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.HttpServletRequest;import java.util.Arrays;/** * <p> * 应用AOP记录拜访日志 * 应用@Before在切入点开始处切入内容 * 应用@After在切入点结尾处切入内容 * 应用@AfterReturning在切入点return内容之后切入内容(能够用来对解决返回值做一些加工解决) * 应用@Around在切入点前后切入内容,并本人管制何时执行切入点本身的内容 * 应用@AfterThrowing用来解决当切入内容局部抛出异样之后的解决逻辑 * <p> * 注解: * Aspect:AOP * Component:Bean * Slf4j:能够间接应用log输入日志 * Order:多个AOP切同一个办法时的优先级,越小优先级越高越大。 * 在切入点前的操作,按order的值由小到大执行 * 在切入点后的操作,按order的值由大到小执行 * * @Author niujinpeng * @Date 2019/1/4 23:29 */@Aspect@Component@Slf4j@Order(1)public class LogAspect { /** * 线程寄存信息 */ ThreadLocal<Long> startTime = new ThreadLocal<>(); /** * 定义切入点 * 第一个*:标识所有返回类型 * 字母门路:包门路 * 两个点..:以后包以及子包 * 第二个*:所有的类 * 第三个*:所有的办法 * 最初的两个点:所有类型的参数 */ @Pointcut("execution(public * net.codingme.boot.controller..*.*(..))") public void webLog() { } /** * 在切入点开始处切入内容 * * @param joinPoint */ @Before("webLog()") public void doBefore(JoinPoint joinPoint) { // 记录申请工夫 startTime.set(System.currentTimeMillis()); // 获取申请域 ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = requestAttributes.getRequest(); // 记录申请内容 log.info("Aspect-URL: " + request.getRequestURI().toLowerCase()); log.info("Aspect-HTTP_METHOD: " + request.getMethod()); log.info("Aspect-IP: " + request.getRemoteAddr()); log.info("Aspect-REQUEST_METHOD: " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName()); log.info("Aspect-Args: " + Arrays.toString(joinPoint.getArgs())); } /** * 在切入点之后解决内容 */ @After("webLog()") public void doAfter() { } /** * 在切入点return内容之后切入内容(能够用来对解决返回值做一些加工解决) */ @AfterReturning(returning = "ret", pointcut = "webLog()") public void doAfterReturning(Object ret) throws Throwable { log.info("Aspect-Response: " + ret); Long endTime = System.currentTimeMillis(); log.info("Aspect-SpeedTime: " + (endTime - startTime.get()) + "ms"); }}
拜访查看拦截器和 AOP 的日志输入。
09:57:15.408 INFO 2836 --- [nio-8080-exec-1] n.c.boot.config.LogHandlerInterceptor : preHandle申请URL:http://localhost:8080/09:57:15.413 INFO 2836 --- [nio-8080-exec-1] net.codingme.boot.config.LogAspect : Aspect-URL: /09:57:15.413 INFO 2836 --- [nio-8080-exec-1] net.codingme.boot.config.LogAspect : Aspect-HTTP_METHOD: GET09:57:15.413 INFO 2836 --- [nio-8080-exec-1] net.codingme.boot.config.LogAspect : Aspect-IP: 0:0:0:0:0:0:0:109:57:15.414 INFO 2836 --- [nio-8080-exec-1] net.codingme.boot.config.LogAspect : Aspect-REQUEST_METHOD: net.codingme.boot.controller.HelloController.index09:57:15.415 INFO 2836 --- [nio-8080-exec-1] net.codingme.boot.config.LogAspect : Aspect-Args: []09:57:15.424 INFO 2836 --- [nio-8080-exec-1] net.codingme.boot.config.LogAspect : Aspect-Response: Greetings from Spring Boot!SpringBoot是一个spring应用程序09:57:15.425 INFO 2836 --- [nio-8080-exec-1] net.codingme.boot.config.LogAspect : Aspect-SpeedTime: 12ms09:57:15.436 INFO 2836 --- [nio-8080-exec-1] n.c.boot.config.LogHandlerInterceptor : postHandle返回modelAndView之前09:57:15.437 INFO 2836 --- [nio-8080-exec-1] n.c.boot.config.LogHandlerInterceptor : afterCompletion执行完申请办法齐全返回之后
3. Servlet,Filter,Listener
Servlet, Filter, Listener 是 Java web 的核心内容,那么在 Springboot 中如何应用呢?
3.1 编写 Servlet
package net.codingme.boot.servlet;import lombok.extern.slf4j.Slf4j;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.io.PrintWriter;/** * <p> * @WebServlet(urlPatterns = "/myservlet") // 定义拜访门路 * @Author niujinpeng * @Date 2019/1/24 16:25 */@Slf4j@WebServlet(urlPatterns = "/myservlet")public class MyServlet extends HttpServlet { @Override public void init() throws ServletException { log.info("Servlet 开始初始化"); super.init(); } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { log.info("Servlet 开始解决 GET 办法"); PrintWriter writer = resp.getWriter(); writer.println("Hello Servlet"); writer.flush(); writer.close(); } @Override protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req, resp); } @Override public void destroy() { log.info("Servlet 开始销毁"); super.destroy(); }}
3.2 编写 Filter
package net.codingme.boot.filter;import lombok.extern.slf4j.Slf4j;import javax.servlet.*;import javax.servlet.annotation.WebFilter;import java.io.IOException;/** * <p> * * @Author niujinpeng * @Date 2019/1/24 16:35 */@Slf4j@WebFilter(urlPatterns = "/*")public class MyFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException { log.info("拦截器开始拦挡"); filterChain.doFilter(request, response); }}
3.3 编写 Listener
package net.codingme.boot.listener;import lombok.extern.slf4j.Slf4j;import javax.servlet.ServletContextEvent;import javax.servlet.ServletContextListener;import javax.servlet.annotation.WebListener;/** * <p> * * @Author niujinpeng * @Date 2019/1/24 16:45 */@Slf4j@WebListenerpublic class MyListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { log.info("监听器开始初始化"); } @Override public void contextDestroyed(ServletContextEvent sce) { log.info("监听器开始销毁"); }}
3.4 增加到容器
增加到容器有两种形式,第一种应用注解扫描。
import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.boot.web.servlet.ServletComponentScan;import org.springframework.context.annotation.ComponentScan;/** * @ServletComponentScan 扫描Servlet,Filter,Listener 增加到容器 */@SpringBootApplication@ServletComponentScanpublic class BootApplication { public static void main(String[] args) { SpringApplication.run(BootApplication.class, args); }}
或者应用配置类想容器中增加。
/** * <p> * 在这里注册Servlet Filter Listener 或者应用 @ServletComponentScan * * @Author niujinpeng * @Date 2019/1/24 16:30 */@Configurationpublic class WebCoreConfig { @Bean public ServletRegistrationBean myServlet() { return new ServletRegistrationBean<>(new MyServlet()); } @Bean public FilterRegistrationBean myFitler() { return new FilterRegistrationBean<>(new MyFilter()); } @Bean public ServletListenerRegistrationBean myListener() { return new ServletListenerRegistrationBean(new MyListener()); } }
启动能够在控制台看到监听器启动。
11:35:03.744 INFO 8616 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1364 ms 11:35:03.798 INFO 8616 --- [ main] net.codingme.boot.listener.MyListener : 监听器开始初始化 11:35:03.892 INFO 8616 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor' 11:35:04.055 INFO 8616 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
拜访 Servlet 能够看到拦截器和 Servlet 失效。
11:36:55.552 INFO 3760 --- [nio-8080-exec-1] net.codingme.boot.servlet.MyServlet : Servlet 开始初始化 11:36:55.556 INFO 3760 --- [nio-8080-exec-1] net.codingme.boot.filter.MyFilter : 拦截器开始拦挡 11:36:55.556 INFO 3760 --- [nio-8080-exec-1] net.codingme.boot.servlet.MyServlet : Servlet 开始解决 GET 办法
文章代码曾经上传到 GitHub Spring Boot Web开发 - 拦挡解决。
文章代码曾经上传到 GitHub Spring Boot Web开发 - Servlet,Filter,Listener。
最初的话
文章曾经收录在 Github.com/niumoo/JavaNotes ,欢送Star和指教。更有一线大厂面试点,Java程序员须要把握的外围常识等文章,也整顿了很多我的文字,欢送 Star 和欠缺,心愿咱们一起变得优良。
文章有帮忙能够点个「赞」或「分享」,都是反对,我都喜爱!
文章每周继续更新,要实时关注我更新的文章以及分享的干货,能够关注「 未读代码 」公众号或者我的博客。