最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

如果Controller里有私有的方法,能成功訪問嗎?

2022-08-07 21:20 作者:限量版范兒  | 我要投稿

背景

寫代碼的時候,復制粘貼的時候,沒注意到方法的屬性,就導致了Controller里有了一個私有的方法,然后訪問這個接口的時候就報了空指針異常,找了好久才找到原因。

來看一個例子

@Service public class MyService { ? ?public String hello() { ? ? ? ?return "hello"; ? ?} } @Slf4j @RestController @RequestMapping("/test") public class MyController { ? ?@Autowired ? ?private MyService myService; ? ?@GetMapping("/public") ? ?public Object publicHello() { ? ? ? ?return myService.hello(); ? ?} ? ?@GetMapping("/protected") ? ?protected Object protectedHello() { ? ? ? ?return myService.hello(); ? ?} ? ?@GetMapping("/private") ? ?private Object privateHello() { ? ? ? ?return myService.hello(); ? ?} } @EnableAspectJAutoProxy @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) public class MyApplication { ? ?public static void main(String[] args) { ? ? ? ?SpringApplication.run(MyApplication.class, args); ? ?} }訪問 http://127.0.0.1:8081/test/public 200 http://127.0.0.1:8081/test/protected 200 http://127.0.0.1:8081/test/private 200

如果在這個基礎之上再加一個切面:

@Slf4j @Aspect @Component public class MyAspect { ? ?@Pointcut("execution(* cn.eagle.li.controller..*.*(..))") ? ?public void controllerSayings() { ? ?} ? ?@Before("controllerSayings()") ? ?public void sayHello() { ? ? ? ?log.info("注解類型前置通知"); ? ?} }訪問 http://127.0.0.1:8081/test/public 200 http://127.0.0.1:8081/test/protected 200 http://127.0.0.1:8081/test/private 500:報空指針異常,原因是myService為null的

原因

  • public 方法

  • protected 方法

  • private 方法

大致可以看到原因,public方法和protected方法訪問的時候,它的類都是真實的類

而private方法是代理的類

cglib代理的鍋

Spring Boot 2.0 開始,默認使用的是cglib代理

@Configuration @ConditionalOnClass({ EnableAspectJAutoProxy.class, Aspect.class, Advice.class, AnnotatedElement.class }) @ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true) public class AopAutoConfiguration { @Configuration @EnableAspectJAutoProxy(proxyTargetClass = false) @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = false) public static class JdkDynamicAutoProxyConfiguration { } @Configuration @EnableAspectJAutoProxy(proxyTargetClass = true) @ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true) public static class CglibAutoProxyConfiguration { } }

入口

不管public還是private的方法,都是這樣執(zhí)行的。

生成代理類字節(jié)碼

? ?public static void main(String[] args) { ? ? ? ?/** 加上這句代碼,可以生成代理類的class文件*/ ? ? ? ?System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "org/springframework/cglib"); ? ? ? ?SpringApplication.run(MyApplication.class, args); ? ?}

部分代理類字節(jié)碼如下:

? ?protected final Object protectedHello() { ? ? ? ?try { ? ? ? ? ? ?MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; ? ? ? ? ? ?if (var10000 == null) { ? ? ? ? ? ? ? ?CGLIB$BIND_CALLBACKS(this); ? ? ? ? ? ? ? ?var10000 = this.CGLIB$CALLBACK_0; ? ? ? ? ? ?} ? ? ? ? ? ?return var10000 != null ? var10000.intercept(this, CGLIB$protectedHello$1$Method, CGLIB$emptyArgs, CGLIB$protectedHello$1$Proxy) : super.protectedHello(); ? ? ? ?} catch (Error | RuntimeException var1) { ? ? ? ? ? ?throw var1; ? ? ? ?} catch (Throwable var2) { ? ? ? ? ? ?throw new UndeclaredThrowableException(var2); ? ? ? ?} ? ?} ? public final Object publicHello() { ? ? ? ?try { ? ? ? ? ? ?MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; ? ? ? ? ? ?if (var10000 == null) { ? ? ? ? ? ? ? ?CGLIB$BIND_CALLBACKS(this); ? ? ? ? ? ? ? ?var10000 = this.CGLIB$CALLBACK_0; ? ? ? ? ? ?} ? ? ? ? ? ?return var10000 != null ? var10000.intercept(this, CGLIB$publicHello$0$Method, CGLIB$emptyArgs, CGLIB$publicHello$0$Proxy) : super.publicHello(); ? ? ? ?} catch (Error | RuntimeException var1) { ? ? ? ? ? ?throw var1; ? ? ? ?} catch (Throwable var2) { ? ? ? ? ? ?throw new UndeclaredThrowableException(var2); ? ? ? ?} ? ?}

public和protected方法會生成上述的方法,而private方法是不會生成這樣的方法

private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable { ? ? ? ?@Override @Nullable public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { Object oldProxy = null; boolean setProxyContext = false; Object target = null; TargetSource targetSource = this.advised.getTargetSource(); try { if (this.advised.exposeProxy) { // Make invocation available if necessary. oldProxy = AopContext.setCurrentProxy(proxy); setProxyContext = true; } // Get as late as possible to minimize the time we "own" the target, in case it comes from a pool... target = targetSource.getTarget(); Class<?> targetClass = (target != null ? target.getClass() : null); List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); Object retVal; // Check whether we only have one InvokerInterceptor: that is, // no real advice, but just reflective invocation of the target. if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) { // We can skip creating a MethodInvocation: just invoke the target directly. // Note that the final invoker must be an InvokerInterceptor, so we know // it does nothing but a reflective operation on the target, and no hot // swapping or fancy proxying. Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args); retVal = methodProxy.invoke(target, argsToUse); } else { // We need to create a method invocation... retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed(); } retVal = processReturnType(proxy, target, method, retVal); return retVal; } finally { if (target != null && !targetSource.isStatic()) { targetSource.releaseTarget(target); } if (setProxyContext) { // Restore old proxy. AopContext.setCurrentProxy(oldProxy); } } } ? ?}

public和protected方法會調(diào)用DynamicAdvisedInterceptor.intercept方法,這里面的this.advised.getTargetSource()可以獲得真實的目標類,這個目標類是注入成功。

換成JDK動態(tài)代理呢

增加配置:

spring: ?aop: ? ?proxy-target-class: false

增加接口:

@RestController public interface MyControllerInterface { ? ?@RequestMapping("/hello/public") ? ?Object publicHello(); ? ?@RequestMapping("/hello/default") ? ?default Object defaultHello() { ? ? ? ?return "hi default"; ? ?} } @Slf4j @RestController @RequestMapping("/test") public class MyController implements MyControllerInterface { ? ?@Autowired ? ?public MyService myService; ? ?@Override ? ?@GetMapping("/public") ? ?public Object publicHello() { ? ? ? ?return myService.hello(); ? ?} ? ?@GetMapping("/protected") ? ?protected Object protectedHello() { ? ? ? ?return myService.hello(); ? ?} ? ?@GetMapping("/private") ? ?private Object privateHello() { ? ? ? ?return myService.hello(); ? ?} }

MyControllerInterface頭上加@RestController的原因是:

protected boolean isHandler(Class<?> beanType) { return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) || AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class)); }http://127.0.0.1:8081/test/public 404 http://127.0.0.1:8081/test/protected 404 http://127.0.0.1:8081/test/private 404 http://127.0.0.1:8081/hello/public 200 http://127.0.0.1:8081/hello/default 200

只能使用接口里的@RequestMapping,實現(xiàn)類里的不生效

鏈接:https://www.dianjilingqu.com/473440.html

如果Controller里有私有的方法,能成功訪問嗎?的評論 (共 條)

分享到微博請遵守國家法律
长治市| 泰顺县| 渭源县| 恩平市| 修文县| 江孜县| 中西区| 岑溪市| 苏州市| 磐石市| 钟祥市| 曲阳县| 平安县| 大冶市| 河曲县| 故城县| 确山县| 灵璧县| 万盛区| 遂川县| 稻城县| 三都| 铜山县| 安多县| 东源县| 延庆县| 洪湖市| 桐梓县| 霍州市| 宜州市| 房山区| 九寨沟县| 马公市| 淮安市| 石林| 满洲里市| 台安县| 哈密市| 景谷| 永平县| 柘荣县|