Spring autowire a stubbed service - duplicate bean

无人久伴 提交于 2019-12-22 08:46:12

问题


Ok. We have the need to @Autowire a different webservice on-the-fly (preferably by toggling a JNDI setting on the webserver) and I'm at a loss on how to do this. This is the way I was approaching the problems..

Two packages: com.mycomp.service.stub com.mycomp.service.impl

One package contains MyServiceStub.java while implement MyService The other package contains MyServiceImpl.java, which implements same

My controller, which requires MyService, has the bean defined as such

@Autowire
private MyService communicator;

My spring-context.xml has the following:

<context:component-scan base-package="com.mycomp" />

At this point I get a DuplicateBean exception during autowiring. Now, I can statically define which bean to autowire in spring-context.xml:

<bean id="communicator" class="com.mycomp.service.impl.MyServiceImpl" />

and everything works fine... But then, how to 'flip' the switch and change over to the Stub method on our QA server? It has no connection to that service, so we need to run with stubs enabled. A JNDI property would be best for this.. but I just can't get my head around how to toggle what bean spring autowires at runtime.

Any help??

Cheers, Chris


回答1:


@Profile solution

You definitely have to try Spring 3.1 @Profile:

@Autowire
private MyService communicator;

//...

@Service
@Profile("prd")
class MyServiceImpl //...

@Service
@Profile("qa")
class MyServiceStub //...

Now depending on which profile is enabled, either DefaultMyService will be initialized or MyServiceStub.

You can choose between profile in various ways:

  • How to set active spring 3.1 environment profile via a properites file and not via an env variable or system property
  • using system property
  • programmatically
  • ...

Spring AOP (explicit around every method)

In this example the aspect wraps around every single MyService method separately and returns stubbed value:

@Aspect
@Service
public class StubAspect {

    @Around("execution(public * com.blogspot.nurkiewicz.MyService.foo(..))")
    public Object aroundFoo(ProceedingJoinPoint pjp) throws Throwable {
        if (stubMode()) {
            return //stub foo() result
        }
        return pjp.proceed();
    }

    @Around("execution(public * com.blogspot.nurkiewicz.MyService.bar(..))")
    public Object aroundBar(ProceedingJoinPoint pjp) throws Throwable {
        if (stubMode()) {
            return //stub bar() result
        }
        return pjp.proceed();
    }

    private boolean stubMode() {
        //whatever condition you want here
        return true;
    }

}

The code is pretty straightforward, unfortunately the return values are buried inside the aspect and you need a separate @Around for every target method. Finally, there is no place for MyServiceStub.

Spring AOP (automatically around all methods)

@Aspect
@Service
public class StubAspect {

    private MyServiceStub stub = //obtain stub somehow

    @Around("execution(public * com.blogspot.nurkiewicz.MyService.*(..))")
    public Object aroundFoo(ProceedingJoinPoint pjp) throws Throwable {
        if (stubMode()) {
            MethodSignature signature = (MethodSignature)pjp.getSignature();
            Method method = signature.getMethod();
            return method.invoke(stub, pjp.getArgs());
        }
        return pjp.proceed();
    }

    private boolean stubMode() {
        //whatever condition you want here
        return true;
    }

}

This approach is more implicit as it automatically wraps every target method, including new methods added in the future. The idea is simple: if stubMode() is off, run the standard method (pjp.proceed()). If it is on - run the exact same method with exact same parameters - but on a different object (stub in this case).

This solution is much better as it involves less manual work (at the price of using raw reflection).

Note that if both MyService implementations are Spring beans (even when one is annotated with @Primary), you might run into weird troubles. But it should be a good start.

See also:

  • Spring 3.1 M1: Introducing @Profile



回答2:


Maybe you can replace the class with a property and deploy your application with different property files. The production version would contain the name of the real class while the QA version would contain the name of a stub.

Maybe this http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/beans.html#beans-factory-extension-factory-postprocessors can help you.



来源:https://stackoverflow.com/questions/9018783/spring-autowire-a-stubbed-service-duplicate-bean

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!