Spring Boot 2 實踐記錄之 條件裝配

實驗項目是想要使用多種數據庫訪問方式,好比 JPA 和 MyBatis。java

項目的 Service 層業務邏輯相同,只是具體實現代碼不一樣,天然是一組接口,兩組實現類的架構比較合理。spring

不過這種模式卻有一個問題,若是 Bean 是按實現類裝配,則在切換數據庫訪問方式時,就須要大量的代碼修改。若是按接口裝配,則會出現歧義(同一接口有兩個實現,沒法自動裝配)。數據庫

雖然能夠使用「首選Bean」或「限定」裝配,可是與直接使用實現類裝配同樣,切換數據庫訪問地,仍然要大量修改源碼。mybatis

通過實驗,使用「條件裝配」實現了利用配置切換數據庫訪問方式,不須要修改代碼了。架構

示例:ide

先定義 Service 接口:spa

public interface UserServiceInterface {
    ......  
}

再定義 MyBatis 實現類:code

@Service
@Conditional(MybatisCondition.class)
public class UserServiceMybatisImpl implements UserServiceInterface {
    ......
}

注意其中的 @Conditional(MybatisCondition.class),MybatisCondition 類必須實現 org.springframework.context.annotation.Condition 接口,該接口僅有一個 matches 方法,當該方法返回真時,UserServiceMybatisImpl 被裝配。blog

MybatisCondition 的 matches 方法的邏輯被實現爲根據配置文件中的 use.data.access.method 屬性是否爲 mybatis,來決定是否裝配 UserServiceMybatisImpl 類:接口

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;

public class MybatisCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Environment env = context.getEnvironment();
        if (env.getProperty("use.data.access.method").equals("mybatis")) {
            return true;
        }
        return false;
    }
}

再定義 SPA 實現類及其 SpaCondition 類,實現方式與 Mybatis 相同,僅在配置項爲 spa 時,裝配 UserServiceSpaImpl 類:

@Service
@Conditional(SpaCondition.class)
public class UserServiceSpaImpl implements UserServiceInterface {
    ......
}
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;

public class SpaCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Environment env = context.getEnvironment();
        if (env.getProperty("use.data.access.method").equals("spa")) {
            return true;
        }
        return false;
    }
}

定義一個類,自動裝配 UserServiceInterface,並打印其類名:

public class Test {
    @Autowired
    private UserServiceInterface userServiceInterface;
    
    public void testUserService() {
        System.out.println(userServiceInterface.getClass();
    }
}      

如今還不能運行,須要添加配置項。

先將配置項配置爲 mybatis:

use.data.access.method = mybatis

運行 Test 類的 testUserService 方法,結果爲:

UserServiceMybatisImpl

將配置項修改成 spa:

use.data.access.method = spa

運行 Test 類的 testUserService 方法,結果爲:

UserServiceSpaImpl

 

不過,有個小小的缺憾,就是在 Idea 中,以下代碼行:

@Autowired
private UserServiceInterface userServiceInterface;

會有錯誤提示:

Could not autowire. There is more than one bean of 'UserServiceInterface' type.
Beans:
userServiceMybatisImpl   (UserServiceMybatisImpl.java) 
userServiceSpaImpl   (UserServiceSpaImpl.java)

因爲配置項是在運行時讀取的,Idea 在靜態語法檢查時,實在沒辦法搞定這個自動裝配的判斷。

不要緊,不影響運行!

相關文章
相關標籤/搜索