Springboot重写业务Bean

场景

svc1.jar 中有一个 TestController类,当前源代码工程 demo 依赖了这个 svc1.jar
当前 demo 工程启动正常,TestController 被初始化,TestController中提供的接口 /svc1/test1/svc1/test2/svc1/test3 均可以正常访问。
因接口 /svc1/test2 对应的方法中逻辑处理不满足需求,需要对这个方法进行重写处理。
该接口在 svc1.jar 包,不能直接修改源代码。

处理方式一(简单处理)

主要思路是在 Spring 注册 Bean 之后进行 PostProcessor 时,将已经注册的 svc1.jar 中的 TestController 从上下文中删除。使我们新创建的继承 TestController 的重写类能被正常加载实例化,而不出现 PathMapping 重复的报错冲突。

主要代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
* 排除容器中的Bean
*/
@Slf4j
@Configuration
public class ExcludeComponentConfiguration implements BeanDefinitionRegistryPostProcessor {

@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
registry.removeBeanDefinition(getDefaultBeanName(FileController.class));
registry.removeBeanDefinition(getDefaultBeanName(UserController.class));
}

/**
* 根据类 class 获取 spring 默认的 beanName
*
* @param clazz clazz
* @return String
*/
private String getDefaultBeanName(Class<?> clazz) {
// 测试内部类 AutowireUtils.ObjectFactoryDelegatingInvocationHandler 的默认 BeanName
// StringUtils.uncapitalizeAsProperty(ClassUtils.getShortName(AutowireUtils.ObjectFactoryDelegatingInvocationHandler.class.getName()))
// spring 6.0 之前使用 StringUtils.uncapitalize()
return org.springframework.util.StringUtils.uncapitalizeAsProperty(ClassUtils.getShortName(clazz.getName()));
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 重写TestController类
*/
@RestController //该注解必须要有,Mapping那些注解不需要
public class OverrideFileController extends TestController {

@Override
public void fileDownload(@RequestParam("fileId") Long fileId,
@RequestParam("isInline") Integer isInline,
HttpServletResponse response, HttpServletRequest request) {
System.out.println("override filedownload...");
}
}

处理方式二(自定义注解)

我们可以自定义一个注解 @ExcludeBean,然后使用该注解来很方便的在任何地方来排除需要排除的类。

1、自定义注解

1
2
3
4
5
6
7
8
9
10
11
12
/** 
* 自定义注解,用来排除原本会在spring上下文中的bean。
* 一般用于删除一个不可以修改的Bean,然后自定义一个类继承原有类然后重写特定的方法取代原来的类
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface ExcludeBean {
Class<?>[] clazz() default {};
String[] name() default {};
}

2、自动配置类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

/**
* 排除 Spring Bean 自动配置类
*/
@AutoConfiguration
public class ExcludeBeanAutoConfiguration implements BeanDefinitionRegistryPostProcessor {

@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
// not implemented
}

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
if (beanFactory instanceof BeanDefinitionRegistry bdr) {
Arrays.stream(beanFactory.getBeanNamesForAnnotation(ExcludeBean.class))
.map(item -> beanFactory.findAnnotationOnBean(item, ExcludeBean.class, false)).filter(Objects::nonNull)
.flatMap(item -> Stream.concat(Arrays.stream(item.name()),
Arrays.stream(item.clazz()).map(cls ->
org.springframework.util.StringUtils.uncapitalizeAsProperty(ClassUtils.getShortName(cls.getName())))))
.distinct()
.forEach(bdr::removeBeanDefinition);
}
}

}

3、配置自动配置

@AutoConfiguration 自动配置注解启用。

4、注解使用示例
我们来重写一个 Controller 的方法,主要应用场景是:目标 Controller 是通过 jar 包依赖过来的,并且我们需要重写其中的一个方法逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 重写 Controller 类 TestApi 中的特定方法
*/
@ExcludeBean(clazz = TestApi.class)
@RestController
public class OverrideTestApi extends TestApi {

/**
* 重写 Hello 方法
*
* @return String
*/
@Override
public String hello() {
return "Hello, Override!";
}
}

原文链接:https://shanhy.blog.csdn.net/article/details/144110051