spring启动流程的扩展点

引子

在思考“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 {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
        // 将指定包中的指定注解扫描
        scanner.addIncludeFilter(new AnnotationTypeFilter(WyyService.class));
        scanner.addIncludeFilter(new AnnotationTypeFilter(WyyReference.class));
		// 注册为beanDefinition
        scanner.findCandidateComponents("org.wyy.simpledubbo").forEach(beanDefinition -> {
            registry.registerBeanDefinition(beanDefinition.getBeanClassName(), beanDefinition);
        });
    }
	// 加载完beanDefinition后,对容器的后置处理
    @Override
    public 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中的URL
    System.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,它也是一个InvocationHandler
    return Proxy.newProxyInstance(type.getClassLoader(), new Class<?>[]{type}, new InvocationHandler() {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("其他操作...");
            System.out.println("执行RPC,调用dubbo...");
            return null;
        }
    });
}

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

小结

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值