Spring and #Autowired - when does the autowiring take place? - spring

I have a class that works like so:
#Component
public class MyClass
{
#Autowired
public void setDataSource(DataSource dataSource)
{
...
}
public void doSomethingUsingDataSource()
{
// use autowired datasource
}
}
My applicationContext.xml contains this:
<context:load-time-weaver/>
<context:component-scan base-package="mypackage" />
This works fine if I instantiate a MyClass in another class:
MyClass mc = new MyClass();
mc.doSomethingUsingDataSource();
However, if MyClass is instead a singleton:
#Component
public class MyClass
{
private static MyClass mc;
#Autowired
public void setDataSource(DataSource dataSource)
{
...
}
private void doSomethingUsingDataSource()
{
// use autowired datasource
}
public static void doSomething()
{
if (mc == null)
{
mc = new MyClass();
}
mc.doSomethingUsingDataSource();
}
}
and I call
MyClass.doSomething();
then I get a NPE because dataSource is null.
Is Spring unable to set the datasource if a new instance of my class is created in this way? Or do I need to change my configuration a bit? Since the first version works it appears my configuration is correct.
Thanks,
Paul

In the end I worked it out by removing the autowiring. I made my class implement ApplicationContextAware and set the data source in the setApplicationContext method, like so:
public void setApplicationContext(ApplicationContext ac) throws BeansException
{
setDataSource((DataSource) ac.getBean("dataSource"));
}
By doing this I was able to keep my class a singleton with static methods.
Paul

Related

How to override DefaultListableBeanFactory in Spring Boot application?

Are there ways to override properties of DefaultListableBeanFactory in Spring Boot application?
For example, I want to set the DefaultListableBeanFactory.allowBeanDefinitionOverriding property to false.
EDIT
Use case.
I have pretty plain class:
#Data
#AllArgsConstructor
class MyTempComponent {
private String value;
}
Which I want use as #Bean in my application but this bean can be defined several times, for example:
#Configuration
class TestConfiguration1 {
#Bean
MyTempComponent myTempComponent() {
return new MyTempComponent("Value 1");
}
}
#Configuration
class TestConfiguration2 {
#Bean
MyTempComponent myTempComponent() {
return new MyTempComponent("Value 2");
}
}
Also there is a consumer of that bean:
#Component
class TestConfiguration3 {
private MyTempComponent myTempComponent;
#Autowired
public TestConfiguration3(MyTempComponent myTempComponent) {
this.myTempComponent = myTempComponent;
}
#PostConstruct
public void print() {
System.out.println(this.myTempComponent.getValue());
}
}
When an application starts it prints "Value 2" message, i.e. initializes myTempComponent bean from TestConfiguration2.
I want to prohibit creation of that bean (and any other beans) if there are two or more candidates.
As I realized I can reach this goal via setting DefaultListableBeanFactory.allowBeanDefinitionOverriding to false.
But how to set this property in Spring Boot application? Could you provide an example please?
You can set
private static class CustomAppCtxInitializer implements ApplicationContextInitializer<GenericApplicationContext> {
#Override
public void initialize(GenericApplicationContext applicationContext) {
applicationContext
.getDefaultListableBeanFactory()
.setAllowBeanDefinitionOverriding(false);
}
}
and then have
public static void main(String[] args) {
try {
final SpringApplication springApplication = new SpringApplication(Application.class);
springApplication.addInitializers(new CustomAppCtxInitializer());

Autowiring not working in springboot application

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.

How can I test a destroy method of a bean is effectively called in a springboot integration test?

My configuration class looks like this :
#SpringBootApplication
public class Application {
#Bean(destroyMethod = "close")
public CassandraClient cassandraClient() { ... }
}
My CassandraClient class has a close() method, which is being invoked when the application context shuts down (I see it through step debugging). However, I can't find a way to test that the close() method is effectively called.
Here is what I would like to test :
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest
#ContextConfiguration(classes = { Application.class })
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
public class ApplicationIntegrationTests implements ApplicationContextAware {
ApplicationContext applicationContext;
#Autowired
CassandraClient cassandraClient;
#Test
public void cassandraClientCloseIsCalled() {
((ConfigurableApplicationContext)applicationContext).close();
// How can I check that cassandraClient.close() has been called once ?
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
I tried adding an aspect to my configuration class to do the counting, but I can't get a pointcut to match the close method. It seems like my aspect is being destroyed before the cassandraClient bean.
I see this question has been here for a while, I looked into this and found a solution that works for me. You can look at it here: Testing Nuts Example. Essentially, almond is a nut that I create using the nut object. Both the init and the destroy methods are private, so I create an extension in another configuration in order to create a mock and be able to verify it with Mockito. This way I can shadow the private methods and generate public methods for this test. But what I think it's interesting in this case for you is that using the #PreDestroy annotation in my #Configuration for the #Test, I can then test the destroy method, just because at that point, all destroy methods have been called. Here is a copy of my code here just to clarify this:
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = {NutsConfiguration.class, NutsMethodsConfigurationTest.NutsTestConfiguration.class})
class NutsMethodsConfigurationTest {
#Autowired
public Nut almond;
#MockBean
public static NutExtended nutExtended;
#Autowired
public ApplicationContext applicationContext;
#Test
void almond() {
ConsolerizerComposer.outSpace()
.orange(almond)
.orange(nutExtended)
.reset();
verify(nutExtended, times(1)).initiate();
}
#Configuration
public static class NutsTestConfiguration {
#Bean
#Primary
#Qualifier("nut")
public NutExtended nut() {
return new NutExtended();
}
#PreDestroy
public void check() {
verify(nutExtended, times(1)).goToCake();
}
}
#Configuration
public static class NutExtended extends Nut {
public void goToCake() {
BROWN.printGenericLn("Going to cake...");
}
public void initiate() {
ORANGE.printGenericLn("Creating %s", toString());
}
}
}
I hope this helps 😊!

how to autowire spring beans from a non-bean object created at runtime?

I have some Jpa repositories and several Entity class. I need to create a helper object for one of my Entity. Inside that helper I use #Autowire to access the Jpa repositories.
#Entity
class A {
#Transient
Helper helper;
...
}
class Helper {
A a;
#Autowired
CRepository repo;
public Helper(A a) {
this.a = a;
}
}
However, the repo is always null. I've tried using SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this) and #Configurable, but both of them failed. Can anybody provide some hint for me?
BTW, A is instantiated inside a rest controller.
Thanks!.
You can use a BeanUtil class to get any bean that created in Springl
#Service
public class BeanUtil implements ApplicationContextAware {
private static ApplicationContext context;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
public static <T> T getBean(Class<T> beanClass) {
return context.getBean(beanClass);
}
}
Then you can get the bean.
MyBean obj = BeanUtil.getBean(MyBean.class);
Use constructor injection instead of field injection; this is a best practice all the time anyway. Then it's trivial to inject your A into the controller and pass it as a constructor argument.
#Configurable annotation works fine, but you need to use #EnableSpringConfigured annotation in any configuration class in order to make it work. Read my answer in other stackoverflow post: spring autowiring not working from a non-spring managed class
Entity class should not contain any helpers, even if transient. For a clean design you need to separate concerns, so the entity should not be aware of your business logic. I cannot help you more since I don't know which is the goal of that helper, but here you have other alternatives:
ALTERNATIVE 1 (based on your description seems that helper is an stateful bean, so it is not candidate to be a #Service, which I personally think it should be)
#Controller
public MyController {
#RequestMapping(...)
public void processRequest() {
A a = new A();
...
Helper helper = new Helper(a); // CRepository is successfully autowired
}
}
#Configurable(autowire = Autowire.BY_TYPE)
public class Helper {
A a;
#Autowired
CRepository repo;
}
#Configuration
#EnableSpringConfigured
public Application {
...
}
ALTERNATIVE 2 (make your Helper class stateless so that spring is aware of your beans without the need of extra stuff like #Confgurable/#EnableSpringConfigured)
#Controller
public MyController {
#Autowired Helper helper; // CRepository is correctly autowired
#RequestMapping(...)
public void processRequest() {
A a = new A();
...
helper.doSomething(a);
}
}
#Service
public class Helper {
// A a; remove dependency to A to make it stateless
#Autowired
CRepository repo;
public Helper() {
}
public void doSomething(A a) {
...
repo.save(a);
}
}
You cannot autowire nothing in your Helper class because it isn't managed by Spring.
You can use this approach:
public class HelperManager {
#Autowired
private ApplicationContext context;
public Helper getHelper(A a) {
return context.getBean(Helper.class, a);
}
Configure Helper to be a prototype bean:
#Configuration
public class MyConfiguration {
#Bean
public HelperManager helperManager() {
return new HelperManager();
}
#Bean
#Scope("prototype")
public Helper helper(A a) {
return new Helper(a);
}
}
And finally in your controller:
#Controller
public class MyController {
#Autowired
private HelperManager helperManager;
public someMethodWhereToInstanceYourHelper(A a) {
...
Helper helper = helperManager.getHelper(a);
...
}
}

How to set Spring application context through setter or constructor in another class

I have a Spring class.
#Service("dbManager")
#Repository
#Transactional
public class DatabaseManager {
GenericXmlApplicationContext context;
#PersistenceContext
private EntityManager em;
public DatabaseManager(GenericXmlApplicationContext context) {
this.context = context;
}
....
} //end of class DatabaseManager
I have SpringUtil class
public class SpringUtil {
public static GenericXmlApplicationContext loadSpringContext(String springXmlFile) {
GenericXmlApplicationContext context = new GenericXmlApplicationContext();
context.load(springXmlFile);
context.refresh();
return context;
} //end of loadSpringContext()
} //end of class SpringUtil
Now in main i am using some thing like
public class Regulator {
public static void main( String[] args ) {
Test test = new Test;
test.start();
} //end of main()
} //end of class Regulator
Here is test class
public class Test {
public void start() {
String springXmlFile = "classpath:spring/plcb-app-context-xml.xml";
GenericXmlApplicationContext context = SpringUtil.loadSpringContext(springXmlFile);
} //end of reportStudent()
} //end of class Test
But i am getting error that
Could not instantiate bean class [...DatabaseManager]: No default constructor
found; nested exception is java.lang.NoSuchMethodException:
...DatabaseManager.<init>()
I want that when DatabaseManager class created then spring context taht i am creating using SpringUtil.loadSpringContext(springXmlFile) must pass to it. How can i do it ?
Thanks
Edit
-------------------
public void switchDataSource(DatabaseType databaseType) {
DriverManagerDataSource dataSource = null;
if (databaseType == DatabaseType.LEGACY) {
dataSource = (DriverManagerDataSource)context.getBean("myLegacyDataSource");
} else if (databaseType == DatabaseType.LS360) {
dataSource = (DriverManagerDataSource)context.getBean("myLs360DataSource");
}
LocalContainerEntityManagerFactoryBean emf = context.getBean("myEmf", LocalContainerEntityManagerFactoryBean.class);
emf.setDataSource(dataSource);
}
#SuppressWarnings("unchecked")
#Transactional(readOnly=true)
public List<Object> getResultList(String query, Class mappingClass) throws Exception {
Query emQuery = em.createNativeQuery(query, mappingClass);
return emQuery.getResultList();
} //end of findTraineeFromLegacy()
Actually i have these two methods in my DatabaseManager class. I am setting context so i can get bean from the context in switchDataSource() method.
One thing that i can do is remove instance filed and change the method to
public void switchDataSource(DatabaseType databaseType, GenericXmlApplicationContext context) {
....
}
This is why i am doing this ?
Thanks
Have a no-arg constructor for DatabaseManager.
Implements ApplicationContextAware in DatabaseManager. Spring will know this bean needs to be notified of the application context:
#Service("dbManager")
#Repository
#Transactional
public class DatabaseManager implements ApplicationContextAware {
private ApplicationContext context;
public DatabaseManager() {...}
#Override
public void setApplicationContext(ApplicationContext appContext) {
this.context = appContext;
}
} //end of class DatabaseManager
however, double think if you really need that injected. In most case you are doing something wrong.
Update:
For your requirement in your update, which you want your DB Manager to switch datasource base on input type, although it doesn't seems very normal doing such thing, you can simply have your DB Manager injected with a Map and do whatever you want, instead of injecting the app context.
#Service("dbManager")
#Repository
#Transactional
public class DatabaseManager implements ApplicationContextAware {
#Resource("&emfBean")
private LocalContainerEntityManagerFactoryBean emfBean;
#Resource("dbManagerDsMap")
private Map<DatabaseType, Datasource> dsMapping;
public DatabaseManager() {...}
public void switchDataSource(DatabaseType databaseType) {
emfBean.setDatasource(dsMapping.get(databaseType));
}
} //end of class DatabaseManager
However I strongly suggest you not doing such thing. Consider having individual entityManagerFactory for each DB you are connecting to, and use the correct emf to connect to DB, instead doing this weird switching logic. I believe it is not supposed to be changed after your application start.

Resources