使用@RequestScope创建String Bean报错,@Scope("request")却正常?
这个问题的核心在于两个注解的默认代理模式设置不同,导致Spring处理final类(比如String)时出现了差异。我来给你详细拆解:
1. 为什么@RequestScope会报错?
先看@RequestScope的注解定义,它是@Scope的组合注解,默认指定了proxyMode = ScopedProxyMode.TARGET_CLASS:
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Scope("request") public @interface RequestScope { @AliasFor(annotation = Scope.class) ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS; }
ScopedProxyMode.TARGET_CLASS意味着Spring会尝试用CGLIB生成目标类的子类作为代理。但java.lang.String是final类,CGLIB无法继承final类生成子类,所以就抛出了你看到的错误:Cannot subclass final class java.lang.String。
2. 为什么@Scope("request")能正常运行?
再看@Scope注解的默认设置,它的proxyMode默认值是ScopedProxyMode.DEFAULT:
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Scope { // 其他属性... ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT; }
ScopedProxyMode.DEFAULT等价于不自动创建代理,只有当你显式指定代理模式时,Spring才会生成代理。所以用@Scope("request")时,Spring直接创建String类型的Bean,不需要生成代理,自然不会触发final类的代理生成错误。
3. 解决方案:如何用@RequestScope创建String类型Bean?
如果你想继续使用@RequestScope,只需要显式把proxyMode设置为ScopedProxyMode.NO,关闭自动代理即可:
@SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } @RequestScope(proxyMode = ScopedProxyMode.NO) @Bean(name = "requestBean") public String hiRequest() { return new String("Hi Request"); } }
当然,继续使用@Scope("request")也是完全没问题的,它默认就不会生成代理。
补充:什么时候需要代理?
这里额外提一句:request scope的Bean需要代理的场景,通常是当你在singleton scope的Bean中注入request scope的Bean时。这时候Spring需要通过代理来延迟获取当前请求的Bean实例,否则会在singleton初始化时(还没有请求上下文)就尝试创建request Bean,导致报错。但如果你的request Bean只是在请求上下文中直接使用,不需要被singleton Bean引用,那么关闭代理完全不影响使用。
内容的提问来源于stack exchange,提问作者Michael Ouyang

