简体   繁体   中英

Developing a Java API and including it in a Spring App

We have a large Spring based App that includes different modules but also custom APIs. While there are modules that need a Spring context since they just wrap parts of the domain model we think that some of our APIs do not.

For example we wrote a wrapper around Jackson / Jersey to provide and consume REST services. There is a central interface to access this API. The implementation however needs another class and that another and so on. So a simple initialization would be

A a = new A(new B(new C(), new D(new E())))

Which we currently are saved from using @Inject . The context for this API scans the package and then gets imported into the target app.

<import resource="classpath:api-context.xml" />

We don't feel that comfortable with this and want to kick the spring context out of the REST wrapper, meaning we want the API not to require one, but are not sure how to do it. It should mean either of the following two:

constructor arguments

In the target context this will require the creation of several beans each initialized with its dependencies

<bean id="a" class="...A">
    <constructor-arg>
        <ref = b " />
    </constructor-arg>
</bean>

<bean id="b" class="...B">
    <constructor-arg>
        <ref = c " />
    </constructor-arg>
</bean>
<!-- And so on -->

Getters and Setters

Or, assuming that A is a specific implementation of the AInterface and AInterface is the central access we could just say, A uses a certain implementation of BInterface by default and so on and actually set them internally with new:

public class A implements AInterface {
    private BInterface b = new B();
    public getB() {return b;}
    public setB(B b) {this.b = b) }
}
// and so on

Then in my target context I can initialize the central access with one line if I want to use the default configuration

<bean id="a" class="...A" />

or use properties to set its B. Then however if I want to change something farther down the line I'd have to initialize all beans and set the properties.

Also it does not seem clean to me if I use new for a service outside my tests.


So we're wondering how do other API developers make their interfaces and beans accessible without relying on a context import (which btw also clutters up the target context with many potentially unneeded beans, like for example if an API provides several services and I only want to use one)?


edit

Not sure if any of this is better:

public class A implements AInterface {
    private BInterface b
    public A() {
        b = new B();
    }
    public getB() {return b;}
    public setB(B b) {this.b = b) }
}

or

public class A implements AInterface {
    private BInterface b
    public A(B b) {
        this.b = b;
    }
}

The latter feels the best from test point of view but it brings us back to the chain I described above where I'll have to initialize all depending beans in my context before I can initialize A. That feels like too much configuration overhead.

One could argue that that's quite normal that all dependencies need to be initialized before using a class and that we should refactor our code. Then however we'll end up with a lot of utility / helper classes which are also not the best design as they are hard to replace or test.

Basically, if your API does not need the Spring context, there is really no reason for putting it there.

Please note that the second method you suggested:

public class A implements AInterface {
    private BInterface b = new B();
    public getB() {return b;}
    public setB(B b) {this.b = b) }
}

Its a bit problematic becasue you initialize interface inside your class, that will cause you problems with testing since you cannot mock these objects. A better solution is to initialize it in the constructor of the class using it.

Just define all your beans to load lazily by default, and then you won't instantiate services you don't use.

<beans default-lazy-init="true">
    <!-- no beans will be pre-instantiated... -->
</beans>

See http://static.springsource.org/spring/docs/2.0.x/reference/beans.html#beans-factory-lazy-init for more details.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM