스프링 3 주석을 사용하여 심플한 공장 패턴 구현
봄 3 주석으로 심플한 공장 패턴을 어떻게 구현하면 좋을지 고민했습니다.서류에서 공장 클래스를 호출하고 공장 방식을 실행할 수 있는 콩을 만들 수 있는 것을 보았습니다.주석만으로 이게 가능한지 궁금했어요.
현재 를 호출하는 컨트롤러가 있습니다.
MyService myService = myServiceFactory.getMyService(test);
result = myService.checkStatus();
My Service는 checkStatus()라는1개의 메서드를 가진 인터페이스입니다.
공장 수업은 다음과 같습니다.
@Component
public class MyServiceFactory {
public static MyService getMyService(String service) {
MyService myService;
service = service.toLowerCase();
if (service.equals("one")) {
myService = new MyServiceOne();
} else if (service.equals("two")) {
myService = new MyServiceTwo();
} else if (service.equals("three")) {
myService = new MyServiceThree();
} else {
myService = new MyServiceDefault();
}
return myService;
}
}
MyServiceOne 클래스는 다음과 같습니다.
@Autowired
private LocationService locationService;
public boolean checkStatus() {
//do stuff
}
이 코드를 실행하면 locationService 변수는 항상 늘이 됩니다.공장 내에서 직접 객체를 만들고 자동 배선이 이루어지지 않기 때문이라고 생각합니다.이 작업을 올바르게 수행하기 위해 주석을 추가할 수 있는 방법이 있습니까?
감사해요.
다음과 같은 것이 도움이 되었습니다.
인터페이스는 로직 방식과 추가 ID 방식으로 구성됩니다.
public interface MyService {
String getType();
void checkStatus();
}
일부 구현:
@Component
public class MyServiceOne implements MyService {
@Override
public String getType() {
return "one";
}
@Override
public void checkStatus() {
// Your code
}
}
@Component
public class MyServiceTwo implements MyService {
@Override
public String getType() {
return "two";
}
@Override
public void checkStatus() {
// Your code
}
}
@Component
public class MyServiceThree implements MyService {
@Override
public String getType() {
return "three";
}
@Override
public void checkStatus() {
// Your code
}
}
공장 자체는 다음과 같습니다.
@Service
public class MyServiceFactory {
@Autowired
private List<MyService> services;
private static final Map<String, MyService> myServiceCache = new HashMap<>();
@PostConstruct
public void initMyServiceCache() {
for(MyService service : services) {
myServiceCache.put(service.getType(), service);
}
}
public static MyService getService(String type) {
MyService service = myServiceCache.get(type);
if(service == null) throw new RuntimeException("Unknown service type: " + type);
return service;
}
}
이러한 구현이 더 쉽고, 더 깔끔하고, 더 확장성이 있다는 것을 알게 되었습니다.새로운 MyService를 추가하는 것은 다른 장소를 변경하지 않고 동일한 인터페이스를 구현하는 다른 스프링빈을 작성하는 것만큼이나 쉽습니다.
맞습니다. 개체를 수동으로 생성하면 스프링이 자동 배선을 수행하도록 할 수 없습니다.또한 봄까지 서비스를 관리하는 것도 고려하십시오.
@Component
public class MyServiceFactory {
@Autowired
private MyServiceOne myServiceOne;
@Autowired
private MyServiceTwo myServiceTwo;
@Autowired
private MyServiceThree myServiceThree;
@Autowired
private MyServiceDefault myServiceDefault;
public static MyService getMyService(String service) {
service = service.toLowerCase();
if (service.equals("one")) {
return myServiceOne;
} else if (service.equals("two")) {
return myServiceTwo;
} else if (service.equals("three")) {
return myServiceThree;
} else {
return myServiceDefault;
}
}
}
하지만 전체적인 디자인은 다소 빈약하다고 생각합니다.MyService 및 패스one/two/three을 추가 합니다.checkStatus()신은 무무 ?취 취? ???
@Component
public class MyServiceAdapter implements MyService {
@Autowired
private MyServiceOne myServiceOne;
@Autowired
private MyServiceTwo myServiceTwo;
@Autowired
private MyServiceThree myServiceThree;
@Autowired
private MyServiceDefault myServiceDefault;
public boolean checkStatus(String service) {
service = service.toLowerCase();
if (service.equals("one")) {
return myServiceOne.checkStatus();
} else if (service.equals("two")) {
return myServiceTwo.checkStatus();
} else if (service.equals("three")) {
return myServiceThree.checkStatus();
} else {
return myServiceDefault.checkStatus();
}
}
}
이것은 아직 설계가 불충분합니다. 왜냐하면 새로운 것을 추가하기 때문입니다.MyService에는 「」가 필요합니다.MyServiceAdapterSRP 반위을을을 을을을을을 실실 을srsr 。그러나 실제로는 이것이 좋은 출발점입니다(힌트: 맵과 전략 패턴).
DruidKuma의 다음 답변
Litte 리팩터(자동 전원 공급 컨스트럭터 포함):
@Service
public class MyServiceFactory {
private static final Map<String, MyService> myServiceCache = new HashMap<>();
@Autowired
private MyServiceFactory(List<MyService> services) {
for(MyService service : services) {
myServiceCache.put(service.getType(), service);
}
}
public static MyService getService(String type) {
MyService service = myServiceCache.get(type);
if(service == null) throw new RuntimeException("Unknown service type: " + type);
return service;
}
}
인터페이스 FactoryBean을 MyServiceFactory에 추가하고(Spring에게 공장임을 알리기 위해), 레지스터(String 서비스, MyService 인스턴스)를 추가하여 각 서비스를 호출합니다.
@Autowired
MyServiceFactory serviceFactory;
@PostConstruct
public void postConstruct() {
serviceFactory.register(myName, this);
}
이렇게 하면 필요에 따라 각 서비스 프로바이더를 모듈로 분리할 수 있습니다.Spring은 도입되어 이용 가능한 서비스 프로바이더를 자동으로 선택합니다.
스프링에 수동으로 자동 배선을 요청할 수 있습니다.
공장에서 Application Context Aware를 구현합니다.다음에, 공장에서 다음의 실장을 실시합니다.
@Override
public void setApplicationContext(final ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
콩을 작성한 후 다음 작업을 수행합니다.
YourBean bean = new YourBean();
applicationContext.getAutowireCapableBeanFactory().autowireBean(bean);
bean.init(); //If it has an init() method.
이렇게 하면 Location Service가 완벽하게 자동 연결됩니다.
공장 클래스로서 기능하는 ServiceLocatorFactoryBean 타입의 빈을 선언적으로 정의할 수도 있습니다.Spring 3에서 지원하고 있습니다.
시그니처(통상은 MyService getService() 또는 MyService getService(String id))와 함께1개 이상의 메서드를 가질 필요가 있는 인터페이스를 사용하여 해당 인터페이스를 구현하는 다이내믹프록시를 작성하는 FactoryBean 실장
다음은 스프링을 사용하여 공장 패턴을 구현하는 예입니다.
여기에서는 Pavel Chern here의 솔루션을 기반으로 이 패턴의 유니버설타입 실장을 할 수 있습니다.그러기 위해서는 Named Service 인터페이스를 도입해야 합니다.
public interface NamedService {
String name();
}
추상 클래스를 추가합니다.
public abstract class AbstractFactory<T extends NamedService> {
private final Map<String, T> map;
protected AbstractFactory(List<T> list) {
this.map = list
.stream()
.collect(Collectors.toMap(NamedService::name, Function.identity()));
}
/**
* Factory method for getting an appropriate implementation of a service
* @param name name of service impl.
* @return concrete service impl.
*/
public T getInstance(@NonNull final String name) {
T t = map.get(name);
if(t == null)
throw new RuntimeException("Unknown service name: " + name);
return t;
}
}
그런 다음 My Service와 같은 특정 객체의 콘크리트 팩토리를 만듭니다.
public interface MyService extends NamedService {
String name();
void doJob();
}
@Component
public class MyServiceFactory extends AbstractFactory<MyService> {
@Autowired
protected MyServiceFactory(List<MyService> list) {
super(list);
}
}
여기서 컴파일 시 MyService 인터페이스 구현 목록을 보여줍니다.
이 접근 방식은 앱 전체에 개체를 이름으로 생성하는 유사한 팩토리가 여러 개 있는 경우 잘 작동합니다(물론 이름으로 개체를 생성하면 비즈니스 로직으로도 충분합니다).이 맵은 String을 키로 사용할 수 있으며 서비스의 기존 구현을 모두 포함합니다.
오브젝트를 생성하는 로직이 다른 경우, 이 추가 로직은 다른 곳으로 이동해, 이러한 공장(이름으로 오브젝트를 취득)과 조합해 동작할 수 있습니다.
모든 서비스 클래스를 매개 변수로 전달하여 "AnnotationConfigApplicationContext"를 인스턴스화할 수 있습니다.
@Component
public class MyServiceFactory {
private ApplicationContext applicationContext;
public MyServiceFactory() {
applicationContext = new AnnotationConfigApplicationContext(
MyServiceOne.class,
MyServiceTwo.class,
MyServiceThree.class,
MyServiceDefault.class,
LocationService.class
);
/* I have added LocationService.class because this component is also autowired */
}
public MyService getMyService(String service) {
if ("one".equalsIgnoreCase(service)) {
return applicationContext.getBean(MyServiceOne.class);
}
if ("two".equalsIgnoreCase(service)) {
return applicationContext.getBean(MyServiceTwo.class);
}
if ("three".equalsIgnoreCase(service)) {
return applicationContext.getBean(MyServiceThree.class);
}
return applicationContext.getBean(MyServiceDefault.class);
}
}
org.springframework를 사용하셔야 할 것 같습니다.beats.factory.config 를 지정합니다.Service Locator Factory Bean.코드를 훨씬 단순화할 수 있습니다.단, MyServiceAdapter는 MyService getMyService 메서드와 alies를 사용하여 클래스를 등록하는 인터페이스 MyServiceAdapter만 생성할 수 있습니다.
코드
bean id="printStrategyFactory" class="org.springframework.beans.factory.config.ServiceLocatorFactoryBean">
<property name="YourInterface" value="factory.MyServiceAdapter" />
</bean>
<alias name="myServiceOne" alias="one" />
<alias name="myServiceTwo" alias="two" />
저는 최근 공장 패턴을 사용하고 싶은 유사한 요구 사항에 대해 작업해 왔습니다만, 향후에 계속 성장해 단일 책임 원칙에 위반되는 if 로직이 마음에 들지 않았습니다.
첫 번째 스텝은 인터페이스를 만들고 getType() 메서드를 사용합니다.지정된 컨텍스트에서는 "1", "2" 등이 반환됩니다.그렇지 않으면 어떤 것이든 상관없습니다.이것은 대부분의 사람들이 위에서 제안한 일반적인 해결책이다.
public interface MyService {
String getType();
void checkStatus();
}
일부 구현:
@Component
public class MyServiceOne implements MyService {
@Override
public String getType() {
return "one";
}
@Override
public void checkStatus() {
// Your code
}
}
@Component
public class MyServiceTwo implements MyService {
@Override
public String getType() {
return "two";
}
@Override
public void checkStatus() {
// Your code
}
}
@Component
public class MyServiceThree implements MyService {
@Override
public String getType() {
return "three";
}
@Override
public void checkStatus() {
// Your code
}
}
공장 자체는 다음과 같습니다.
@Service
public class MyServiceFactory {
@Autowired
private List<MyService> services;
public static MyService getService(final String type) {
return services
.stream().filter(service -> type.equals(service.getType()))
.findFirst()
.orElseThrow(throw new RuntimeException("Unknown service type: " + type));
}
}
이 솔루션에서는 유형에 대한 인스턴스의 주요 값을 저장하기 위해 별도의 맵이 필요하지 않습니다.이 솔루션은 공장 출하 시 List auto wireing이 있기 때문에 코드를 변경하지 않고도 확장할 수 있습니다.따라서 My Service의 향후 구현은 쉽게 실행할 수 있습니다.따라서 단일 책임 원칙도 보장됩니다.
Java 8을 사용하면서 streams()와 술어를 사용했는데, 이전 버전에서는 loop에 대해 단순합니다.
이는 새로운 인스턴스를 생성하는 위의 답변의 변형입니다.
경우,Service만 의존하다Spring관리 콩
public interface MyService {
//Code
}
@Component("One")
@Scope("prototype")
public class MyServiceOne implements MyService {
//Code
public MyServiceOne(Dependency dep){
...
}
}
@Component("Two")
@Scope("prototype")
public class MyServiceTwo implements MyService {
//Code
}
public class Factory {
Map<String,MyService> services;
ApplicationContext context;
Dependency dep;
public Factory(Map<String, MyService> components, ApplicationContext context, Dependency dep) {
...
}
MyService service(String type){
return context.getBean(services.get(type).getClass());
}
}
@Configuration
public class Config {
@Bean
Factory languageFactory(Map<String,Service> map, ApplicationContext context, Dependency dep){
return new Factory(map,context,dep);
}
}
것이 되지 않는 커스텀파라미터를 Spring 중 해 보세요.
- 빈 생성자를 추가하여 Bean을 처음 발견했을 때 인스턴스화할 수 있도록 합니다.
@Component("One")
@Scope("prototype")
public class MyServiceOne implements MyService {
//Code
public MyServiceOne(){
...
}
public MyServiceOne(Dependency dep){
...
}
public MyServiceOne(Dependency dep, Integer myFactoryValue){
...
}
}
- 또는 검출할 구성에서 수동으로 생성할 수도 있습니다.
\\ no longer available in autoscan
public class MyServiceOne implements MyService {
//Code
public MyServiceOne(){
...
}
public MyServiceOne(Dependency dep, Integer myFactoryValue){
...
}
}
@Configuration
public class Config {
@Bean("One")
@Scope("prototype")
Service serviceOne(){
// used only for dynamic discovery
return new ServiceOne();
}
...
@Bean
Factory languageFactory(Map<String,Service> map, ApplicationContext context, Dependency dep){
return new Factory(map,context,dep);
}
}
두 솔루션 모두 공장 출하 방법을 다음과 같이 정의할 수 있습니다.
public class Factory {
....
MyService service(String type, Integer someParameter){
// you provide the parameters for the constructor
return context.getBean(services.get(type).getClass(),dep,someParameter);
}
}
이것을 시험해 보세요.
public interface MyService {
//Code
}
@Component("One")
public class MyServiceOne implements MyService {
//Code
}
@Component("Two")
public class MyServiceTwo implements MyService {
//Code
}
DruidKuma와 jumping_monkey의 답변을 따릅니다.
또한 옵션도 포함하여 코드를 좀 더 깔끔하게 만들 수 있습니다.
public static MyService getService(String type) {
return Optional.ofNullable(myServiceCache.get(type))
.orElseThrow(() -> new RuntimeException("Unknown service type: " + type));
}
스프링은 빌드 시 사용해야 할 경로가 명확하지 않으면 자동 배선되지 않습니다.공장에서는 변경되지 않으므로 Location Service를 Autowire하여 다른 서비스에 전달할 수 있습니다.클래스에 서비스, 저장소 등 여러 종속성이 있는 경우 이 작업은 다소 번거로울 수 있습니다.
「MyService」클래스에 대한 의존도가 높지 않은 경우는, 다음과 같이 할 수 있습니다.
@Component
public class MyServiceFactory(){
@Autowired
LocationService locationService;
public static MyService getMyService(String service){
service = service.toLowerCase();
switch(service){
case "one":
return new MyServiceOne(locationService);
case "two":
return new MyServiceTwo(locationService);
case "three":
return new MyServiceThree(locationService);
default:
return new MyServiceDefault(locationService);
}
}
}
MyServiceOne 클래스:
@Service
public class MyServiceOne implements MyService{
public LocationService locationService;
public MyServiceOne(LocationService service){
locationService = service;
}
@Override
public checkStatus(){
// your code
}
}
MyService 인터페이스
interface MyService{
boolean checkStatus();
}
public interface MyService {
public void save();
//Code
}
@Component("One")
public class MyServiceOne implements MyService {
//Code
public void save(){
System.out.println("one");
}
}
@Component("Two")
public class MyServiceTwo implements MyService {
//Code
public void save(){
System.out.println("two");
}
}
public class FatoryClass{
@Autowired
//@Qualifier("One") //This is your default child class use qualifier or just my sample
MyService One;
public MyService setMyservice(int typeId){
switch(typeId){
case 1:
One = new MyServiceTwo();
break;
default:
System.out.println("Default child instance");
}
return One;
}
}
@Service
public class serviceComponent{
@Autowired
FatoryClass facto;
public void setFactoryMethod(int typeId){
facto.setMyService(typeId);
facto.save();
}
}
언급URL : https://stackoverflow.com/questions/6390810/implement-a-simple-factory-pattern-with-spring-3-annotations
'programing' 카테고리의 다른 글
| C#에서 사전을 JSON 문자열로 변환하려면 어떻게 해야 하나요? (0) | 2023.03.01 |
|---|---|
| '읽기 전용' 유형에 '값' 속성이 없습니다.< { } > (0) | 2023.03.01 |
| 색인 페이지의 WordPress wp_title 공백 (0) | 2023.03.01 |
| 루비의 JSON 파일에서 구문 분석 및 중첩된 해시에서 숫자 추출 (0) | 2023.03.01 |
| 지시문 내 템플리트 사용자 정의 (0) | 2023.03.01 |