'WebService'주석이 추가 된 인터페이스 빈을 만드는 모든 기능을 구현 한 방법은 다음과 같습니다. 또한 프록시 구현 내 Autowiring을 지원합니다. (패키지 선언 및 import 문은 아래 코드에서 생략되었습니다.) 먼저 WebService
및 WebServiceOperation
주석을 작성했습니다.
WebService에 주석
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface WebService {
String service();
String namespace();
}
WebService를 조작 주석
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface WebServiceOperation {
String operation();
}
다음 단계는 지정된 패키지 WebService
모든 주석 첨부 인터페이스를 스캔한다. Spring은 패키지 스캐닝을 위해 ClassPathScanningCandidateComponentProvider
을 제공하지만 인터페이스를 감지하지 못합니다. 자세한 내용은 this question 및 it's answer을 참조하십시오. 그래서 ClassPathScanningCandidateComponentProvider
을 확장하고 isCandidateComponent
메소드를 오버로드했습니다. 이 시점에서
ClassPathScanner
public class ClassPathScanner extends ClassPathScanningCandidateComponentProvider {
public ClassPathScanner(final boolean useDefaultFilters) {
super(useDefaultFilters);
}
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
return beanDefinition.getMetadata().isIndependent();
}
}
나는 웹 서비스를 활성화하고 WebService
주석 인터페이스를 포함하는 웹 서비스 패키지를 제공하기 위해 EnableWebServices
주석을 만들었습니다.
EnableWebServices 주석
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import({
WebServiceProxyConfig.class,
WebServiceProxyBeansRegistrar.class
})
public @interface EnableWebServices {
@AliasFor("basePackages")
String[] value() default {};
@AliasFor("value")
String[] basePackages() default {};
}
이 주석은 다음과 같이, 인터페이스를 스캔 패키지 클래스 주석 Configuration
일부에 적용될 수있다.
@EnableWebServices({
"a.b.c.webservices",
"x.y.z.webservices"
})
이 WebService
및 WebServiceOperation
주석에 주어진 정보에서 실제 웹 서비스를 호출합니다 동적 프록시 생성에 대해 생각하는 시간이다. Java는 InvocationHandler
인터페이스 구현을 제공하고 로직을 invoke
메소드로 제공해야하는 동적 프록시를 생성하는 메커니즘을 제공합니다. 나는 이것을 다음과 같이 구현했다. WebServiceProxy
'TheWebServiceCaller'유형의 bean이 웹 서비스를 호출하는 모든 불쾌한 논리를 포함한다고 가정 해보자. 난 그냥 그것을 삽입하고 TheWebServiceInfo
(WebService
및 WebServiceOperation
주석에서 추출) 및 요청 개체와 함께 call
메서드를 호출 할 수 있습니다.
TheWebServiceInfo
public class TheWebServiceInfo {
private String service;
private String namespace;
private String operation;
}
WebServiceProxy (모든 필드 getter 및 setter가 있다고 가정)InvocationHandler
의 Implementaion은 프록시를 생성하는 (다른 정보와 함께) Proxy.newProxyInstance
에 전달
public class WebServiceProxy implements InvocationHandler {
@Autowired
private TheWebServiceCaller caller;
@Override
public Object invoke(Object target, Method method, Object[] args) throws Exception {
Object request = (null != args && args.length > 0) ? args[0] : null;
WebService webService = method.getDeclaringClass().getAnnotation(WebService.class);
WebServiceOperation webServiceOperation = method.getAnnotation(WebServiceOperation.class);
TheWebServiceInfo theInfo = createTheWebServiceInfo(webService, webServiceOperation);
return caller.call(theInfo, request);
}
private TheWebServiceInfo createTheWebServiceInfo(WebService webService, WebServiceOperation webServiceOperation) {
TheWebServiceInfo theInfo = new TheWebServiceInfo();
theInfo.setService(webService.service());
theInfo.setNamespace(webService.namespace());
theInfo.setOperation(webServiceOperation.operation());
return theInfo;
}
}
사물. 나는 각각의 WebService
인터페이스에 대해 별도의 프록시 객체를 필요로한다. 이제 프록시 인스턴스 생성을위한 팩토리를 만들고 이름은 'WebServiceProxyBeanFactory'입니다. 이 팩토리로 작성된 인스턴스는 대응하는 WebService
주석이 붙은 인터페이스의 bean가됩니다.
조금 후에 'WebServiceProxy'및 WebServiceProxyBeanFactory
을 bean으로 노출합니다. 'WebServiceProxyBeanFactory'에서 WebServiceProxy
을 삽입하여 사용합니다. createWebServiceProxyBean
은 제네릭을 사용합니다. 이건 중요하다.
WebServiceProxyBeanFactory
public class WebServiceProxyBeanFactory {
@Autowired
WebServiceProxy webServiceProxy;
@SuppressWarnings("unchecked")
public <WS> WS createWebServiceProxyBean(ClassLoader classLoader, Class<WS> clazz) {
return (WS) Proxy.newProxyInstance(classLoader, new Class[] {clazz}, webServiceProxy);
}
}
당신이 기억하는 경우는, 이전의 나는 EnableWebServices
주석에 WebServiceProxyConfig
을 가져 왔습니다. WebServiceProxyConfig
은 빈으로 WebServiceProxy
및 WebServiceProxyBeanFactory
을 표시하는 데 사용됩니다.
WebServiceProxyConfig
@Configuration
public class WebServiceProxyConfig {
@Bean
public WebServiceProxy webServiceProxy() {
return new WebServiceProxy();
}
@Bean(name = "webServiceProxyBeanFactory")
public WebServiceProxyBeanFactory webServiceProxyBeanFactory() {
return new WebServiceProxyBeanFactory();
}
}
이제 모든 장소에 있습니다. 이제 웹 서비스 패키지 검색을 시작하고 동적 프록시를 빈으로 등록하기위한 후크를 작성해야 할 때입니다. ImportBeanDefinitionRegistrar
구현을 제공합니다.이 클래스에서
@Configuration
public class WebServiceProxyBeansRegistrar implements ImportBeanDefinitionRegistrar, BeanClassLoaderAware {
private ClassPathScanner classpathScanner;
private ClassLoader classLoader;
public WebServiceProxyBeansRegistrar() {
classpathScanner = new ClassPathScanner(false);
classpathScanner.addIncludeFilter(new AnnotationTypeFilter(WebService.class));
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
String[] basePackages = getBasePackages(importingClassMetadata);
if (ArrayUtils.isNotEmpty(basePackages)) {
for (String basePackage : basePackages) {
createWebServicProxies(basePackage, registry);
}
}
}
private String[] getBasePackages(AnnotationMetadata importingClassMetadata) {
String[] basePackages = null;
MultiValueMap<String, Object> allAnnotationAttributes =
importingClassMetadata.getAllAnnotationAttributes(EnableWebServices.class.getName());
if (MapUtils.isNotEmpty(allAnnotationAttributes)) {
basePackages = (String[]) allAnnotationAttributes.getFirst("basePackages");
}
return basePackages;
}
private void createWebServicProxies(String basePackage, BeanDefinitionRegistry registry) {
try {
for (BeanDefinition beanDefinition : classpathScanner.findCandidateComponents(basePackage)) {
Class<?> clazz = Class.forName(beanDefinition.getBeanClassName());
WebService webService = clazz.getAnnotation(WebService.class);
String beanName = StringUtils.isNotEmpty(webService.bean())
? webService.bean() : ClassUtils.getShortNameAsProperty(clazz);
GenericBeanDefinition proxyBeanDefinition = new GenericBeanDefinition();
proxyBeanDefinition.setBeanClass(clazz);
ConstructorArgumentValues args = new ConstructorArgumentValues();
args.addGenericArgumentValue(classLoader);
args.addGenericArgumentValue(clazz);
proxyBeanDefinition.setConstructorArgumentValues(args);
proxyBeanDefinition.setFactoryBeanName("webServiceProxyBeanFactory");
proxyBeanDefinition.setFactoryMethodName("createWebServiceProxyBean");
registry.registerBeanDefinition(beanName, proxyBeanDefinition);
}
} catch (Exception e) {
System.out.println("Exception while createing proxy");
e.printStackTrace();
}
}
}
WebServiceProxyBeansRegistrar, 나는 EnableWebServices
주석에 제공하는 모든 패키지를 추출 하였다. 추출한 각 패키지에 대해 ClassPathScanner
을 사용하여 검사했습니다. (여기에서 논리는 WebService
주석이 달린 인터페이스 만 필터링하도록 수정 될 수 있습니다.) 감지 된 각 인터페이스에 대해 bean 정의를 등록했습니다. 내가 webServiceProxyBeanFactory
을 사용했고 classLoader와 인터페이스 타입을 가지고 createWebServiceProxyBean
이라고 불렀습니다. 이 팩토리 메소드는 봄 이후에 호출 될 때 인터페이스와 동일한 타입의 빈을 반환 할 것이므로 올바른 타입의 빈이 등록된다. 이 bean은 인터페이스 유형으로 어디에서나 주입 할 수 있습니다. 또한 WebServiceProxy
은 다른 빈을 삽입하여 사용할 수 있습니다. 따라서 autowiring도 예상대로 작동합니다.
'InvocationHandler'를 구현 한'WebServiceProxy '를 bean으로 노출시키고'BeanFactoryPostProcessor.postProcessBeanFactory()'에서 시도해 보았습니다. 여기서'beanFactory.getBean (WebServiceProxy.class)'는'WebServiceProxy' 인스턴스를 리턴하지만 'Autowired'주석 필드는 null입니다. 여기에 'WebServiceProxy'인스턴스를 가져 와서 Proxy.newProxyInstance()에 전달해야합니다. –
물론 BFPP가 beanFactory를 처리하는 동안 autowired 필드가 없습니다. 필드는 BeanPostProcessor.postProcessBeforeInitialization 단계에서 자동으로 실행되며 BFPP 후에 실행됩니다. –
BFPP 단계에서 프록시를 만들 필요가 없습니다. 초기화 단계에서 일부 빈 주위에 프록시를 작성하려고한다고 생각하면 BeanFactoryPostProcessor가 아닌 BeanPostProcessor를 작성하고 postProcessAfterInitialization에서 프록시를 작성해야합니다. –