I am learning the scope dependency of injected beans in Spring framework. I was learning to solve the narrow scope bean dependency from websites. But I am not able to see the resolution as explained in the websites. I have tried using the method of Inject ApplicationContext bean into MySingletonBean to get instance of MyPrototypeBean. However I do not see any difference in the bean creation.
#SpringBootApplication
#ComponentScan("com.learning.spring.basics.scope.proxy")
public class SpringScopeProxyApplication {
public static void main(String...strings) throws InterruptedException {
ApplicationContext ctx = SpringApplication.run(SpringScopeProxyApplication.class, strings);
SingletonBean bean1=ctx.getBean(SingletonBean.class);
/**
* Singleton bean is wired with a proototypeBean. But since a singleton bean is created only once by the container
* even the autowired proptotyeBean is also created once.
*/
bean1.display();
SingletonBean bean2=ctx.getBean(SingletonBean.class);
bean2.display();
}
}
#Component
public class SingletonBean {
#Autowired
// prototypeBean is of scope prototype injected into singleton bean
private PrototypeBean prototypeBean;
//Fix 1 - Inject ApplicationContext and make the singletonbean applicationcontextaware
#Autowired
private ApplicationContext applicationContext;
public ApplicationContext getApplicationContext() {
return applicationContext;
}
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
public PrototypeBean getPrototypeBean() {
return prototypeBean;
}
public void setPrototypeBean(PrototypeBean prototypeBean) {
this.prototypeBean = prototypeBean;
}
// Fix -1 - get the prototypeBean object from applicationContext everytime the method is called
public void display() {
applicationContext.getBean(PrototypeBean.class).showTime();
//prototypeBean.showTime();
}
PrototypeBean
package com.learning.spring.basics.scope.proxy;
import java.time.LocalDateTime;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
#Component
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class PrototypeBean {
public void showTime() {
System.out.println("Time is "+LocalDateTime.now().toString());
}
}
Expected Results:The time stamp should be different as the prototype scoped bean should be a new instance
Actual results: The timestamps are same with no difference
This is not right way to validate SCOPE_PROTOTYPE bean because showTime is an instance methods that gets executed when you invoke it through object so you will always get the different timestamp.But i'm not sure how you are getting same timestamp, but below is an example
#SpringBootApplication
#ComponentScan("com.learning.spring.basics.scope.proxy")
public class SpringScopeProxyApplication {
public static void main(String...strings) throws InterruptedException {
ApplicationContext ctx = SpringApplication.run(SpringScopeProxyApplication.class, strings);
SingletonBean bean1=ctx.getBean(PrototypeBean.class);
System.out.println(bean1);
SingletonBean bean2=ctx.getBean(PrototypeBean.class);
System.out.println(bean2);
}
}
You will get the two different object references
com.learning.spring.basics.scope.proxy.PrototypeBean#48976e6d
com.learning.spring.basics.scope.proxy.PrototypeBean#2a367e93
Related
I have a requirement where I had to use Spring beans inside a POJO class to retrieve a MyDomainClass object. Since Autowiring the beans inside the class which is not managed by Spring isn't possible, I had to explicitly get the ApplicationContext from another util class and retrieve the bean from applicationContext object. Please find the code below
#Component
public class ApplicationContextUtils implements ApplicationContextAware{
private static ApplicationContext applicationContext;
#Override
public void setApplicationContext(ApplicationContext appContext)
throws BeansException {
applicationContext = appContext;
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
}
The code for using the applicationContext and retrieving the bean is below
public class MyNonBeanClass{
public MyDomainClass retrieveMyDomainObj(){
ApplicationContext applicationContext=ApplicationContextUtils.getApplicationContext();
TestBean testBean=(TestBean)applicationContext.getBean("testBean");
return testBean.retrieve(param1,param2);
}
}
This code is working as expected and I am able to fetch the beans from the application context successfully. But writing the test case has been challenging to say the least. I am not even sure if we will be able to write the test case for my non bean class but I would still like to try. I am trying to use PowerMock to mock the static invocation of getApplicationContext() in ApplicationContextUtils. But I am unable to get the applicationContext necessary for getting the TestBean object. And a NullPointerException is thrown when the getBean() is called on applicationContext. Please help with this.
#RunWith(PowerMockRunner.class)
#PrepareForTest(ApplicationContextUtils.class)
#ContextConfiguration(classes= { BaseTestContext.class})
public class MyNonBeanClassTest implements ApplicationContextAware{
MyNonBeanClass myNonBeanClass;
ApplicationContext applicationContext;
#Test
public void test_retrieveMyDomainObj(){
myNonBeanClass=new MyNonBeanClass();
PowerMockito.mockStatic(ApplicationContextUtils.class);
when(ApplicationContextUtils.getApplicationContext()).thenReturn(applicationContext);
assertNotNull(myNonBeanClass.retrieveMyDomainObj(param1, param2));
}
#Override
public void setApplicationContext(ApplicationContext appContext) throws BeansException {
// TODO Auto-generated method stub
this.applicationContext=appContext;
}
public ApplicationContext getApplicationContext(){
return this.applicationContext;
}
}
I am trying to create a Spring boot application with JFrame. I can see my beans in applicationContext but they are not getting autowired. I am unable to find the reason for this issue. Can someone help me with this?
Here is the code:
JavauiApplication - it is showing both userManager and userNameRepository is beans
#SpringBootApplication
public class JavauiApplication implements CommandLineRunner {
#Autowired
private ApplicationContext appContext;
public static void main(String[] args) {
new SpringApplicationBuilder(JavauiApplication.class).headless(false).run(args);
java.awt.EventQueue.invokeLater(() -> new InputNameForm().setVisible(true));
}
#Override
public void run(String... args) throws Exception {
String[] beans = appContext.getBeanDefinitionNames();
Arrays.sort(beans);
for (String bean : beans) {
System.out.println(bean);
}
}
}
InputNameForm.java -> userManager coming null
#Component
public class InputNameForm extends javax.swing.JFrame {
/**
* Creates new form InputNameForm
*/
public InputNameForm() {
initComponents();
}
#Autowired
UserManager userManager;
private void submitButtonActionPerformed(java.awt.event.ActionEvent evt) {
userManager.setName(firstName.getText(), lastName.getText());
}
/**
* #param args the command line arguments
*/
public static void main(String args[]) {
try {
for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
javax.swing.UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (ClassNotFoundException ex) {
java.util.logging.Logger.getLogger(InputNameForm.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
}
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new InputNameForm().setVisible(true);
}
});
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JTextField firstName;
private javax.swing.JLabel firstNameLabel;
private javax.swing.JTextField lastName;
private javax.swing.JLabel lastNameLabel;
private javax.swing.JButton submitButton;
// End of variables declaration//GEN-END:variables
}
UserManager.java -> userNameRepository is coming null
#Component
public class UserManager {
#Autowired
UserNameRepository userNameRepository;
public void setName(String firstName, String lastName) {
userNameRepository.save(new UserName(firstName, lastName));
System.out.println(userNameRepository.findAllByFirstName(firstName));
}
}
It's a very common problem and it occurs because newcomers don't understand how the IoC container works.
Firstly, BeanDefinitionReader reads metadata about your beans from XML, Annotations(#Component, #Service etc), JavaConfig or Groovy script.
There are several BeanPostProcessor's which is responsible for reading all of these Spring annotation you're writing(#Autowired etc).
BeanFactory creates all BeanPostProcessor's then it creates all of your beans.
What happen if you create your bean with #Autowired dependencies via new operator? Nothing, because it isn't actually a bean. The object you created isn't related to IoC container. You may have the bean already in your ApplicationContext if you marked it with #Component(for example) but the object which was created via new operator wont be processed by Spring(annotations won't work).
Hope this helps.
PS: The lifecycle is simplified.
I had the same problem few days ago. What I undertood was that GUI builders like the one that comes with netbeans will automatically create components using new keyword. This means that those components won't be manage by spring. The code usually loks like this:
private void initComponents() {
jPanel1 = new javax.swing.JPanel(); //This component will not be managed by spring.
//...
}
You could use the following class provided here, to make it work.
#Component
public class BeanProvider {
private static ApplicationContext applicationContext;
// Autowires the specified object in the spring context
public static void autowire(Object object) {
applicationContext.getAutowireCapableBeanFactory().autowireBean(object);
}
#Autowired
private void setApplicationContext(ApplicationContext applicationContext) {
BeanProvider.applicationContext = applicationContext;
}
}
The top level SwingApp class:
#SpringBootApplication
public class SwingApp implements CommandLineRunner {
public static void main(String[] args) {
new SpringApplicationBuilder(SwingApp.class)
.headless(false).bannerMode(Banner.Mode.OFF).run(args);
}
#Override
public void run(String... args) throws Exception {
SwingUtilities.invokeLater(() -> {
MainFrame frame = new MainFrame();
frame.setVisible(true);
});
}
}
The MainFrame class:
public class MainFrame extends javax.swing.JFrame {
public MainFrame() {
initComponents();
}
private void initComponents() {
//Gui Builder generated code. Bean not managed by spring.
//Thus, autowired inside CustomPanel won't work if you rely on ComponentScan.
jPanel1 = new CustomJPanel();
//...
}
private CustomJPanel jPanel1;
}
The panel class where you want to autowire things:
//#Component //not needed since it wont work with gui generated code.
public class CustomJPanel extends javax.swing.JPanel{
#Autowired
private SomeRepository someRepository
public CustomJPanel(){
BeanProvider.autowire(this); //use someRepository somewhere after this line.
}
}
I have the same problem in a JavaFx project. Service and Component annotated classes were null in UI controllers even if it was shown in context that it was created. Below code worked for me
#Component
public class FxmlLoaderWithContext {
private final ApplicationContext context;
#Autowired
public FxmlLoaderWithContext(ApplicationContext context) {
this.context = context;
FXMLLoader fxmlloader = new FXMLLoader();
fxmlloader.setControllerFactory(context::getBean); //this row ensure services and components to be autowired
}
}
I think it returns null because you using command new to create object, such as new InputNameForm(). When creating object like that, the object isn't managed by Spring. That's why autowired not working.
The solution is registering your class as a bean.
You can use a class like in here.
#Component
public class BeanProvider {
private static ApplicationContext applicationContext;
public static void autowire(Object object) {
applicationContext.getAutowireCapableBeanFactory().autowireBean(object);
}
#Autowired
private void setApplicationContext(ApplicationContext applicationContext) {
BeanProvider.applicationContext = applicationContext;
}
}
And then, in your class InputNameForm constructor, call this:
class InputNameForm() {
BeanProvider.autowire(this);
...
}
And that's it. Spring will take care the rest.
I have a spring application in which I am trying to inject many beans of the same type. I do not know how many of these beans there will be before runtime, so it seems natural to use the factory pattern, as I cannot configure each bean in my java config class. However, these beans need to have some of their fields wired by Spring, and when I create them with "new" in my factory, they are of course not Spring managed.
Is there a way to have the beans I create in my factory class be managed by Spring? Or is the factory pattern the wrong way to go about this?
I am fairly new to posting, so please let me know if any more information is necessary.
You can define a beanFactory wired with the dependencies needed for your bean, then manually injected them in each new bean created by the beanFactory. For example:
public class MyBean {
private Dependency1 dep1;
private Dependency2 dep2;
public MyBean(Dependency1 dep1, Dependency2 dep2) {
this.dep1 = dep1;
this.dep2 = dep2;
}
}
#Component
public class MyBeanFactory {
#Autowired
private Dependency1 dep1;
#Autowired
private Dependency2 dep2;
public MyBean createInstance() {
return new MyBean(dep1, dep2);
}
}
#Component
public class MyBeanConsumer {
#Autowired
private MyBeanFactory myBeanFactory;
public void foo() {
final MyBean bean = myBeanFactory.createInstance();
}
}
You can't use #Autowired because of the variable number of beans, but you can still make use of ApplicationContextAware to create obtain the beans.
Using that you can programmatically create prototype beans from your Java code if the type of bean has been defined before in the configuration, or alternatively you can create the new object in your factory using new, and then set the dependencies by using this same method.
This is an example of an implementation:
public final class ApplicationContextProvider implements ApplicationContextAware {
private static ApplicationContext CONTEXT;
public void setApplicationContext(ApplicationContext context) throws BeansException {
CONTEXT = context;
}
public static Object getBean(String beanName) {
return CONTEXT != null ? CONTEXT.getBean(beanName) : null;
}
public static <T> T getBean(Class<T> objectClass) {
return CONTEXT != null ? CONTEXT.getBean(objectClass) : null;
}
}
I need to load the applicationcontext in java class in which the applicationcontextaware bean is defined. I need to access the other beans inside the applicationcontext.xml using the applicationcontextaware. I dont want to load the context using
ClassPathXmlApplicationContext("applicationContext.xml");
I need to access the beans inside the applicationContext like this
ApplicationContextAccess.getInstance().getApplicationContext.getbean("BeanName");
Applicationcontextacess implemented as singleton class:
public class ApplicationContextAccess implements ApplicationContextAware {
private ApplicationContext applicationContext = null;
private static ApplicationContextAccess applicationContextAccess=null;
private ApplicationContextAccessor() {
}
public static synchronized ApplicationContextAccess getInstance() {
if(applicationContextAccess == null)
{
applicationContextAccess = new ApplicationContextAccess();
}
return applicationContextAccess;
}
public void ApplicationContext getApplicationContext() {
return applicationContext;
}
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
applicationContext = applicationContext;
}
}
I need to access the beans inside the applicationContext like this ApplicationContextAccess.getInstance().getApplicationContext.getbean("BeanName");
But I have a doubt how the getApplicationContext loads the applicationContext.xml........?
Is there a way to discover all the BeanFactories defined by Spring programmatically. I want to create a status debug page which prints out the names and class types of every bean in the spring application context, however I don't know how to obtain a list of all the ApplicationContexts.
You can wire a BeanFactoryPostProcessor with your ApplicationContext that will allow you to traverse the BeanDefinition's of a passed ConfigurableListableBeanFactory which will represent all of the beans from your ApplicationContext.
With this instance of ConfigurableListableBeanFactory, you can find all beans of a type (getBeansOfType()), or all beans with a given annotation (getBeansWithAnnotation()), among other things.
You can use the ApplicationContext aware to do this.
#Component
public class PrintSpringBeansInContext implements ApplicationContextAware
{
private ApplicationContext applicationContext;
#Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException
{
this.applicationContext = applicationContext;
}
public void print()
{
String[] beanNames = this.applicationContext.getBeanDefinitionNames();
StringBuilder printBuilder = new StringBuilder("Spring Beans In Context: ");;
for(String beanName : beanNames)
{
printBuilder.append("\n");
printBuilder.append(" Bean Name: ");
printBuilder.append(beanName);
printBuilder.append(" Bean Class: ");
printBuilder.append(this.applicationContext.getBean(beanName).getClass());
}
System.out.println(printBuilder.toString());
}
}
You can test this
#ContextConfiguration(locations={"classpath:context.xml"})
#RunWith(SpringJUnit4ClassRunner.class)
public class PrintContextTest
{
#Autowired
private PrintSpringBeansInContext service;
#Test
public void printBeans()
{
Assert.assertNotNull(service);
service.print();
}
}
Code below is a Spring listener which can be registered with the main spring.xml file for the web application, it builds a map of all the child application contexts and exposes this map as a property. The class below can be injected into any spring bean that needs it using #Autowired.
import java.util.Hashtable;
import java.util.Map;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ApplicationContextEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.ContextStartedEvent;
public class ContextsApplicationListener implements ApplicationListener<ApplicationContextEvent> {
private Map<String,ApplicationContext> contextMap = new Hashtable<String,ApplicationContext>();
#Override
public void onApplicationEvent(ApplicationContextEvent event) {
if( event instanceof ContextStartedEvent || event instanceof ContextRefreshedEvent){
this.getContextMap().put(event.getApplicationContext().getDisplayName(), event.getApplicationContext());
}
}
public Map<String,ApplicationContext> getContextMap() {
return contextMap;
}
}
enter code here