范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

老生常谈你真的理解过滤器拦截器ControllerAdvice和AOP吗

  面试官:说说过滤器和拦截器的区别? 这个问题面试题库算是比较经典的,这两个我相信很多同学在工作中都有接触过,但如果没有经过系统的整理,还真的不好说出个123来,那老湿机在此这面就把它俩和常用的AOP、ControllerAdvice放一起,带你做一个比较全面的认识。1. 了解4种拦截方法的执行顺序
  先上一个栗子,看四种拦截方法并驾齐驱使用时,谁先谁后:/**  * ============Filter过滤器============  */ @Slf4j @WebFilter(urlPatterns = "/*") public class DemoFilter implements Filter {     @Override     public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {         log.info("Filter 进入");         filterChain.doFilter(request, response);         log.info("Filter 退出");     } } l /**  * ============Interceptor过滤器============  */ public class DemoInterceptor implements HandlerInterceptor {     @Override     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {         log.info("Interceptor preHandle 进入");         return true;     }     @Override     public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {         log.info("Interceptor postHandle 进入");     }     @Override     public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {         log.info("Interceptor afterCompletion 进入");     } }  /**  * ============ControllerAdvice============  */ @ControllerAdvice public class DemoControllerAdvice {      @InitBinder     public void init(WebDataBinder binder) {         log.info("ControllerAdvice init 进入");         binder.setFieldDefaultPrefix("user.");     }     @ExceptionHandler(Exception.class)     public String handleException(Exception e) {         log.info("ControllerAdvice handleException 进入");         return "error";     } }  /**  * ============AOP============  */ @Aspect @Component public class DemoAspect {     @Pointcut("(@target(org.springframework.web.bind.annotation.RestController)) " +             "&& within(cn.demo.api..*) && execution(public * *(..))")     public void pointcut() {     }      @Around(value = "pointcut()")     public Object around(ProceedingJoinPoint pjp) throws Throwable {         log.info("Aop 进入");         Object proceed = pjp.proceed();         log.info("Aop 退出");         return proceed;     } }  /**  * ============控制器入口============  */ @RestController @RequestMapping("/demo") public class DemoController {     @RequestMapping("/test")     public Map test(@ModelAttribute("user")User user) {         log.info("业务:user.name: {}", user.getName());         Map result = new HashMap<>(2);         result.put("code", "200");         int i  = 1;         i = i / 0;         return result;     } } 复制代码
  定义好Demo示例,接着来一发:http://localhost:8080/demo/test?user.name=宫三公子 :结果: Filter 进入 Interceptor preHandle 进入 ControllerAdvice init 进入 Aop 进入 业务:user.name: 宫三公子 ControllerAdvice handleException 进入 Interceptor afterCompletion 进入 Filter 退出 复制代码
  好了,我们似乎已经看到,4种拦截方法的执行顺序是这样子:
  2. 知晓4种拦截方法应用场景
  那么针对这4种拦截方法,它们涉及的应用场景、实现技术、作用力度都各不相同,为了有一个比较清晰的对比,老司机我简单粗暴,直接整理好啦,大伙干:
  3. 掌握4种拦截方法的原理
  如果只晓得执行顺序和应用场景可能过不了面试大大狠辣的毒眼,知其然并知其所以然,我们就自废10根头发,来研究一下它们的原理吧。3.1 过滤器
  虽然一个过滤器在一次请求中只能调用一次,但是根据具体业务需求,可以生成多个不同类型的Filter并依次执行,这就是过滤器的链式调用,可以通过指定Order排序,值越小越靠前(默认根据Filter的名称进行自然排序),新建了4个Filter,Order依次为0-3,演示一波,符合预期:
  接下来我们关注整个链式调用的核心: FilterChain接口,内部定义了doFilter接口方法,tomcat容器提供了ApplicationFilterChain作为FilterChain的具体实现:public final class ApplicationFilterChain implements FilterChain { //  private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0]; private Servlet servlet; public void doFilter(ServletRequest request, ServletResponse response) { 	internalDoFilter(request, response); }  private void internalDoFilter(ServletRequest request, ServletResponse response) { 	if (this.pos < this.n) { 		ApplicationFilterConfig filterConfig = this.filters[this.pos++]; 		Filter filter = filterConfig.getFilter(); 		filter.doFilter(request, response, this);              	} else { 		this.servlet.service(request, response); 	}  } } 复制代码
  内部定义了ApplicationFilterConfig[] filters 过滤器配置列表,每一个ApplicationFilterConfig内部持有一个Filter实例,另一个比较重要的是Servlet,实例化后对应原生HttpServlet或SpringMVC的DispatcherServlet,当拦截器链路执行完成后,会调用Servlet中service方法做后续的Url路由映射、业务处理以及视图响应等流程了(这个后面研究SpringMVC的请求流程来详细再分析), 好了,我们通过Debug可以看到,filters中除了服务默认的一些请求filter,我们自己定义的4个filter也以定义好的顺序排入其中了:
  过滤器整体执行流程Like this:
  3.2 拦截器
  拦截器调用流程比较复杂,我这面根据源码梳理了核心请求流程和简要说明,感兴趣的同学可以继续一探究竟:源码位于:org.springframework.web.servlet.DispatcherServlet#doDispatch方法
  Tip一下:网上有些朋友说拦截器是基于反射、动态代理来实现的,通过司机对源码分析,反射倒是有用到,主要从Spring的IOC容器中获取了拦截器对象,并放在AbstractHandlerMapping的adaptedInterceptors全局对象中,在上图的第二步就匹配满足的拦截器作为当前请求的拦截器列表,没有动态代理就没有影子!。3.3 ControllerAdvice
  其实,ControllerAdvice和拦截器实现有异曲同工之处,要是说用什么技术手段,那应该也只能说是反射吧,在也主要在上一步的doDispatch方法中,它主要是在分布第4步t通过反射对InitBinder的参数的设置和第6步进行统一的异常捕获,重点看看第6步:在processDispatchResult处理结果方法内部,调用processHandlerException方法进行异常相关的处理逻辑,我们可以看到它的主要工作就是遍历handlerExceptionResolvers来进行异常对应的处理,我们自定义的全局异常ExceptionHandlerExceptionResolver实例控制着所有的Controller类,debug源码:
  那什么时候进行设置的以及如何设置的呢,值得我们思考一下,怀着好奇心,我们点开ExceptionHandlerExceptionResolver来看看,原来重点是实现了InitializingBean的afterPropertiesSet方法,在容器启动时候检测带ControllerAdvice注解的类和类中带ExceptionHandler注解的方法,并加入到我们刚才看到的exceptionHandlerCache中:public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExceptionResolver implements ApplicationContextAware, InitializingBean {     private final Map, ExceptionHandlerMethodResolver> exceptionHandlerCache = new ConcurrentHashMap(64);     // 重点实现了afterPropertiesSet     public void afterPropertiesSet() {         this.initExceptionHandlerAdviceCache();     }      private void initExceptionHandlerAdviceCache() {         if (this.getApplicationContext() != null) { 	   // 寻找所有带有ControllerAdvice注解的类             List adviceBeans = ControllerAdviceBean.findAnnotatedBeans(this.getApplicationContext());             Iterator var2 = adviceBeans.iterator();              while(var2.hasNext()) {                 ControllerAdviceBean adviceBean = (ControllerAdviceBean)var2.next();                 Class<?> beanType = adviceBean.getBeanType();             		// 寻找该类下面的方法是否有ExceptionHandler注解,如果有,则收集到mappedMethods列表中                 ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(beanType); 		// 如果不为空,则加入到全局异常拦截缓存中                 if (resolver.hasExceptionMappings()) {                     this.exceptionHandlerAdviceCache.put(adviceBean, resolver);                 }                         }              }     } }  复制代码3.4 AOP
  AOP这块想着网上其实很多同学梳理得很好了,到底要不要整理,但考虑到完整性,也避免同学们再额外找文章跳来跳去,就一并简化整理了,接着继续: 前面说到,AOP是有两种实现方式,相信大家都耳熟能详:JDK动态代理和CGLib,带大家复习一下,先来一个JDK动态代理的栗子://1.  动态代理是基于接口访问的,先定义用户服务接口 public interface UserService {     User getUser(String name); }  // 2. 具体用户服务实现类,也即被代理对象 public class UserServiceImpl implements UserService{     @Override     public User getUser(String name) {         User user = new User();         user.setName("宫三公子");         System.out.println("用户名:" + user.getName());         return user;     } }  // 3. 代理工具,用于服务增强 public class JDKProxyr implements InvocationHandler {     private Object target;     public JDKProxy(Object target) {         this.target = target;     }      @Override     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {         System.out.println("before-------JDK Proxy");         Object invoke = method.invoke(target, args);//通过反射执行,目标类的方法         System.out.println("after-------JDK Proxy");         return invoke;     } }  // 4. 测试方法 public class JDKProxyTest {     public static void main(String[] args) {         UserServiceImpl userService = new UserServiceImpl();         JDKProxy handler = new JDKProxy(userService);         UserService proxyInstance = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(),                 userService.getClass()                         .getInterfaces(), handler);         User user = proxyInstance.getUser("宫三公子");     } }  复制代码
  测试结果:before-------JDK Proxy 用户名:宫三公子 after-------JDK Proxy 复制代码
  CGLib的代理方式是为我们需要被代理的具体类生成一个子类,即将需被代理的方法进行Override重写:// CGLib代理工具,用于生成业务增强 public class CGLibProxy implements MethodInterceptor {    public Object intercept(Object arg0, Method method, Object[] objects, MethodProxy proxy) throws Throwable {        System.out.println("before-------CGLib Proxy");        Object result = proxy.invokeSuper(arg0, objects);        System.out.println("after-------CGLib Proxy");        return result;    } }  // 测试方法 public class CGLibProxyTest {    public static void main(String[] args) {        CGLibProxy proxy = new CGLibProxy();        Enhancer enhancer = new Enhancer();        enhancer.setSuperclass(UserServiceImpl.class);        //回调方法的参数为代理类对象CglibProxy,最后增强目标类调用的是代理类对象CglibProxy中的intercept方法        enhancer.setCallback(proxy);        UserServiceImpl userService = (UserServiceImpl) enhancer.create();        userService.getUser("宫三公子");        // 打印增强效果        System.out.println("打印userService的增强结果:");        ReflectUtils.printMethods(userService.getClass());    } } 复制代码
  打印结果,我们可以看到被代理的方法已经执行了增强逻辑:before-------CGLib Proxy 用户名:宫三公子 after-------CGLib Proxy  打印userService的增强结果:  public final getUser(java.lang.String);   public setCallback(int,net.sf.cglib.proxy.Callback);   public static CGLIB$findMethodProxy(net.sf.cglib.core.Signature);   public static CGLIB$SET_THREAD_CALLBACKS([Lnet.sf.cglib.proxy.Callback;);   public static CGLIB$SET_STATIC_CALLBACKS([Lnet.sf.cglib.proxy.Callback;);   public getCallback(int);   public getCallbacks();   public setCallbacks([Lnet.sf.cglib.proxy.Callback;);   private static final CGLIB$BIND_CALLBACKS(java.lang.Object);   static CGLIB$STATICHOOK1();   final CGLIB$hashCode$3();   final CGLIB$clone$4();   final CGLIB$toString$2();   final CGLIB$equals$1(java.lang.Object);   final CGLIB$getUser$0(java.lang.String);   public final equals(java.lang.Object);   public final toString();   public final hashCode();   protected final clone();   public newInstance([Lnet.sf.cglib.proxy.Callback;);   public newInstance([Ljava.lang.Class;,[Ljava.lang.Object;,[Lnet.sf.cglib.proxy.Callback;);   public newInstance(net.sf.cglib.proxy.Callback); 复制代码
  好了,我们看完了基于Jdk动态代理和CGLib代理的两种Demo,那我们继续看看Spring是怎么玩的吧。 在Spring的启动过程中,有很多的扩展点,比较让我们熟知的BeanPostPorcessor,主要在Bean进行initializeBean初始化后,调用org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization,内部对所有的BeanPostPorcessor对象进行遍历,调用postProcessAfterInitialization进行处理,而Aop就是基于该扩展点实现的:
  沿着这条链路Debug调试,最终可以定位到具体决定调用JDK动态代理还是使用CGLib,我这代理的是DemoController,那生成的就是CGLig代理啦:
  OK,关于过滤器、拦截器、ControllerAdvice以及AOP,本文从执行顺序、应用场景、实现技术以及原理等进行了总结,看完了,朋友们如果有收获就是对我最大得肯定。
  来源:https://juejin.cn/post/7212175872091029559

网约车市场彻底回暖?7。03亿的订单量,根本不足以满足行业需求前几天交通运输部给出了8月份的网约车平台交互数据,总的来看,整个8月份的情况是要比7月份还要好一些。7。03亿的订单量,相较上个月增长了1。1,看起来这算是在整个下行的网约车环境下韩国女首富李富真抵押325。3万股三星电子股票,获1000亿韩元贷款韩联社9月14日消息,据韩国金融监督院电子公告系统14日消息,新罗酒店社长李富真上月19日抵押325。3万股三星电子股票,从韩国投资证券公司获得1000亿韩元(约合人民币5亿元)贷段子玩笑谐音引发的涨停板A股这些无厘头炒作也是个流派?9月14日上午交易时段,ST洲际小幅走低。截至上午收盘,收跌2。37,已经抹平了前一日的涨幅。在最新一期脱口秀大会第五季上,脱口秀演员House讲述了投资ST洲际的惨痛经历,精准演中兴通讯已经在车用操作系统智能网联方面进行布局9月13日,中兴通讯在互动平台表示,目前,公司已在操作系统智能网联等方面进行布局,对于汽车芯片公司持续保持关注。面向汽车的智能化网联化,公司始终定位于数字化汽车基础能力提供商。作为工信部十年来消费品工业规上企业数量增加23。7封面新闻记者滕晗9月14日,工业和信息化部消费品工业司司长何亚琼透露,十年来,消费品工业规上企业数量增加23。7,达到17。2万家,占全国工业38。5。工业和信息化部消费品工业司司58岁自驾游阿姨出逃两年归来决定离婚还彼此自由大象新闻记者赵丹文受访人供图一个完成母职的女人可以怎样活?河南郑州58岁的苏敏走出不一样的路。两年前的秋日,困于没有温度的婚姻生活多年,退休后的苏敏用一趟归期未定的自驾游寻找救赎。当生活给了你一个美丽的背影,不要追逐,不要超越,更不要面对引子昨天去审车,路遇一个背影很美丽的女人,我们后来都称她是背影杀手。当她穿着高跟鞋,扭动翘臀小蛮腰,肩披长发,风情万种,楚楚动人,衣着性感,有节奏地走过的瞬间,吸引了很多男人的目光国家卫健委昨日新增本土确诊病例126例9月14日024时,31个省(自治区直辖市)和新疆生产建设兵团报告新增确诊病例167例。其中境外输入病例41例(上海8例,广东8例,云南7例,福建6例,四川4例,辽宁3例,北京2例蒋英丈夫是钱学森,学生是歌唱家李双江,她如何活成一代传奇?即使是穿着打扮与众人无异,千万人中一回头,你也会一眼认出她,因为她的气质独一无二。这是钱学森对妻子蒋英的赞美。蒋英的确非常优秀,她不但相貌出类拔萃,而且才华出众,在音乐方面有着很高理想L8奇骏ePOWER等领衔9月申报新车汇总太平洋汽车网新车频道当众多专家都在预测9月销量会有大幅增长,可实现金九银十的良好态势,不妨先来看看9月公示的新车申报。其中包括理想L8(询底价查参配)飞凡F7新款起亚K3等重磅新车詹姆斯对萨沃尔的处罚不对,对性别种族歧视零容忍太阳老板罗伯特萨沃尔因为种族歧视性别歧视的言行被NBA禁赛一年罚款1000万。勒布朗詹姆斯发推表示处罚不够严厉看了几遍萨沃尔的报道,我必须诚实地说,我们联盟绝对做错了,我不需要解释
日本人均GDP再创新低,日本经济是否已经没有前景?日前,根据日本内阁府发布的国民经济计划年度推算显示,以衡量一个国家富裕程度的人均GDP来看,日本2021年的人均GDP为39,803美元,虽然仍然属于发达国家的行列,但却比往年更低感受数字技术带来澎湃动能来自首届全球数字贸易博览会的一线观察感受数字技术带来澎湃动能来自首届全球数字贸易博览会的一线观察机器人搭载着特斯拉自动驾驶技术和神经网络技术,未来可以服务千家万户炫酷的3D数字技术带你穿越到宋朝街景中拍张个人写真给挖张家界莓茶入选全国商标品牌建设优秀案例近日,国家知识产权局办公室公布商标品牌建设优秀案例名单,由张家界市农业科学技术研究所选送的张家界遇见莓好张家界莓茶品牌建设故事入选全国商标品牌建设区域建设类优秀案例,全国优秀案例入意式美学与现代声学技术的完美结晶AlbedoAptica阿特卡落地箱不用我介绍,相信很多看到图片的读者,立即就猜到这是一款来自意大利的音箱了。没错,这是意大利品牌Albedo推出的Aptica阿特卡落地箱。这个品牌近年在代理商的推广下频频亮相各大音广州车展合创王勇初创阶段力求尽快从0到1发力MPV市场前装改装出品搜狐汽车汽车咖啡馆编者按12月30日,第二十届广州国际汽车展览会正式拉开帷幕。作为本年度唯一举办的A级车展,2022广州车展吸引到国内外主流车企的悉数参与,一大批重磅车型与超豪亚信科技用数智技术赋能双碳未来不久前的12月7日,2022年世界网络转型大奖(NetworkTransformationAwards2022)在英国伦敦揭晓,凭借网络智能化产品体系的5G网络节能产品与解决方案,Aruba谢建国加码技术创新,赋能企业网络现代化改造环球网科技报道记者郑湘琪从WLAN到一体化有线无线网络,再到边缘到云NaaS(网络即服务),Aruba不断聚焦消费者的需求,在技术产品上创新。在我们产品策略和市场策略的引导下,我们冬游贵州丨这个冬天,到毕节织金游古城探洞穴吃烙锅春赏花夏避暑秋风情冬康养,多彩贵州四季都有醉人的魅力。眼下,冬游贵州正当时!冬游贵州,你可以登山赏雪亲近大自然村寨寻冬,感受民族节庆欢乐温暖过冬,尽享泉世界疗养南方滑雪,感受运动激湿热不除,全身受苦!只一个中成药,消积导滞,清利湿热不再受苦大家好,我是李医生!我们都知道湿热难除,中医总打比方说,湿热呢,就像面里边掺了油,很难分离。最近好多人来咨询湿热怎么祛除,今天呢,我跟大家分享一个祛除湿热,效果很好的中成药,一起来今年冬天火了这件飞行装,时髦帅气又保暖,搭配裙裤都好看在冬天应该怎么穿,才能避开臃肿感,又非常时髦耐看呢?很多人每年一到冬天,就非常的焦头烂额,因为面对着臃肿感十足的羽绒服,显得老土又难以驾驭的大衣,我们就会有更多挑选单品的难题。其实这才是12月最美旅行地!从冬天美到春天,第一个就心动了晨起开门雪满山,雪晴云淡日光寒。图虫天上海时间过得真快啊。从仲春到盛夏,从金秋到隆冬,原以为2020年还很远,但转眼间,2019年只剩最后一个月。那些2018年说好,2019年要去