Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions koupleless-base-plugin/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@
<version>${sofa.ark.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<scope>provided</scope>
</dependency>

</dependencies>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alipay.sofa.koupleless.plugin.context;

import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.boot.ApplicationContextFactory;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.Ordered;

/**
* 目的是自定义 beanFactory 的销毁行为
* 1、基于Spring Boot ApplicationContextFactory SPI扩展 当WebApplicationType.REACTIVE创建一个上下文 和父类的区别是指定了自定义的BeanFactory
* 2、仅处理WebApplicationType.REACTIVE 当返回为null时 有其他SPI扩展去处理 即其他ApplicationContextFactory去处理 不同的WebApplicationType
* 3、指定 BizDefaultListableBeanFactory{@link com.alipay.sofa.koupleless.plugin.context.BizDefaultListableBeanFactory} 来自定义子模块的销毁行为
*
* @author duanzhiqiang
* @version BizAnnotationConfigReactiveWebServerApplicationContext.java, v 0.1 2024年11月08日 16:23 duanzhiqiang
*/
public class BizAnnotationConfigReactiveWebServerApplicationContext extends
AnnotationConfigReactiveWebServerApplicationContext {
/**
* 构造器
*
* @param beanFactory 指定的beanFactory
*/
public BizAnnotationConfigReactiveWebServerApplicationContext(DefaultListableBeanFactory beanFactory) {
super(beanFactory);
}

/**
* 替换上下文创建行为利用 spring boot factories 扩展 并在默认的优先级前
* {@link ApplicationContextFactory} registered in {@code spring.factories} to support
* {@link AnnotationConfigServletWebServerApplicationContext}.
*/
static class Factory implements ApplicationContextFactory, Ordered {

@Override
public ConfigurableApplicationContext create(WebApplicationType webApplicationType) {
return (webApplicationType != WebApplicationType.REACTIVE) ? null : createContext();
}

/**
* 创建上下文
*
* @return 上下文
*/
private ConfigurableApplicationContext createContext() {
//自定义BeanFactory的销毁
BizDefaultListableBeanFactory beanFactory = new BizDefaultListableBeanFactory();
return new BizAnnotationConfigReactiveWebServerApplicationContext(beanFactory);
}

@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alipay.sofa.koupleless.plugin.context;

import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.boot.ApplicationContextFactory;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.Ordered;

/**
* 目的是自定义 beanFactory 的销毁行为
* 1、基于Spring Boot ApplicationContextFactory SPI扩展 当WebApplicationType.SERVLET创建一个上下文 和父类的区别是指定了自定义的BeanFactory
* 2、仅处理WebApplicationType.SERVLET 当返回为null时 有其他SPI扩展去处理 即其他ApplicationContextFactory去处理 不同的WebApplicationType
* 3、指定 BizDefaultListableBeanFactory{@link com.alipay.sofa.koupleless.plugin.context.BizDefaultListableBeanFactory} 来自定义子模块的销毁行为
*
* @author duanzhiqiang
* @version BizAnnotationConfigServletWebServerApplicationContext.java, v 0.1 2024年11月08日 16:23 duanzhiqiang
*/
public class BizAnnotationConfigServletWebServerApplicationContext extends
AnnotationConfigServletWebServerApplicationContext {
/**
* 构造器 并使用自定义的 beanFactory
*
* @param beanFactory 指定的beanFactory
*/
public BizAnnotationConfigServletWebServerApplicationContext(DefaultListableBeanFactory beanFactory) {
super(beanFactory);
}

/**
* 替换上下文创建行为利用 spring boot factories 扩展 并在默认的优先级前
* {@link ApplicationContextFactory} registered in {@code spring.factories} to support
* {@link AnnotationConfigServletWebServerApplicationContext}.
*/
static class Factory implements ApplicationContextFactory, Ordered {

@Override
public ConfigurableApplicationContext create(WebApplicationType webApplicationType) {
return (webApplicationType != WebApplicationType.SERVLET) ? null : createContext();
}

/**
* 创建上下文
*
* @return 上下文
*/
private ConfigurableApplicationContext createContext() {
//自定义BeanFactory的销毁
BizDefaultListableBeanFactory beanFactory = new BizDefaultListableBeanFactory();
return new BizAnnotationConfigServletWebServerApplicationContext(beanFactory);
}

@Override
public int getOrder() {
return HIGHEST_PRECEDENCE;
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alipay.sofa.koupleless.plugin.context;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;

import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
* 1、当模块获取基座单例时bean 记录引用(由于无法通过名称获取bean 因为在子模块中 beanName是可以重新定义的)
* 2、在模块创建这个复用的bean时 不注册销毁行为 支持复用bean 注册单例或者是其他scope
* 3、通过记录的复用的bean 引用来判断是基座复用bean
*
* @author duanzhiqiang
* @version BizDefaultListableBeanFactory.java, v 0.1 2024年11月08日 16:45 duanzhiqiang
*/
public class BizDefaultListableBeanFactory extends DefaultListableBeanFactory {

/**
* 是否是基座beanFactory
*/
private final boolean isBaseBeanFactory;

/**
* 在创建时 额外判断是否是基座bean
*/
public BizDefaultListableBeanFactory() {
super();
this.isBaseBeanFactory = !isOnBiz();
}

/**
* 基座bean复用bean集合引用
*/
private static final Set<Object> BASE_FACTORY_REUSE_BEAN_SET = Collections.newSetFromMap(new ConcurrentHashMap<>());

/**
* 模块的类加载器名
*/
private static final String BIZ_CLASSLOADER = "com.alipay.sofa.ark.container.service.classloader.BizClassLoader";

/**
* 在子模块获取 base 的bean 记录这个bean 引用
*
* @param name the name of the bean to retrieve
* @param requiredType the required type of the bean to retrieve
* @param args arguments to use when creating a bean instance using explicit arguments
* (only applied when creating a new instance as opposed to retrieving an existing one)
* @param typeCheckOnly whether the instance is obtained for a type check,
* not for actual use
* @param <T> the required type of the bean to retrieve
* @return bean
* @throws BeansException 异常
*/
@Override
protected <T> T doGetBean(String name, Class<T> requiredType, Object[] args,
boolean typeCheckOnly) throws BeansException {

T bean = super.doGetBean(name, requiredType, args, typeCheckOnly);

// 只有是基座isBaseBeanFactory 但获取bean时是模块发起调用(即复用基座的bean时) 记录下复用的基座bean
if (isBaseBeanFactory && isOnBiz() && isSingleton(name)) {
BASE_FACTORY_REUSE_BEAN_SET.add(bean);
}

return bean;
}

@Override
public void destroySingletons() {
super.destroySingletons();
//复用bean在 基座销毁时清空
if (isBaseBeanFactory) {
BASE_FACTORY_REUSE_BEAN_SET.clear();
}
}

/**
* 判断是否需要销毁
* 扩展如果是模块上下文且是基座复用的bean 则不需要进行销毁
*
* @param bean the bean instance to check
* @param mbd the corresponding bean definition
* @return false 不需要销毁 true 需要销毁
*/
@Override
protected boolean requiresDestruction(Object bean, RootBeanDefinition mbd) {
//如果是模块上下文且是基座复用的bean 则不需要进行销毁
if (!isBaseBeanFactory && isBaseReuseBean(bean)) {
//不注册DisposableBean
return false;
}
return super.requiresDestruction(bean, mbd);
}

/**
* 是否是基座复用的bean
*
* @param bean bean 实例
* @return true 是 false 不是
*/
private boolean isBaseReuseBean(Object bean) {
return BASE_FACTORY_REUSE_BEAN_SET.contains(bean);
}

/**
* 判断是否在biz中 而不是基座中
*
* @return true 在biz中
*/
private boolean isOnBiz() {
ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
return contextClassLoader == null
|| BIZ_CLASSLOADER.equals(contextClassLoader.getClass().getName());
}

/**
* Getter method for property <tt>isBase</tt>.
*
* @return property value of isBase
*/
public boolean isBaseBeanFactory() {
return isBaseBeanFactory;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alipay.sofa.koupleless.plugin.context;

import org.springframework.boot.ApplicationContextFactory;
import org.springframework.boot.WebApplicationType;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.Ordered;

/**
* WebApplicationType.NONE 自定义上下文
* 1、基于Spring Boot ApplicationContextFactory SPI扩展 当WebApplicationType.NONE创建一个上下文
* 2、仅处理WebApplicationType.NONE 当返回为null时 有其他SPI扩展去处理 即其他ApplicationContextFactory去处理 不同的WebApplicationType
* 3、指定 BizDefaultListableBeanFactory{@link com.alipay.sofa.koupleless.plugin.context.BizDefaultListableBeanFactory} 来自定义子模块的销毁行为
* <p>
* Custom ApplicationContextFactory for WebApplicationType.NONE that uses BizDefaultListableBeanFactory
* to prevent destruction of base singleton beans when a module is destroyed.
* This factory has the highest precedence and only handles non-web application contexts.
*
* @author duanzhiqiang
* @version DefaultApplicationContextFactory.java, v 0.1 2024年11月12日 17:55 duanzhiqiang
*/
public class DefaultApplicationContextFactory implements ApplicationContextFactory, Ordered {
@Override
public ConfigurableApplicationContext create(WebApplicationType webApplicationType) {
return (webApplicationType != WebApplicationType.NONE) ? null
: new AnnotationConfigApplicationContext(new BizDefaultListableBeanFactory());
}

@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,10 @@ org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
com.alipay.sofa.koupleless.plugin.spring.SkipAutoConfigurationImportFilter

org.springframework.context.ApplicationContextInitializer=\
com.alipay.sofa.koupleless.plugin.spring.BizApplicationContextInitializer
com.alipay.sofa.koupleless.plugin.spring.BizApplicationContextInitializer


org.springframework.boot.ApplicationContextFactory=\
com.alipay.sofa.koupleless.plugin.context.BizAnnotationConfigReactiveWebServerApplicationContext.Factory,\
com.alipay.sofa.koupleless.plugin.context.BizAnnotationConfigServletWebServerApplicationContext.Factory, \
com.alipay.sofa.koupleless.plugin.context.DefaultApplicationContextFactory