首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計綜合問答

Spring Tomcat 啟動時攔截 DAO 實現類進行依賴注入:ApplicationContextAware 與 BeanPostProcessor 實現

     編輯:程式設計綜合問答

需求分析

Tomcat 啟動階段,自動識別所有繼承特定基類的 DAO 實現類,並向其注入公共依賴(如 DataSource、RedisTemplate 等)。

方案一:實現 ApplicationContextAware + BeanFactoryPostProcessor

在容器實例化 Bean 前,掃描並修改 Bean 定義:

@Component
public class DaoInjector implements ApplicationContextAware, BeanFactoryPostProcessor {
    private ConfigurableListableBeanFactory beanFactory;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        // 可選:獲取上下文
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        this.beanFactory = beanFactory;
        String[] beanNames = beanFactory.getBeanDefinitionNames();
        for (String beanName : beanNames) {
            BeanDefinition bd = beanFactory.getBeanDefinition(beanName);
            String className = bd.getBeanClassName();
            if (className != null) {
                try {
                    Class clazz = Class.forName(className);
                    // 判斷是否為目標 DAO 實現類(如實現 BaseDao 接口或繼承 BaseDaoImpl)
                    if (BaseDao.class.isAssignableFrom(clazz) && !clazz.isInterface()) {
                        // 動態添加依賴注入屬性
                        MutablePropertyValues mpv = bd.getPropertyValues();
                        if (!mpv.contains("dataSource")) {
                            mpv.addPropertyValue(new PropertyValue("dataSource", new RuntimeBeanReference("dataSource")));
                        }
                        if (!mpv.contains("redisTemplate")) {
                            mpv.addPropertyValue(new PropertyValue("redisTemplate", new RuntimeBeanReference("redisTemplate")));
                        }
                    }
                } catch (ClassNotFoundException ignored) {}
            }
        }
    }
}

方案二:BeanPostProcessor 後置處理(推薦)

在 Bean 初始化後、注入完成前,通過反射強制設置字段:

@Component
public class DaoInjectionPostProcessor implements BeanPostProcessor, Ordered {
    @Autowired private DataSource dataSource;
    @Autowired private RedisTemplate redisTemplate;

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        Class clazz = bean.getClass();
        // 掃描當前類及父類字段
        while (clazz != null && clazz != Object.class) {
            for (Field field : clazz.getDeclaredFields()) {
                if (field.getType() == DataSource.class && field.getAnnotation(Autowired.class) != null) {
                    field.setAccessible(true);
                    ReflectionUtils.setField(field, bean, dataSource);
                } else if (field.getType() == RedisTemplate.class && field.getAnnotation(Autowired.class) != null) {
                    field.setAccessible(true);
                    ReflectionUtils.setField(field, bean, redisTemplate);
                }
            }
            clazz = clazz.getSuperclass();
        }
        return bean;
    }

    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE; // 優先執行
    }
}

方案三:自定義註解 + ImportBeanDefinitionRegistrar(最優雅)


@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EnableDaoInjection {}

@Configuration
public class DaoInjectionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        // 掃描標註 @Repository 或特定包下的類,自動註冊 BeanPostProcessor
        registry.registerBeanDefinition("daoInjectionProcessor", 
            BeanDefinitionBuilder.genericBeanDefinition(DaoInjectionPostProcessor.class).getBeanDefinition());
    }
}

// 啟用類上添加 @EnableDaoInjection

最佳實踐建議

  1. 優先使用 構造器注入 或 @Autowired 標準方式,避免「黑魔法」增加維護難度。
  2. 若確需自動化,採用 BeanPostProcessor 方案二,非侵入式、順序可控。
  3. 配合 @ConditionalOnMissingBean 避免重複注入。
  4. 參考 CSDN 專欄 完整項目結構。

Spring 提供豐富擴展點,合理選擇可實現靈活的自動化裝配,但務必權衡複雜度與可讀性。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved