Looking at JavaConfig for the first time. Here’s some thoughts:
1. The @Configuration annotation allows you to setup auto-wire and lazy initialization but also seems required if you don’t specify anything. Seems like a bit of overhead since you have to explicitly pass the Configuration objects into the ApplicationContext constructor. Shouldn’t that just assume everything passed to it is a Configuration or do you really need to define this annotation?
2. Okay, I’m totally dumb-founded by @Bean. Is this really forcing you to instantiate each of your beans by hand? Doesn’t this fundamentally go against DI/IoC/whatever? One of the benefits of DI is not ever instantiating you classes if you can avoid it. I when I say not ever I really mean not one single line of production code ever calls new on any class that is injected. Unit tests of course are exceptions.
The reason I’m being a bit harsh on this that over the weekend I did a sizable refactoring to Vertigo-Java and wanted to ensure that I didn’t break anyone. We’ll since Vertigo-Java applications should use DI for everything, I knew I could change constructors without impacting clients. And I did. I created new interfaces, implementations and refactored a lot of code out of services into other locations. I also added new bindings to classes like ServletContext and injected them into places I had originally been using static holders and sure enough, the 3 or so applications I had internally compiled and passed all their tests without a single line of code change. Here’s a simple example:
Before: public class MyServiceImpl implements MyService { @Inject public MyServiceImpl() {} } After: public class MyServiceImpl implements MyService { @Inject public MyServiceImpl(Transport transport, PathResolver pathResolver) {} }
Now, since I also defined default implementations for these interfaces using the @ImplementedBy, nothing broke. This was great for me. I could change quite a lot of code and as long as everyone was just using the MyService interface and things were correctly injected, I didn’t break anyone.
Seems to be that @Bean pretty much forces you back to refactoring hell. Agreed that I only refactor the Configuration objects, but that means each Configuration object in all of my projects, libraries, components, etc and all of the projects that use them and projects that use those projects and…. You get the cascading problem of Configuration refactoring. This just sounds disastrous. Am I missing something?
Plus, it doesn’t seem like JavaConfig can even do automatic constructor injection. You HAVE to create your own objects. Is this really true, because that seems extremely painful to me. I much prefer constructor injection to setter injection if for nothing else than correctness.
3. ExternalBean seems like it handles strongly typed by-hand dependency injection to named objects. Other than that, it seems like it doesn’t do a whole lot. Of course, it seems to be a requirement because of the way that JavaConfig forces you to instantiate and wire up objects by hand.
4. I’m not even sure what to think about visibility modifiers. They seem to be something I would never use because they seem like a configuration nightmare. I guess it seems like if I’m gonna create an object, anyone who needs it should get it. I guess I’m lacking the experience of needing to hide objects from each other. This seems like it could also cause some major headaches for large development organizations.
5. Another thing that seems to go against my general sense of reason is that the fact that since you are defining methods, which can just be invoked in any Java code, the behavior of these methods varies depending on whether you are calling them directly or whether Spring is calling them. For example:
@Configuration public class MyConfig { @Bean(scope = DefaultScopes.SINGLETON) public Person rod() { return new Person("Rod Johnson"); } }
If I call this code:
MyConfig mc = new MyConfig(); mc.rod(); mc.rod(); mc.rod(); mc.rod();
Each invocation will return a new instance. However, if I pass this to Spring:
ApplicationContext ac = new AnnotationApplicationContext(MyConfiguration.class.getName()); ac.getBean("rod"); ac.getBean("rod"); ac.getBean("rod"); ac.getBean("rod"); ac.getBean("rod");
Each invocation will return the same instance. This doesn’t seem right to me. The code no longer behaves as standard Java code, which seems to be one of the big concepts behind JavaConfig. Instead, the behavior is supplemented by the annotations of JavaConfig and how Spring uses those to return instances.
I think overall that JavaConfig is a good step away from the generally complex and error prone Spring configuration XML, but doesn’t quite “do it” for me. It seems clunkier than it could be and with obvious examples of simplistic injection models such as Guice, I was hoping that it would have been more stream-lined.