当前位置: 首页 > 科技观察

SpringBoot可以将接口的方法定义为私有的吗?

时间:2023-03-21 19:48:17 科技观察

我们在Controller中定义接口的时候,通常是这样的:@GetMapping("/01")publicStringhello(Mapmap){map.put("name","javaboy");return"forward:/index";}估计很少有人会把接口方法定义为private吧?那我们不禁要问,如果非要定义成私有方法,能行吗?带着这个疑问,我们开始今天的源码解读~我们在使用SpringBoot的时候,经常会看到HandlerMethod类型。比如我们定义一个拦截器的时候,如果拦截目标是一个方法,preHandle的第三个参数就是HandlerMethod(下面这个案例摘自松哥之前的视频:教你SpringBoot自定义注解):@ComponentpublicclassIdempotentInterceptorimplementsHandlerInterceptor{@AutowiredTokenServicetokenService;@OverridepublicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseObjecthandlerresponse,)throwsException{if(!(handlerinstanceofHandlerMethod)){返回真;}//省略...返回真;}//...}我们在阅读SpringMVC源码的时候,也会反复看到这个HandlerMethod,那么它到底是什么意思呢?今天想和朋友们讨论一下这个问题,把这个问题搞清楚了,前面的问题大家也就明白了。一、概述可以看到,HandlerMethod体系下的类并不多:HandlerMethod封装了Handler和具体处理请求的Method。InvocableHandlerMethod在HandlerMethod的基础上增加了调用的功能。ServletInvocableHandlerMethod在InvocableHandlerMethod的基础上增加了对@ResponseStatus注解的支持,并增加了对返回值的处理。ConcurrentResultHandlerMethod在ServletInvocableHandlerMethod的基础上增加了对异步结果的处理。基本上就是这四个组成部分。接下来,宋兄就来详细说说这四个组成部分。2.HandlerMethod2.1bridgedMethod在正式介绍HandlerMethod之前,我想先跟大家说说bridgedMethod,因为在HandlerMethod中会涉及到,有的朋友可能没有听说过bridgedMethod,所以宋哥在这里做一个简单的介绍。首先测试一下大家,下面的代码会不会编译报错?publicinterfaceAnimal{voideat(Tt);}publicclassCatimplementsAnimal{@Overridepublicvoideat(Strings){System.out.println("cateat"+s);}}publicclassDemo01{publicstaticvoidmain(String[]args){Animalanimal=newCat();animal.eat(newObject());}}首先我们定义一个Animal接口,它定义了一个eat方法,同时声明了一个泛型。Cat实现了Animal接口并将通用类型定义为String。当我调用它时,声明的类型是Animal,而实际类型是Cat。这时会调用eat方法,传入Object对象,猜猜会发生什么?如果调用eat方法的时候传入的是String类型,那肯定没问题,但是如果不是String呢?宋大哥先说结论:编译没问题,运行就报错。如果你们在电脑上写上面的代码,就会发现这样的问题。开发工具提示的参数类型其实是Object。以松哥的IDEA为例,如下:可以看到,当我写代码的时候,开发工具会提示参数类型是Object,有的朋友会觉得奇怪,明明是泛型,它怎么会变成一个对象呢?我们可以通过反射看到Cat类中有哪些方法,代码如下:for(Methodmethod:methods){Stringname=method.getName();类[]parameterTypes=method.getParameterTypes();System.out.println(name+"("+Arrays.toString(parameterTypes)+")");}}}运行结果如下:可以看出在实际运行过程中,其实有两个eat方法,一个参数是String类型,一个参数是Object类型。这是怎么回事?这个参数类型为Object的方法实际上是由Java虚拟机在运行时创建的。这个方法就是我们所说的桥接方法。本节小标题叫bridgedMethod,就是HandlerMethod源码中的变量名。bridge最后多了一个d,意思是变成桥接方法,也就是原来的方法,参数是String。你可以在下面的源码中看到bridgedMethod,你知道这意味着具有相同参数类型的原始方法。2.2HandlerMethod简介接下来我们简单了解一下HandlerMethod。我们之前分析HandlerMapping的时候(见:),涉及到HandlerMethod。创建HandlerMethod的入口方法是createWithResolvedBean,所以这里我们从这个方法开始:publicHandlerMethodcreateWithResolvedBean(){Objecthandler=this.bean;if(this.beaninstanceofString){StringbeanName=(String)this.bean;handler=this.beanFactory.getBean(beanName);}returnnewHandlerMethod(this,handler);}这个方法主要是确认handler类型,如果handler是String类型,根据beanName重新从Spring容器中寻找handler对象,然后构建HandlerMethod:privateHandlerMethod(HandlerMethodhandlerMethod,Objecthandler){this.bean=handler;this.beanFactory=handlerMethod.beanFactory;这个.beanType=handlerMethod.beanType;this.method=handlerMethod.method;this.bridgedMethod=handlerMethod.bridgedMethod;this.parameters=handlerMethod.parameters;this.responseStatus=handlerMethod.responseStatus;this.responseStatusReason=handlerMethod.responseStatusReason;this.resolvedFromHandlerMethod=handlerMethod;this.description=handlerMmethod.description;}这里的参数比较简单,没什么好说的,只有两个地方值得介绍:parameters和responseStatusparametersparameters其实就是方法参数,对应的type就是MethodParameter。这个类的源码我这里就不贴出来了,给大家说说封装的内容包括:参数序号(parameterIndex)、参数嵌套层级(nestingLevel)、参数类型(parameterType)、参数注解(parameterAnnotations))、参数名查找器(parameterNameDiscoverer)、参数名(parameterName)等。HandlerMethod还提供了两个内部类来封装MethodParameter,即:HandlerMethodParameter:本次封装的方法调用的参数。ReturnValueMethodParameter:这个继承自HandlerMethodParameter,封装了方法的返回值,返回值中的parameterIndex为-1。请注意,两者中的方法都是bridgedMethod。responseStatus主要是处理方法的@ResponseStatus注解。该注解用于描述方法的响应状态码。用法如下:@GetMapping("/04")@ResponseBody@ResponseStatus(code=HttpStatus.OK)publicvoidhello4(@SessionAttribute("name"){System.out.println("name="+name);}从这段代码可以看出,@ResponseStatus注解其实很不灵活,不实用,当我们定义一个使用接口的时候,很难预料到该接口的响应状态码是200。在handlerMethod中,调用其构造方法时,会调用evaluateResponseStatus方法处理@ResponseStatus注解,如下:privatevoidevaluateResponseStatus(){if(annotation==null){annotation=AnnotatedElementUtils.findMergedAnnotation(getBeanType(),ResponseStatus.class);}if(annotation!=null){this.responseStatus=annotation.code();this.responseStatusReason=annotation.reason();}}可以看到,这段代码也比较简单,找到注解,解析里面的值,赋值给对应的变量。现在小伙伴们应该明白HandlerMethod是什么意思了吧。3、InvocableHandlerMethod从名字就可以知道,InvocableHandlerMethod可以调用HandlerMethod中的具体方法,也就是bridgedMethod。我们先看看InvocableHandlerMethod中声明的属性:privateHandlerMethodArgumentResolverCompositeresolvers=newHandlerMethodArgumentResolverComposite();privateParameterNameDiscovererparameterNameDiscoverer=newDefaultParameterNameDiscoverer();,宋大哥在之前的文章中已经和大家聊过这个问题了。parameterNameDiscoverer:这个用来获取参数名,在MethodParameter中会用到。dataBinderFactory:这个用来创建WebDataBinder,会在参数解析器中使用。具体的请求调用方法是invokeForRequest,我们来看看:@NullablepublicObjectinvokeForRequest(NativeWebRequestrequest,@NullableModelAndViewContainermavContainer,Object...providedArgs)throwsException{Object[]args=getMethodArgumentValues(request,mavContainer,providedArgs);返回doInvoke(args);}@NullableprotectedObjectdoInvoke(Object...args)throwsException{Methodmethod=getBridgedMethod();ReflectionUtils.makeAccessible(方法);尝试{if(KotlinDetector.isSuspendingFunction(method)){returnCoroutinesUtilsinvokeSuspendingFunction(method,getBean(),args);}returnmethod.invoke(getBean(),args);}catch(InvocationTargetExceptionex){//省略...}}首先调用getMethodArgumentValues方法依次获取所有参数的值,这些参数值组成一个数组,然后调用doInvoke方法执行。在doInvoke方法中,先获取bridgedMethod并设置为可见(意思是我们在Controller中定义的接口方法也可以是私有的),然后直接通过Reflection调用即可。不看SpringMVC源码的时候就知道接口方法最后肯定是通过反射来调用的。现在,经过层层分析,我们终于在这里找到了反射调用的代码。最后松哥又来说下责任参数据解析的getMethodArgumentValues方法:protectedObject[]getMethodArgumentValues(NativeWebRequestrequest,@NullableModelAndViewContainermavContainer,Object...providedArgs)throwsException{MethodParameter[]parameters=getMethodParameters();如果(ObjectUtils.isEmpty(参数)){返回EMPTY_ARGS;}Object[]args=newObject[parameters.length];for(inti=0;i

最新推荐
猜你喜欢