简体   繁体   中英

How to register spring beans into a spring context, from outside spring context?

I have the following components

  • A Web application exposed using Spring MVC (war file).
  • A library (jar-file), which is aware of the fact that whoever is using the library, uses spring.

Scenario

My library has a class that needs to register itself as a bean in to the web application context, without telling the users of the library to change their code (xml, spring context, or classes).

So, in other words, I have a Class, and a spring context, I want from only my class to register on this spring context without changing anything in it. So is there any time spring will scan the classpath for classes implementing a certain interface, if so what interface?

What I've tried

  • Using BeanFactoryPostProcessor but this requires me to access the application context and call addBeanFactoryPostProcessor as far as I can find.

  • Using ApplicationContextInitializer but this forces me to change the web.xml

  • A bunch of other things like different Aware interfaces but I have not found anything yet that allows my class to self-register to the Bean Factory and be picked up in the spring context, I want to be able to use things like the Scheduled annotation. And I always want others in the spring context to be able to find my class by Autowired annotation.

Examples

Class that I own and inside my library

package spring.outside

public class ClassA {
    @Scheduled(fixedDelay=5000)
    public void doSomething() {
    }
}

Class that the user of my library owns

package spring.inside

public class ClassB {
    @Autowired 
    private ClassA classA;
}

With a spring file containing something like this, that I do not own

    <context:component-scan base-package="spring.inside"/>


If this is not possible I would like to explore the possibilities of what I can do in my class that is inside spring to, as easily as possible, tell the spring context to pick it up.

Examples

package spring.outside

public class ClassA {
    @Scheduled(fixedDelay=5000)
    public void doSomething() {
    }
}

Class that the user of my library owns

package spring.inside

public class ClassB {
    @Autowired 
    private ClassA classA;
}

Another class that is meant to initiate the ClassA and put it into the spring application context, I just show the code of some of the different things I've tested in it now since I don't know how it should look like.

package spring.inside

public class ClassC implements BeanFactoryPostProcessor, ApplicationContextInitializer, BeanFactoryAware, ApplicationContextAware {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        beanFactory.registerSingleton("classA", new ClassA());
    }

    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        applicationContext.addBeanFactoryPostProcessor(this);
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        ((ConfigurableListableBeanFactory)beanFactory).registerSingleton("classA", new ClassA());
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        ((ConfigurableApplicationContext)applicationContext.getParentBeanFactory()).addBeanFactoryPostProcessor(this);
        ((ConfigurableListableBeanFactory)applicationContext.getParentBeanFactory()).registerSingleton("classA", new ClassA());
    }
}

Tried

  • Adding it using BeanFactoryPostProcessor and registerSingleton
  • Adding it using ApplicationContextInitializer then using the addBeanFactoryPostProcessor in combination with the BeanFactoryPostProcessor
  • Adding it by BeanUtils.instantiate
  • Adding it by implementing ApplicationContextAware and casting it to a ConfigurableApplicationContext and adding the BeanFactoryPostPrecessor implementation, and I've also tried to typecast to ConfigurableListableBeanFactory and registerSingleton.
  • Adding it with BeanFactoryAware and registerSingleton as well.
  • Making my ClassC to a config where I add componentScan for spring.outside package works perfectly, but feels very wrong to do it this way.

But all of these seem to work partially, but it always ends up getting some timing error such as: ClassA needs input variables in the constructor that is Autowired inside ClassC, some of these are set when ClassC creates ClassA. Another scenario I saw was ClassB not being able to Autowired ClassA because that was done before. So what would be the correct and nice way of doing this?

I'm running spring version 3.2.11.RELEASE

If anyone knows how to solve this problem in a nice way and not something that feels like "ugly hack" I would be very happy to know!

Thanks and best regards!

In this case you must include ClassA as a spring bean in the Spring Context XML

<bean id="classA" class="spring.inside.ClassA">

or using a Configuration class

package spring.inside;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import spring.outside.ClassA;

@Configuration
public class AppConfiguration {    

    @Bean
    ClassA classA() {
        return new ClassA();
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM