具有自动装配功能的动态代理 Bean
Dynamic Proxy Bean with Autowiring capability
在我正在处理的基于 spring 的项目中,有一层调用 Web 服务的功能。对于每个 Web 服务操作,使用几乎相同的代码创建一个方法,但使用一些不同的、特定于操作的信息(例如服务名称、操作名称、名称空间等)。
我正在用接口和带注释的方法替换这一层。例如,下面的代码是为 web 服务 ("foo").
的操作 "fetchBar" 提供的
package a.b.c.webservices;
@WebService(service="foo", namespace="...")
public interface FooWebService {
@WebServiceOperation(operation="fetchBar")
BarRespons fetchBar(BarRequest request) throws WebServiceException;
}
现在我想通过一些机制,spring 允许我从一些指定的包创建动态代理 bean,我可以使用下面的代码调用 Web 服务。
package a.b.c.business;
import a.b.c.webservices.FooWebService;
public class FooBusiness {
@Autowired
FooWebService fooWebService;
public Bar getBar() {
Bar bar = null;
BarRequest request;
//create request
BarResponse response = fooWebService.fetchBar(request);
//extrac bar from response
return bar;
}
}
为了实现这一点,我通过提供 InvocationHandler
的实现,使用 java.lang.reflect.Proxy.newProxyInstance
创建了动态 bean 实例。但是自动装配在提供的 invocationHandler
实现及其进一步的依赖项中不起作用。
我尝试了以下方法来实现这一点。
- 使用
ConfigurableListableBeanFactory.registerSingleton
方法实现 BeanFactoryPostProcessor.postProcessBeanFactory
并注册了 bean。
- 已实现
ImportBeanDefinitionRegistrar.registerBeanDefinitions
并尝试使用 BeanDefinitionRegistry.registerBeanDefinition
但我对如何提供支持自动装配的正确 Bean 定义感到困惑。
任何人都可以告诉我缺少什么吗?如果我的方向不对,请指导我。
你的InvocationHandler是bean吗?您应该将其创建为一个 bean,而不仅仅是一个让 Autowired 工作的简单对象
以下是我如何实现创建 'WebService' 注释接口的 bean 的所有功能,并且还支持代理实现中的自动装配。 (以下代码中省略了包声明和导入语句)
首先,我创建了 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 and 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();
}
}
此时我创建了 EnableWebServices
注释以启用 Web 服务并提供包含 WebService
注释接口的 Web 服务包。
启用 Web 服务注释
@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
注解class的包来扫描接口,如下。
@EnableWebServices({
"a.b.c.webservices",
"x.y.z.webservices"
})
是时候考虑动态代理创建了,它将根据 WebService
和 WebServiceOperation
注释中给出的信息调用实际的 Web 服务。 Java 提供了一种创建动态代理的机制,它需要提供 InvocationHandler
接口的实现并在其 invoke
方法中提供逻辑。我将这个实现命名为 WebServiceProxy
假设一个 'TheWebServiceCaller' 类型的 bean 包含调用 Web 服务的所有讨厌的逻辑。我刚刚注入它并使用 TheWebServiceInfo
(从 WebService
和 WebServiceOperation
注释中提取)和请求对象调用它的 call
方法。
TheWebServiceInfo(假设所有字段都有getters和setters)
public class TheWebServiceInfo {
private String service;
private String namespace;
private String operation;
}
WebServiceProxy
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;
}
}
InvocationHandler
的实现被传递给 Proxy.newProxyInstance
(连同一些其他信息)以创建代理对象。我需要为每个 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
公开为 beans。
WebServiceProxyConfig
@Configuration
public class WebServiceProxyConfig {
@Bean
public WebServiceProxy webServiceProxy() {
return new WebServiceProxy();
}
@Bean(name = "webServiceProxyBeanFactory")
public WebServiceProxyBeanFactory webServiceProxyBeanFactory() {
return new WebServiceProxyBeanFactory();
}
}
现在一切就绪。是时候编写一个钩子来开始扫描 Web 服务包并将动态代理注册为 bean 了。我将提供 ImportBeanDefinitionRegistrar
.
的实现
WebServiceProxyBeansRegistrar
@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();
}
}
}
在此class中,我提取了EnableWebServices
注释中提供的所有包。对于每个提取的包,我使用 ClassPathScanner
进行扫描。 (这里可以细化逻辑以仅过滤 WebService
个带注释的接口)。对于每个检测到的接口,我都注册了一个 bean 定义。请注意,我使用了 webServiceProxyBeanFactory
并使用 classLoader 和接口类型调用了它的 createWebServiceProxyBean
。这个工厂方法,当稍后被spring调用时,会return与接口相同类型的bean,所以注册了正确类型的bean。这个 bean 可以注入到任何具有接口类型的地方。此外,WebServiceProxy
可以注入和使用任何其他 bean。因此自动装配也将按预期工作。
我在考虑同样的问题,但在更轻量级的上下文中。我不需要动态加载所有 web 服务客户端。因此,我改为使用 FactoryBean,并在这个工厂 bean 中构建了动态代理。这是服务自动装配的一个示例:
public class CurrencyServiceWithDynamicProxy extends AbstractFactoryBean<CurrencyService> {
ServiceClientConfiguration clientConfiguration;
Object proxy;
@Autowired
public CurrencySyncFactoryDynamicProxy(ServiceClientConfigurationProvider serviceClientConfigurationProvider) {
this.clientConfiguration = serviceClientConfigurationProvider.createClientConfig("currency");
proxy = Proxy.newProxyInstance(getClass().getClassLoader(), new Class<?>[] { getObjectType() }, new MyInvocationHandler());
}
@Override
public Class<CurrencySync> getObjectType() {
// TODO Auto-generated method stub
return CurrencyService.class;
}
@Override
public CurrencySync createInstance() throws Exception {
// do some creational logic
return (CurrencySync)proxy;
}
public CurrencySync createService() {
JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
factory.setServiceClass(getObjectType());
factory.getFeatures().add(som features);
return getObjectType().cast(factory.create());
}
}
关于公认的答案,这个工厂示例可以很容易地扩展成一个更动态的版本。
在我正在处理的基于 spring 的项目中,有一层调用 Web 服务的功能。对于每个 Web 服务操作,使用几乎相同的代码创建一个方法,但使用一些不同的、特定于操作的信息(例如服务名称、操作名称、名称空间等)。
我正在用接口和带注释的方法替换这一层。例如,下面的代码是为 web 服务 ("foo").
的操作 "fetchBar" 提供的package a.b.c.webservices;
@WebService(service="foo", namespace="...")
public interface FooWebService {
@WebServiceOperation(operation="fetchBar")
BarRespons fetchBar(BarRequest request) throws WebServiceException;
}
现在我想通过一些机制,spring 允许我从一些指定的包创建动态代理 bean,我可以使用下面的代码调用 Web 服务。
package a.b.c.business;
import a.b.c.webservices.FooWebService;
public class FooBusiness {
@Autowired
FooWebService fooWebService;
public Bar getBar() {
Bar bar = null;
BarRequest request;
//create request
BarResponse response = fooWebService.fetchBar(request);
//extrac bar from response
return bar;
}
}
为了实现这一点,我通过提供 InvocationHandler
的实现,使用 java.lang.reflect.Proxy.newProxyInstance
创建了动态 bean 实例。但是自动装配在提供的 invocationHandler
实现及其进一步的依赖项中不起作用。
我尝试了以下方法来实现这一点。
- 使用
ConfigurableListableBeanFactory.registerSingleton
方法实现BeanFactoryPostProcessor.postProcessBeanFactory
并注册了 bean。 - 已实现
ImportBeanDefinitionRegistrar.registerBeanDefinitions
并尝试使用BeanDefinitionRegistry.registerBeanDefinition
但我对如何提供支持自动装配的正确 Bean 定义感到困惑。
任何人都可以告诉我缺少什么吗?如果我的方向不对,请指导我。
你的InvocationHandler是bean吗?您应该将其创建为一个 bean,而不仅仅是一个让 Autowired 工作的简单对象
以下是我如何实现创建 'WebService' 注释接口的 bean 的所有功能,并且还支持代理实现中的自动装配。 (以下代码中省略了包声明和导入语句)
首先,我创建了 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 and 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();
}
}
此时我创建了 EnableWebServices
注释以启用 Web 服务并提供包含 WebService
注释接口的 Web 服务包。
启用 Web 服务注释
@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
注解class的包来扫描接口,如下。
@EnableWebServices({
"a.b.c.webservices",
"x.y.z.webservices"
})
是时候考虑动态代理创建了,它将根据 WebService
和 WebServiceOperation
注释中给出的信息调用实际的 Web 服务。 Java 提供了一种创建动态代理的机制,它需要提供 InvocationHandler
接口的实现并在其 invoke
方法中提供逻辑。我将这个实现命名为 WebServiceProxy
假设一个 'TheWebServiceCaller' 类型的 bean 包含调用 Web 服务的所有讨厌的逻辑。我刚刚注入它并使用 TheWebServiceInfo
(从 WebService
和 WebServiceOperation
注释中提取)和请求对象调用它的 call
方法。
TheWebServiceInfo(假设所有字段都有getters和setters)
public class TheWebServiceInfo {
private String service;
private String namespace;
private String operation;
}
WebServiceProxy
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;
}
}
InvocationHandler
的实现被传递给 Proxy.newProxyInstance
(连同一些其他信息)以创建代理对象。我需要为每个 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
公开为 beans。
WebServiceProxyConfig
@Configuration
public class WebServiceProxyConfig {
@Bean
public WebServiceProxy webServiceProxy() {
return new WebServiceProxy();
}
@Bean(name = "webServiceProxyBeanFactory")
public WebServiceProxyBeanFactory webServiceProxyBeanFactory() {
return new WebServiceProxyBeanFactory();
}
}
现在一切就绪。是时候编写一个钩子来开始扫描 Web 服务包并将动态代理注册为 bean 了。我将提供 ImportBeanDefinitionRegistrar
.
WebServiceProxyBeansRegistrar
@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();
}
}
}
在此class中,我提取了EnableWebServices
注释中提供的所有包。对于每个提取的包,我使用 ClassPathScanner
进行扫描。 (这里可以细化逻辑以仅过滤 WebService
个带注释的接口)。对于每个检测到的接口,我都注册了一个 bean 定义。请注意,我使用了 webServiceProxyBeanFactory
并使用 classLoader 和接口类型调用了它的 createWebServiceProxyBean
。这个工厂方法,当稍后被spring调用时,会return与接口相同类型的bean,所以注册了正确类型的bean。这个 bean 可以注入到任何具有接口类型的地方。此外,WebServiceProxy
可以注入和使用任何其他 bean。因此自动装配也将按预期工作。
我在考虑同样的问题,但在更轻量级的上下文中。我不需要动态加载所有 web 服务客户端。因此,我改为使用 FactoryBean,并在这个工厂 bean 中构建了动态代理。这是服务自动装配的一个示例:
public class CurrencyServiceWithDynamicProxy extends AbstractFactoryBean<CurrencyService> {
ServiceClientConfiguration clientConfiguration;
Object proxy;
@Autowired
public CurrencySyncFactoryDynamicProxy(ServiceClientConfigurationProvider serviceClientConfigurationProvider) {
this.clientConfiguration = serviceClientConfigurationProvider.createClientConfig("currency");
proxy = Proxy.newProxyInstance(getClass().getClassLoader(), new Class<?>[] { getObjectType() }, new MyInvocationHandler());
}
@Override
public Class<CurrencySync> getObjectType() {
// TODO Auto-generated method stub
return CurrencyService.class;
}
@Override
public CurrencySync createInstance() throws Exception {
// do some creational logic
return (CurrencySync)proxy;
}
public CurrencySync createService() {
JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
factory.setServiceClass(getObjectType());
factory.getFeatures().add(som features);
return getObjectType().cast(factory.create());
}
}
关于公认的答案,这个工厂示例可以很容易地扩展成一个更动态的版本。