做微商去哪些社交网站,建设网站费,镇江网站,自媒体账号博主看到新服务是封装的自定义异常#xff0c;准备入手剖析一下#xff0c;自定义的异常是如何进行抓住我们请求的方法的异常#xff0c;并进行封装返回到。废话不多说#xff0c;先看看如何才能实现封装异常#xff0c;先来一个示例#xff1a; 1 ControllerAdvice2 pub…博主看到新服务是封装的自定义异常准备入手剖析一下自定义的异常是如何进行抓住我们请求的方法的异常并进行封装返回到。废话不多说先看看如何才能实现封装异常先来一个示例 1 ControllerAdvice2 public class TstExceptionHandle{3 4 ExceptionHandler(Exception.class)5 public void myExceptionHandle(HttpServletResponse response){6 response.setStatus(403);7 System.out.println(做封装处理);8 }9
10 } 博主只做了简单的配置示例主要的是进行源码剖析Springboot是如何获取自定义异常并进行返回的。来吧 第一步肯定是在Springboot启动的过程中进行的异常处理初始化于是就找到了handlerExceptionResolver类在创建该类的时候会进行添加我们自定义异常。 1 public HandlerExceptionResolver handlerExceptionResolver(2 Qualifier(mvcContentNegotiationManager) ContentNegotiationManager contentNegotiationManager) {3 ListHandlerExceptionResolver exceptionResolvers new ArrayList();4 //不用管这个方法这个方法主要进行的是调用实现了WebMvcConfigurer接口bean的configureHandlerExceptionResolvers方法系统的都是空方法5 configureHandlerExceptionResolvers(exceptionResolvers);6 if (exceptionResolvers.isEmpty()) {7 //我们的在这里才添加我们看看这个方法8 addDefaultHandlerExceptionResolvers(exceptionResolvers, contentNegotiationManager);9 }
10 extendHandlerExceptionResolvers(exceptionResolvers);
11 HandlerExceptionResolverComposite composite new HandlerExceptionResolverComposite();
12 composite.setOrder(0);
13 composite.setExceptionResolvers(exceptionResolvers);
14 return composite;
15 } 1 protected final void addDefaultHandlerExceptionResolvers(ListHandlerExceptionResolver exceptionResolvers,2 ContentNegotiationManager mvcContentNegotiationManager) {3 4 ExceptionHandlerExceptionResolver exceptionHandlerResolver createExceptionHandlerExceptionResolver();5 exceptionHandlerResolver.setContentNegotiationManager(mvcContentNegotiationManager);6 exceptionHandlerResolver.setMessageConverters(getMessageConverters());7 exceptionHandlerResolver.setCustomArgumentResolvers(getArgumentResolvers());8 exceptionHandlerResolver.setCustomReturnValueHandlers(getReturnValueHandlers());9 if (jackson2Present) {
10 exceptionHandlerResolver.setResponseBodyAdvice(
11 Collections.singletonList(new JsonViewResponseBodyAdvice()));
12 }
13 if (this.applicationContext ! null) {
14 exceptionHandlerResolver.setApplicationContext(this.applicationContext);
15 }
16 //上面的 都是设置的属性跟我们没啥大关系主要在这里进行的添加自定义异常处理
17 exceptionHandlerResolver.afterPropertiesSet();
18 exceptionResolvers.add(exceptionHandlerResolver);
19
20 ResponseStatusExceptionResolver responseStatusResolver new ResponseStatusExceptionResolver();
21 responseStatusResolver.setMessageSource(this.applicationContext);
22 exceptionResolvers.add(responseStatusResolver);
23
24 exceptionResolvers.add(new DefaultHandlerExceptionResolver());
25 } 最主要的初始化过程在这里从这些代码中就可以看到为什么我们自定义异常需要进行使用ControllerAdvice并且方法使用ExceptionHandler(Exception.class)注解了 1 Override2 public void afterPropertiesSet() {3 // Do this first, it may add ResponseBodyAdvice beans4 //走这里初始化添加5 initExceptionHandlerAdviceCache();6 7 if (this.argumentResolvers null) {8 ListHandlerMethodArgumentResolver resolvers getDefaultArgumentResolvers();9 this.argumentResolvers new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
10 }
11 if (this.returnValueHandlers null) {
12 ListHandlerMethodReturnValueHandler handlers getDefaultReturnValueHandlers();
13 this.returnValueHandlers new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
14 }
15 }
16
17
18 org/springframework/web/servlet/mvc/method/annotation/ExceptionHandlerExceptionResolver.java
19 private void initExceptionHandlerAdviceCache() {
20 if (getApplicationContext() null) {
21 return;
22 }
23 //看到这里基本就知道啥意思了找出带有ControllerAdvice的注解bean
24 ListControllerAdviceBean adviceBeans ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
25 for (ControllerAdviceBean adviceBean : adviceBeans) {
26 Class? beanType adviceBean.getBeanType();
27 if (beanType null) {
28 throw new IllegalStateException(Unresolvable type for ControllerAdviceBean: adviceBean);
29 }
30 //找出当前bean的异常处理方法
31 ExceptionHandlerMethodResolver resolver new ExceptionHandlerMethodResolver(beanType);
32 if (resolver.hasExceptionMappings()) {
33 this.exceptionHandlerAdviceCache.put(adviceBean, resolver);
34 }
35 if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
36 this.responseBodyAdvice.add(adviceBean);
37 }
38 }
39
40 if (logger.isDebugEnabled()) {
41 int handlerSize this.exceptionHandlerAdviceCache.size();
42 int adviceSize this.responseBodyAdvice.size();
43 if (handlerSize 0 adviceSize 0) {
44 logger.debug(ControllerAdvice beans: none);
45 }
46 else {
47 logger.debug(ControllerAdvice beans:
48 handlerSize ExceptionHandler, adviceSize ResponseBodyAdvice);
49 }
50 }
51 } 找到类后是如何找到方法的呢主要看如何创建ExceptionHandlerMethodResolver的过程。 1 public ExceptionHandlerMethodResolver(Class? handlerType) {2 //EXCEPTION_HANDLER_METHODS的定义3 //public static final MethodFilter EXCEPTION_HANDLER_METHODS method -4 // AnnotatedElementUtils.hasAnnotation(method, ExceptionHandler.class);5 //所以他会寻找带有ExceptionHandler注解的方法6 for (Method method : MethodIntrospector.selectMethods(handlerType, EXCEPTION_HANDLER_METHODS)) {7 //寻找方法注解上配置的捕获的异常类并添加如果有两个方法都对一个异常进行自定义处理了怎么办呢。8 for (Class? extends Throwable exceptionType : detectExceptionMappings(method)) {9 //他会出异常的。不过前提是同一个类里不同类对同一个异常进行自定义的话谁在前面就有谁来处理
10 addExceptionMapping(exceptionType, method);
11 }
12 }
13 } 添加自定义异常的时候抛异常是在这里 1 private void addExceptionMapping(Class? extends Throwable exceptionType, Method method) {
2 Method oldMethod this.mappedMethods.put(exceptionType, method);
3 //在这里已经显示出来了博主就不试了
4 if (oldMethod ! null !oldMethod.equals(method)) {
5 throw new IllegalStateException(Ambiguous ExceptionHandler method mapped for [
6 exceptionType ]: { oldMethod , method });
7 }
8 } 好了。所有异常添加完毕了我们来测试一下异常来的时候Springboot是如何选择自定义异常并返回的我们上面所有的操作都是在创建HandlerExceptionResolver时进行的为什么要添加到HandlerExceptionResolver这里呢看一下代码 1 //第一次请求进来时会先查找是否有自定义异常如果有的话添加没有记录日志就完了2 private void initHandlerExceptionResolvers(ApplicationContext context) {3 this.handlerExceptionResolvers null;4 5 if (this.detectAllHandlerExceptionResolvers) {6 // Find all HandlerExceptionResolvers in the ApplicationContext, including ancestor contexts.7 //这里会在beanfactroy中查找到HandlerExceptionResolver类刚才初始化的时候我们所有的自定义异常都在里面 8 MapString, HandlerExceptionResolver matchingBeans BeanFactoryUtils9 .beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true, false);
10 if (!matchingBeans.isEmpty()) {
11 this.handlerExceptionResolvers new ArrayList(matchingBeans.values());
12 // We keep HandlerExceptionResolvers in sorted order.
13 AnnotationAwareOrderComparator.sort(this.handlerExceptionResolvers);
14 }
15 }
16 else {
17 try {
18 HandlerExceptionResolver her
19 context.getBean(HANDLER_EXCEPTION_RESOLVER_BEAN_NAME, HandlerExceptionResolver.class);
20 this.handlerExceptionResolvers Collections.singletonList(her);
21 }
22 catch (NoSuchBeanDefinitionException ex) {
23 // Ignore, no HandlerExceptionResolver is fine too.
24 }
25 }
26
27 // Ensure we have at least some HandlerExceptionResolvers, by registering
28 // default HandlerExceptionResolvers if no other resolvers are found.
29 if (this.handlerExceptionResolvers null) {
30 this.handlerExceptionResolvers getDefaultStrategies(context, HandlerExceptionResolver.class);
31 if (logger.isTraceEnabled()) {
32 logger.trace(No HandlerExceptionResolvers declared in servlet getServletName()
33 : using default strategies from DispatcherServlet.properties);
34 }
35 }
36 } 走完初始化经过过滤器拦截器终于到了我们的请求方法我们的方法还报错了所以会走到异常中我们DispatcherServlet会进行抓住异常然后回调用我们的processDispatchResult方法大家可以自己看一下org/springframework/web/servlet/DispatcherServlet.java的源码然后我们来分析一下这个方法都干啥了吧 1 private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,2 Nullable HandlerExecutionChain mappedHandler, Nullable ModelAndView mv,3 Nullable Exception exception) throws Exception {4 5 boolean errorView false;6 7 if (exception ! null) {8 if (exception instanceof ModelAndViewDefiningException) {9 logger.debug(ModelAndViewDefiningException encountered, exception);
10 mv ((ModelAndViewDefiningException) exception).getModelAndView();
11 }
12 else {
13 Object handler (mappedHandler ! null ? mappedHandler.getHandler() : null);
14 //如果请求方法有异常则进行处理并返回ModelAndView
15 mv processHandlerException(request, response, handler, exception);
16 errorView (mv ! null);
17 }
18 }
19 .........
20 } 那Springboot是如何选择哪一个是符合条件的自定义异常处理呢如果我们定义了两个处理类都对同一个异常进行捕获并返回不一样的信息咋办呢看源码吧 1 //这里会选择符合条件的自定义异常2 protected ServletInvocableHandlerMethod getExceptionHandlerMethod(3 Nullable HandlerMethod handlerMethod, Exception exception) {4 5 Class? handlerType null;6 7 if (handlerMethod ! null) {8 // Local exception handler methods on the controller class itself.9 // To be invoked through the proxy, even in case of an interface-based proxy.
10 handlerType handlerMethod.getBeanType();
11 ExceptionHandlerMethodResolver resolver this.exceptionHandlerCache.get(handlerType);
12 if (resolver null) {
13 resolver new ExceptionHandlerMethodResolver(handlerType);
14 this.exceptionHandlerCache.put(handlerType, resolver);
15 }
16 Method method resolver.resolveMethod(exception);
17 if (method ! null) {
18 return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method);
19 }
20 // For advice applicability check below (involving base packages, assignable types
21 // and annotation presence), use target class instead of interface-based proxy.
22 if (Proxy.isProxyClass(handlerType)) {
23 handlerType AopUtils.getTargetClass(handlerMethod.getBean());
24 }
25 }
26 //exceptionHandlerAdviceCache这个map是我们添加 的自定义异常
27 for (Map.EntryControllerAdviceBean, ExceptionHandlerMethodResolver entry : this.exceptionHandlerAdviceCache.entrySet()) {
28 ControllerAdviceBean advice entry.getKey();
29 //这个判断条件是查看是否有符合条件的自定义异常如果有两个的话
30 if (advice.isApplicableToBeanType(handlerType)) {
31 ExceptionHandlerMethodResolver resolver entry.getValue();
32 Method method resolver.resolveMethod(exception);
33 if (method ! null) {
34 return new ServletInvocableHandlerMethod(advice.resolveBean(), method);
35 }
36 }
37 }
38
39 return null;
40 } 逻辑基本是上面的但是真正处理是否符合是在这里的一个方法中 1 public boolean isApplicableToBeanType(Nullable Class? beanType) {2 return this.beanTypePredicate.test(beanType);3 }4 public boolean test(Class? controllerType) {5 ///默认不配的其他属性的时候是返回true的就是对所有包下的异常都适用6 if (!hasSelectors()) {7 return true;8 }9 else if (controllerType ! null) {
10 //我们的ControllerAdvice注解是有basePackages属性的只有匹配成功才会返回否则就算自定义异常想要捕获不在捕获包范围下不管该异常
11 for (String basePackage : this.basePackages) {
12 if (controllerType.getName().startsWith(basePackage)) {
13 return true;
14 }
15 }
16 for (Class? clazz : this.assignableTypes) {
17 if (ClassUtils.isAssignable(clazz, controllerType)) {
18 return true;
19 }
20 }
21 for (Class? extends Annotation annotationClass : this.annotations) {
22 if (AnnotationUtils.findAnnotation(controllerType, annotationClass) ! null) {
23 return true;
24 }
25 }
26 }
27 return false;
28 } 到这里基本如何写自定义异常、以及为什么这么写、底层做了哪些判断都已经讲解完了自定义异常在工作中还是非常常用的一种手段因为我们不可能暴露出我们内部的错误信息直接返回给用户不仅用户体验不好并且安全性也极其差。