Spring @Autowired 无法在抽象超级上使用 final 方法 class
Spring @Autowired not working with final method on abstract super class
我有一个带有自动装配字段和最终方法的抽象超级 class。此外还有一个具体的 sub class 方法调用 super class.
上的最终方法
摘要超级class:
public abstract class AbstractSuper {
@Autowired
protected AuthenticationService authenticationService;
@Autowired
protected JobRepository jobRepository;
public final void startOnSuper() {
test();
}
private void test() {
assert authenticationService != null;
assert jobRepository != null;
}
}
具体实现:
@Component
@RequiredArgsConstructor
public class ConcreteImpl extends AbstractSuper {
private final PersonService personService;
public void startOnImpl() {
super.startOnSuper();
}
}
JUnit:
@Autowired
private ConcreteImpl concrete;
@Test
public void startOnImpl() {
concrete.startOnImpl();
}
@Test
public void startOnSuper() {
concrete.startOnSuper();
}
为什么 startOnImpl 测试有效而 startOnSuper 测试失败?为什么从子 class 或 class 外部调用方法很重要?对象的状态不应该是一样的吗?
startOnSuper
方法失败正是因为它具有 final
修饰符,而 startOnImpl
方法中没有。测试 class 中的调用 concrete.startOnImpl();
和 concrete.startOnSuper();
不直接调用 class 实例,而是动态创建的代理。 ConcreteImpl#startOnImpl
里面的调用super.startOnSuper();
,没有经过代理(其实在进入这个方法之前就已经通过了),而是调用实例本身的方法,所以不会报错。
The key thing to understand here is that the client code inside the main(..) method of the Main class has a reference to the proxy. This means that method calls on that object reference are calls on the proxy. As a result, the proxy can delegate to all of the interceptors (advice) that are relevant to that particular method call. However, once the call has finally reached the target object (the SimplePojo reference in this case), any method calls that it may make on itself, such as this.bar() or this.foo(), are going to be invoked against the this reference, and not the proxy. Section 5.8.1
在这种情况下,代理是基于 class(基于 class 的代理)创建的,这意味着它如何工作的一些规则,包括不将最终修改应用到public 或受保护的方法(将通过代理访问)。
这个代理是一个在运行时动态创建的包装器,需要覆盖所有可访问的方法,这样它们就不会被直接调用,而是始终处于代理本身的控制模式中。如果该方法是最终的,则它不能被覆盖,这将导致意外行为。
简单的解决方案:
Remove the final modifier.
但这也取决于您在项目中使用的设计,我不记得这种情况下还有其他实用的替代方案。
您可以在以下链接中阅读更多相关信息:
- https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#aop-proxying(这是官方文档。current 将引用当前版本。您可以覆盖它。它的版本。示例:current -> 5.3.9)
- https://deinum.biz/2020-07-03-Autowired-Field-Null/#aop(这是一篇解释略有不同的博客,并提供了一些示例)
我有一个带有自动装配字段和最终方法的抽象超级 class。此外还有一个具体的 sub class 方法调用 super class.
上的最终方法摘要超级class:
public abstract class AbstractSuper {
@Autowired
protected AuthenticationService authenticationService;
@Autowired
protected JobRepository jobRepository;
public final void startOnSuper() {
test();
}
private void test() {
assert authenticationService != null;
assert jobRepository != null;
}
}
具体实现:
@Component
@RequiredArgsConstructor
public class ConcreteImpl extends AbstractSuper {
private final PersonService personService;
public void startOnImpl() {
super.startOnSuper();
}
}
JUnit:
@Autowired
private ConcreteImpl concrete;
@Test
public void startOnImpl() {
concrete.startOnImpl();
}
@Test
public void startOnSuper() {
concrete.startOnSuper();
}
为什么 startOnImpl 测试有效而 startOnSuper 测试失败?为什么从子 class 或 class 外部调用方法很重要?对象的状态不应该是一样的吗?
startOnSuper
方法失败正是因为它具有 final
修饰符,而 startOnImpl
方法中没有。测试 class 中的调用 concrete.startOnImpl();
和 concrete.startOnSuper();
不直接调用 class 实例,而是动态创建的代理。 ConcreteImpl#startOnImpl
里面的调用super.startOnSuper();
,没有经过代理(其实在进入这个方法之前就已经通过了),而是调用实例本身的方法,所以不会报错。
The key thing to understand here is that the client code inside the main(..) method of the Main class has a reference to the proxy. This means that method calls on that object reference are calls on the proxy. As a result, the proxy can delegate to all of the interceptors (advice) that are relevant to that particular method call. However, once the call has finally reached the target object (the SimplePojo reference in this case), any method calls that it may make on itself, such as this.bar() or this.foo(), are going to be invoked against the this reference, and not the proxy. Section 5.8.1
在这种情况下,代理是基于 class(基于 class 的代理)创建的,这意味着它如何工作的一些规则,包括不将最终修改应用到public 或受保护的方法(将通过代理访问)。
这个代理是一个在运行时动态创建的包装器,需要覆盖所有可访问的方法,这样它们就不会被直接调用,而是始终处于代理本身的控制模式中。如果该方法是最终的,则它不能被覆盖,这将导致意外行为。
简单的解决方案:
Remove the final modifier.
但这也取决于您在项目中使用的设计,我不记得这种情况下还有其他实用的替代方案。
您可以在以下链接中阅读更多相关信息:
- https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#aop-proxying(这是官方文档。current 将引用当前版本。您可以覆盖它。它的版本。示例:current -> 5.3.9)
- https://deinum.biz/2020-07-03-Autowired-Field-Null/#aop(这是一篇解释略有不同的博客,并提供了一些示例)