如何使用 Spring 和 Java 创建动态代理
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12619663/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to create dynamic proxy with Spring and Java
提问by Xoke
I have this situation: I have one interface Service which aggregates all service interfaces. So for example if I have two interfaces ILoginService1 and ILoginService2 the Service interface looks like this
我有这种情况:我有一个接口 Service,它聚合了所有服务接口。因此,例如,如果我有两个接口 ILoginService1 和 ILoginService2,则服务接口如下所示
Service extends ILoginService1,ILoginService2.
I need this interface to be accessible in a given context like this:
我需要在给定的上下文中可以访问此接口,如下所示:
service.login();
This is my solution (something similar to http://artofsoftwarereuse.com/tag/dynamic-proxy/):
这是我的解决方案(类似于http://artofsoftwarereuse.com/tag/dynamic-proxy/):
I create one annotation ServiceFacade, which I put on Service interface, then I have BeanPostProcessor in which I create DynamicProxy for the Service interface. But the problem is that Service interface isn't pick up from spring component scan, even in the case I put @Component on it, but other components are put in Spring container.
我创建了一个注释 ServiceFacade,将其放在 Service 接口上,然后使用 BeanPostProcessor,在其中为 Service 接口创建 DynamicProxy。但问题是 Service 接口不是从 spring 组件扫描中提取的,即使在我把 @Component 放在上面的情况下,但其他组件被放在 Spring 容器中。
How can I fix my solution so far or I'm missing something or is there other solutions? Here is source code: applicationContext.xml
到目前为止,我如何修复我的解决方案,或者我遗漏了什么,或者还有其他解决方案吗?这是源代码:applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:annotation-config/>
<context:component-scan base-package="org.finki.auction.ui.application"/>
<context:component-scan base-package="org.finki.auction.services"/>
</beans>
Annotation:
注解:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ServiceFacade{}
Invocation Handler for Dynamic Proxy:
动态代理的调用处理程序:
/**
*
*/
package org.finki.auction.services;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
/**
*
*/
@Component("serviceLayer")
public class ServiceLayer implements InvocationHandler, ApplicationContextAware
{
private static ApplicationContext applicationContext = null;
private static Map<String, String> serviceMap = new HashMap<>();
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
Object result;
try
{
String searchKey = method.getName();
String beanName = serviceMap.get(searchKey);
Object methodObject = applicationContext.getBean(beanName);
result = method.invoke(methodObject, args);
} catch (InvocationTargetException e)
{
throw e.getTargetException();
} catch (Exception e)
{
throw new RuntimeException("unexpected invocation exception: " + e.getMessage());
}
return result;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
{
ServiceLayer.applicationContext = applicationContext;
Map<String, Object> beans = applicationContext.getBeansWithAnnotation(Service.class);
for (Map.Entry<String, Object> entryBean : beans.entrySet())
{
String beanName = entryBean.getKey();
Object beanObject = entryBean.getValue();
Method[] beanMethods = beanObject.getClass().getDeclaredMethods();
for (Method bMethod : beanMethods)
{
serviceMap.put(bMethod.getName(), beanName);
}
}
}
}
BeanPostProcessor class:
BeanPostProcessor 类:
/**
*
*/
package org.finki.auction.services.annotation;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import org.finki.auction.services.Service;
import org.finki.auction.services.ServiceLayer;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
*
*/
@Component("serviceFacadeProcessor")
public class ServiceFacadeProcessor implements BeanPostProcessor, ApplicationContextAware
{
private static ApplicationContext applicationContext = null;
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException
{
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException
{
Class<?> clz = bean.getClass();
Class<?>[] tmpInterfaces = clz.getInterfaces();
System.out.println("ServiceFacadeProcessor : " + bean);
if (tmpInterfaces != null && tmpInterfaces.length == 1
&& tmpInterfaces[0].isAnnotationPresent(ServiceFacade.class))
{
System.out.println("Find serviceFacade >>>>");
Class<?>[] interfaces = Arrays.copyOf(tmpInterfaces, tmpInterfaces.length + 1);
interfaces[tmpInterfaces.length] = Service.class;
ClassLoader cl = bean.getClass().getClassLoader();
ServiceLayer serviceLayerBean = applicationContext.getBean("serviceLayer", ServiceLayer.class);
Object t = Proxy.newProxyInstance(cl, interfaces, serviceLayerBean);
System.out.println("Find serviceFacade <<<<");
return t;
}
return bean;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
{
ServiceFacadeProcessor.applicationContext = applicationContext;
}
}
So, my problem is not the configuration, my problem is how to attach Service interface to spring container in order to be caught by BeanPostProcessor and create dynamic proxy for it. It's is my solution so far maybe I'm missing something, but if someone have better way doing it, just let me now. Thanks in advance
所以,我的问题不是配置,我的问题是如何将 Service 接口附加到 spring 容器以便被 BeanPostProcessor 捕获并为其创建动态代理。到目前为止,这是我的解决方案,也许我遗漏了一些东西,但如果有人有更好的方法,现在就让我来吧。提前致谢
Solution:
解决方案:
/**
*
*/
package org.finki.auction.services.annotation;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import org.finki.auction.services.Service;
import org.finki.auction.services.ServiceLayer;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* @author
*
*/
@Component
public class ServiceFactoryBean implements FactoryBean<Service>, ApplicationContextAware
{
private static ApplicationContext applicationContext = null;
@Override
public Service getObject() throws Exception
{
Class<?>[] tmpInterfaces = Service.class.getInterfaces();
Class<?>[] interfaces = Arrays.copyOf(tmpInterfaces, tmpInterfaces.length + 1);
interfaces[tmpInterfaces.length] = Service.class;
ServiceLayer serviceLayerBean = applicationContext.getBean("serviceLayer", ServiceLayer.class);
ClassLoader cl = serviceLayerBean.getClass().getClassLoader();
Object t = Proxy.newProxyInstance(cl, interfaces, serviceLayerBean);
return (Service) t;
}
@Override
public Class<?> getObjectType()
{
return Service.class;
}
@Override
public boolean isSingleton()
{
return true;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
{
ServiceFactoryBean.applicationContext = applicationContext;
}
}
Also need to delete BeanPostProcessor and annotation.
还需要删除 BeanPostProcessor 和 annotation。
回答by finrod
I run into something similar and believe you can get your scenario working using Spring's Java Configuration feature.
我遇到了类似的情况,并相信您可以使用 Spring 的 Java 配置功能让您的场景工作。
@Configuration
public class ServiceConfiguration {
// you can wire your service1 and service2 here
@Bean
Service service() {
// create and return dynamic proxy here
}
}
This way you will end up with a bean of type 'Service' and name 'service' which will be your dynamic proxy with invocation handler etc.
通过这种方式,您最终将获得一个类型为“Service”且名称为“service”的 bean,它将成为您的具有调用处理程序等的动态代理。
I'm sure Java Configuration will not limit you to the approach outlined above (where you wire your service1 and service2 into the config) - methinks that is implementation detail.
我确信 Java 配置不会限制您使用上面概述的方法(您将 service1 和 service2 连接到配置中) - 我认为这是实现细节。