我遇到这种情况: 我有一个接口服务,它聚合所有服务接口。因此,例如,如果我有两个接口ILoginService1和ILoginService2,则Service接口看起来像这样
Service extends ILoginService1,ILoginService2.
我需要在给定的上下文中访问此接口,如下所示:
service.login();
这是我的解决方案(类似于http://artofsoftwarereuse.com/tag/dynamic-proxy/):
我创建了一个注释ServiceFacade,我把它放在Service接口上,然后我有BeanPostProcessor,我在其中为Service接口创建DynamicProxy。 但问题是Service接口没有从spring组件扫描中获取,即使我将@Component放在其上,但其他组件也放在Spring容器中。
到目前为止,我如何修复我的解决方案,或者我错过了什么或者是否有其他解决方案? 这是源代码: 的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>
注释:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ServiceFacade{}
动态代理的调用处理程序:
/**
*
*/
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类:
/**
*
*/
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;
}
}
所以,我的问题不是配置,我的问题是如何将服务接口附加到spring容器以便被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.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;
}
}
还需要删除BeanPostProcessor和注释。
答案 0 :(得分:3)
我遇到了类似的事情,相信你可以使用Spring的Java配置功能让你的场景工作。
@Configuration
public class ServiceConfiguration {
// you can wire your service1 and service2 here
@Bean
Service service() {
// create and return dynamic proxy here
}
}
这样你最终会得到一个'Service'类型的bean,并命名为'service',它将是你的动态代理,带有调用处理程序等。
我确信Java配置不会限制您使用上面概述的方法(将service1和service2连接到配置中) - 这是实现细节。