通过InvocationHandler注入服务

时间:2013-12-02 02:32:31

标签: java spring

Spring中是否有一种干净的方式(没有XML)将接口连接到调用处理程序?目前我必须做这样的事情:

@Inject
private ServiceProxyCreator services;

private MyServiceInterface service;

private MyServiceInterface getService() {
   if ( service == null )
      service = services.createProxy( MyServiceInterface.class );

   return service;
}

其中#createProxy只是这样的实现:

@SuppressWarnings( "unchecked" )
public <T> T createProxy( Class<T> type ) {
    JobRpcHandler handler = new JobRpcHandler();
    handler.setServiceName( type.getSimpleName() );
    return (T) Proxy.newProxyInstance(
        type.getClassLoader(), new Class[]{type}, handler );
}

但是在Spring中使用所有这些DI功能似乎我应该能够自动执行此操作,以便我可以简单地执行以下操作:

@Inject
private MyService service;

以某种方式定制注入,我不知道在幕后创建代理而无需调用#createProxy。

有关更优雅方法的任何建议吗?

1 个答案:

答案 0 :(得分:1)

看看FactoryBean。你可以这样写自己的:

public class ServiceProxyFactoryBean implements FactoryBean<Object>
    private Class<T> type;

    public DutySetFactoryBean(Class<?> type) {
        this.type = type;
    }

    @Override
    public synchronized Object getObject() {
        JobRpcHandler handler = new JobRpcHandler();
        handler.setServiceName(type.getSimpleName());
        return Proxy.newProxyInstance(type.getClassLoader(), new Class[]{type}, handler);
    }

    @Override
    public Class<?> getObjectType() {
        return type;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }
}

并在配置文件中使用它:

<bean class="package.name.ServiceProxyFactoryBean">
  <constructor-arg>
    <value type="java.lang.Class">package.name.MyServiceInterface</value>
  </constructor-arg>
</bean>

或者,使用Java配置,那样:

@Bean
public ServiceProxyFactoryBean myServiceFactoryBean() {
    return new ServiceProxyFactoryBean(MyServiceInterface.class);
}

@Bean
public MyServiceInterface myService() {
  return (MyServiceInterface)sessionFactoryBean().getObject();
}

如果要为类路径中的所有带注释的接口自动创建代理,可以定义自己的BeanDefinitionRegistryPostProcessor。在这里,您必须使用以下模式使用ResourceLoader扫描类路径:

MetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory();
Resource[] resources = patternResolver.getResources(
        "classpath*:" + packageName.replace('.', '/') + "/**/*.class");
for (Resource resource : resources) {
    MetadataReader reader = metadataReaderFactory.getMetadataReader(resource);
    if (!reader.getAnnotationMetadata().isAnnotated(
            MyProxyAnnotation.class.getName())) {
        continue;
    }
    Class<?> cls = Class.forName(reader.getClassMetadata().getClassName(), true,
            resourceLoader.getClassLoader());
    String factoryBeanName = createNewName();
    BeanDefinitionBuilder bdb = BeanDefinitionBuilder.genericBeanDefinition(
            ServiceProxyFactoryBean.class);
    bdb.addConstructorArgValue(cls);
    registry.registerBeanDefinition(factoryBeanName, bdb.getBeanDefinition());
    bdb = BeanDefinitionBuilder.genericBeanDefinition(cls);
    bdb.setFactoryBean(factoryBeanName, "getBean");
    registry.registerBeanDefinition(createNewName(), bdb.getBeanDefinition());
}

现在,对于所有使用MyProxyAnnotation注释的接口,您都有一个代理,您可以将其注入到bean中。例如:

@MyProxyAnnotation
public interface MyServiceInterface {
    void foo();
}

@Component
public class MyBean {
    @Autowired
    private MyServiceInterface myService;
}

这就是全部。无需配置。

我不确定此代码是否有效甚至可编译。这不是最终的解决方案,只是你应该走向的一般方式。所以你应该研究和调试一下。