偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

Spring Boot 定義接口的方法是否可以聲明為 private?

開(kāi)發(fā) 前端
調(diào)用 setResponseStatus 方法處理@ResponseStatus? 注解,具體的處理邏輯是這樣:如果沒(méi)有添加 @ResponseStatus 注解,則什么都不做;如果添加了該注解,并且 reason 屬性不為空,則直接輸出錯(cuò)誤,否則設(shè)置響應(yīng)狀態(tài)碼。這里需要注意一點(diǎn),如果響應(yīng)狀態(tài)碼是 200,就不要設(shè)置 reason,否則會(huì)按照 error 處理。

我們?cè)?Controller 中定義接口的時(shí)候,一般都是像下面這樣:

@GetMapping("/01")
public String hello(Map<String,Object> map){
map.put("name", "javaboy");
return "forward:/index";
}

估計(jì)很少有人會(huì)把接口方法定義成 private 的吧?那我們不禁要問(wèn),如果非要定義成 private 的方法,那能運(yùn)行起來(lái)嗎?

帶著這個(gè)疑問(wèn),我們開(kāi)始今天的源碼解讀~

在我們使用 Spring Boot 的時(shí)候,經(jīng)常會(huì)看到 HandlerMethod 這個(gè)類(lèi)型,例如我們?cè)诙x攔截器的時(shí)候,如果攔截目標(biāo)是一個(gè)方法,則 preHandle 的第三個(gè)參數(shù)就是 HandlerMethod(以下案例選自松哥之前的視頻:手把手教你 Spring Boot 自定義注解):

@Component
public class IdempotentInterceptor implements HandlerInterceptor {
@Autowired
TokenService tokenService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (!(handler instanceof HandlerMethod)) {
return true;
}
//省略...
return true;
}
//...
}

我們?cè)陂喿x SpringMVC 源碼的時(shí)候,也會(huì)反復(fù)看到這個(gè) HandlerMethod,那么它到底是什么意思?今天我想和小伙伴們捋一捋這個(gè)問(wèn)題,把這個(gè)問(wèn)題搞清楚了,前面的問(wèn)題大家也就懂了。

1.概覽

圖片

可以看到,HandlerMethod 體系下的類(lèi)并不多:

HandlerMethod

封裝 Handler 和具體處理請(qǐng)求的 Method。

InvocableHandlerMethod

在 HandlerMethod 的基礎(chǔ)上增加了調(diào)用的功能。

ServletInvocableHandlerMethod

在 InvocableHandlerMethod 的基礎(chǔ)上增了對(duì) @ResponseStatus 注解的支持、增加了對(duì)返回值的處理。

ConcurrentResultHandlerMethod

在 ServletInvocableHandlerMethod 的基礎(chǔ)上,增加了對(duì)異步結(jié)果的處理。

基本上就是這四個(gè),接下來(lái)松哥就來(lái)詳細(xì)說(shuō)一說(shuō)這四個(gè)組件。

2.HandlerMethod

2.1 bridgedMethod

在正式開(kāi)始介紹 HandlerMethod 之前,想先和大家聊聊 bridgedMethod,因?yàn)樵?HandlerMethod 中將會(huì)涉及到這個(gè)東西,而有的小伙伴可能還沒(méi)聽(tīng)說(shuō)過(guò) bridgedMethod,因此松哥在這里做一個(gè)簡(jiǎn)單介紹。

首先考考大家,下面這段代碼編譯會(huì)報(bào)錯(cuò)嗎?

public interface Animal<T> {
void eat(T t);
}
public class Cat implements Animal<String> {
@Override
public void eat(String s){
System.out.println("cat eat " + s);
}
}
public class Demo01 {
public static void main(String[] args){
Animal animal = new Cat();
animal.eat(new Object());
}
}

首先我們定義了一個(gè) Animal 接口,里邊定義了一個(gè) eat 方法,同時(shí)聲明了一個(gè)泛型。Cat 實(shí)現(xiàn)了 Animal 接口,將泛型也定義為了 String。當(dāng)我調(diào)用的時(shí)候,聲明類(lèi)型是 Animal,實(shí)際類(lèi)型是 Cat,這個(gè)時(shí)候調(diào) eat 方法傳入了 Object 對(duì)象大家猜猜會(huì)怎么樣?如果調(diào)用 eat 方法時(shí)傳入的是 String 類(lèi)型那就肯定沒(méi)問(wèn)題,但如果不是 String 呢?

松哥先說(shuō)結(jié)論:編譯沒(méi)問(wèn)題,運(yùn)行報(bào)錯(cuò)。

如果小伙伴們?cè)谧约弘娔X上寫(xiě)出上面這段代碼,你會(huì)發(fā)現(xiàn)這樣一個(gè)問(wèn)題,開(kāi)發(fā)工具中提示的參數(shù)類(lèi)型竟然是 Object,以松哥的 IDEA 為例,如下:

圖片

大家看到,在我寫(xiě)代碼的時(shí)候,開(kāi)發(fā)工具會(huì)給我提示,這個(gè)參數(shù)類(lèi)型是 Object,有的小伙伴會(huì)覺(jué)得奇怪,明明是泛型,怎么變成 Object 了?

我們可以通過(guò)反射查看 Cat 類(lèi)中到底有哪些方法,代碼如下:

public class Demo01 {
public static void main(String[] args){
Method[] methods = Cat.class.getMethods();
for (Method method : methods) {
String name = method.getName();
Class<?>[] parameterTypes = method.getParameterTypes();
System.out.println(name+"("+ Arrays.toString(parameterTypes) +")");
}
}
}

運(yùn)行結(jié)果如下:

圖片

可以看到,在實(shí)際運(yùn)行過(guò)程中,竟然有兩個(gè) eat 方法,一個(gè)的參數(shù)為 String 類(lèi)型,另一個(gè)參數(shù)為 Object 類(lèi)型,這是怎么回事呢?

這個(gè)參數(shù)類(lèi)型為 Object 的方法其實(shí)是 Java 虛擬機(jī)在運(yùn)行時(shí)創(chuàng)建出來(lái)的,這個(gè)方法就是我們所說(shuō)的 bridge method。本節(jié)的小標(biāo)題叫做 bridgedMethod,這是 HandlerMethod 源碼中的變量名,bridge 結(jié)尾多了一個(gè) d,含義變成了被 bridge 的方法,也就是參數(shù)為 String 的原方法,大家在接下來(lái)的源碼中看到了 bridgedMethod 就知道這表示參數(shù)類(lèi)型不變的原方法。

2.2 HandlerMethod 介紹

接下來(lái)我們來(lái)簡(jiǎn)單看下 HandlerMethod。

在我們前面分析 HandlerMapping 的時(shí)候(參見(jiàn):),里邊有涉及到 HandlerMethod,創(chuàng)建 HandlerMethod 的入口方法是 createWithResolvedBean,因此這里我們就從該方法開(kāi)始看起:

public HandlerMethod createWithResolvedBean(){
Object handler = this.bean;
if (this.bean instanceof String) {
String beanName = (String) this.bean;
handler = this.beanFactory.getBean(beanName);
}
return new HandlerMethod(this, handler);
}

這個(gè)方法主要是確認(rèn)了一下 handler 的類(lèi)型,如果 handler 是 String 類(lèi)型,則根據(jù) beanName 從 Spring 容器中重新查找到 handler 對(duì)象,然后構(gòu)建 HandlerMethod:

private HandlerMethod(HandlerMethod handlerMethod, Object handler){
this.bean = handler;
this.beanFactory = handlerMethod.beanFactory;
this.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 = handlerMethod.description;
}

這里的參數(shù)都比較簡(jiǎn)單,沒(méi)啥好說(shuō)的,唯一值得介紹的地方有兩個(gè):parameters 和 responseStatus。

parameters

parameters 實(shí)際上就是方法參數(shù),對(duì)應(yīng)的類(lèi)型是 MethodParameter,這個(gè)類(lèi)的源碼我這里就不貼出來(lái)了,主要和大家說(shuō)一下封裝的內(nèi)容包括:參數(shù)的序號(hào)(parameterIndex),參數(shù)嵌套級(jí)別(nestingLevel),參數(shù)類(lèi)型(parameterType),參數(shù)的注解(parameterAnnotations),參數(shù)名稱(chēng)查找器(parameterNameDiscoverer),參數(shù)名稱(chēng)(parameterName)等。

HandlerMethod 中還提供了兩個(gè)內(nèi)部類(lèi)來(lái)封裝 MethodParameter,分別是:

HandlerMethodParameter:這個(gè)封裝方法調(diào)用的參數(shù)。

ReturnValueMethodParameter:這個(gè)繼承自 HandlerMethodParameter,它封裝了方法的返回值,返回值里邊的 parameterIndex 是 -1。

注意,這兩者中的 method 都是 bridgedMethod。

responseStatus

這個(gè)主要是處理方法的 @ResponseStatus 注解,這個(gè)注解用來(lái)描述方法的響應(yīng)狀態(tài)碼,使用方式像下面這樣:

@GetMapping("/04")
@ResponseBody
@ResponseStatus(code = HttpStatus.OK)
public void hello4(@SessionAttribute("name"){
System.out.println("name = " + name);
}

從這段代碼中大家可以看到,其實(shí) @ResponseStatus 注解靈活性很差,不實(shí)用,當(dāng)我們定義一個(gè)接口的時(shí)候,很難預(yù)知到該接口的響應(yīng)狀態(tài)碼是 200。

在 handlerMethod 中,在調(diào)用其構(gòu)造方法的時(shí)候,都會(huì)調(diào)用 evaluateResponseStatus 方法處理 @ResponseStatus 注解,如下:

private void evaluateResponseStatus(){
ResponseStatus annotation = getMethodAnnotation(ResponseStatus.class);
if (annotation == null) {
annotation = AnnotatedElementUtils.findMergedAnnotation(getBeanType(), ResponseStatus.class);
}
if (annotation != null) {
this.responseStatus = annotation.code();
this.responseStatusReason = annotation.reason();
}
}

可以看到,這段代碼也比較簡(jiǎn)單,找到注解,把里邊的值解析出來(lái),賦值給相應(yīng)的變量。

這下小伙伴們應(yīng)該明白了 HandlerMethod 大概是個(gè)怎么回事。

3.InvocableHandlerMethod

看名字就知道,InvocableHandlerMethod 可以調(diào)用 HandlerMethod 中的具體方法,也就是 bridgedMethod。我們先來(lái)看下 InvocableHandlerMethod 中聲明的屬性:

private HandlerMethodArgumentResolverComposite resolvers = new HandlerMethodArgumentResolverComposite();
private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
@Nullable
private WebDataBinderFactory dataBinderFactory;

主要就是這三個(gè)屬性:

resolvers:這個(gè)不用說(shuō),參數(shù)解析器,前面的文章中松哥已經(jīng)和大家聊過(guò)這個(gè)問(wèn)題了。

parameterNameDiscoverer:這個(gè)用來(lái)獲取參數(shù)名稱(chēng),在 MethodParameter 中會(huì)用到。

dataBinderFactory:這個(gè)用來(lái)創(chuàng)建 WebDataBinder,在參數(shù)解析器中會(huì)用到。

具體的請(qǐng)求調(diào)用方法是 invokeForRequest,我們一起來(lái)看下:

@Nullable
public Object invokeForRequest(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object[] args = getMethodArgumentValues(request, mavContainer, providedArgs);
return doInvoke(args);
}
@Nullable
protected Object doInvoke(Object... args) throws Exception {
Method method = getBridgedMethod();
ReflectionUtils.makeAccessible(method);
try {
if (KotlinDetector.isSuspendingFunction(method)) {
return CoroutinesUtils.invokeSuspendingFunction(method, getBean(), args);
}
return method.invoke(getBean(), args);
}
catch (InvocationTargetException ex) {
// 省略 ...
}
}

首先調(diào)用 getMethodArgumentValues 方法按順序獲取到所有參數(shù)的值,這些參數(shù)值組成一個(gè)數(shù)組,然后調(diào)用 doInvoke 方法執(zhí)行,在 doInvoke 方法中,首先獲取到 bridgedMethod,并設(shè)置其可見(jiàn)(意味著我們?cè)?Controller 中定義的接口方法也可以是 private 的),然后直接通過(guò)反射調(diào)用即可。當(dāng)我們沒(méi)看 SpringMVC 源碼的時(shí)候,我們就知道接口方法最終肯定是通過(guò)反射調(diào)用的,現(xiàn)在,經(jīng)過(guò)層層分析之后,終于在這里找到了反射調(diào)用代碼。

最后松哥再來(lái)說(shuō)一下負(fù)責(zé)參數(shù)解析的 getMethodArgumentValues 方法:

protected Object[] getMethodArgumentValues(NativeWebRequest request, @Nullable ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
MethodParameter[] parameters = getMethodParameters();
if (ObjectUtils.isEmpty(parameters)) {
return EMPTY_ARGS;
}
Object[] args = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
MethodParameter parameter = parameters[i];
parameter.initParameterNameDiscovery(this.parameterNameDiscoverer);
args[i] = findProvidedArgument(parameter, providedArgs);
if (args[i] != null) {
continue;
}
if (!this.resolvers.supportsParameter(parameter)) {
throw new IllegalStateException(formatArgumentError(parameter, "No suitable resolver"));
}
try {
args[i] = this.resolvers.resolveArgument(parameter, mavContainer, request, this.dataBinderFactory);
}
catch (Exception ex) {
// 省略...
}
}
return args;
}
  • 首先調(diào)用 getMethodParameters 方法獲取到方法的所有參數(shù)。
  • 創(chuàng)建 args 數(shù)組用來(lái)保存參數(shù)的值。
  • 接下來(lái)一堆初始化配置。
  • 如果 providedArgs 中提供了參數(shù)值,則直接賦值。
  • 查看是否有參數(shù)解析器支持當(dāng)前參數(shù)類(lèi)型,如果沒(méi)有,直接拋出異常。
  • 調(diào)用參數(shù)解析器對(duì)參數(shù)進(jìn)行解析,解析完成后,賦值。

是不是,很 easy!

4.ServletInvocableHandlerMethod

ServletInvocableHandlerMethod 則是在 InvocableHandlerMethod 的基礎(chǔ)上,又增加了兩個(gè)功能:

  • 對(duì)@ResponseStatus 注解的處理
  • 對(duì)返回值的處理

Servlet 容器下 Controller 在查找適配器時(shí)發(fā)起調(diào)用的最終就是 ServletInvocableHandlerMethod。

這里的處理核心方法是 invokeAndHandle,如下:

public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
setResponseStatus(webRequest);
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
disableContentCachingIfNecessary(webRequest);
mavContainer.setRequestHandled(true);
return;
}
}
else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
mavContainer.setRequestHandled(false);
try {
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
throw ex;
}
}

首先調(diào)用父類(lèi)的 invokeForRequest 方法對(duì)請(qǐng)求進(jìn)行執(zhí)行,拿到請(qǐng)求結(jié)果。

調(diào)用 setResponseStatus 方法處理@ResponseStatus? 注解,具體的處理邏輯是這樣:如果沒(méi)有添加 @ResponseStatus 注解,則什么都不做;如果添加了該注解,并且 reason 屬性不為空,則直接輸出錯(cuò)誤,否則設(shè)置響應(yīng)狀態(tài)碼。這里需要注意一點(diǎn),如果響應(yīng)狀態(tài)碼是 200,就不要設(shè)置 reason,否則會(huì)按照 error 處理。

接下來(lái)就是對(duì)返回值的處理了,returnValueHandlers#handleReturnValue 方法松哥在之前的文章中和大家專(zhuān)門(mén)介紹過(guò),這里就不再贅述,傳送門(mén):Spring Boot 中如何統(tǒng)一 API 接口響應(yīng)格式?。

事實(shí)上,ServletInvocableHandlerMethod 還有一個(gè)子類(lèi) ConcurrentResultHandlerMethod,這個(gè)支持異步調(diào)用結(jié)果處理,因?yàn)槭褂脠?chǎng)景較少,這里就不做介紹啦。

責(zé)任編輯:武曉燕 來(lái)源: 江南一點(diǎn)雨
相關(guān)推薦

2022-08-26 01:02:31

接口privateSpring

2021-04-16 11:02:40

Spring BootprivateJava

2023-03-16 08:14:57

2025-05-28 03:00:00

2022-07-07 14:18:43

SpringWeb應(yīng)用設(shè)計(jì)

2023-10-16 11:12:29

2024-10-18 08:00:00

SpringBoot框架開(kāi)發(fā)

2024-10-14 17:18:27

2022-06-04 12:25:10

解密加密過(guò)濾器

2021-03-09 13:18:53

加密解密參數(shù)

2024-08-01 09:10:03

2022-07-27 08:49:34

接口加密解密

2010-10-27 16:41:40

2009-06-22 09:01:57

Spring聲明式事務(wù)

2025-01-08 09:35:55

Spring性能監(jiān)控

2024-09-27 15:24:15

Spring數(shù)據(jù)加解密

2025-06-06 08:28:56

2025-02-22 08:00:00

AgentSpringBootJava

2020-03-19 10:44:19

DockerSpring Boo單層鏡像

2024-08-12 10:13:01

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)