Spring如何重写内置Bean(Controller、Service等)
Spring重写内置Bean(Controller、Service等)
场景
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 重复的报错冲突。
主要代码如下:
/**
* 排除容器中的Bean
*
* @author 单红宇
* @since 2024/11/28 13:14
*/
@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()));
}
}
/**
* 重写FileController类
*
* @author 单红宇
* @since 2024/11/28 12:03
*/
@RestController //该注解必须要有,Mapping那些注解不需要
public class OverrideFileController extends FileController {
@Override
public void fileDownload(@RequestParam("fileId") Long fileId,
@RequestParam("isInline") Integer isInline,
HttpServletResponse response, HttpServletRequest request) {
System.out.println("override filedownload...");
}
}
处理方式二(自定义注解)
我们可以自定义一个注解 @ExcludeBean,然后使用该注解来很方便的在任何地方来排除需要排除的类。
1、自定义注解
/**
* 自定义注解,用来排除原本会在spring上下文中的bean。
* 一般用于删除一个不可以修改的Bean,然后自定义一个类继承原有类然后重写特定的方法取代原来的类
*
* @author 单红宇
* @since 2024/12/5 9:36
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface ExcludeBean {
Class<?>[] clazz() default {};
String[] name() default {};
}2、自动配置类
/**
* 排除 Spring Bean 自动配置类
* @author 单红宇
* @since 2024/12/5 9:13
*/
@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 自动配置注解启用的方法详见文章: SpringBoot 自动配置 @AutoConfiguration 在 2.7 版本新增
4、注解使用示例
我们来重写一个 Controller 的方法,主要应用场景是:目标 Controller 是通过 jar 包依赖过来的,并且我们需要重写其中的一个方法逻辑。
/**
* 重写 Controller 类 TestApi 中的特定方法
*
* @author 单红宇
* @since 2024/11/28 9:06
*/
@ExcludeBean(clazz = TestApi.class)
@RestController
public class OverrideTestApi extends TestApi {
/**
* 重写 Hello 方法
*
* @return String
*/
@Override
public String hello() {
return "Hello, Override!";
}
}补充
如果是基于一个接口然后有多个实现类的多实例场景,使用的地方是基于接口注入的。
也可以使用 @Primary 注解来修饰特定的类。
但是对于如 Controller 以及其他没有定义并通过接口注入方式的可能就要使用我们本文的方法了。
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
相关文章
SpringBoot定时任务两种(Spring Schedule 与 Quartz 整合 )实现方法
本篇文章主要介绍了SpringBoot定时任务两种(Spring Schedule 与 Quartz 整合 )实现方法,详细的介绍了Spring Schedule 与 Quartz 整合的两种方法,有兴趣的可以了解一下。2017-03-03
SpringCloud使用AOP统一处理Web请求日志实现步骤
这篇文章主要为大家介绍了SpringCloud使用AOP统一处理Web请求日志实现步骤,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪2023-08-08
SpringBoot 项目中的图片处理策略之本地存储与路径映射
在SpringBoot项目中,静态资源存放在static目录下,使得前端可以通过URL来访问这些资源,我们就需要将文件系统的文件路径与URL建立一个映射关系,把文件系统中的文件当成我们的静态资源即可,本文给大家介绍SpringBoot本地存储与路径映射的相关知识,感兴趣的朋友一起看看吧2023-12-12
SSH框架网上商城项目第24战之Struts2中处理多个Model请求的方法
这篇文章主要为大家详细介绍了SSH框架网上商城项目第24战之Struts2中处理多个Model请求的方法,感兴趣的小伙伴们可以参考一下2016-06-06
IDEA报错:Process terminated的问题及解决
这篇文章主要介绍了IDEA报错:Process terminated的问题及解决方案,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教2023-11-11


最新评论