사용자 정의 bean 용기 코드 가 독성 향상

10905 단어 springboot
개발 중 에 이런 장면 이 자주 있다.
특정한 유형의 표지 에 따라 서로 다른 업무 논리 에 따라 우 리 는 보통 if (type. equals (xxxx) 나 switch 문 구 를 사용 하여 논리 적 으로 처리한다.
이렇게 하 는 것 은 당연히 아무런 문제 가 없다.
업무 논리 가 점점 복잡 해 지고 유형 표지 가 많아 지면 if 판단 이 증가 하거나 switch case 분기 가 많아 지 는 것 을 피하 기 어렵다. 이런 코드 는 너무 지루 하고 코드 의 중복 성 이 비교적 크 거나 강요 격 이 높 지 않다.
본 고 는 사용자 정의 Bean 용기 의 개발 방식 을 바탕 으로 코드 의 판단 가 지 를 없 애고 코드 의 가 독성 을 향상 시 키 는 것 을 소개 한다.
우 리 는 demo 를 통 해 어떻게 이런 인 코딩 방식 을 실현 하 는 지 보 았 다.
1. 정의 인터페이스
먼저 하나의 인 터 페 이 스 를 정의 하 는데 주로 두 가지 방법 이 있다.
public interface AbstractService {

    /**
    *   serviceName
    *   bean    
    * @return
    */
    String serviceName();

    /**
    *    service  
    * @param parm
    * @return
    */
    T execute(Object parm);
}

구현 클래스 는 serviceName 을 실행 하고 구체 적 인 유형 을 되 돌려 야 합 니 다. 서로 다른 bean 구현 클래스 는 이 반환 값 이 중복 되 지 않도록 주의 하 십시오.
execute 방법 은 업무 방법 입 니 다. 여 기 는 시범 일 뿐 실제 개발 에 서 는 임의의 통용 업무 방법 이 될 수 있 습 니 다.
2. 인터페이스 구현
이어서 실현 클래스 를 작성 하여 인 터 페 이 스 를 실현 합 니 다.
2.1. ServiceAImpl 태그 유형 은 ServiceA
@Component
public class ServiceAImpl implements AbstractService {

    @Override
    public String serviceName() {
        return "ServiceA";
    }

    @Override
    public DemoA execute(Object parm) {
        System.out.println("ServiceAImpl execute");
        return new DemoA().setName("DemoA");
    }
}

2.2 ServiceBImpl 태그 유형 은 ServiceB
@Component
public class ServiceBImpl implements AbstractService {

    @Override
    public String serviceName() {
        return "ServiceB";
    }

    @Override
    public DemoB execute(Object parm) {
        System.out.println("ServiceBImpl execute");
        return new DemoB().setName("DemoB");
    }
}

2.3, 사용자 정의 Bean 컨 텍스트 작성
중요 한 장면 입 니 다. 빈 컨 텍스트 를 만 들 고 AbstractService 집합 을 주입 해 야 합 니 다.
@Component
public class ServiceContext {

    // IService  ,key=serviceName,velue=  
    private static Map SERVICE_CONTEXT;

    @Autowired
    List services;

    @PostConstruct
    void init() {
        SERVICE_CONTEXT = new ConcurrentHashMap<> ();
        if (services == null) {
            return;
        }
        //  IService         serviceContext
        for(AbstractService service : services) {
            SERVICE_CONTEXT.put(service.serviceName(), service);
        }
        System.out.println(JSON.toJSONString(SERVICE_CONTEXT));
    }

    /**
    *   serviceName    
    * @param serviceName
    * @return
    */
    public AbstractService getServiceImpl(String serviceName) {
        return SERVICE_CONTEXT.get(serviceName);
    }
}

사실 설명 은 잘 알 고 있 습 니 다. 먼저 맵 을 정의 합 니 다. key 는 String 입 니 다. 위의 인터페이스 에서 돌아 온 serviceName 을 대표 합 니 다.
value 는 인터페이스 구현 클래스 bean 인 스 턴 스 입 니 다.
이 어 @ Autowired 를 통 해 AbstractService 집합 을 주입 합 니 다. 여 기 는 List 입 니 다.Spring 용기 초기 화가 완료 되면 AbstractService 의 구현 클래스 를 모두 List 에 불 러 옵 니 다.
@ PostConstruct 태그 의 초기 화 방법 을 옮 겨 다 닙 니 다. List < AbstractService > 이 며, 우리 가 초기 화 한 맵 에 순서대로 불 러 옵 니 다.key = AbstractService. serviceName () 의 반환 값, value 는 AbstractService 인 스 턴 스 입 니 다.
getServiceImpl (String serviceName) 을 업무 에 사용 하도록 정의 합 니 다. 구체 적 인 serviceName 표 지 를 통 해 Bean 인 스 턴 스 를 얻 을 수 있 습 니 다.이것 도 서비스 Name 이 중복 되 지 않 는 이유 입 니 다.
2.4 테스트
이 주요 논리 작성 이 완료 되 었 습 니 다. 테스트 클래스 를 만들어 서 구체 적 으로 어떻게 사용 하 는 지 테스트 해 보 겠 습 니 다.
public static void main(String[] args) {
    ConfigurableApplicationContext applicationContext = SpringApplication.run(DemoApplication.class, args);
    //   bean Context
    ServiceContext serviceContext = applicationContext.getBean("serviceContext", ServiceContext.class);
    //   serviceName          
    AbstractService serviceA = serviceContext.getServiceImpl("ServiceA");
    AbstractService serviceB = serviceContext.getServiceImpl("ServiceB");
    //   service  
    serviceA.execute(null);
    serviceB.execute(null);
}

Spring 컨 텍스트 에서 ServiceContext 를 가 져 오고 구체 적 인 serviceName 을 통 해 해당 하 는 Bean 인 스 턴 스 를 가 져 오고 인 스 턴 스 의 execute 방법 을 호출 합 니 다.실행 결 과 는 다음 과 같 습 니 다.
ServiceAImpl execute
ServiceBImpl execute

이것 은 아직 직관 적 이지 않 은 것 같 아서 우 리 는 업무 장면 을 모 의 했다.
업 무 는 serviceName 을 먼저 판단 한 다음 에 구체 적 인 값 에 따라 실행 논 리 를 선택해 야 합 니 다.
정상 적 인 상황 에서 우 리 는 이렇게 업무 코드 를 작성 할 것 이다.
if ("ServiceA".equals(serviceName)) {
    serviceA.execute()
    return;
}

if ("ServiceB".equals(serviceName)) {
    serviceB.execute()
    return;
}

...

서비스 Name 이 100 개 있 으 면 100 개의 if 분기 가 있어 야 합 니 다. switch 도 마찬가지 입 니 다.
그러나 본 논문 의 인 코딩 방식 을 취하 면 이렇게 써 야 한다.
...    serviceContext  ,         @Autowired/@Resource  ...
AbstractService service = serviceContext.getServiceImpl(serviceName);
service.execute()

이렇게 하면 우 리 는 서비스 Name 형식 을 추가 한 후에 대응 하 는 실현 클래스 를 개발 하면 된다.
전통 적 인 인 인 코딩 방식 이 라면 서비스 구현 을 추가 하 는 것 외 에 if / switch 판단 논 리 를 수정 해 야 하 며 유연성 이 부족 하고 오류 가 발생 하기 쉽다.
이곳 은 사실상 개방 폐쇄 원칙 의 구현 이다.전통 적 인 방식 은 수정 과 확장 에 모두 개방 적 이 고 이런 방식 은 확장 개발 에 대해 폐쇄 적 이다.특히 복잡 한 업무 장면 의 개발 에 적합 하 다.
 
3. 원리
원 리 를 간단히 말 하 다.
Spring 프레임 워 크 는 집합 유형 에 대한 의존 주입 을 지원 합 니 다. 집합 유형 에 대한 의존 주입 과 검색 에 작용 하 는 applicationContext 구현 클래스 는? ListableBeanFactory。
우 리 는 소스 코드 가 어떻게 이 특성 을 실현 하 는 지 보 자.
구체 적 인 논 리 는 org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency 이 방법 은
이 방법 을 열 고 아래 줄 에 중점 을 두 세 요.
Object arg = beanFactory.resolveDependency(currDesc, beanName, autowiredBeans, typeConverter);

resolve Dependency 방법 에 들 어가 면 다음 줄 을 보고 doResolve Dependency 방법 으로 이동 합 니 다.
result = doResolveDependency(descriptor, requestingBeanName, 
autowiredBeanNames, typeConverter);

아래 의 논리 에 중점 을 두다.
Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
if (multipleBeans != null) {
    return multipleBeans;
}

이 곳 의 resolve MultipleBeans 방법 은 여러 조건 에 맞 는 Bean 을 분석 하면 분석 결 과 를 되 돌려 주 는 것 으로 논리 적 이다.
그렇다면 구체 적 인 해석 결 과 는 무엇 일 까?resolve MultipleBeans 방법 으로 들 어 갑 니 다.
private Object resolveMultipleBeans(DependencyDescriptor descriptor, @Nullable String beanName,
            @Nullable Set autowiredBeanNames, @Nullable TypeConverter typeConverter) {

    Class> type = descriptor.getDependencyType();
    //     
    if (type.isArray()) {
        Class> componentType = type.getComponentType();
        ResolvableType resolvableType = descriptor.getResolvableType();
        Class> resolvedArrayType = resolvableType.resolve();
        if (resolvedArrayType != null && resolvedArrayType != type) {
            type = resolvedArrayType;
            componentType = resolvableType.getComponentType().resolve();
        }
        if (componentType == null) {
            return null;
        }
        Map matchingBeans = findAutowireCandidates(beanName, componentType,
                new MultiElementDescriptor(descriptor));
        if (matchingBeans.isEmpty()) {
            return null;
        }
        if (autowiredBeanNames != null) {
            autowiredBeanNames.addAll(matchingBeans.keySet());
        }
        TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
        Object result = converter.convertIfNecessary(matchingBeans.values(), type);
        if (getDependencyComparator() != null && result instanceof Object[]) {
            Arrays.sort((Object[]) result, adaptDependencyComparator(matchingBeans));
        }
        return result;
    }
    //     , List set
    else if (Collection.class.isAssignableFrom(type) && type.isInterface()) {
        Class> elementType = descriptor.getResolvableType().asCollection().resolveGeneric();
        if (elementType == null) {
            return null;
        }
        Map matchingBeans = findAutowireCandidates(beanName, elementType,
                new MultiElementDescriptor(descriptor));
        if (matchingBeans.isEmpty()) {
            return null;
        }
        if (autowiredBeanNames != null) {
            autowiredBeanNames.addAll(matchingBeans.keySet());
        }
        TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
        Object result = converter.convertIfNecessary(matchingBeans.values(), type);
        if (getDependencyComparator() != null && result instanceof List) {
            ((List>) result).sort(adaptDependencyComparator(matchingBeans));
        }
        return result;
    }
    // Map  
    else if (Map.class == type) {
        ResolvableType mapType = descriptor.getResolvableType().asMap();
        Class> keyType = mapType.resolveGeneric(0);
        if (String.class != keyType) {
            return null;
        }
        Class> valueType = mapType.resolveGeneric(1);
        if (valueType == null) {
            return null;
        }
        Map matchingBeans = findAutowireCandidates(beanName, valueType,
                new MultiElementDescriptor(descriptor));
        if (matchingBeans.isEmpty()) {
            return null;
        }
        if (autowiredBeanNames != null) {
            autowiredBeanNames.addAll(matchingBeans.keySet());
        }
        return matchingBeans;
    }
    else {
        return null;
    }
}

여기 가 바로 @ Autowired 주입 집합 유형의 핵심 입 니 다.
  • 먼저 주입 유형 을 판단 하고 배열, Collection, Map 등 유형 이 라면 원소 데 이 터 를 주입 한다. 즉 원소 유형 과 같은 Bean 을 찾 아 집합 에 주입 한다.
  • 여기 서 Map 유형 을 중점적으로 강조 하면 Map 의 key 는 Bean 의 name 이 고 value 는 정 의 된 요소 유형 과 같은 Bean 임 을 알 수 있다.
    // Map key
    Class> keyType = mapType.resolveGeneric(0);
    if (String.class != keyType) {
        return null;
    }
    // Map value
    Class> valueType = mapType.resolveGeneric(1);
    if (valueType == null) {
        return null;
    }
    즉, 업무 상 외부 type 에 의존 하지 않 으 면 우 리 는 맵 집합 을 직접 주입 할 수 있다. 예 를 들 어
    @Autowired
    private Map map;
    이렇게 하면 인터페이스 Bean Interface 의 실현 을 모두 맵 에 주입 할 수 있 고 key 의 값 은 구체 적 인 Bean 의 name 이 며 value 는 Bean 인 스 턴 스 이다.4. 소결
  • 4. 소결
    본 고 에서 우 리 는 사례 와 소스 코드 를 통 해 전방위 적 으로 Spring 이 집합 유형 에 대한 주입 방식 을 나 타 냈 다.요약:
  • Spring 은 집합 류 를 주입 하 는 동시에 집합 범 형 류 의 인 스 턴 스 를 집합 에 채 워 집합 초기 값 으로 한다.
  • list, set 에 입력 한 것 은 주입 유형 Spring 관리의 인 스 턴 스 입 니 다. map 에 대해 Spring 은 service 의 이름 을 key 로 하고 대상 을 value 로 패키지 하여 Map 에 들 어 갑 니 다.
  • List 유형 에 대해 서 는 @ Order 를 통 해 List 가입 순 서 를 지정 할 수 있 습 니 다.구현 클래스 에 @ Order (value) 주 해 를 추가 하면 됩 니 다. 값 이 작 을 수록 초기 화 될 수록 List
  • 에 먼저 들 어 갑 니 다.

    좋은 웹페이지 즐겨찾기