spring启动流程的扩展点
引子
在思考“dubbo是如何基于spring实现的”这个问题时,再次瞟了一眼Spring容器的refresh
方法,解决了之前的一个疑惑。
refresh方法
简单梳理一下,我的归纳是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服务的注册、订阅和注入。这个是怎么实现的?
- 向注册中心注册和定义:自定义注解被扫描到,就可以进行相应的处理;
- 标记了
@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启动的流程简述。