开发者

How to get beans created by FactoryBean spring managed?

开发者 https://www.devze.com 2023-02-10 03:39 出处:网络
The FactoryBean can be used to programmatically create objects which might require complex instantiation logic.

The FactoryBean can be used to programmatically create objects which might require complex instantiation logic.

However, it seems that the beans created by the FactoryBean doesn't become spring managed. Is this interpretation correct? If so, are there any nice workarounds? A short code sample is included to illustrate my problem.

ApplicationContext:

<bean id="searcher" class="some.package.SearcherFactory" /> 
<bean id="service" class="some.package.Service" /> 

Factory implementation:

public class SearcherFactory implements FactoryBean<Searcher> {

    @Override
    public Searcher getObject() throws Exception {
        return new Searcher(); // not so complex after all ;)
    }

    @Override
    public Class<Searcher> getObjectType() {
        retur开发者_StackOverflown Searcher.class;
    }
    .... 
}

Class created by the factory:

public class Searcher() {
      private Service service;

      @Autowired
      public void setService(Service service) {
           // never invoked
           this.service=service;
      } 
}


Here is an abstract FactoryBean implementation that does autowiring for you:

public abstract class AbstractAutowiringFactoryBean<T> extends
    AbstractFactoryBean<T> implements ApplicationContextAware{

    private ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(
        final ApplicationContext applicationContext){
        this.applicationContext = applicationContext;
    }

    @Override
    protected final T createInstance() throws Exception{
        final T instance = doCreateInstance();
        if(instance != null){
            applicationContext
              .getAutowireCapableBeanFactory()
              .autowireBean(instance);
        }
        return instance;
    }

    /**
     * Create the bean instance.
     * 
     * @see #createInstance()
     */
    protected abstract T doCreateInstance();

}

Extend it, implement the getObjectType() and doCreateInstance() methods and you're up and running with autowiring.

Note: BeanPostProcessors are not applied, that would require additional code.


The object created by the FactoryBean are managed by Spring, but not instantiated or configured by Spring. By using a FactoryBean, you take responsibility for that yourself. All injection and config must be handled by the FactoryBean

There is an alternative which may work better for you - use annotation-based config instead of XML-based config. This means you can have complex instantiation logic in Java, whilst still using things like @Autowired on the objects themselves.

I tend to use annotation-style config for all non-trivial Spring apps now, it makes many things a lot easier.


What about this ?

<bean id="serviceFactory"
      class="some.package.SearcherFactory" />


<bean id="service"
      factory-bean="serviceFactory"
      factory-method="getObject"/>

... and then just inject the bean 'service' and do not care about the factory in your code


A manual way would be:

  1. Inject the dependencies in the factory bean
  2. set them manually on the target object.

You can also inject ApplicationContext in the factory bean (or get it by implementing ApplicationContextAware), and do ctx.getAutowireCapableBeanFactory().autowireBean(bean)

I admit both feel strange, though.

But in fact, if the logic is that simple (only instantiation), use prototype scope.


A FactoryBean is an interface that you, as a developer, implements when writing factory classes and you want the object created by the factory class to be managed as a bean by Spring, while a BeanFactory on the other hand, represents the Spring IoC container, it contains the managed beans and provides access to retrieving them. It is part of the core of the framework which implements the base functionality of an inversion of control container.

In most cases you won't find yourself using or implementing the BeanFactory interface directly, unless you are extending the core functionality of the framework. While you would do implement the FactoryBean when you have objects that are created by Factories that needs to be managed by Spring.

In more succinct words, the BeanFactory represents the Spring container while the FactoryBean represents factory classes whose created object are picked up and registered as a bean in the container.

File: context.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
                http://www.springframework.org/schema/beans
                http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="sha" class="MessageDigestFactoryBean">
        <property name="algorithm" value="SHA1"/>
    </bean>

    <bean id="md5" class="MessageDigestFactoryBean"/>

</beans>


File: Main.java

import java.security.MessageDigest;

import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;

public class Main {
  public static void main(String[] args) {
    XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("context.xml"));
    String d1 = (String) factory.getBean("sha");
    String d2 = (String) factory.getBean("md5");
    System.out.println(d1);
    System.out.println(d2);
  }

}

class MessageDigestFactoryBean implements FactoryBean, InitializingBean {
  private static final String DEFAULT_ALGORITHM = "MD5";

  private String algorithm = DEFAULT_ALGORITHM;

  public Object getObject() throws Exception {
    return this.algorithm;
  }

  public Class getObjectType() {
    return MessageDigest.class;
  }

  public boolean isSingleton() {
    return true;
  }

  public void setAlgorithm(String algorithm) {
    this.algorithm = algorithm;
  }

  public void afterPropertiesSet() throws Exception {
    this.algorithm += " after setting";
  }
}
0

精彩评论

暂无评论...
验证码 换一张
取 消