您的位置:首页 > 科技 > 能源 > spring启动流程的扩展点

spring启动流程的扩展点

2025/1/7 21:12:18 来源:https://blog.csdn.net/wyy546792341/article/details/141201405  浏览:    关键词:spring启动流程的扩展点

spring启动流程的扩展点

  • 引子
  • refresh方法
    • 获取容器
    • 设置容器
    • 容器初始化完成
  • Spring中dubbo的实现
    • 自定义注解如何被扫描到容器中
  • 小结

引子

在思考“dubbo是如何基于spring实现的”这个问题时,再次瞟了一眼Spring容器的refresh方法,解决了之前的一个疑惑。

refresh方法

简单梳理一下,我的归纳是3步:

  1. 获取容器
  2. 设置容器
  3. 容器初始化完成

获取容器

ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

这里获取到的容器只是完成了实例化的容器,需要对容器对象进行属性设置,完成对象的初始化,容器才能变成可用状态。

思考:refresh是容器刷新的方法,但方法内部操作的都是BeanFactory
这里需要了解BeanFactory在Spring中的地位,它是Spring容器的核心概念,用接口文档的话来说:它是Spring容器的根接口。知道了BeanFactory本身就是一个容器后,在容器刷新的方法中操作BeanFactory也就能理解了。

设置容器

又可细分为:

  • 后置处理器的设置
    • BeanFactory后置处理器的调用
    • bean后置处理器的注册
  • 消息、事件、监听器的设置
	// Allows post-processing of the bean factory in context subclasses.postProcessBeanFactory(beanFactory);StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");// Invoke factory processors registered as beans in the context.invokeBeanFactoryPostProcessors(beanFactory);// Register bean processors that intercept bean creation.registerBeanPostProcessors(beanFactory);beanPostProcess.end();// Initialize message source for this context.initMessageSource();// Initialize event multicaster for this context.initApplicationEventMulticaster();// Initialize other special beans in specific context subclasses.onRefresh();// Check for listener beans and register them.registerListeners();

在后置处理器的设置之前,有一行代码postProcessBeanFactory(beanFactory);,这里困惑了我很久,方法名称导致我认为这也是后置处理器的一部分,但代码上的注解和下方代码块start() end(),都说明这里和后置处理器的逻辑不是一体的。这里的postProcessBeanFactory()确实也是后处理,是对传入的BeanFactory进行后续处理,它和下方的onRefresh()一样,都是模板方法,提供给子类一个修改入口。

后置处理器设置这一块,逻辑边界很清晰,start和end之间,理解以下概念后就清晰了。

  • beanDefinition【bd: bean的元数据,类似Class和实例对象的关系】
  • beanFactory、beanFactoryPostProcessor【容器,容器的后置处理器,处理加载的元数据,即bd】
  • bean、beanPostProcessor【容器中的实例,实例的后置处理器,bd已加载,尚未实例化,beanPostProcessor接口提供实例化前后的方法用于影响bean的实例化过程】

容器初始化完成

即:finishBeanFactoryInitialization(beanFactory);
上面后置处理器的代码段中,调用了beanFactory的后置处理器,注册了bean的后置处理器,bean的后置处理器的调用就在这个方法中

获取容器后、设置容器、最后完成容器的初始化,这个逻辑是通顺的。在结束容器初始化的过程中,完成了对剩下的非懒加载的单例的实例化操作。

Spring中dubbo的实现

在Spring集成dubbo的过程中,通过dubbo的@DubboService@DubboReference注解,完成dubbo服务的注册、订阅和注入。这个是怎么实现的?

  1. 向注册中心注册和定义:自定义注解被扫描到,就可以进行相应的处理;
  2. 标记了@DubboReference的字段,可以注入代理对象,实际调用时则执行真正的RPC调用。

自定义注解如何被扫描到容器中

使用beanPostProcessor不行,因为beanPostProcessor的after和before方法,拦截的都是bean。自定义注解的类还不是bean,无法被拦截。那就通过beanFactoryPostProcessor,将自定义注解的类封装成beanDefinition放入容器。

// 自定义注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WyyService {String value() default "";
}
//注解修饰类
@WyyService
public interface DemoService {
}
// 自定义beanFactory后置处理器
@Component
public class CustomAnnoInjectProcessor implements BeanDefinitionRegistryPostProcessor {@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);// 将指定包中的指定注解扫描scanner.addIncludeFilter(new AnnotationTypeFilter(WyyService.class));scanner.addIncludeFilter(new AnnotationTypeFilter(WyyReference.class));// 注册为beanDefinitionscanner.findCandidateComponents("org.wyy.simpledubbo").forEach(beanDefinition -> {registry.registerBeanDefinition(beanDefinition.getBeanClassName(), beanDefinition);});}// 加载完beanDefinition后,对容器的后置处理@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {final List<Field> referenceFields = new ArrayList<>();String[] beanNames = beanFactory.getBeanDefinitionNames();// 获取所有的reference注解属性,模仿dubbo对服务进行处理for (String beanName : beanNames) {Class<?> beanClass = beanFactory.getType(beanName);if (beanClass != null) {Field[] fields = beanClass.getDeclaredFields();for (Field field : fields) {if (field.isAnnotationPresent(WyyReference.class)) {referenceFields.add(field);}}}}doSubscribe(referenceFields);}private void doSubscribe(List<Field> referenceFields) {System.out.println("检查zk是否连接...");System.out.println("向注册中心发起一次精准订阅,同时检查服务是否存在...");// do other like: cache service list, balabala。。。referenceFields.forEach(field -> {System.out.println("订阅: "  + field.getType().getName());});}
}

自定义注解可以被扫描后,可以使用beanPostProcessor进行拦截:

// 对@WyyService进行拦截
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {Class<?> beanClass = bean.getClass();if(beanClass.isAnnotationPresent(WyyService.class)) {WyyService wyyService = beanClass.getAnnotation(WyyService.class);String serviceName = wyyService.value();if(StringUtils.isEmpty(serviceName)) {serviceName = beanClass.getSimpleName();}// 向注册中心注册registerService(serviceName, bean);}return bean;
}private void registerService(String serviceName, Object bean) {// 封装当前接口IP,端口,参数等,类似dubbo中的URLSystem.out.println("向注册中心注册服务: "+serviceName);
}
// 拦截@WyyReference
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {Field[] fields = bean.getClass().getDeclaredFields();for (Field field : fields) {if (field.isAnnotationPresent(WyyReference.class)) {// 生成代理对象,并注入到bean的字段中Object proxy = createProxy(field.getType());field.setAccessible(true);try{field.set(bean, proxy);// 设置属性} catch (Exception e) {e.printStackTrace();}}}return bean;
}private Object createProxy(Class<?> type) {System.out.println("创建代理对象...");//JDK动态代理,类似dubbo 中InvokerInvocationHandler,它也是一个InvocationHandlerreturn Proxy.newProxyInstance(type.getClassLoader(), new Class<?>[]{type}, new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("其他操作...");System.out.println("执行RPC,调用dubbo...");return null;}});
}

在此基础上,再添加其他服务治理相关的,如负载均衡、重试容错等

小结

Spring提供的各种扩展点[postProcessor],何时用?怎么用?
dubbo启动的流程简述。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com