覆盖重写原有SpringBean的⼏种⽅式
⽬录
场景
⽅法1 直接在⾃⼰⼯程中建同包同类名的类进⾏替换
⽅法2 采⽤@Primary注解
⽅法3 排除需要替换的jar包中的类
⽅法4 @Bean 覆盖
⽅法5 使⽤BeanDefinitionRegistryPostProcessor
场景
什么情况下要覆写原有的Spring Bean ?例如引⼊的第三⽅jar包中的某个类有些问题,然有没有提供或者嫌编译太费事,这个时间可以考虑覆写原有的类。
⽅法1 直接在⾃⼰⼯程中建同包同类名的类进⾏替换
⽅法2 采⽤@Primary注解
该⽅法适⽤于接⼝实现类,⾃⼰创建⼀个原jar包接⼝的实现类,然后类上加上@Primary注解,spring则默认加载该类实例化出的Bean。
下⾯的例⼦:⼀个接⼝ RemoteIdmService,原先jar包中只有⼀个实现类 RemoteIdmServiceImpl,现在⾃⼰⼯程⾥⾯创建⼀个CustomRemoteIdmServiceImpl 继承RemoteIdmService接⼝,然后发现所有调⽤RemoteIdmService接⼝⾥⾯的⽅法实际调⽤⾛的是CustomRemoteIdmServiceImpl ⾥⾯的⽅法。
⽅法3 排除需要替换的jar包中的类
使⽤ @ComponentScan ⾥⾯的 excludeFilters 功能排除调⽤要替换的类,然后⾃⼰写个类继承替换的类即可。
下⾯的例⼦是替换掉 jar包中的PersistentTokenServiceImpl类
1. @SpringBootApplication
2. @ComponentScan(excludeFilters = {
3. @ComponentScan.Filter(type =
4. FilterType.ASSIGNABLE_TYPE, classes = {
5. PersistentTokenServiceImpl.class})})
6. public class Application {
7.
8. public static void main([] args) {
9.
10. new SpringApplication(Test.class).run(args);
11. }
12. }
1. @Service
2. public class MyPersistentTokenServiceImpl extends PersistentTokenServiceImpl{
3.
4. @Override
5. public Token saveAndFlush(Token token) {
6.
7. // 覆写该⽅法的业务逻辑
8. tokenCache.Id(), token);
9. idmIdentityService.saveToken(token);
10. return token;
11. }
12.
13. @Override
14. public void delete(Token token) {
15.
16. // 覆写该⽅法的业务逻辑
17. tokenCache.Id());
18. idmIdentityService.Id());
19. }
20.
21. @Override
22. public Token getPersistentToken( tokenId) {
23.
24. // 覆写该⽅法的业务逻辑
25. return getPersistentToken(tokenId, false);
26. }
27. }
⽅法4 @Bean 覆盖
⽅法5 使⽤BeanDefinitionRegistryPostProcessor BeanDefinitionRegistryPostProcessor 说⽩了就是可以在初始化Bean之前修改Bean的属性,甚⾄替换原先准备要实例化的bean。实战演⽰:
假设jar包中有⼀个类 MyTestService,正常情况下它会被spring⾃动扫描到注⼊IOC容器中去。
1. package st.ister.jar;
2.
3. import org.springframework.stereotype.Service;
4.
5. import x.annotation.PostConstruct;
6. import x.annotation.PreDestroy;
7.
8. /** * @author guzt */
9. @Service("myTestService")
10. public class MyTestService {
11.
12.
13. private name1;
14. private name2;
15. private name3;
16.
17. public MyTestService() {
18.
19. this.name1 = "";
20. this.name2 = "";
21. this.name3 = "";
22. }
23. public MyTestService( name1, String name2, String name3) {
24.
25. this.name1 = name1;
26. this.name2 = name2;
27. this.name3 = name3;
28. }
29.
30. @PostConstruct
31. public void init() {
32.
33. System.out.println("MyTestService init");
34. }
35.
36. @PreDestroy
37. public void destory() {
38.
39. System.out.println("MyTestService destroy");
40. }
41.
42. public void show() {
43.
44. System.out.println("------------------------");
45. System.out.println("我是jar中通过注解@Service主动加⼊Spring的IOC⾥⾯的");
46. System.out.println("------------------------");
47. }
48.
49. public String getName1() {
50.
51. return name1;
52. }
53. public void setName1(String name1) {
54.
55. this.name1 = name1;
56. }
57. public String getName2() {
58.
59. return name2;
60. }
61. public void setName2(String name2) {
62.
63. this.name2 = name2;
64. }
65. public String getName3() {
66.
67. return name3;
68. }
69. public void setName3(String name3) {
70.
71. this.name3 = name3;
72. }
73. }
⾃⼰⼯程中继承该类,并且覆写⾥⾯的show中的⽅法
1. package st.ister;
2.
3. import st.ister.jar.MyTestService;
4.
5. /** * @author guzt */
6. public class MyTestServiceIpml extends MyTestService {
7.
8.
9. public MyTestServiceIpml() {
10.
11. }
12.
13. public MyTestServiceIpml(String name1, String name2, String name3) {
14.
15. super(name1, name2, name3);
16. }
17.
18. @Override
19. public void show() {
20.
21. System.out.println("------------------------");
22. System.out.println("我是被BeanDefinitionRegistry⼿动注册到Spring的IOC⾥⾯的");
23. System.out.println("------------------------");
24. }
25. }
然后实现 BeanDefinitionRegistryPostProcessor 接⼝,修改原来bean定义,主要查看postProcessBeanDefinitionRegistry⽅法的实现,先清空原bean定义,注册我们⾃⼰的bean定义来达到替换的⽬的。
1. package st.ister;
2.
3. import org.slf4j.Logger;
4. import org.slf4j.LoggerFactory;
5. import org.springframework.beans.BeansException;
6. import org.springframework.fig.ConfigurableListableBeanFactory;
7. import org.springframework.beans.factory.support.BeanDefinitionBuilder;
8. import org.springframework.beans.factory.support.BeanDefinitionRegistry;
9. import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
10. import org.springframework.stereotype.Component;
11. import org.springframework.web.bind.annotation.RestController;
12.
13. import .util.Map;
14.
15. /** * @author amdin */
16. @Component
17. public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
18.
19.
20. private Logger logger = Class());
21.
22. @Override
23. public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
24.
25. logger.info("bean 定义查看和修改...");
26.
27. String beanName = "myTestService";
28.
29. // 先移除原来的bean定义
30. veBeanDefinition(beanName);
31.
32. // 注册我们⾃⼰的bean定义
33. BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinition(MyTestServiceIpml.class);
34. // 如果有构造函数, 有⼏个构造函数的就设置⼏个没有就不⽤设置
35. beanDefinitionBuilder.addConstructorArgValue("构造1");
36. beanDefinitionBuilder.addConstructorArgValue("构造2");
37. beanDefinitionBuilder.addConstructorArgValue("构造3");
38. // 设置 init⽅法没有就不⽤设置
39. beanDefinitionBuilder.setInitMethodName("init");
40. // 设置 destory⽅法没有就不⽤设置
41. beanDefinitionBuilder.setDestroyMethodName("destory");
42. // 将Bean 的定义注册到Spring环境
43. isterBeanDefinition("myTestService", BeanDefinition());
44. }
45.
46. @Override
47. public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
48.
49. // bean的名字为key, bean的实例为value
50. Map<String, Object> beanMap = BeansWithAnnotation(RestController.class);
51. logger.info("所有 RestController 的bean {}", beanMap);
52. }
53. }
写⼀个业务类BusinessTestService测试⼀下,期望结果:所有⽤到 MyTestService的地⽅实际调⽤的变成了MyTestServiceIpml⾥⾯的⽅法。
1. package st.ister;
2.
3. import st.ister.jar.MyTestService;
4. import org.springframework.stereotype.Service;
5.
6. import x.annotation.PostConstruct;
7. import x.annotation.Resource;
8.
9. /** * @author guzt */
10. @Service
实例化bean的三种方式11. public class BusinessTestService {
12.
13.
14. @Resource
15. private MyTestService myTestService;
16.
17. @PostConstruct
18. public void init() {
19.
20. System.out.Name1());
21. System.out.Name2());
22. System.out.Name3());
23.
24. // 看看到底是哪⼀个Bean
25. myTestService.show();
26. }
27.
28. }
OVER …