骚操作,实现DependsOnByType
背景
最近在做需求的时候遇到一个小问题,我需要让A
的所有实现类bean
去依赖B
接口的所有实现类bean
,且考虑到日后的实现类bean
还会增加,需要有一劳永逸的方法,而不是每次在新增后需要再去进行维护。
思路
核心在于控制bean
的创建顺序,我需要控制A
接口的所有实现类bean
在B
接口的所有实现类bean
之前进行创建。
调研
@Order注解 or Ordered接口
场景使用场景
第一个想到的就是@Order
注解
比较经典的是: 我们在结合Spring
框架编码策略模式
的时候,通过@Resource or@Autowired
注入List<策略接口>
,就能拿到所有策略
让后对于具体策略使用@Order
注解,或者让类实现Ordered
接口控制顺序,这样在注入的时候List
中的策略就会根据具体value
进行升序。
以下是一个比较简单的案例
public interface TestInterface {
}
public abstract class AbstractTest implements TestInterface {
}
@Order(2)
@Service
public class Test1 extends AbstractTest {
}
@Order(3)
@Service
public class Test2 extends AbstractTest {
}
@Order(1)
@Service
public class Test3 extends AbstractTest {
}
启动项目,Debug
可以看到确实是按照我们@Order
注解定义的顺序升序了~
真的不行
上面只是举个常见例子,下面实测下是不是不行~
public interface TestA {
}
@Order(2)
@Service
public class TestA_1 implements TestA{
public TestA_1() {
System.out.println("TestA_1");
}
}
public interface TestB {
}
@Order(1)
@Service
public class TestB_1 implements TestB {
public TestB_1() {
System.out.println("TestB_1");
}
}
默认情况下(不使用@Order
注解),根据文件是从上到下加载,所以默认TestA_1
比TestB_1
先加载,但是我们在使用@Order
注解发现结果还是一样,依然是**TestA_1
比TestB_1
先加载**
为什么不行呢
org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons
因为实例化bean
时,是根据beanDefinitionNames
的顺序来一个一个实例化的,并没有根据我们的@Order
注解或者Ordered
接口定义的优先级来进行排序,所以@Order
注解没用!
/** List of bean definition names, in registration order. */
private volatile List<String> beanDefinitionNames = new ArrayList<>(256);
public void preInstantiateSingletons() throws BeansException {
if (logger.isTraceEnabled()) {
logger.trace("Pre-instantiating singletons in " + this);
}
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);
// 遍历创建
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
if (isFactoryBean(beanName)) {
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
if (bean instanceof FactoryBean) {
FactoryBean<?> factory = (FactoryBean<?>) bean;
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged(
(PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit,
getAccessControlContext());
}
else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
getBean(beanName);
}
}
}
else {
getBean(beanName);
}
}
}
}
@DependsOn注解扩展性太差
第二个想到的就是@DependsOn
注解,这个肯定没问题。
public interface TestA {
}
@DependsOn("testB_1")
@Service
public class TestA_1 implements TestA{
public TestA_1() {
System.out.println("TestA_1");
}
}
public interface TestB {
}
@Service
public class TestB_1 implements TestB {
public TestB_1() {
System.out.println("TestB_1");
}
}
启动项目,确实发现,TestB_1
在TestA_1
之前被创建了
但是啊,@DependsOn
注解只支持传**beanName
的数组**,那如果A
接口、B
接口的实现类bean
越来越多,那岂不是每一个A
接口实现类bean
都要加上@DependsOn
注解,且value
配置一大堆B
接口的实现类beanName
数组,这样配置起来就太太太麻烦了!
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DependsOn {
String[] value() default {};
}
发现骚操作
回到思路,核心在于控制bean
的创建顺序,我需要控制A
接口的所有实现类bean
在B
接口的所有实现类bean
之前进行创建。
仔细想想,Spring
给我们留下来那么多扩展接口,难道就没有一个可以利用的吗?
答案肯定不是,不然我这篇文章也出不来
代码实现
这不,就被我发现一个扩展接口InstantiationAwareBeanPostProcessorAdapter
@Component
public class ExtendDependsOnByType extends InstantiationAwareBeanPostProcessorAdapter implements BeanFactoryAware {
private ListableBeanFactory listableBeanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
if (beanFactory instanceof ListableBeanFactory) {
this.listableBeanFactory = (ListableBeanFactory) beanFactory;
}
}
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
if (TestA.class.isAssignableFrom(beanClass)) {
listableBeanFactory.getBeansOfType(TestB.class);
}
return null;
}
}
public interface TestA {
}
@Service
public class TestA_1 implements TestA{
public TestA_1() {
System.out.println("TestA_1");
}
}
@Service
public class TestA_2 implements TestA{
public TestA_2() {
System.out.println("TestA_2");
}
}
public interface TestB {
}
@Service
public class TestB_1 implements TestB {
public TestB_1() {
System.out.println("TestB_1");
}
}
@Service
public class TestB_2 implements TestB {
public TestB_2() {
System.out.println("TestB_2");
}
}
多加点类试试,哈哈哈哈
骚操作原理
再提一遍,核心在于控制bean
的创建顺序,我需要控制A
接口的所有实现类bean
在B
接口的所有实现类bean
之前进行创建。
A接口bean实例化时机点
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {
// ...... 省略
try {
// bean实例化之前
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean;
}
}
catch (Throwable ex) {
throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
"BeanPostProcessor before instantiation of bean failed", ex);
}
try {
// 真正去创建bean
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
if (logger.isTraceEnabled()) {
logger.trace("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
}
catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
// A previously detected exception with proper bean creation context already,
// or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
throw ex;
}
catch (Throwable ex) {
throw new BeanCreationException(
mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
}
}
翻阅源码,在createBean
方法中,会去调用doCreateBean
真正去创建bean
但在此之前会调用resolveBeforeInstantiation
,即实例化之前会做一些操作
protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
Object bean = null;
if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
Class<?> targetType = determineTargetType(beanName, mbd);
if (targetType != null) {
bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
if (bean != null) {
bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
}
}
}
mbd.beforeInstantiationResolved = (bean != null);
}
return bean;
}
protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
if (result != null) {
return result;
}
}
}
return null;
}
通过applyBeanPostProcessorsBeforeInstantiation
方法,会调用InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation
这个时机就是我们要的A
接口所有实现类bean
实例化的时机点
则可以先写出如下代码
@Component
public class ExtendDependsOnByType extends InstantiationAwareBeanPostProcessorAdapter {
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
// 如果是TestA接口的实现类进行实例化
if (TestA.class.isAssignableFrom(beanClass)) {
}
}
}
先创建B接口所有bean
接下来就是要先创建B
接口的所有bean
稍微了解Spring
框架的朋友们应该是知道如下源码的
// org.springframework.beans.factory.support.AbstractBeanFactory
@Override
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
@Override
public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
return doGetBean(name, requiredType, null, false);
}
@Override
public Object getBean(String name, Object... args) throws BeansException {
return doGetBean(name, null, args, false);
}
没错,从BeanFactory
获取bean
,其核心代码最终如下,正是createBean
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
所以思路这不就来了,我们可以在A
接口实现类bean
实例化时,进行B
接口getBean
,当然getBean
只是返回单个,我们需要All
这样就可以先去创建B
接口的所有bean
了,那么怎么getAllBean
呢?
ApplicationContextUtil.context.getBeansOfType()
上面那行代码相信不用我多说吧,根据Type
获取所有bean
,源码一看是ListableBeanFactory
提供的方法
再瞅一瞅,是否满足期望,可以看到源码中是获取到Type
的所有beanName
,然后遍历进行getBean
public <T> Map<String, T> getBeansOfType(
@Nullable Class<T> type, boolean includeNonSingletons, boolean allowEagerInit) throws BeansException {
// 获取目标Type的所有beanName
String[] beanNames = getBeanNamesForType(type, includeNonSingletons, allowEagerInit);
Map<String, T> result = new LinkedHashMap<>(beanNames.length);
for (String beanName : beanNames) {
try {
// 遍历进行getBean
Object beanInstance = getBean(beanName);
if (!(beanInstance instanceof NullBean)) {
result.put(beanName, (T) beanInstance);
}
}
catch (BeanCreationException ex) {
// ......省略
}
}
return result;
}
@Override
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
所以写出最终代码
@Component
public class ExtendDependsOnByType extends InstantiationAwareBeanPostProcessorAdapter implements BeanFactoryAware {
private ListableBeanFactory listableBeanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
if (beanFactory instanceof ListableBeanFactory) {
this.listableBeanFactory = (ListableBeanFactory) beanFactory;
}
}
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
if (TestA.class.isAssignableFrom(beanClass)) {
listableBeanFactory.getBeansOfType(TestB.class);
}
return null;
}
}
结尾
最终写出满足目标期望的代码也是也就十几行,也不是什么高超的设计,无非就是利用Spring
预留的扩展接口来完成‘骚操作’
比较满意的是我并没有妥协使用@DependsOn
来完成目标期望,为了以后的扩展付出了一定的时间与精力,不过很好,最终完美达到了我的期望~
最后,我是 Code皮皮虾 ,会在以后的日子里跟大家一起学习,一起进步!
觉得文章不错的话,求一个点赞~,更多精彩内容尽在->JavaCodes。
转载自:https://juejin.cn/post/7237877400549621821