Why spring beans registered using registerSingleton() are not visible in getBeanDefinitionNames()? - spring

When Spring bean is registered using ConfigurableListableBeanFactory.registerSingleton() method, it's not available in ApplicationContext.getBeanDefinitionNames().
Is it by design? Maybe I can use some other method to get registered beans?

You can register and destroy your singleton using DefaultListableBeanFactory
Inject the ApplicationContext by #Autowired Spring annotation
Initialize a ConfigurableApplicationContext object
ConfigurableApplicationContext configContext = ConfigurableApplicationContext) applicationContext;
Initialize a DefaultListableBeanFactory object by using configContext
DefaultListableBeanFactory beanRegistry = (DefaultListableBeanFactory) configContext.getBeanFactory();
Use beanRegistry instance to handle your Spring beans
//Register a singleton bean
beanRegistry.registerSingleton(beanName,object);
//Destroy a singleton bean from context
beanRegistry.destroySingleton(beanName);

As the name getBeanDefinitionNames implies it will return the names of all BeanDefinitions available in the ApplicationContext. A BeanDefinition is the recipe on how to create a bean.
When using registerSingleton to register an arbitrary bean you created outside the scope of the ApplicationContext this obviously doesn't have a BeanDefinition and hence it will not be available in that list of names.

Related

Determine Springs bean instantiation order in Grails 3.3 with GORM 6.1.x

I am using a Grails 3.3 application that uses GORM 6.1.6.RELEASE, Spring-Boot 1.5.4.RELEASE and Spring Core 4.3.9.RELEASE behind the scene. I am trying to declare a Spring bean that get initialized just before Hibernate starts to validate the underlying database schema.
Here is what I like to do. I want to register my Flyway as a Spring bean and inject the dataSource bean into it. In order to have Flyway run before Hibernate starts to validate the current database schema, I add my flyway bean as a dependency onto the sessionFactory bean. The order would be as follows:
dataSource bean
flyway bean
hiberateDatastore bean
GORM 6.1 uses org.grails.orm.hibernate.HibernateDatastore as a Spring bean to initialize the Hibernate ORM and the database. The sessionFactory bean declares the hibernateDatastore#getSessionFactory as factory class.
Therefore the hibernateDatastore always is created first.
What is the way in Grails 3.3 to create a custom Spring bean that has to run after the connection to the database is available but before the Hibernate stuff gets initialized?
In previous versions of Grails 3.x it was possible to declare it in resources.groovy like this.
beans = {
if (Environment.current == Environment.PRODUCTION) {
flyway(Flyway) { bean ->
bean.initMethod = 'migrate'
dataSource = ref('dataSource')
locations = 'classpath:db/h2'
baselineOnMigrate = true
}
BeanDefinition sessionFactoryBeanDef = getBeanDefinition('hibernateDatastore')
if (sessionFactoryBeanDef) {
def dependsOnList = ['flyway'] as Set
if (sessionFactoryBeanDef.dependsOn?.length > 0) {
dependsOnList.addAll(sessionFactoryBeanDef.dependsOn)
}
sessionFactoryBeanDef.dependsOn = dependsOnList as String[]
}
}
}
I don't think Spring provides a visualisation of a 'bean instantion tree' however you could set the log level for org.springframework.beans.factory.support.DefaultListableBeanFactory to DEBUG and you'll get output like this:
Creating shared instance of singleton bean '...fully qualified class name...'
Returning cached instance of singleton bean '...fully qualified class name...'
You could review this log output for beans from the Hibernate namespace.
I presume you'll use the results to declare a DependsOn relationship so just for completeness this would look like:
#Bean
public SomeHibernateClass createHibernate() {
...
}
#Bean
#DependsOn("createHibernate")
public MyClass createMine() {
...
}
Grails 3.3.0 changed the mechanism of the dataSource bean creation. The Grails project lead stated in the related issue:
Previous versions of Grails created the dataSource bean separately from the session factory. We would need to restore this behaviour I guess so the dataSource can be referenced without triggering the creation of the sessionFactory
After upgrading to Grails 3.3.1 the dataSource bean is again available before the session factory gets created. This solves the problem.

Initializing ApplicationContext in Servlet

I am building an application with Spring, Hibernate, JSP and Servlets. For each form action method i am passing the values to Servlet and i am declaring ApplicationContext to load spring.xml in all the servlets.. Is there any way i can delcare ApplicationContext at one place in servlet and get all beans there...
I declare
ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
Student student = (Student) ac.getBean("student");
I declare this in all servlets..is there any central place to declare this and get beans in servlet..
Let Spring autowire your beans wherever you need them. This is the preferred way of doing things. If you are not familiar with the #Autowired annotation in spring, just do this-
Add this to your something-servlet.xml
<context:component-scan base-package="you_base_package" />
<mvc:annotation-driven />
your_base_package is the base package which contains your Student
class and other classes which you want to autowire(or instantiate).
Annotate your Student class and others with #Component.
Use this to get an instance of Student and others wherever needed.
#Autowired Student student;

How to create a bean after ServletContext initialized in Spring Boot?

I have a bean, which implements ServletContextAware and BeanFactoryPostProcessor interfaces. I need this this bean register into the applicationContext after the ServletContext finished initialization, because I use some parameters in the servletContext to init this bean.
I am using the Spring Boot, the bean name is SpringBeanProcessorServletAware. I have add it into a configuration bean.
#Bean
public static SpringBeanProcessorServletAware springBeanProcessor() {
SpringBeanProcessorServletAware p = new SpringBeanProcessorServletAware();
return p;
}
My issue is that the bean is created before my container set servletContext to it. Then I can't get the parameters from the servletContext. How to control that the bean must be created after my servletContext has been created completely?

Spring default behavior for lazy-init

I am beginner to spring, ESP Inversion of control. I was puzzled understanding the difference between the following
<bean id="demo" class="Demo" lazy-init="false"/>
<bean id="demo" class="Demo" lazy-init="true"/>
<bean id="demo" class="Demo" lazy-init="default"/>
To my understanding : lazy-init=false creates the bean at the startup and lazy-init=true doesn't create a bean at the startup rather creates the bean upon request for a particular bean.
Correct me here, If my interpretation is wrong.
what exactly the default behavior of lazy-init is? How would it instantiate?
The default behaviour is false:
By default, ApplicationContext implementations eagerly create and
configure all singleton beans as part of the initialization process.
Generally, this pre-instantiation is desirable, because errors in the
configuration or surrounding environment are discovered immediately,
as opposed to hours or even days later. When this behavior is not
desirable, you can prevent pre-instantiation of a singleton bean by
marking the bean definition as lazy-initialized. A lazy-initialized
bean tells the IoC container to create a bean instance when it is
first requested, rather than at startup.
I suggest reading up
For those coming here and are using Java config you can set the Bean to lazy-init using annotations like this:
In the configuration class:
#Configuration
// #Lazy - For all Beans to load lazily
public class AppConf {
#Bean
#Lazy
public Demo demo() {
return new Demo();
}
}
For component scanning and auto-wiring:
#Component
#Lazy
public class Demo {
....
....
}
#Component
public class B {
#Autowired
#Lazy // If this is not here, Demo will still get eagerly instantiated to satisfy this request.
private Demo demo;
.......
}
The lazy-init="default" setting on a bean only refers to what is set by the default-lazy-init attribute of the enclosing beans element. The implicit default value of default-lazy-init is false.
If there is no lazy-init attribute specified on a bean, it's always eagerly instantiated.
lazy-init is the attribute of bean. The values of lazy-init can be true and false. If lazy-init is true, then that bean will be initialized when a request is made to bean. This bean will not be initialized when the spring container is initialized
and
if lazy-init is false then the bean will be initialized with the spring container initialization.
When we use lazy-init="default" as an attribute in element, the container picks up the value specified by default-lazy-init="true|false" attribute of element and uses it as lazy-init="true|false".
If default-lazy-init attribute is not present in element than lazy-init="default" in element will behave as if lazy-init-"false".

Can't Autowire ServletContext

I'm new with Spring, and I'm trying to use the #Autowire annotation for the ServletContext with my class attribute:
#Controller
public class ServicesImpl implements Services{
#Autowired
ServletContext context;
I defined the bean for this class in my dispatcher-servlet.xml:
<bean id="services" class="com.xxx.yyy.ServicesImpl" />
But when I try to run a JUnit test it gives the following error:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [javax.servlet.ServletContext] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
I thought that the ServletContext injection was automatic with spring... How can I solve this?
Thanks!
EDIT: I want to use the servletContext to call the getRealPath() method. Is there any alternative?
Implement the ServletContextAware interface and Spring will inject it for you
#Controller
public class ServicesImpl implements Services, ServletContextAware{
private ServletContext context;
public void setServletContext(ServletContext servletContext) {
this.context = servletContext;
}
You'll probably want to take a look at MockServletContext which can be used in unit tests.
ServletContext is not a Spring bean and it can, therefore, not be injected unless you implement ServletContextAware.
If one thinks in modules or layers then the servlet context shouldn't be available outside the web module/layer. I suppose your ServicesImpl forms part of the business or service layer.
If you gave a little more context we might be able to suggest better alternatives.

Resources