Java 带弹簧和模块的六边形结构
我正在尝试在Spring Boot应用程序上使用多个Java 带弹簧和模块的六边形结构,java,spring,maven,architecture,microservices,Java,Spring,Maven,Architecture,Microservices,我正在尝试在Spring Boot应用程序上使用多个Maven模块实现六边形体系结构。主要思想依赖于抽象,而不是具体化。 我创建了三个模块测试核心,测试适配器,测试应用程序,基本上如所述: 其主要思想是具有下一个依赖项: 测试适配器取决于测试核心 测试应用程序取决于测试核心 就这些。但是,如果没有第三个依赖项,测试应用程序依赖于测试适配器,则无法实现。在我使用的示例中,他们是这样做的(添加了直接依赖项)。然而,我希望避免这种情况,因为我希望架构师设计没有这种耦合。 是否有可能以某种方式实施?
Maven
模块实现六边形体系结构。主要思想依赖于抽象,而不是具体化。
我创建了三个模块测试核心,测试适配器,测试应用程序,基本上如所述:
其主要思想是具有下一个依赖项:
- 测试适配器取决于测试核心
- 测试应用程序取决于测试核心
package com.example.core;
public interface FooService { String execute(); }
package com.example.adapter;
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Qualifier;
import com.example.core.FooService;
@Service
@Qualifier("fooService")
public class FooServiceImpl implements FooService {
public String execute() { return "Hello world!"; }
}
package com.example;
@SpringBootApplication(scanBasePackages = {"com.example"})
public class Application {
@Autowired
@Qualifier("fooService")
FooService fooService;
public static void main(String[] args) { SpringApplication.run(Application.class, args); }
@Bean
public CommandLineRunner commandLineRunner(ApplicationContext ctx) { return args -> { System.out.println(fooService.execute()); }; }
}
package com.example.core;
@Configuration
@ComponentScan(basePackages = {"com.example.core"})
public class ConfigCore { }
package com.example.adapter;
@Configuration
@ComponentScan(basePackages = {"com.example.adapter"})
public class ConfigAdapter { }
package com.example;
import com.example.core.ConfigCore;
import com.example.adapter.ConfigAdapter;
@Configuration
@ComponentScan(basePackages = {"com.example"})
@Import({ConfigCore.class, ConfigAdapter.class})
public class ConfigApplication { }
FooServiceImpl.java
package com.example.core;
public interface FooService { String execute(); }
package com.example.adapter;
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Qualifier;
import com.example.core.FooService;
@Service
@Qualifier("fooService")
public class FooServiceImpl implements FooService {
public String execute() { return "Hello world!"; }
}
package com.example;
@SpringBootApplication(scanBasePackages = {"com.example"})
public class Application {
@Autowired
@Qualifier("fooService")
FooService fooService;
public static void main(String[] args) { SpringApplication.run(Application.class, args); }
@Bean
public CommandLineRunner commandLineRunner(ApplicationContext ctx) { return args -> { System.out.println(fooService.execute()); }; }
}
package com.example.core;
@Configuration
@ComponentScan(basePackages = {"com.example.core"})
public class ConfigCore { }
package com.example.adapter;
@Configuration
@ComponentScan(basePackages = {"com.example.adapter"})
public class ConfigAdapter { }
package com.example;
import com.example.core.ConfigCore;
import com.example.adapter.ConfigAdapter;
@Configuration
@ComponentScan(basePackages = {"com.example"})
@Import({ConfigCore.class, ConfigAdapter.class})
public class ConfigApplication { }
Application.java
package com.example.core;
public interface FooService { String execute(); }
package com.example.adapter;
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Qualifier;
import com.example.core.FooService;
@Service
@Qualifier("fooService")
public class FooServiceImpl implements FooService {
public String execute() { return "Hello world!"; }
}
package com.example;
@SpringBootApplication(scanBasePackages = {"com.example"})
public class Application {
@Autowired
@Qualifier("fooService")
FooService fooService;
public static void main(String[] args) { SpringApplication.run(Application.class, args); }
@Bean
public CommandLineRunner commandLineRunner(ApplicationContext ctx) { return args -> { System.out.println(fooService.execute()); }; }
}
package com.example.core;
@Configuration
@ComponentScan(basePackages = {"com.example.core"})
public class ConfigCore { }
package com.example.adapter;
@Configuration
@ComponentScan(basePackages = {"com.example.adapter"})
public class ConfigAdapter { }
package com.example;
import com.example.core.ConfigCore;
import com.example.adapter.ConfigAdapter;
@Configuration
@ComponentScan(basePackages = {"com.example"})
@Import({ConfigCore.class, ConfigAdapter.class})
public class ConfigApplication { }
在\test application\pom.xml中没有对测试适配器的写入依赖关系
com.example
测试适配器
0.0.1-快照
编译
我经常收到下一个错误
上下文初始化期间遇到异常-取消刷新尝试:org.springframework.beans.factory.unsatifiedpendencyException:创建名为“application”的bean时出错:通过字段“fooService”表示的未满足的依赖关系;嵌套异常为org.springframework.beans.factory.NoSuchBeanDefinitionException:没有“com.example.core.FooService”类型的合格bean可用:至少需要1个符合autowire候选条件的bean。依赖项注释:{@org.springframework.beans.factory.annotation.Autowired(required=true),@org.springframework.beans.factory.annotation.Qualifier(value=fooService)}
正如您所看到的@Qualifier
注释在这里没有帮助@ComponentScan(basePackages={“com.example”})
,@SpringBootApplication(scanBasePackages={“com.example”})
解决方案是实施以下步骤:
ConfigCore.java
package com.example.core;
public interface FooService { String execute(); }
package com.example.adapter;
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Qualifier;
import com.example.core.FooService;
@Service
@Qualifier("fooService")
public class FooServiceImpl implements FooService {
public String execute() { return "Hello world!"; }
}
package com.example;
@SpringBootApplication(scanBasePackages = {"com.example"})
public class Application {
@Autowired
@Qualifier("fooService")
FooService fooService;
public static void main(String[] args) { SpringApplication.run(Application.class, args); }
@Bean
public CommandLineRunner commandLineRunner(ApplicationContext ctx) { return args -> { System.out.println(fooService.execute()); }; }
}
package com.example.core;
@Configuration
@ComponentScan(basePackages = {"com.example.core"})
public class ConfigCore { }
package com.example.adapter;
@Configuration
@ComponentScan(basePackages = {"com.example.adapter"})
public class ConfigAdapter { }
package com.example;
import com.example.core.ConfigCore;
import com.example.adapter.ConfigAdapter;
@Configuration
@ComponentScan(basePackages = {"com.example"})
@Import({ConfigCore.class, ConfigAdapter.class})
public class ConfigApplication { }
ConfigAdapter.java
package com.example.core;
public interface FooService { String execute(); }
package com.example.adapter;
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Qualifier;
import com.example.core.FooService;
@Service
@Qualifier("fooService")
public class FooServiceImpl implements FooService {
public String execute() { return "Hello world!"; }
}
package com.example;
@SpringBootApplication(scanBasePackages = {"com.example"})
public class Application {
@Autowired
@Qualifier("fooService")
FooService fooService;
public static void main(String[] args) { SpringApplication.run(Application.class, args); }
@Bean
public CommandLineRunner commandLineRunner(ApplicationContext ctx) { return args -> { System.out.println(fooService.execute()); }; }
}
package com.example.core;
@Configuration
@ComponentScan(basePackages = {"com.example.core"})
public class ConfigCore { }
package com.example.adapter;
@Configuration
@ComponentScan(basePackages = {"com.example.adapter"})
public class ConfigAdapter { }
package com.example;
import com.example.core.ConfigCore;
import com.example.adapter.ConfigAdapter;
@Configuration
@ComponentScan(basePackages = {"com.example"})
@Import({ConfigCore.class, ConfigAdapter.class})
public class ConfigApplication { }
ConfigApplication.java
package com.example.core;
public interface FooService { String execute(); }
package com.example.adapter;
import org.springframework.stereotype.Service;
import org.springframework.beans.factory.annotation.Qualifier;
import com.example.core.FooService;
@Service
@Qualifier("fooService")
public class FooServiceImpl implements FooService {
public String execute() { return "Hello world!"; }
}
package com.example;
@SpringBootApplication(scanBasePackages = {"com.example"})
public class Application {
@Autowired
@Qualifier("fooService")
FooService fooService;
public static void main(String[] args) { SpringApplication.run(Application.class, args); }
@Bean
public CommandLineRunner commandLineRunner(ApplicationContext ctx) { return args -> { System.out.println(fooService.execute()); }; }
}
package com.example.core;
@Configuration
@ComponentScan(basePackages = {"com.example.core"})
public class ConfigCore { }
package com.example.adapter;
@Configuration
@ComponentScan(basePackages = {"com.example.adapter"})
public class ConfigAdapter { }
package com.example;
import com.example.core.ConfigCore;
import com.example.adapter.ConfigAdapter;
@Configuration
@ComponentScan(basePackages = {"com.example"})
@Import({ConfigCore.class, ConfigAdapter.class})
public class ConfigApplication { }
并将相应的依赖项(如上所述)放到pom.xml。那样的话,一切正常。然而,正如我提到的,我认为这不是正确的方法。您能帮助我理解在测试适配器和测试应用程序之间消除这种依赖关系的可能性吗 这实际上与六边形结构无关 SpringBootApplication在类路径中需要在ApplicationContext中公开所有bean 我假设在您的例子中,这个类位于测试应用程序中。 如果您没有对测试适配器设置任何依赖项,则不会加载配置,因此ComponentScan无效 甚至SpringBoot中的神奇自动配置bean也必须至少在pom.xml的运行时范围内声明 此外,在六边形架构实现中,我们通常使用应用程序模块来组成bean注入策略。所以你应该把你的配置放在这层里面 适配器用于与域交互,例如Rest层 给定的应用程序有一个单独的模块,应用程序和适配器在其中聚集。但如果你想分割它,你基本上会得到:
- 在应用程序模块中:配置和*应用程序
- 在youtube适配器中:youtube包(在这种情况下为SPI)
- 在rest适配器中:控制器和资源
的主要目的是将业务逻辑与技术部分分离。没有什么规定适配器应该是独立的,但它可以是您的实现选择:)这与六边形体系结构没有真正的关系 SpringBootApplication在类路径中需要在ApplicationContext中公开所有bean 我假设在您的例子中,这个类位于测试应用程序中。 如果您没有对测试适配器设置任何依赖项,则不会加载配置,因此ComponentScan无效 甚至SpringBoot中的神奇自动配置bean也必须至少在pom.xml的运行时范围内声明 此外,在六边形架构实现中,我们通常使用应用程序模块来组成bean注入策略。所以你应该把你的配置放在这层里面 适配器用于与域交互,例如Rest层 给定的应用程序有一个单独的模块,应用程序和适配器在其中聚集。但如果你想分割它,你基本上会得到:
- 在应用程序模块中:配置和*应用程序
- 在youtube适配器中:youtube包(在这种情况下为SPI)
- 在rest适配器中:控制器和资源
的主要目的是将业务逻辑与技术部分分离。没有任何内容表明适配器应该是独立的,但它也可以是您的实现选择:)避免不必要的特定性,例如使用
限定符
,除非您要管理已知冲突。您是否尝试使用运行时
而不是编译
?这应该行得通。确认测试适配器