点击关注上方蓝字,阅读更多干货~
看到这个标题,想必大家就都明白我们本文章所分享的主要内容了,下面我们就一起去看看所使用的spring框架中的一些扩展点。
spring本身是一套流程化、标准化的代码,它为了使自己更易扩展或方便使用者有更多的机会去整合,以接口这种易扩展的方式留出口子,让以spring框架为基础的家族更易扩展,功能更加强大,使用起来也更加方便。通过实现接口的形式,让我们开发者能与spring的bean过程和容器的生命周期更加贴合,而我们的关注力便可以更多的放在具体的业务实现上。因此,spring在设计的时候提供了很多扩展点。
那么spring扩展点是什么?有哪些呢?我们接下来就详细聊聊吧。
我们已经知道spring中的主要功能是对bean对象的管理,也就是spring容器替我们管理了bean对象的一生。所以,spring在对bean的管理过程中提供了一些特定的接口,这些接口会在spring管理bean或者容器的过程中被调用,让使用者可以参与或扩展对bean的管理。那么,我们来简单回顾下bean的一生吧。
从上图可以看出,spring对bean的管理从bean定义开始,会从创建bean的统一定义对象BeanDefinition,经过实例化、初始化创建出完整的bean对象,而在使用后会经过销毁的过程对bean进行销毁。
初始化
在bean的初始化过程中通过InitializationBean和配置init-method方法来扩展初始化阶段
初始化后
通过BeanPostProcessor的postProcessAfterInitialization方法来实现对bean初始化后的扩展
BeanFactoryPostProcessor是在spring容器加载了bean的定义文件之后,在bean实例化之前执行的,可以根据自己的需求修改beanDefinition。
/**
* 定义一个bean。默认为singleton
*/
"beanDemo") (
public class BeanDemo {
}
/**
* 自定义BeanFactoryPostProcessor
*/
public class BeanFactoryPostProcessorDemo implements BeanFactoryPostProcessor {
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
BeanDefinition beanDefinition = configurableListableBeanFactory.getBeanDefinition("beanDemo");
// 将BeanDemo改为原型
beanDefinition.setScope("prototype");
}
}
/**
* 读取bean
*/
4j
public class Application {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(Application.class, args);
BeanDemo beanDemo = (BeanDemo)applicationContext.getBean("beanDemo");
log.info("beanDemo== {}", beanDemo.toString());
BeanDemo beanDemo1 = (BeanDemo)applicationContext.getBean("beanDemo");
log.info("beanDemo1== {}", beanDemo1.toString());
log.info("beanDemo == beanDemo1 : {}", beanDemo.equals(beanDemo1));
}
}
// 结果
// beanDemo== com.mainto.demo.spring.BeanDemo@4b6e1c0
// beanDemo1== com.mainto.demo.spring.BeanDemo@561b61ed
// beanDemo == beanDemo1 : false
// 通过这个演示我们已经在BeanFactoryPostProcessor中将bean的类型改为原型模式,所以每次获取的bean才不是相同的
这个接口是BeanFactoryPostProcessor的一个子接口,可以添加自定义beanDefinition。
/**
* 注意哦,这里没有生成Bean的注解
*/
public class BeanDefinitionDemo {
}
/**
* 实现BeanDefinitionRegistryPostProcessor将BeanDefinitionDemo注册到BeanDefinitionRegistry中
*/
public class BeanDefinitionRegistryPostProcessorDemo implements BeanDefinitionRegistryPostProcessor {
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
// 注册BeanDefinitionDemo
RootBeanDefinition beanDefinition = new RootBeanDefinition();
beanDefinition.setBeanClass(BeanDefinitionDemo.class);
beanDefinitionRegistry.registerBeanDefinition("beanDefinitionDemo", beanDefinition);
}
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
}
4j
public class Application {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(Application.class, args);
Object beanDefinitionDemo = applicationContext.getBean("beanDefinitionDemo");
log.info("beanDefinitionDemo == {}", beanDefinitionDemo);
}
}
// 结果
// beanDefinitionDemo == com.mainto.demo.spring.BeanDefinitionDemo@3dffc764
// 结果生成了bean哦
ConfigurationClassParser#processImports方法会读取@Import中属性的值,最后生成bean。@Import注解属性有三种写法,导入普通类、导入ImportBeanDefinitionRegistrar和导入ImportSelector。
导入普通类
4j
public class ImportNormalDemo {
public void demo() {
log.info("this is importNormalDemo");
}
}
4j
(ImportNormalDemo.class)
public class Application {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(Application.class, args);
ImportNormalDemo bean = applicationContext.getBean(ImportNormalDemo.class);
bean.demo();
}
}
// 结果
// com.mainto.demo.spring.ImportNormalDemo : this is importNormalDemo
// 结果获取到了这个ImportNormalDemo 的bean
4j
public class ImportBeanDefinitionRegistryClassDemo {
public void demo() {
log.info("this is ImportBeanDefinitionRegistryClassDemo");
}
}
/**
* 实现了ImportBeanDefinitionRegistrar接口
*/
public class ImportBeanDefinitionRegistryDemo implements ImportBeanDefinitionRegistrar {
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition();
rootBeanDefinition.setBeanClass(ImportBeanDefinitionRegistryClassDemo.class);
registry.registerBeanDefinition("importBeanDefinitionRegistryClassDemo", rootBeanDefinition);
}
}
4j
(ImportBeanDefinitionRegistryDemo.class)
public class Application {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(Application.class, args);
ImportBeanDefinitionRegistryClassDemo bean = applicationContext.getBean(ImportBeanDefinitionRegistryClassDemo.class);
bean.demo();
}
}
// 结果
// .s.ImportBeanDefinitionRegistryClassDemo : this is ImportBeanDefinitionRegistryClassDemo
4j
public class ImportSelectClassDemo {
public void demo() {
log.info("this is ImportSelectClassDemo");
}
}
public class ImportSelectorDemo implements ImportSelector {
public String[] selectImports(AnnotationMetadata annotationMetadata) {
return new String[]{"com.mainto.demo.spring.bean.ImportSelectClassDemo"};
}
}
4j
(ImportSelectorDemo.class)
public class Application {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(Application.class, args);
ImportSelectClassDemo bean = applicationContext.getBean(ImportSelectClassDemo.class);
bean.demo();
}
}
// 结果
// c.m.d.spring.bean.ImportSelectClassDemo : this is ImportSelectClassDemo
这类接口可以用于给bean注入一些bean或者容器相关的信息,常见接口有:
BeanFactoryAware注入BeanFactory容器
ApplicationContextAware注入ApplicationContext
BeanNameAware注入bean的名称
EnvironmentAware注入能获取到的Environment对象,进而可以获取各种系统变量信息
4j
public class AwareDemo implements BeanFactoryAware, ApplicationContextAware, BeanNameAware, EnvironmentAware {
private BeanFactory beanFactory;
private ApplicationContext applicationContext;
private String beanName;
private Environment environment;
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
public void setBeanName(String s) {
this.beanName = s;
log.info("beanName : {}", s);
}
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public void setEnvironment(Environment environment) {
this.environment = environment;
}
}
InitDestroyAnnotationBeanPostProcessor会在初始化前这个步骤中执行@PostConstruct的方法
4j
public class PostConstructDemo {
private String name;
public PostConstructDemo() {
log.info(" 对象构造时 name:{}", name);
}
public void init() {
name = "123";
log.info("执行过postConstruct后 name:{}", name);
}
public void demo() {
log.info("执行过postConstruct后 name:{}", name);
}
}
4j
public class Application {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(Application.class, args);
PostConstructDemo bean = applicationContext.getBean(PostConstructDemo.class);
bean.demo();
}
}
// 结果
// c.m.demo.spring.bean.PostConstructDemo : 对象构造时 name:null
// c.m.demo.spring.bean.PostConstructDemo : 执行过postConstruct...
// c.m.demo.spring.bean.PostConstructDemo : 执行过postConstruct后 name:123
这个接口中afterPropertiesSet会在bean对象实例化后,且在所有属性注入完成后执行,可以处理一些实例化后一些初始业务逻辑。
4j
public class InitializingBeanDemo implements InitializingBean {
public InitializingBeanDemo() {
log.info("this is 构造方法");
}
public void afterPropertiesSet() throws Excc.m.d.spring.bean.InitializingBeanDemo : this is 构造方法eption {
log.info("this is InitializingBean afterPropertiesSet方法");
}
}
//结果
// c.m.d.spring.bean.InitializingBeanDemo : this is 构造方法
// c.m.d.spring.bean.InitializingBeanDemo : this is InitializingBean afterPropertiesSet方法
通过这个接口我们可以对容器中的bean对象进行定制处理。当所有的非懒加载的bean初始化后会回调该接口的afterSingletonsInstantiated方法。
4j
"beanDemo") (
public class BeanDemo {
private String name;
public BeanDemo() {
log.info("BeanDemo 构造实例时 name : {}", name);
}
public void custom(String name) {
this.name = name;
}
public void print() {
log.info("BeanDemo custom方法执行后 name : {}", name);
}
}
public class SmartInitializingSingletonDemo implements SmartInitializingSingleton, ApplicationContextAware {
private ApplicationContext applicationContext;
public void afterSingletonsInstantiated() {
BeanDemo bean = applicationContext.getBean(BeanDemo.class);
bean.custom("test_SmartInitializingSingletonDemo");
}
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
4j
public class Application {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(Application.class, args);
BeanDemo bean = applicationContext.getBean(BeanDemo.class);
bean.print();
}
}
// 结果
// com.mainto.demo.spring.bean.BeanDemo : BeanDemo 构造实例时 name : null
// com.mainto.demo.spring.bean.BeanDemo : BeanDemo custom方法执行后 name : test_SmartInitializingSingletonDemo
2.2.1.2.5 @PreDestroy
4j
"beanDemo") (
public class BeanDemo implements DisposableBean {
private String name;
public BeanDemo() {
log.info("BeanDemo 构造实例时 name : {}", name);
}
public void custom(String name) {
this.name = name;
}
public void print() {
log.info("BeanDemo custom方法执行后 name : {}", name);
}
public void preDestroy() {
log.info("preDestroy... ");
}
public void destroy() throws Exception {
log.info("destroy... ");
}
}
4j
"beanDemo") (
public class BeanDemo implements DisposableBean {
private String name;
public BeanDemo() {
log.info("BeanDemo 构造实例时 name : {}", name);
}
public void custom(String name) {
this.name = name;
}
public void print() {
log.info("BeanDemo custom方法执行后 name : {}", name);
}
public void preDestroy() {
log.info("preDestroy... ");
}
public void destroy() throws Exception {
log.info("destroy... ");
}
}
这个接口主要提供了两个回调方法,分别在bean初始化前后被调用。
postProcessorBeforeInitialization方法会在bean初始化前调用
postProcessorAfterInitialization方法会在bean初始化后调用,Spring中的Aop就是基于初始化后实现的
通过这个接口我们可以对spring管理的bean进行再次加工,比如可以修改bean的属性,给bean生成一个动态代理等等。
public interface TestService {
void print();
}
4j
public class TestServiceImpl1 implements TestService{
public void print() {
log.info("this is testServiceImpl1");
}
}
4j
public class TestServiceImpl2 implements TestService{
public void print() {
log.info("this is TestServiceImpl2");
}
}
4j
"beanDemo") (
public class BeanDemo implements DisposableBean {
private String name;
"testServiceImpl1") (
private TestService testService;
public BeanDemo() {
log.info("BeanDemo 构造实例时 name : {}", name);
}
public void custom(String name) {
this.name = name;
}
public void print() {
log.info("BeanDemo custom方法执行后 name : {}", name);
testService.print();
}
public void preDestroy() {
log.info("preDestroy... ");
}
public void destroy() throws Exception {
log.info("destroy... ");
}
}
4j
public class BeanPostProcessorDemo implements BeanPostProcessor, ApplicationContextAware {
private ApplicationContext applicationContext;
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if("beanDemo".equals(beanName)) {
log.info("BeforeInitialization beanDemo : {}", beanName);
}
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if("beanDemo".equals(beanName)) {
log.info("AfterInitialization beanDemo : {}", beanName);
Class<?> aClass = bean.getClass();
Field[] declaredFields = aClass.getDeclaredFields();
for(Field field : declaredFields) {
field.setAccessible(true);
if(field.getType().isInterface()
&& field.getType().getName().equals("com.mainto.demo.spring.service.TestService")) {
String value = field.getAnnotation(Qualifier.class).value();
field.set(bean, applicationContext.getBean(value, TestService.class));
}
}
}
return bean;
}
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
4j
public class Application {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(Application.class, args);
BeanDemo bean = applicationContext.getBean(BeanDemo.class);
bean.print();
}
}
// 结果
// c.m.demo.spring.BeanPostProcessorDemo : BeforeInitialization beanDemo : beanDemo
// c.m.demo.spring.BeanPostProcessorDemo : AfterInitialization beanDemo : beanDemo
// c.m.d.spring.service.TestServiceImpl1 : this is testServiceImpl1
如果我们想创建一个完全由我们控制的bean,那么就可以使用这个接口,但这种方式创建出来的bean是不会经过完整生命周期的,只会经过初始化后。
public class DemoBean {
}
public class DemoBeanFactoryBean implements FactoryBean {
public Object getObject() throws Exception {
return new DemoBean();
}
public Class<?> getObjectType() {
return DemoBean.class;
}
/**
* 工厂管理的的bean对象是否为单例的
* 如果该方法返回true, 那么通过getObject()方法返回的对象都是同一个对象
* @return
*/
public boolean isSingleton() {
return false;
}
}
public class Application {
public static void main(String[] args) throws Exception {
ConfigurableApplicationContext applicationContext = SpringApplication.run(Application.class, args);
Object bean = applicationContext.getBean("demoBeanFactoryBean");
System.out.println(bean);
Object bean1 = applicationContext.getBean("demoBeanFactoryBean");
System.out.println(bean1);
System.out.println(bean.equals(bean1));
// 如果想获取DemoBeanFactoryBean这个bean 获取的时候需要添加&
DemoBeanFactoryBean demoBeanFactoryBean = (DemoBeanFactoryBean)applicationContext.getBean("&demoBeanFactoryBean");
System.out.println(demoBeanFactoryBean);
Object object = demoBeanFactoryBean.getObject();
System.out.println(object);
System.out.println(bean.equals(object));
}
}
// 结果
// 因isSingleton方法返回的是false
// com.mainto.demo.spring.bean.DemoBean@7fe82967
// com.mainto.demo.spring.bean.DemoBean@50850539
// false
// com.mainto.demo.spring.DemoBeanFactoryBean@65e21ce3
// com.mainto.demo.spring.bean.DemoBean@6c3659be
// false
实现这个接口可以用于对需要增强的方法,在调用之前、调用过程中以及调用之后进行增强。
4j
public class MethodInterceptorDemo implements MethodInterceptor {
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
String name = methodInvocation.getMethod().getName();
log.info("method name:{}", name);
return methodInvocation.proceed();
}
}
public class InterceptorConfig {
public static final String traceExecution = "execution(* com.mainto.demo.spring.service..*.*(..))";
public DefaultPointcutAdvisor defaultPointcutAdvisor2() {
MethodInterceptorDemo interceptor = new MethodInterceptorDemo();
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression(traceExecution);
// 配置增强类advisor
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
advisor.setPointcut(pointcut);
advisor.setAdvice(interceptor);
return advisor;
}
}
/**
* @author jlk
* @Description
* @Title: BeanDemo
* @Package: com.mainto.demo
* @CreateTime:
*/
4j
"beanDemo") (
public class BeanDemo implements DisposableBean {
private String name;
"testServiceImpl1") (
private TestService testService;
public BeanDemo() {
log.info("BeanDemo 构造实例时 name : {}", name);
}
public void custom(String name) {
this.name = name;
}
public void print() {
log.info("BeanDemo custom方法执行后 name : {}", name);
testService.print();
}
public void preDestroy() {
log.info("preDestroy... ");
}
public void destroy() throws Exception {
log.info("destroy... ");
}
}
public interface TestService {
void print();
}
4j
public class TestServiceImpl1 implements TestService{
public void print() {
log.info("this is testServiceImpl1");
}
}
4j
public class Application {
public static void main(String[] args) throws Exception {
ConfigurableApplicationContext applicationContext = SpringApplication.run(Application.class, args);
BeanDemo bean = applicationContext.getBean(BeanDemo.class);
bean.print();
}
}
// 结果
// c.m.demo.spring.MethodInterceptorDemo : method name:print
// c.m.d.spring.service.TestServiceImpl1 : this is testServiceImpl1
通过实现这个接口可以订阅容器的发布的事件,比如ContextRefreshedEvent,来处理一些特定的义务逻辑 。
4j
public class ApplicationListenerDemo implements ApplicationListener {
public void onApplicationEvent(ApplicationEvent applicationEvent) {
log.info("applicationEvent : {}", applicationEvent.getClass());
}
}
// 结果
// c.m.demo.spring.ApplicationListenerDemo : applicationEvent : class org.springframework.boot.web.servlet.context.ServletWebServerInitializedEvent
// c.m.demo.spring.ApplicationListenerDemo : applicationEvent : class org.springframework.context.event.ContextRefreshedEvent
// com.mainto.demo.Application : Started Application in 3.671 seconds (JVM running for 4.51)
// c.m.demo.spring.ApplicationListenerDemo : applicationEvent : class org.springframework.boot.context.event.ApplicationStartedEvent
// c.m.demo.spring.ApplicationListenerDemo : applicationEvent : class org.springframework.boot.availability.AvailabilityChangeEvent
// c.m.demo.spring.ApplicationListenerDemo : applicationEvent : class org.springframework.boot.context.event.ApplicationReadyEvent
// c.m.demo.spring.ApplicationListenerDemo : applicationEvent : class org.springframework.boot.availability.AvailabilityChangeEvent
Lifecycle可以用来处理一些具有生命周期的义务,比如启动时注册一个客户端,然后为了避免资源位置占用,在容器关闭时需要停止客户端的注册。
4j
public class LifecycleDemo implements SmartLifecycle {
private final AtomicBoolean isRunning = new AtomicBoolean(false);
public void start() {
log.info("LifecycleDemo start ...");
isRunning.compareAndSet(false, true);
}
public void stop() {
log.info("LifecycleDemo stop ...");
isRunning.compareAndSet(true, false);
}
public boolean isRunning() {
return isRunning.get();
}
}
// 结果
// com.mainto.demo.spring.LifecycleDemo : LifecycleDemo start ...
// 关闭进程时:com.mainto.demo.spring.LifecycleDemo : LifecycleDemo stop ...
4j
public class ApplicationRunnerDemo implements ApplicationRunner {
public void run(ApplicationArguments args) throws Exception {
log.info("args : {}", args.getOptionNames());
}
}
4j
public class CommandLineRunnerDemo implements CommandLineRunner {
public void run(String... args) throws Exception {
log.info("args:{}", args);
}
}
这两个接口是在同一个方法内被调用,SpringApplication#callRunners方法统一被触发,这两者没什么很大的区别,只是在接收方法入参上有些区别。
CommandLineRunner方法入参为可变参数,接收的是java-jar启动jar包时的可变参数,没做过处理
Initializers
\ =
org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\
org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener
配置好spring.factories文件就可以使用SpringFactoriesLoader.loadFactoryNames找到对应的类,在springboot的自动配置类中就是这样去处理的。
// 在工程目录下新建包 ext
// 在ext包下创建配置类 TestConfiguration
public class TestConfiguration {
}
// 在META-INF/spring.factories文件中增加内容
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
ext.TestConfiguration
4j
public class Application {
public static void main(String[] args) throws Exception {
ConfigurableApplicationContext applicationContext = SpringApplication.run(Application.class, args);
TestConfiguration bean = applicationContext.getBean(TestConfiguration.class);
System.out.println(bean);
}
}
// 结果
// ext.TestConfiguration$$EnhancerBySpringCGLIB$$c2f80267@63d5874f
接下来用我们现在经常使用springcloud中的几个关键组件来说明在其他框架中是如何与spring进行整合的。
/**
* 这个方法是实现ApplicationListener需要重写的方法,看到这个方法入参就可以看出,
* 这个AbstractAutoServiceRegistration监听的事件是 WebServerInitializedEvent
* WebServerInitializedEvent在springboot中有两个子类,咱们常用的是ServletWebServerInitializedEvent
* ServletWebServerInitializedEvent会在webServer启动的方法里发布
*/
@Override
@SuppressWarnings("deprecation")
public void onApplicationEvent(WebServerInitializedEvent event) {
bind(event);
}
/**
* 重写ApplicationListener的bind方法处理监听的事件触发的逻辑
*/
@Deprecated
public void bind(WebServerInitializedEvent event) {
ApplicationContext context = event.getApplicationContext();
if (context instanceof ConfigurableWebServerApplicationContext) {
if ("management".equals(((ConfigurableWebServerApplicationContext) context)
.getServerNamespace())) {
return;
}
}
// 使用CAS设置端口号
this.port.compareAndSet(0, event.getWebServer().getPort());
// 开始启动
this.start();
}
public void start() {
if (!isEnabled()) {
if (logger.isDebugEnabled()) {
logger.debug("Discovery Lifecycle disabled. Not starting");
}
return;
}
// only initialize if nonSecurePort is greater than 0 and it isn't already running
// because of containerPortInitializer below
// 判断是否正在运行,如果没有运行进行启动逻辑
if (!this.running.get()) {
// 发布InstancePreRegistreredEvent事件
this.context.publishEvent(
new InstancePreRegisteredEvent(this, getRegistration()));
// 这就开始注册服务喽
register();
// 发布InstanceRegisteredEvent 事件
this.context.publishEvent(
new InstanceRegisteredEvent<>(this, getConfiguration()));
// 使用CAS设置运行状态为true
this.running.compareAndSet(false, true);
}
}
在nacos的客户端jar包中有个NacosWatch的类,我们来看下其继承关系:
图3 NacosWatch类的继承关系
从上图可以看出nacos实现了SmartLifecyle这个接口,重写了start和stop这两个接口。
public void start() {
// 使用CAS设置运行状态为true
if (this.running.compareAndSet(false, true)) {
// 设置时间监听
EventListener eventListener = listenerMap.computeIfAbsent(buildKey(),
event -> new EventListener() {
public void onEvent(Event event) {
if (event instanceof NamingEvent) {
List<Instance> instances = ((NamingEvent) event)
.getInstances();
// 根据ip和端口号选择instance
Optional<Instance> instanceOptional = selectCurrentInstance(
instances);
// 如果存在重设元数据信息
instanceOptional.ifPresent(currentInstance -> {
resetIfNeeded(currentInstance);
});
}
}
});
// 根据nacos配置获取NamingService
NamingService namingService = nacosServiceManager
.getNamingService(properties.getNacosProperties());
try {
// 订阅配置中的服务
namingService.subscribe(properties.getService(), properties.getGroup(),
Arrays.asList(properties.getClusterName()), eventListener);
}
catch (Exception e) {
log.error("namingService subscribe failed, properties:{}", properties, e);
}
// 给watchFuture赋值定时线程
this.watchFuture = this.taskScheduler.scheduleWithFixedDelay(
this::nacosServicesWatch, this.properties.getWatchDelay());
}
}
public void stop() {
// 当容器停止时,nacos也需要执行停止逻辑
// 使用CAS将运行状态改为false
if (this.running.compareAndSet(true, false)) {
// 判断观测任务是否为null,如果不为null,则停止任务
if (this.watchFuture != null) {
// shutdown current user-thread,
// then the other daemon-threads will terminate automatic.
this.taskScheduler.shutdown();
this.watchFuture.cancel(true);
}
EventListener eventListener = listenerMap.get(buildKey());
try {
NamingService namingService = nacosServiceManager
.getNamingService(properties.getNacosProperties());
// 解除订阅
namingService.unsubscribe(properties.getService(), properties.getGroup(),
Arrays.asList(properties.getClusterName()), eventListener);
}
catch (Exception e) {
log.error("namingService unsubscribe failed, properties:{}", properties,
e);
}
}
}
从开始接触请求同名(同域名或者服务名)多实例开始就有一个概念一直贯穿其中,那就是负载,那么在springcloud中是如何负载的呢?没错,那就是使用我们的ribbon或者说是LoadBalancer,那么LoadBalancer又是如何和spring进行整合的呢?
/**
* 这里是用SamrtInitializingSingleton
* 当所有懒加载的bean初始化后,spring容器会回调afterSingletonsInstantiated方法
* 也就是遍历所有的自定义RestTemplate,给RestTemplate添加拦截器实现负载拦截
*/
public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
return () -> restTemplateCustomizers.ifAvailable(customizers -> {
for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
for (RestTemplateCustomizer customizer : customizers) {
customizer.customize(restTemplate);
}
}
});
}
false) (proxyBeanMethods =
/**
* 条件注入
*/
"org.springframework.retry.support.RetryTemplate") (
static class LoadBalancerInterceptorConfig {
/**
* 注入LoadBalanceInterceptor Bean
*/
public LoadBalancerInterceptor loadBalancerInterceptor(
LoadBalancerClient loadBalancerClient,
LoadBalancerRequestFactory requestFactory) {
return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
}
/**
* 注入RestTemplateCustomizer Bean
* 并重写customize方法
*/
// 条件注入
public RestTemplateCustomizer restTemplateCustomizer(
final LoadBalancerInterceptor loadBalancerInterceptor) {
return restTemplate -> {
List<ClientHttpRequestInterceptor> list = new ArrayList<>(
restTemplate.getInterceptors());
// 给RestTemplate 添加负载拦截
list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);
};
}
}
通过上述代码,我们知道了LoadBalancer是如何与Spring整合的,接下来我们一起看看spring cloud openFeign是怎么跟spring整合的。
同上文一样,我们还是先找出关键类。我们在使用feign的时候,除了要引入相关依赖包,是否还要在启动类上添加注解@EnableFeignClients?大家有没有尝试点进去看过呢?
public EnableFeignClients
图4 FeignClientsRegistrar类的继承关系
这个类里我们能看到ImportBeanDefinitionRegistrar这个接口,这个接口可以让我们添加一些自定义的BeanDefinition。
/**
* 重写 ImportBeanDefinitionRegistrar#registerBeanDefinitions方法
* ImportBeanDefinitionRegisrar是spring其中一个扩展点,这个接口的主要功能是提供开发者
* 可以扩展注册自定义的BeanDefinition
* AnnotationMetadata 是注解元数据
* BeanDefinitionRegistry可以理解成BeanDefinition的操作接口类,都会存储到beanDefinitionMap中
*/
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
// 注册默认配置
registerDefaultConfiguration(metadata, registry);
// 注册feignClient
registerFeignClients(metadata, registry);
}
/**
* 注册feignClient
*/
public void registerFeignClients(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
// 存放解析@EnableFeignClients注解中的内容,找到所有的FeignClient
LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>();
Map<String, Object> attrs = metadata
.getAnnotationAttributes(EnableFeignClients.class.getName());
final Class<?>[] clients = attrs == null ? null
: (Class<?>[]) attrs.get("clients");
if (clients == null || clients.length == 0) {
ClassPathScanningCandidateComponentProvider scanner = getScanner();
scanner.setResourceLoader(this.resourceLoader);
scanner.addIncludeFilter(new AnnotationTypeFilter(FeignClient.class));
Set<String> basePackages = getBasePackages(metadata);
for (String basePackage : basePackages) {
candidateComponents.addAll(scanner.findCandidateComponents(basePackage));
}
}
else {
for (Class<?> clazz : clients) {
candidateComponents.add(new AnnotatedGenericBeanDefinition(clazz));
}
}
// 遍历扫描到的FeignClient BeanDefinition 注册bean
for (BeanDefinition candidateComponent : candidateComponents) {
if (candidateComponent instanceof AnnotatedBeanDefinition) {
// verify annotated class is an interface
// 校验被FeignClient标注的类是一个接口
AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
Assert.isTrue(annotationMetadata.isInterface(),
"@FeignClient can only be specified on an interface");
Map<String, Object> attributes = annotationMetadata
.getAnnotationAttributes(FeignClient.class.getCanonicalName());
// 获取client名称
String name = getClientName(attributes);
// 注册clientConfiguration
registerClientConfiguration(registry, name,
attributes.get("configuration"));
// 注册feignClient
registerFeignClient(registry, annotationMetadata, attributes);
}
}
/**
* 注册FeignClient
* 1.根据注解信息获取类名拿到class对象
* 2.生成FeignClientFactoryBean 对象
* 3.将FeignClientFactoryBean封装成beanDefinition对象并注册
*/
private void registerFeignClient(BeanDefinitionRegistry registry,
AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
String className = annotationMetadata.getClassName();
Class clazz = ClassUtils.resolveClassName(className, null);
ConfigurableBeanFactory beanFactory = registry instanceof ConfigurableBeanFactory
? (ConfigurableBeanFactory) registry : null;
String contextId = getContextId(beanFactory, attributes);
String name = getName(attributes);
// 这里体现了使用的另一个扩展点 FactoryBean
// 如果需要定义一个逻辑相对复杂,且需要注册成bean, 但不好用@Bean注解来生成bean可以使用FactoryBean
FeignClientFactoryBean factoryBean = new FeignClientFactoryBean();
factoryBean.setBeanFactory(beanFactory);
factoryBean.setName(name);
factoryBean.setContextId(contextId);
factoryBean.setType(clazz);
// 定义BeanDefinition
BeanDefinitionBuilder definition = BeanDefinitionBuilder
.genericBeanDefinition(clazz, () -> {
factoryBean.setUrl(getUrl(beanFactory, attributes));
factoryBean.setPath(getPath(beanFactory, attributes));
factoryBean.setDecode404(Boolean
.parseBoolean(String.valueOf(attributes.get("decode404"))));
Object fallback = attributes.get("fallback");
if (fallback != null) {
factoryBean.setFallback(fallback instanceof Class
? (Class<?>) fallback
: ClassUtils.resolveClassName(fallback.toString(), null));
}
Object fallbackFactory = attributes.get("fallbackFactory");
if (fallbackFactory != null) {
factoryBean.setFallbackFactory(fallbackFactory instanceof Class
? (Class<?>) fallbackFactory
: ClassUtils.resolveClassName(fallbackFactory.toString(),
null));
}
return factoryBean.getObject();
});
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
definition.setLazyInit(true);
validate(attributes);
AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
beanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, className);
beanDefinition.setAttribute("feignClientsRegistrarFactoryBean", factoryBean);
// has a default, won't be null
boolean primary = (Boolean) attributes.get("primary");
beanDefinition.setPrimary(primary);
String[] qualifiers = getQualifiers(attributes);
if (ObjectUtils.isEmpty(qualifiers)) {
qualifiers = new String[] { contextId + "FeignClient" };
}
// beanDefinition包装类
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
qualifiers);
// 注册beanDefinition
// 这些注册好的beanDefinition会在后面spring生成bean的时候创建出bean对象
// 具体的逻辑是:
// 当AbstractApplicationContext refresh时在执行finishBeanFactoryInitialization(beanFactory);
// 最后调用DefaultListableBeanFactory#preInstantiateSingletons方法生成bean对象
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
}
/**
* 自动装配类
*/
public class EnableRocketmqNormalMsgConsumerAutoConfig implements ApplicationContextAware, SmartInitializingSingleton {
public void afterSingletonsInstantiated() {
// 读取配置获取所有的订阅关系
List<RocketmqNormalDTO> rocketmqNormalDTOList = this.initSubscriptionTable();
if(CollectionUtils.isEmpty(rocketmqNormalDTOList)){
return;
}
// 遍历订阅关系开始构建容器,生成beanDefiniation 注册bean
for(RocketmqNormalDTO rocketmqNormalDTO : rocketmqNormalDTOList) {
String containerName = concatContainerName(rocketmqNormalDTO.getGroupId());
GenericApplicationContext genericApplicationContext = (GenericApplicationContext) this.applicationContext;
genericApplicationContext.registerBean(containerName, RocketmqNormalMsgConsumerContainer.class, () -> {
return this.createContainer(rocketmqNormalDTO.getSubscriptionTable(), rocketmqNormalDTO.getProperties(), containerName);
}, new BeanDefinitionCustomizer[0]);
// 获取containerBean
RocketmqNormalMsgConsumerContainer consumerContainer = genericApplicationContext.getBean(containerName, RocketmqNormalMsgConsumerContainer.class);
if(!consumerContainer.getHasRun().get()){
try{
consumerContainer.start();
} catch (Exception e) {
throw new RocketmqException("container start error", e);
}
}
log.info("容器启动完成,consumerContainerName : {}", containerName);
}
}
/**
* 创建容器
* @param subscriptionTable
* @return
*/
private RocketmqNormalMsgConsumerContainer createContainer(Map<Subscription, MessageListener> subscriptionTable,
Properties properties,
String containerName){
RocketmqNormalMsgConsumerContainer consumerContainer = new RocketmqNormalMsgConsumerContainer();
consumerContainer.setSubscriptionTable(subscriptionTable);
consumerContainer.setProperties(properties);
consumerContainer.setContainerName(containerName);
return consumerContainer;
}
}
public class RocketmqNormalMsgConsumerContainer implements InitializingBean, SmartLifecycle, ApplicationContextAware {
private ApplicationContext applicationContext;
private ConsumerBean consumerBean;
private Map<Subscription, MessageListener> subscriptionTable;
private Properties properties;
private AtomicBoolean hasRun = new AtomicBoolean(false);
private String containerName;
/**
* 重写InitializingBean的afterPropertiesSet方法,这个方法是在bean实例化好了后调用
*/
public void afterPropertiesSet() throws Exception {
// 实例化consumerBean
this.consumerBean = initConsumerBean();
}
private ConsumerBean initConsumerBean(){
ConsumerBean consumerBean = new ConsumerBean();
consumerBean.setProperties(this.properties);
consumerBean.setSubscriptionTable(this.subscriptionTable);
return consumerBean;
}
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
/**
* 设置自动启动
*/
public boolean isAutoStartup() {
return true;
}
/**
* 重写SmartLifecycle的stop方法
* 这个方法在容器关闭的时候被调用
* 这里用来关闭consumerBean
*/
public void stop(Runnable callback) {
this.stop();
callback.run();
}
/**
* 重写SmartLifecycle的start方法
* 这个方法在容器开始启动的时候被调用
* 这里用来开启consumerBean
*/
public void start() {
if(this.hasRun.get()){
throw new RocketmqException("this container has run");
}
if(this.hasRun.compareAndSet(false, true)){
this.consumerBean.start();
log.info("容器已经启动:{}", this.getContainerStartDesc());
}
}
private String getContainerStartDesc(){
StringBuffer descBuffer = new StringBuffer();
descBuffer.append("name:").append(this.containerName).append(",")
.append("properties:").append(JSON.toJSONString(this.properties)).append(",")
.append("subscriptionTable:").append(JSON.toJSONString(this.subscriptionTable));
return descBuffer.toString();
}
/**
* 重写Lifecycle的stop方法
* 这个方法在容器开始关闭的时候被调用
* 这里用来关闭consumerBean
*/
public void stop() {
if(this.hasRun.get()){
if(this.hasRun.compareAndSet(true, false)){
log.info("容器:{} 已经stop", this.getContainerName());
if(Objects.nonNull(this.consumerBean)){
this.consumerBean.shutdown();
log.info("consumerBean has shutdown");
}
}
}
}
public boolean isRunning() {
return this.hasRun.get();
}
public int getPhase() {
return Integer.MAX_VALUE;
}
}
上面聊了spring的扩展点,也聊了一些在我们熟悉的springcloud的一些使用,那么下面我们一起看看这些扩展点的具体应用场景。
如果你对spring的扩展点、扩展机制有所了解的话,那么对你理解其他框架是如何整合spring时有非常大的帮助,比如上面介绍过的nacos的整合,上面只是写出了关键类,那么这个关键类是怎么找到的呢?
首先,根据上文之前的习惯看提供的客户端jar包中是否有spring.factories,看里面内容中使用了哪几个接口的扩展机制,按照starter的老习惯可能会注意org.springframework.boot.autoconfigure.EnableAutoConfiguration这个里面有哪些类。
其次,观察每个类,看一下是否有使用spring扩展点的这种关键字眼,当看到NacosAutoServiceRegistration这个类里面有start和stop,那么这时候我们可以联想到:这应该与Lifecycle或者ApplicationListener存在关系,点开父类可以发现它果然实现了ApplicationListener。
最后,你根据找到的关键类,就能大致理解nacos客户端是怎样与spring整合的。
现在看起来spring扩展点是不是非常好用?但也要注意使用spring扩展点的前后逻辑要保持一致,时刻谨记spring生命周期的线性逻辑,确保不会逻辑冲突。
加里克,来自缦图互联网中心中台团队。
--------END--------
也许你还想看