spring 中 @Autowired 最终 setter 和非最终 setter 之间的区别
Difference between @Autowired final setter and non-final setter in spring
假设:
abstract class CommonService {
protected VipMapper vipMapper;
@Autowired
public final void setVipMapper(VipMapper vipMapper) {
this.vipMapper = vipMapper;
}
}
@Service
public class BookService extends CommonService {
public int find() {
return vipMapper.findVip(); // return 100
}
}
@SpringBootTest
class BookServiceTest {
@Autowired
private BookService bookService;
@Test
void find() {
VipMapper v = new VipMapper() {
@Override
public int findVip() { // This method will not execute
return 10;
}
};
bookService.setVipMapper(v);
int find = bookService.find(); // find = 100 (not 10)
}
}
1.当setVipMapper
方法是final
时我无法注入VipMapper
而当[=15时我可以注入的原因是什么? =] 方法不是 final
?
2. 如何在 运行 时间内注入 VipMapper
但仍然使用 @Autowired
final
setter?
更新
我正在使用 Spring
+ Mybatis
源代码:
https://bitbucket.org/nguyentanh/Whosebug
使用上面的代码,当 运行 测试 findVipCustomerTop3
时,我得到一个错误连接。但是当去掉final in CommonService.java
(或者@Transactional
in BookService.java
)时,测试成功
根据我的理解,您正在为 setVipMapper() 使用 @autowired,因此它已经使用默认的 findVip() 返回 100 注入了 VipMapper。因此,将 setVipMapper() 定义为 final 不会再更改您传递的值
你的问题不在于自动装配。 VipMapper
正确自动连接,并且您正尝试在测试中通过 bookService.setVipMapper(v);
手动替换映射器。它不会替换您传递的 vipMapper
。要检查此行为,请在您的服务中定义一个 getter 以获取 vipMapper
,它将 return 由 spring 自动装配的原始 vipMapper
。
请记住,您使用的不是原始 BookService
class 的实例,而是 BookService
的子 class这是 运行 生成的时间。
您的原始问题遗漏了一条重要信息,即您服务中的 @Transactional
注释。只要有 @Transactional
注释,Spring 实际上就需要创建一个代理。现在 spring 将选择 JDK dynamic proxy
或 CGLIB proxy
为您的图书服务创建代理。由于您的服务没有接口,JDK 动态代理选择是不可能的,所以 spring 留给 CGLIB 代理。
CGLIB 代理有其局限性。
With CGLIB, final methods cannot be advised, as they cannot be overridden in runtime-generated subclasses
如果你想实际替换它,这里有技巧。在测试中添加以下内容 class 而不是行 bookService.setVipMapper(v);
((BookService)AopProxyUtils.getSingletonTarget(bookService))
.setVipMapper(v);
- 以上选项是硬核方式,但有助于理解概念。还有另一个选项告诉 spring 在您的测试中创建
BookService
,并在创建 BookService
. 时使用自动装配的模拟 vipMapper
@SpringBootTest
class BookServiceTest {
@Autowired
private BookService bookService;
@MockBean
private VipMapper vipMapper;
@Test
void find() {
when(vipMapper.findVip()).thenReturn(10);
bookService.setVipMapper(v);
int find = bookService.find();
}
}
参考
假设:
abstract class CommonService {
protected VipMapper vipMapper;
@Autowired
public final void setVipMapper(VipMapper vipMapper) {
this.vipMapper = vipMapper;
}
}
@Service
public class BookService extends CommonService {
public int find() {
return vipMapper.findVip(); // return 100
}
}
@SpringBootTest
class BookServiceTest {
@Autowired
private BookService bookService;
@Test
void find() {
VipMapper v = new VipMapper() {
@Override
public int findVip() { // This method will not execute
return 10;
}
};
bookService.setVipMapper(v);
int find = bookService.find(); // find = 100 (not 10)
}
}
1.当setVipMapper
方法是final
时我无法注入VipMapper
而当[=15时我可以注入的原因是什么? =] 方法不是 final
?
2. 如何在 运行 时间内注入 VipMapper
但仍然使用 @Autowired
final
setter?
更新
我正在使用 Spring
+ Mybatis
源代码: https://bitbucket.org/nguyentanh/Whosebug
使用上面的代码,当 运行 测试 findVipCustomerTop3
时,我得到一个错误连接。但是当去掉final in CommonService.java
(或者@Transactional
in BookService.java
)时,测试成功
根据我的理解,您正在为 setVipMapper() 使用 @autowired,因此它已经使用默认的 findVip() 返回 100 注入了 VipMapper。因此,将 setVipMapper() 定义为 final 不会再更改您传递的值
你的问题不在于自动装配。
VipMapper
正确自动连接,并且您正尝试在测试中通过bookService.setVipMapper(v);
手动替换映射器。它不会替换您传递的vipMapper
。要检查此行为,请在您的服务中定义一个 getter 以获取vipMapper
,它将 return 由 spring 自动装配的原始vipMapper
。请记住,您使用的不是原始
BookService
class 的实例,而是BookService
的子 class这是 运行 生成的时间。您的原始问题遗漏了一条重要信息,即您服务中的
@Transactional
注释。只要有@Transactional
注释,Spring 实际上就需要创建一个代理。现在 spring 将选择JDK dynamic proxy
或CGLIB proxy
为您的图书服务创建代理。由于您的服务没有接口,JDK 动态代理选择是不可能的,所以 spring 留给 CGLIB 代理。CGLIB 代理有其局限性。
With CGLIB, final methods cannot be advised, as they cannot be overridden in runtime-generated subclasses
如果你想实际替换它,这里有技巧。在测试中添加以下内容 class 而不是行
bookService.setVipMapper(v);
((BookService)AopProxyUtils.getSingletonTarget(bookService))
.setVipMapper(v);
- 以上选项是硬核方式,但有助于理解概念。还有另一个选项告诉 spring 在您的测试中创建
BookService
,并在创建BookService
. 时使用自动装配的模拟
vipMapper
@SpringBootTest
class BookServiceTest {
@Autowired
private BookService bookService;
@MockBean
private VipMapper vipMapper;
@Test
void find() {
when(vipMapper.findVip()).thenReturn(10);
bookService.setVipMapper(v);
int find = bookService.find();
}
}
参考