4

I'm in the process of moving some Xml-Based spring config over to Java-based.

Previously, I've consciously mixed Xml <bean class='foo' /> declarations with @Component mappings on a case-by-case basis, as a means of documenting non-obvious beans.

A typical use case would be if I'm declaring beans that will modify the behaviour of spring itself, I'll declare these explicitly, rather than let them be discovered, purely to improve the clarity of the config file.

However, often these beans will need a degree of @Autowiring. If I new the bean up myself within a Java config, I become responsible for performing this wiring -- which is pointless.

Is there a pure Java configuration option that explicitly provides a class back to Spring, and let's it manage the instantiation?

ie:

Given the class:

public class MyFunkySpringBean implements InitializingBean {
    @Autowired Foo foo;
}

In XML config, this would be declared as simply as:

<bean class="MyFunkySpringBean" />

Is there an equivalent in Java syntax, where I can explicitly declare the bean class to Spring, but not be responsible for providing the actual instance -- leaving that to Spring.

Eg:

@Configuration 
public class MyAppConfig {

    // Explictly provide the class, not the instance to Spring
    @Bean
    public MyFunkySpringBean funkyBean; // No instance -- it's up to Spring to build
}

To be clear, I don't want to have to do this:

@Configuration 
public class MyAppConfig {

    @Bean
    public MyFunkySpringBean funkyBean() {
         MyFunkySpringBean bean = new MyFunkySpringBean();
         bean.foo = new Foo();
         // other dependencies go here
         return bean;
    }

}

Does facility like this exist within Spring?

Marty Pitt
  • 28,822
  • 36
  • 122
  • 195
  • I don't understand the question. You want to provide a class and let Spring manage its lifecycle for you (which is the common behavior) or you want to define how the class should be generated and let Spring manage this logic when autowiring it? – Luiggi Mendoza Apr 26 '14 at 16:06
  • 1
    The first one. I want an XML-Free way to provide a class and let Spring manage it's lifecycle. I don't want to be responsible for `new`ing it up. However, I don't want it discovered via `@Component` scanning -- I want to be explicit about the declaration of this class. – Marty Pitt Apr 26 '14 at 16:09
  • Your rephrased question still does not use *constructor autowiring*! I can see that you don't understand how property autowiring works in Spring... there are *bean post processors*, which detects properties and methods annotated with `@Autowired` annotation. These post processors are still active when using Java config. You don't need to do the autowiring manually. It will be done by Spring! Your presumption that you need to call `bean.foo = new Foo()` is incorrect... check again Sotirios answer (mainly the emphasized statement). – Pavel Horal Apr 30 '14 at 09:08
  • To be more clear - bean instantiation and dependency injection (including autowiring) are two separate processes... unless you are using constructor injection. *Constructor autowiring* is the only thing which is currently not possible with Java config. In Java config you are responsible for creating the instance... however as soon as the instance is returned from the `@Bean` method, it will be subject for additional processing - proxying, autowiring, special interface and annotation handling (factory beans, initializing beans, common annotations, ...). All the Spring magic is still there. – Pavel Horal Apr 30 '14 at 09:15

4 Answers4

3

I understand from your comments that you are looking for a way how to achieve constructor autowiring (with type or annotation autowiring) in Java config... i.e. equivalent of:

<bean class="com.example.Foo" autowire="constructor" />

I've checked Spring's source to find who is actually responsible for the constructor resolution and autowiring and it is a combination of package private class ConstructorResolver and AbstractAutowireCapableBeanFactory. So that is something you will not be able to use on your own.

If you really really want to have constructor autowiring without XML, you can implement your own BeanDefinitionRegistryPostProcessor and manually register that bean definitions yourself:

public class CustomBeanDefinitionRegistrar implements BeanDefinitionRegistryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        // Not interested in this method
    }

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
        registry.registerBeanDefinition("foo", BeanDefinitionBuilder.
                genericBeanDefinition(Foo.class).
                setAutowireMode(AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR).
                getBeanDefinition());
    }

}

This bean factory post processor then needs to be registered via static @Bean method in your @Configuration:

@Configuration
public class MyAppConfig {

    public static BeanFactoryPostProcessor customBeanDefinitionRegistrar() {
        return new CustomBeanDefinitionRegistrar();
    }

}

I admit it is a bit "wild approach", but it seems to be the only way how to achieve constructor autowiring with pure Java config.

Pavel Horal
  • 17,782
  • 3
  • 65
  • 89
2

You need to notify Spring a way to discover the beans that will manage. You only have three ways (there's a fourth one but it is more complex and requires annotations as well, explained here):

  • XML based configuration:

    <bean id="foo" class="some.package.Foo" />
    
  • Annotation configuration:

    @Component
    public class Foo {
    }
    
  • Create a @Configuration class that declares the beans explicitly:

    @Configuration
    public class MyConfiguration {
        @Bean
        public Foo getFoo() {
            return new Foo(); //foo doesn't need any annotation at all.
        }
    }
    

AFAIK you're able to do something similar using CDI:

class MyConfiguration {

    @Produces
    Foo foo = new Foo();
}

Or by injecting a resource from another container, like JPA:

class MyConfiguration {

    //JPA will provide the object reference based on the persistence unit
    //CDI will just work as a by-pass to inject this reference in other beans
    @PersistenceContext //JPA annotation
    @Produces
    EntityManaged em;
}

But even CDI needs a way to know how to initialize the bean or where to retrieve it first.

Refer to:

Community
  • 1
  • 1
Luiggi Mendoza
  • 85,076
  • 16
  • 154
  • 332
  • @MartyPitt I'm really missing the point of having that. How would Spring know what instance to create and how to create it? What is it managing? – Sotirios Delimanolis Apr 26 '14 at 16:28
  • @MartyPitt I doubt of this. What would happen if your bean doesn't offer a default constructor, how Spring should manage it? Or what if you declare an abtract class or an interface, how is supposed that Spring have to create such instance? – Luiggi Mendoza Apr 26 '14 at 16:32
  • @SotiriosDelimanolis & LuiggiMendonza : Gents -- There's no difference between what is being expressed here, and what is possible to express via Xml config. In XML, it's possible (though, not valid) to declare definitions of abstract classes, classes with non-default constructors, ambiguous classes, primitive classes, and a plethora of other configs that are invalid. There's a rich resolution algo that evaluates & either rejects or allows those configs. I submit that Java configs should be allowed to get explicitly included in the same resolution process -- that's all. – Marty Pitt Apr 26 '14 at 20:10
  • 1
    @MartyPitt Yes, the Java alternative of that is a `@Bean` method. I still don't see a valid use case for what you propose or a valid solution. – Sotirios Delimanolis Apr 26 '14 at 20:14
  • No, they're very different. @Bean provides a value. ("Here's a `foo`. I built it for you"). Xml provides a class, but no value, and leaves it to the Container to provide one. ("Build me a `foo`"). The responsibilities & obligations of the two are miles apart. The closest Java equivalent to this concept is `@Component`. But, that's implicit / discovered, not explicit / decalared, like the xml version. – Marty Pitt Apr 26 '14 at 20:19
2

In newer versions of Spring (as of 4.2) you can use org.springframework.context.annotation.Import e.g.

@Configuration
@Import(MyFunkySpringBean.class)
public class MyAppConfig {
   ... other config ...
}
artbristol
  • 32,010
  • 5
  • 70
  • 103
1

Does facility like this exist within Spring?

The answer is kind of, but it is not clean.

All bean definitions in Spring depend on a BeanDefinition object in the ApplicationContext.

With an XML configuration like

<bean class="com.example.SomeType">
</bean>

Spring will generate a RootBeanDefinition with that class type and as a singleton, but with no other information about how it should be constructed. Instead the ApplicationContext will determine how to create the bean.

With Java configuration like

@Bean
public com.example.SomeType someType() {
    return new com.example.SomeType();
}

Spring will create a RootBeanDefinition but specifically state that the bean should be generated from the @Configuration bean as a factory-bean and its someType() method as a factory-method. The ApplicationContext therefore doesn't have to decide for itself, it just does what the BeanDefinition tells it.

(Incomplete, I'll be back later to finish this.)


You have to understand that XML and Java configurations are different but try to achieve the same thing.

When you declare an XML <bean> element like so

<bean class="com.example.SomeType">
</bean>

You are telling Spring to use the parameterless constructor of SomeType to instantiate the bean class and create a bean.

In Java, the equivalent would be

@Bean
public com.example.SomeType someType() {
    return new com.example.SomeType();
}

Similarly,

<bean class="com.example.SomeType">
    <constructor-arg type="java.lang.String" value="hello world"/>
    <property name="some" value="some value" />
</bean>

is telling Spring to use a constructor with one parameter of type String with that given value to instantiate the bean class. Then set some property. The equivalent is

@Bean
public com.example.SomeType someType() {
    com.example.SomeType someType = new com.example.SomeType("hello world");
    someType.setSome("some value");
    return someType;
}

These beans will still be candidates for autowiring just like any other. If they have @Autowired fields or methods, those will be injected.

All the above configs tell Spring exactly how to create the bean. Something like this

@Bean
public MyFunkySpringBean funkyBean; // No instance -- it's up to Spring to build

doesn't really say anything unless conventions are set. I don't see any advantage to that feature that you can't get with a classic @Bean method.

Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
  • 1
    In the XML example, if `com.example.SomeType` doesn't provide a parameterless constructor, but declares a parameterized constructor, annotated with `@Autowired`, it will be invoked, and the appropriate values injected by Spring. What's the Java equivalent? – Marty Pitt Apr 26 '14 at 20:52
  • In the XML example, you're not telling Spring which constructor to invoke. You're telling it : "Here's a class -- Use reflection to work out what to do next". There's no guarantees made that a parameterless constructor exists. In XML you may choose to get more explicit, by providing constructor args. But, omitting them and providing an `@Autowired` parameterized constructor on the bean is also valid. And, if you don't provide a valid config, Spring will tell you that too. There is no Java equivalent of that which is explicit. (Only, the *implicit* `@Component` annotation) – Marty Pitt Apr 26 '14 at 21:01
  • @MartyPitt So what you're looking for is implicit constructor invocation through a Java configuration? – Sotirios Delimanolis Apr 26 '14 at 21:05
  • @MartyPitt But also still be able to use property setters (or any other method) on the bean like you would in a `@Bean` method? – Sotirios Delimanolis Apr 26 '14 at 21:08
  • I'm looking for an explicit java mechanism that submits a type to the same instantiation criteria as the xml version. (ie., use reflection, invoke a parameterless constructor if it exists, or invoke the `@Autowired` parameterized constructor, and wire it up). – Marty Pitt Apr 26 '14 at 21:10
  • I hadn't considered using the getters / setters like in a @Bean method, and don't consider it to be a material point of difference. If it is possible, I guess it would behave like invoking getters / setters on any other container-constructed injected beans. – Marty Pitt Apr 26 '14 at 21:12
  • @MartyPitt Now when you have mentioned autowiring for constructors your question makes a lot more sense. Can you maybe try to rephrase somehow your question to make this clear? You will prevent trivial answers explaining differences between XML and Java configs... Mentioning this in the Spring issue would make things also much clearer (i.e. what is the expected benefit of your requirement). – Pavel Horal Apr 26 '14 at 21:30
  • @MartyPitt probably my 4th case would suit your expectations, but you still have to decorate the class with `@Configurable`. Check the edit on my answer. – Luiggi Mendoza Apr 26 '14 at 21:33