Spring Class Global Method
来源:互联网 发布:数据精灵稳定版源 编辑:程序博客网 时间:2024/06/05 05:30
今天在遇到一个有意思的问题,那就是我们可以在Spring的xml配置文件里面可以定义bean的全局的init和destroy方法。如下图所示:
我们可以看到在beans这个标签里面可以定义一些bean的全局属性,其中包括default-init-method与default-destroy-method方法:
- init-method:Spring容器初始化bean的时候会调用的方法
- destroy-method : Spring容器销毁的时候,bean会调用的方法
有一个小伙伴就问Spring可不可以使用在类里面来实现这一功能呢?如果大家用过Spring 3.0里面的新特性,就是使用Class来定义Bean,就是使用@Configuration和@Bean注解。具体可以参看之前的blog – Spring Bean Type。它的实现是基于Spring容器的扩展BeanFactoryPostProcessor, 它是可以修改Spring BeanDefinition配置的元数据.关于Spring IOC的扩展可以参看 – Spring Container Extension。关于Spring IOC的过程我总结如下:
Resource –> BeanDefinition –> BeanWrapper –> Object
- 配置各种资源文件,包括xml, 注解(@Component及其继承注解@Service,@Controller等),Class(使用@Configuration和@Bean),Properties/Yml文件(Spring boot应用很多),Spring把它抽象为Resource接口。
- Spring把这些资源文件解析成BeanDefinition,也就是Bean的定义。里面包括在资源配置文件里面配置的bean的各种定义。里面最重要的两个方法是:
getConstructorArgumentValues()
通过构造器依赖注入,getPropertyValues()
通过setter方法注入 - Spring 把BeanDefinition通过构造器初始化bean,并通过
new BeanWrapperImpl(beanInstance)
把这个对象包装成BeanWrapper。然后通过BeanWrapper进行依赖注入。关于BeanWrapper可以参看 – Spring IOC BeanWrapper.然后通过BeanWrapper的getWrappedInstance()
获取到需要的对象,完成整个IOC的过程。
我可以看到Spring暴露给使用者的核心概念是Bean,而Spring IOC过程当中,框架里面的核心概念是BeanDefinition。那么我们需要全局的给Bean添加init-method和destroy-method。我们只需要修改BeanDefinition就好了。如果大家看过上面的 Spring Container Extension就知道,我们可以使用BeanFactoryPostProcessor这个接口。下面就是我基于注解实现全局初始化与全局销毁方法:以Spring boot为测试类。
总体思路:定义全局初始化与全局销毁的方法注解,这个注解需要与@Component注解配合使用。
1、全局初始化方法注解
定义全局初始化方法注解 – GlobalInitMethod,value是你定义的全局初始化方法名称。
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface GlobalInitMethod { String value() default "";}
2、全局销毁方法注解
定义全局销毁方法注解 – GlobalDestroyMethod ,value是你定义的全局销毁方法名称。
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface GlobalDestroyMethod { String value() default "";}
3、全局初始化方法与销毁方法配置
通过@Configration
把@GlobalInitMethod
与@GlobalDestroyMethod
加载到Spring IOC容器当中,当然你也可以使用@Component注解。但是使用@Configration更符合场景,因为这个是Spring的xml配置class化。虽然Spring支持@Component注解。
@Configuration@GlobalInitMethod(value = "initMethod")@GlobalDestroyMethod(value = "destroyMethod")public class GlobalInitDestroyMethodConfig {}
4、全局初始化方法注解解析类
把之前的Class配置解析,找到默认配置的全局初始化与销毁方法,并设置到Spring IOC容器的每一个BeanDefinition当中。
@Componentpublic class GlobalInitDestroyMethodPostProcessor implements BeanDefinitionRegistryPostProcessor { private static final String GLOBAL_INIT_METHOD = GlobalInitMethod.class.getName(); private static final String GLOBAL_DESTROY_METHOD = GlobalDestroyMethod.class.getName(); private static final String GLOBAL_METHOD_NAME = "value"; @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { String[] beanDefinitionNames = registry.getBeanDefinitionNames(); if(beanDefinitionNames == null || beanDefinitionNames.length == 0) { return; } GlobalMethodDefinition globalMethodDefinition = new GlobalMethodDefinition(); // 查找 全局初始化方法与全局销毁方法配置 for (String beanDefinitionName : beanDefinitionNames) { BeanDefinition beanDefinition = registry.getBeanDefinition(beanDefinitionName); if(beanDefinition instanceof ScannedGenericBeanDefinition) { ScannedGenericBeanDefinition scannedBeanDefinition = ScannedGenericBeanDefinition.class.cast(beanDefinition); String globalInitMethodName = extractGlobalMethod(scannedBeanDefinition, GLOBAL_INIT_METHOD); if(!StringUtils.isEmpty(globalInitMethodName)) { globalMethodDefinition.setInitMethodName(globalInitMethodName); } String globalDestroyMethodName = extractGlobalMethod(scannedBeanDefinition, GLOBAL_DESTROY_METHOD); if(!StringUtils.isEmpty(globalDestroyMethodName)) { globalMethodDefinition.setDestroyMethodName(globalDestroyMethodName); } } if(globalMethodDefinition.isHasGlobalInitMethod() && globalMethodDefinition.isHasGlobalDestroyMethod()) { break; } } // 为BeanDefinition配置全局初始化和销毁方法 for (String beanDefinitionName : beanDefinitionNames) { BeanDefinition beanDefinition = registry.getBeanDefinition(beanDefinitionName); if(beanDefinition instanceof AbstractBeanDefinition) { AbstractBeanDefinition abstractBeanDefinition = (AbstractBeanDefinition) beanDefinition; if(globalMethodDefinition.isHasGlobalInitMethod()) { if(StringUtils.isEmpty(abstractBeanDefinition.getInitMethodName())){ abstractBeanDefinition.setInitMethodName(globalMethodDefinition.getInitMethodName()); abstractBeanDefinition.setEnforceInitMethod(false); } } if(globalMethodDefinition.isHasGlobalDestroyMethod()) { if(StringUtils.isEmpty(abstractBeanDefinition.getDestroyMethodName())) { abstractBeanDefinition.setDestroyMethodName(globalMethodDefinition.getDestroyMethodName()); abstractBeanDefinition.setEnforceDestroyMethod(false); } } } } } private String extractGlobalMethod(ScannedGenericBeanDefinition scannedBeanDefinition, String annotationName) { AnnotationMetadata metadata = scannedBeanDefinition.getMetadata(); MultiValueMap<String, Object> allInitAnnotationAttributes = metadata.getAllAnnotationAttributes(annotationName); if(allInitAnnotationAttributes != null && allInitAnnotationAttributes.containsKey(GLOBAL_METHOD_NAME)) { List<Object> methodValue = allInitAnnotationAttributes.get(GLOBAL_METHOD_NAME); String value = (String) methodValue.get(0); if(StringUtils.hasText(value)) { return value; } } return null; } @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { // do nothing } private class GlobalMethodDefinition { private String initMethodName; private String destroyMethodName; private boolean hasGlobalInitMethod; private boolean hasGlobalDestroyMethod; public String getInitMethodName() { return initMethodName; } public void setInitMethodName(String initMethodName) { this.initMethodName = initMethodName; this.hasGlobalInitMethod = true; } public String getDestroyMethodName() { return destroyMethodName; } public void setDestroyMethodName(String destroyMethodName) { this.destroyMethodName = destroyMethodName; this.hasGlobalDestroyMethod = true; } public boolean isHasGlobalInitMethod() { return hasGlobalInitMethod; } public boolean isHasGlobalDestroyMethod() { return hasGlobalDestroyMethod; } }}
5、Spring boot启动类
通过Spring boot启动服务
@SpringBootApplicationpublic class Bootstrap { public static void main(String[] args) { SpringApplication.run(Bootstrap.class, args); }}
6、测试类
通过@Component定义一个bean,并写一个Class配置的全局初始化方法并为测试。
@Componentpublic class GlobalMethodBean { public void initMethod(){ System.out.println("init"); }}
可以看到在Spring容器启动的时候会调用到GlobalMethodBean#initMethod.
有一个小小的不足之处,初始化方法命名最好不要使用init(),我在测试的时候DispatcherServlet调用Servlet的init方法会报错,因为它还依赖于其它类。所以init方法命名最好特别一点。
- Spring Class Global Method
- Global Supervised Descent Method
- Ruby 中的各种变量(local/instance/class/global variable and assignment method)
- Class,Method
- Class,Method
- Class Method
- Class method
- global/class placement new
- Python的method, class method, static method
- 反射:Class、Method
- pthread_create in class method
- Java Class && Method
- static & Class method
- Python class method
- java8 class::method lambda
- Python class method
- class&constructor&method&field
- Ruby中的 class method, instance method, singleton method/class
- 精选机器学习&深度学习视频资源合集 !
- CSS样式(CSS可见性、css之内容移除)
- [Android][AS]Error:Failed to find Build Tools revision 26.0.2
- MS COCO数据集目标检测评估(Detection Evaluation)(来自官网)
- 数据类型
- Spring Class Global Method
- Linux 安装 mysql
- Aras innovator: 如何制作一个itemtype的BOM结构
- python 列表操作(增删查)
- [比赛交流] AI法官
- 贪心算法例题
- AI 与区块链会碰撞出什么样的火花?要取代传统银行系统!?
- pytorch的tf.transpose
- Mysql无法创建外键的原因汇总