In #Configuration class it is possible to create spring bean by using method with #Bean annotation
#Component
public class Foo {
}
public class Bar {
private Foo foo;
public Bar(Foo foo) {
this.foo = foo;
}
}
#Configuration
public class BarConfig {
#Bean
public Bar bar(Foo foo) {
return new Bar(foo);
}
}
But injecting Foo to BarConfig class and using it that way can let you create Bar as well:
#Component
public class Foo {
}
public class Bar {
private Foo foo;
public Bar(Foo foo) {
this.foo = foo;
}
}
#Configuration
public class BarConfig {
#Autowired
private Foo foo;
#Bean
public Bar bar() {
return new Bar(foo); // bar() without args
}
}
Is there any difference, from spring perspective, between them? Is 2nd incorrect? What would it break or what would not work because of it?
I managed to find that with 2nd there is no visible dependency to foo from Bar but would it affect anything? Refreshing/reloading spring context would pick up the change in foo while remaking bar, wouldn't it?
The only difference between this two types of autowiring is amount of code that has to be written.
I recommend you to use the first option simply because it is shorter.
The second option is suitable in case if you have to specify #Qualifier annotation in pair with #Autowired and the been which you want to inject declared in separate configuration.
Related
I'm developing upon a java core library were change is held to a minimum, it holds a class Foo, where Foo is used by a spring rest interface and autowired into the rest interface.
I have to extend Foo to override one of it's method that is called upon Foo.init(), to alter the initiation a bit.
My FooExtend is autowired from another class in my project, and I'm hoping to get these two to reference the same object, the FooExtend object, but at the moment I get one Foo object and one FooExtend. How should I solve this? Examples below
#Component
public class Foo{
#PostConstruct
private void init() {
startStuff();
}
protected void startStuff(){
//Stuff done here
}
}
#Component
public class FooExtend extends Foo{
#PostConstruct
private void init() {
//Nothing is done here
}
#Override
protected void startStuff(){
//Different altered stuff is done here
}
}
Try to exclude the Foo class from scan
#ComponentScan(value = {'your.package.here'}, excludeFilters = {
#ComponentScan.Filter(classes = { Foo.class })
})
There are 2 possible answers depending on your requirements:
1) You want to register both Foo and FooExtend in your Spring context.
In such case you can use #Qualifier annotation to inject one instance or another:
#Autowired
#Qualifier("fooExtend")
Foo foo;
2) You only want to register FooExtend in your Spring context.
#ComponentScan(value = {'your.package.here'}, excludeFilters = {
#ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE, value=Foo.class))
}
However it is required that Foo and FooExtend are in different packages so that FooExtend is NOT exluded by the above filter.
Assume we have two spring config files: ConfigA.java and ConfigB.java.
Here's how ConfigA.java may look like:
#Configuration
class ConfigA {
#Scope("prototype")
#Bean public Foo fooPrototype() {
return new Foo(params);
}
}
And now I want to inject a few instances of Foo to a number of singleton-scoped beans declared in ConfigB.java:
#Configuration
#Import(ConfigA.class)
class ConfigB {
#Bean public Bar bar() {
return new Bar(*** how to inject Foo instance here? ***);
}
#Bean public Buzz buzz() {
return new Buzz(*** how to inject Foo instance here? ***);
}
}
If I had a single configuration file, I would simply replace the blocks enclosed in asterisks with fooPrototype().
But, how to inject different Foo instances to bar() and buzz() beans provided fooPrototype() is declared in a different configuration file?
This looks similar to the example in the Spring documentation §5.12.5 Composing Java-based configurations.
This same page gives a solution: You can autowire the configuration beans.
#Configuration
#Import(ConfigA.class)
class ConfigB {
#Autowired ConfigA configA;
#Bean public Bar bar() {
return new Bar(configA.fooPrototype());
}
#Bean public Buzz buzz() {
return new Buzz(configA.fooPrototype());
}
}
Can't you just pass fooPrototype as a method arg? E.g.:
#Bean public Bar bar(Foo fooPrototype) {
return new Bar(fooPrototype);
}
#Bean public Buzz buzz(Foo fooPrototype) {
return new Buzz(fooPrototype);
}
This occurs when I subclass an #Configuration annotated class, and feed this to the AnnotationConfigApplicationContext
These classes below summarize the scenario. Here is the full source
public class Bar {}
public class Foo {
private Bar bar;
public Foo(Bar bar) { this.bar = bar; }
#Override public String toString() {
return super.toString() + "(" + bar + ")";
}
}
#Configuration
public class BaseAppConfig {
#Bean public Foo foo() { return new Foo(bar()); }
#Bean public Bar bar() { return new Bar(); }
}
/** Omitting #Configuration here */
public class AppConfig extends BaseAppConfig {
#Bean #Override public Bar bar() { return new Bar(); }
}
public class App {
public static void main(String[] args) {
try (AnnotationConfigApplicationContext ctx = new
AnnotationConfigApplicationContext(AppConfig.class)) {
System.out.println(ctx.getBean(Foo.class).toString());
System.out.println(ctx.getBean(Bar.class).toString());
}
}
}
This prints two Bar instances where I expect to see the same instance two times:
Foo#3e9b1010(Bar#6c3708b3)
Bar#6f1fba17
Because you are omitting the #Configuration annotation (and because it isn't #Inherited)
/** Omitting #Configuration here */
public class AppConfig extends BaseAppConfig {
Specifying the class in the AnnotationConfigApplicationContext
AnnotationConfigApplicationContext ctx = new
AnnotationConfigApplicationContext(AppConfig.class)
marks it as a regular bean class, not a #Configuration bean class.
This means that #Bean methods are operating in lite mode.
In contrast to the semantics for bean methods in #Configuration
classes, 'inter-bean references' are not supported in lite mode.
Instead, when one #Bean-method invokes another #Bean-method in lite
mode, the invocation is a standard Java method invocation; Spring does
not intercept the invocation via a CGLIB proxy. This is analogous to
inter-#Transactional method calls where in proxy mode, Spring does not
intercept the invocation — Spring does so only in AspectJ mode.
Emphasis mine. It means that this that the bar() call in
return new Foo(bar());
is really just calling bar() again, it isn't returning the old instance created.
#Configuration works by having Spring create a proxy of your annotated class which caches the instances returned by #Bean factory methods. Since you've removed #Configuration, Spring doesn't apply this caching and method calls work normally, in your case returning a new instance.
Rather than use direct Bar object creation you should use spring bean management:
#Configuration
public class BaseAppConfig {
#Bean #Autowired public Foo foo(Bar bar) { return new Foo(bar); }
#Bean public Bar bar() { return new Bar(); }
}
See spring reference for details.
Currently testing with dagger, what I want to do is instantiate and inject different Bar implementations. How can I inject fields in provided fields?
for example:
Module:
#Module(
injects = {
Main.class
},
complete = false,
library = true
)
public class ExampleTestModule {
#Provides
public Foo providesFoo() {
return new Foo();
}
#Provides
public Bar providesBar(BarImpl impl) {
// return new BarImpl(); // null
return impl;
}
}
Main:
public class Main {
#Inject
Foo foo;
}
Foo:
public class Foo {
#Inject
Bar bar;
}
Bar:
public interface Bar {
}
BarImpl
public class BarImpl implements Bar {
}
TestCase:
public class ApplicationTest extends ApplicationTestCase<Application> {
public ApplicationTest() {
super(Application.class);
}
public void testFoo() {
Main main = new Main();
ObjectGraph.create(new ExampleTestModule()).inject(main);
assertNotNull(main.foo);
}
public void testFooBar() {
Main main = new Main();
ObjectGraph.create(new ExampleTestModule()).inject(main);
assertNotNull(main.foo.bar);
}
}
Main.Foo is not null but Main.Foo.Bar is null.
You are never injecting bar into foo.
ObjectGraph.create(new ExampleTestModule()).inject(main);
This line will only look at the fields of main which are annotated by #Inject, and inject them. There is no recursive behavior.
Fixing the problem
Let's go step-by-step:
You provided complete = false and library = true in your Module. You should only use these if really necessary. Dagger will give you warnings when something is wrong, and these properties surpress these warnings. For example, removing them raises the following warning when compiling:
Error:(11, 8) error: No injectable members on BarImpl. Do you want to add an injectable constructor? required by providesBar(BarImpl) for ExampleTestModule.
Let's add an empty injectable constructor to BarImpl, as it suggests:
public class BarImpl implements Bar {
#Inject
BarImpl(){
}
}
Compiling will give a new error:
Error:(11, 8) error: Graph validation failed: You have these unused #Provider methods:
1. ExampleTestModule.providesBar()
Set library=true in your module to disable this check.
Apparently, providesBar() is never used. That means, the bar field in Foo will never be injected. You can do two things:
Inject bar manually:
ObjectGraph graph = ObjectGraph.create(new ExampleTestModule());
graph.inject(main);
graph.inject(main.foo);
Use injectable constructors (Preferred option):
public class Foo {
Bar bar;
#Inject
Foo(Bar bar){
this.bar = bar;
}
}
Using the injectable constructor, you will now have a compile error in providesFoo(), since you don't supply a Bar instance in the Foo constructor. The nice thing about Dagger is, you can safely completely remove this method. Since Foo is annotated with #Injectable, everywhere it needs to inject a Foo instance, it uses this constructor. And when it uses this constructor, it notices it needs a Bar instance, and injects this as well.
Finally, we can remove the #Inject annotation from the Foo field in Main, and create an injectable constructor. Using ObjectGraph.get(Class<?>) we can retrieve a fully instantiated Main instance.
The result
The end result should look like this:
Module:
#Module(
injects = Main.class
)
public class ExampleTestModule {
#Provides
public Bar providesBar(BarImpl impl) {
return impl;
}
}
Main:
public class Main {
Foo foo;
#Inject
Main(Foo foo) {
this.foo = foo;
}
}
Foo:
public class Foo {
Bar bar;
#Inject
Foo(Bar bar){
this.bar = bar;
}
}
Bar:
public interface Bar {
}
BarImpl:
public class BarImpl implements Bar {
#Inject
BarImpl(){
}
}
ApplicationTest:
public class ApplicationTest extends ApplicationTestCase<Application> {
public ApplicationTest() {
super(Application.class);
}
public void testFoo() {
Main main = ObjectGraph.create(new ExampleTestModule()).get(Main.class);
assertNotNull(main.foo);
}
public void testFooBar() {
Main main = ObjectGraph.create(new ExampleTestModule()).get(Main.class);
assertNotNull(main.foo.bar);
}
}
Conclusion
From the result, we can conclude some things:
Don't just add library = true and complete = false to your module. This should only be necessary when using multiple complex modules.
Try to use injectable constructors. This is what Dagger's built for, and works best. An extra perk is that you can now have your fields private, like they should be.
When using injectable constructors, you really only need to create providesXXX methods when injecting instances of interfaces, like we did with Bar and BarImpl. Because, hey, that's exactly what Dependency Injection is for, right?
I am in the process of converting non-Spring code into Spring (v3.2) code. I have read through the Spring documentation but I am having trouble wrapping my head around the following situation shown below.
Classes Foo and Buz are managed by Spring and are singletons. Class Bar and MyHyperlinkListener need to be managed by Spring and should be prototype (though I'm not sure how to annotate these correctly).
The instance of Buz inside of Bar is currently null because Bar is not being managed by Spring. How would these classes be correctly annotated to allow this? Does a Factory class for Bar need to be created, and what would that look like?
The constructor for Bar is being passed a 'this'(aka JFrame) from inside the Foo class. I am not sure how to modify the code to handle a 'this' in Spring. I'm guessing this is another Factory that accepts a JFrame, but I'm not sure how to code that.
#Named
public class Foo extends JFrame{
private Bar bar;
private void doSomeWork(int x){
bar = new Bar( new MyHyperlinkListener(this), x);
}
}
public class Bar extends JPanel{
#Inject
private Buz buz;
public Bar(MyHyperlinkListener mhl, int x){
}
}
public class MyHyperlinkListener implements HyperlinkListener{
private JFrame frame;
public MyHyperlinkListener(JFrame frame){
this.frame=frame;
}
//...code omitted
}
#Named
public class Buz{
}
#Configuration
public class MyConfiguration{
}
public class RunMe{
public static void main(String[] args){
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.scan("myPackage");
context.refresh();
context.registerShutdownHook();
Foo foo = context.getBean(Foo.class);
foo.setVisible(true);
}
}
You could make the Bar class #Configurable, which will allow it to be managed by Spring whenever an instance is created.
#Configurable
public class Bar extends JPanel{
#Inject
private Buz buz;
public Bar(MyHyperlinkListener mhl, int x){
}
}
Alternatively, you can use a bean factory to create your instances of Bar. This will have the same effect:
#Configuration
public class BarFactory
{
#Bean
public Bar createBar(MyHyperlinkListener mhl, int x) {
return new Bar(mhl, x);
}
}
Both methods are valid, and mainly differ in how they expose the management. They will also allow the constructor you wish to use.