I'm trying to change some legacy code to use DI with Spring framework. I have a concrete case for which I'm wondering which is the most proper way to implement it.
It is a java desktop application. There is a DataManager interface used to query / change data from the data store. Currently there is only one implementation using a XML file for store, but in the future it is possible to add SQL implementation. Also for unit testing I may need to mock it.
Currently every peace of code that needs the data manager retrieves it by using a factory. Here is the source code of the factory:
public class DataManagerFactory
{
private static DataManagerIfc dataManager;
public static DataManagerIfc getInstance()
{
// Let assume synchronization is not needed
if(dataManager == null)
dataManager = new XMLFileDataManager();
return dataManager;
}
}
Now I see 3 ways to change the application to use DI and Spring.
I. Inject the dependency only in the factory and do not change any other code.
Here is the new code:
public class DataManagerFactory
{
private DataManagerIfc dataManager;
public DataManagerFactory(DataManagerIfc dataManager)
{
this.dataManager = dataManager;
}
public DataManagerIfc getDataManager()
{
return dataManager;
}
public static DataManagerIfc getInstance()
{
return getFactoryInstance().getDataManager();
}
public static DataManagerFactory getFactoryInstance()
{
ApplicationContext context =
new ClassPathXmlApplicationContext(new String[] {"com/mypackage/SpringConfig.xml"});
return context.getBean(DataManagerFactory.class);
}
}
And the XML with the bean description:
<bean id="dataManagerFactory"
class="com.mypackage.DataManagerFactory">
<constructor-arg ref="xmlFileDataManager"/>
</bean>
<bean id="xmlFileDataManager"
class="com.mypackage.datamanagers.xmlfiledatamanager.XMLFileDataManager">
</bean>
II. Change every class that is using the data manager so it takes it through the constructor and store it as a class variable. Make Spring bean definitions only for the "root" classes from where the chain of creation starts.
III. Same as II. but for every class that is using the data manager create a Spring bean definition and instantiate every such class by using the Spring Ioc container.
As I'm new to the DI concept, I will appreciate every advice what will be the correct and "best practice" solution.
Many thanks in advance.
Use option 3.
The first option keeps your code untestable. You won't be able to easily mock the static factory method so that it returns a mock DataManager.
The second option will force you to have the root classes know all the dependencies of all the non-root classes in order to make the code testable.
The third option really uses dependency injection, where each bean only know about its direct dependencies, and is injected by the DI container.
Well... why did you write the factory in the first place? Spring is not intended to make you change how you write code (not just to suit Spring that is), so keeping the factory is correct as it uses well-known pattern. Injecting the dependency into the factory will retain that behaviour.
Option 3 is the correct route to take. By using such a configuration you can usefully take components of your configuration and use them in new configurations, and everything will work as expected.
As a rule of thumb, I would expect one call to Spring to instantiate the application context and get the top-level bean. I wouldn't expect to make repeated calls to the Spring framework to get multiple beans. Everything should be injected at the correct level to reflect responsibilities etc.
Beware (since you're new to this) that you don't plumb in your data manager into every class available! This is quite a common mistake to make, and if you've not abstracted out and centralised responsibilities sufficiently, you'll find you're configuring classes with lots of managers. When you see you're doing this it's a good time to step back and look at your abstractions and componentisation.
Related
Anyone can explain how Spring defines the bean creation mechanism when create a bean which depends on a list of other beans? It would be good to show the part of Spring specification on how it's defined.
Code like:
public interface Test {
}
#Service
public class TestImpl1 implements Test{
}
#Service
public class TestImpl2 implements Test{
}
public class TestContainer {
List<Test> testList;
TestContainer() {
testList = new ArrayList<>();
}
public void addTest(Test test) {
testList.add(test);
}
}
then
#Bean
public TestContainer testContainer(List<Test> testList) {
TestContainer testContainer = new TestContainer();
for (Test test : testList) {
testContainer.addTest(test);
}
return testContainer;
}
Question is really: when creating bean for TestContainer, how does Spring figure out what should be in List testList?
This looks like what you are looking for:
https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-factory-class
You haven't specified what exactly do you want to know about bean creation but here is the minimum you should know. By default all beans are singleton scoped (only created once during the container life-cycle and for all the subsequent request the same instance is returned). All singleton scoped beans are created eagerly. If the singleton bean is dependent on some other beans (needs them for instantiation) then those other beans will be instantiated with it/right before it, doesn't matter whether they are singletons or not, marked as lazy or not.
This is part of the spring documentation I think on their website. Look for it. The whole spring documentation is worth to read, even if it is a long read (https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#spring-core)
But mostly, spring would first read all the XML, anotations and other source of config data and get a list of beans to init. Then it would get a list of dependencies between beans constructing kind of a tree. As the dependencies need to be initialized first, there an obvious orderining for initialization.
On top, you can set your own priorities for beans so that you can get some beans initialized first or last for example.
For your specific case, spring will inspect the code source as well as use the Java reflection API to figure out you want a collection of interface Test implementations. So spring would look for ALL the defined bean that match and return them that not more complex than that.
It's mostly using the concepts of graph data structure where the beans become the nodes of the graph and they are resolved using topological sort.
I am looking for a simple way to control the instantiation of a declarative service.
What I want is something like a factory which is asked to return an instance of a component, but where I can control the instantiation.
Currently I just found the ComponentFactory where I just can call newInstance with a given dictionary. But what I really want, is something like this, assuming IComponent is the declarative service interface and MyComponent is the implementation class
public class MyComponentFactory implements ? {
public IComponent newInstance() {
return new MyComponent("firstParameter", "secondParameter");
}
}
Is there something possible like this with declarative services, or do I need to use my own service registration in a bundle activation code?
Thanks in advance
DS does not provide for the level of instance creation indirection you are looking for. DS will always use the public no-args constructor and then call the specified activator method to complete the instance initialization.
One alternative for such service instantiation control is using a combination of DS and CM.
You must set configuration-policy="require" in the DS xml and use CM to create a configuration instance that will be used to pass a Dictionary containing all properties that you need (only types supported by DS of course) to the created service instance. You can even use a propertie file plus Felix File Install for that connfiguration.
If that is not enough you have another alternative that is to track for new created service and call a setup(args) method just after you have added a configuration using CM.
I have a spring service that provides configuration data. When the service is invoked by the GUI, it loads the configuration data from the database. It turns out that this happens quite often during the rendering of a single request. I want to optimize this by caching the configuration data. However, I am not sure if this is a good programming style or if it is "allowed" to have an instance variable in a service.
Here is some example code of what I am thinking of doing:
#Serivce("MyConfigService")
public class MyConfigServiceImpl implements MyConfigService {
private Config cachedConfig;
#Override
public Config loadConfig() {
if (cachedConfig != null) {
// load config
cachedConfig = loadedConfig;
}
return cachedConfig;
}
#Override
public saveConfig(Config config) {
cachedConfig = null;
// save the configuration
}
}
Having a instance variable (not managed by spring) introduces the possibility of the service becoming thread unsafe. So I try to avoid them, or make sure they are thread safe.
You may want to look at #configurable and #postconstuct annotations to achieve your goals.
Are instance variables allowed in Spring service layer classes? Sure.
Is it a good idea to use one to save a reference to the Config object here?
Maybe, maybe not.
You're not showing how Config normally gets loaded... Does the same Config instance get returned to all users? i.e. - When User1 calls saveConfig then User2 calls loadConfig User2 gets the Config object User1 saved.
If so, you should be able to cache the value with no problems.
Also, instead of implementing it yourself, you could use Spring's annotation-based caching.
Instance variables is what Spring IoC container is all about; what is dubious in your design is that you have your own lazy-loading logic in loadConfig—that's the concern you must leave to Spring via lazy-init=true (or similar, I don't remember exactly). The design will also probably involve lookup methods and posibly request-scoped beans.
I have an elaborate Spring bean setup for integration tests. Now I'm looking into writing a Robot library to expose my test data creation / behavior execution / assertion methods to Robot tests.
However what I understand from the Robot Framework user guide is that Robot can only instantiate library classes by calling a constructor. This is a bummer because I'd rather have my instances managed by Spring.
Ideally, I'd want to be able to give Robot the path to the application context and the bean name for the library. Failing that, I'd want Robot to be able to invoke a static factory method rather than a constructor, so I'm not forced to create a new instance.
One workaround I thought of is to create the Spring context in a static initializer and wire my dependencies by fetching beans from that context.
My original class looks like:
public class MyAwesomeTests {
#Autowired
private ThisHelper thisHelper;
#Autowired
private ThatHelper thatHelper;
// implementations of test steps and such
}
So I would change the above #Autowired fields to be protected, and create a subclass that statically initializes the Spring context and defines a Robot-friendly constructor:
public class RobotFriendlyTests extends MyAwesomeTests {
private static final ApplicationContext CONTEXT = new ClassPathXmlApplicationContext(...);
public RobotFriendlyTests() {
this.thisHelper = (ThisHelper) CONTEXT.getBean("thisHelper");
this.thatHelper = (ThatHelper) CONTEXT.getBean("thatHelper");
}
}
This should work, but it feels somewhat clunky. Is there a better way I should consider? Better yet, is there a Robot extension that already does this for me?
Have you thought about using Spring #Configurable, then even instances created by a normal new will become spring managed beans.
#See Spring Reference Chapter 7.8.1 Using AspectJ to dependency inject domain objects with Spring
There's a Robot Framework extension that supports using Spring to wire test libraries, take a look at: http://code.google.com/p/robotframework-javalibcore/wiki/SpringLibrary
I am not entirely sure whether it supports your case since I am not familiar at all with Spring.
I have a bunch of java custom tags that use spring managed beans.. since i cant find a way to inject into a custom tag, i created a helper class that provides static methods to "getTheObjectINeedBean()" for all the spring bean objects i need.. I do not like this approach at all.
i really want to be able to inject a spring managed bean into the custom tag
Is there a way? As far as my research goes, I understand there is no way to do this, because the custom tag is container managed
Thanks,
Billy
You are correct there isn't a simple way to use dependency-injection in jstl tags, because they are not managed by spring, and cannot be. However there are (at least) two workarounds:
#Configurable - aspectJ allows you to plug a weaver at load-time/compile-time, so that even objects that are not instantiated by spring can be spring aware. See here
You can create a base tag class for your project, and call an init(..) method from every doStartTag(..) method. There, you can get the ServletContext from the pageContext, and thus obtain the spring ApplicationContext (via ApplicationContextUtils). Then:
AutowireCapableBeanFactory factory = appCtx.getAutowireCapableBeanFactory();
factory.autowireBean(this);
Neither options are perfect as they require either some additional code, or some "black magic"
To expand on #Bozho's post, I have gotten this to work like so: (in spring 3.0 there is no ApplicationContextUtils that I could find)
public class LocationTag extends RequestContextAwareTag {
#Autowired
PathComponent path;
...
#Override
protected int doStartTagInternal() throws Exception {
if (path == null) {
log.debug("Autowiring the bean");
WebApplicationContext wac = getRequestContext().getWebApplicationContext();
AutowireCapableBeanFactory acbf = wac.getAutowireCapableBeanFactory();
acbf.autowireBean(this);
}
return SKIP_BODY;
}
}
The solution as described above works but some background and additional code snippets are, quite likely, useful.
1) The doStartTagInternal method is invoked from the doStartTag method.
2) I was forced to set the pageContext first before invoking the doStartTag
3) I did a lookup of the bean as opposed to the autowiring. To me this seems more straightforward: (YourBeanProxy) autowireCapableBeanFactory.getBean("yourBeanName")
Hopefully this additional info is useful.