Spring test/production application context - java

Why when running spring tests with #ContextConfiguration(...) #Autowired works automatically and when running Java application I get NullPointerException?
With following example I get NullPointerException:
public class FinalTest {
#Autowired
private App app;
public FinalTest() {
}
public App getApp() {
return app;
}
public void setApp(App app) {
this.app = app;
}
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
FinalTest finalTest = new FinalTest();
finalTest.getApp().getCar().print();
finalTest.getApp().getCar().getWheel().print();
}
}
With following example it works:
public class FinalTest {
private App app;
public FinalTest() {
}
public App getApp() {
return app;
}
public void setApp(App app) {
this.app = app;
}
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
FinalTest finalTest = new FinalTest();
finalTest.setApp((App)context.getBean("app"));
finalTest.getApp().getCar().print();
finalTest.getApp().getCar().getWheel().print();
}
}
In tests no need of doing context.getBean(), it just works with #Autowired:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={"/applicationContext-test.xml"})
public class AppTest{
#Autowired
private App app;
#Test
public void test(){
assertEquals("This is a SEAT_test car.", this.app.getCar().toString());
assertEquals("This is a 10_test wheel.", this.app.getCar().getWheel().toString());
}
}
Thanks.

Anytime you use #Autowired, the class into which the dependency is going to be injected needs to be managed by Spring.
A test with:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={"/applicationContext-test.xml"})
is managed by Spring. When the annotations do not exist, the class is not managed by Spring and therefor no
dependency injection is performed

You're expecting Spring to be able to inject beans into an instance it doesn't manage.
You're creating your object manually
FinalTest finalTest = new FinalTest();
Spring can only inject beans into objects it manages. Here, Spring has nothing to do with the object created above.
Declare a FinalTest bean in your context and retrieve it. It will have been autowired if your configuration is correct.

Related

Spring dynamic #Bean registration with SpringApplicationBuilder failing in test

Trying to register beans dynamically via SpringApplicationBuilder class and it's working when running the app, but when trying to execute the test and trying to verify that the beans are defined in the context, they fail for the dynamic bean. Feel like I have to use another "magical" annotation for the tests for them to properly load the dynamic beans.
This is the code used and if you run the tests you will see that both cases will fail. BarService will fail also because FooService is registered dynamically via builder, but if you would remove the dependency it will pass the BarService test.
SpringApp.java
class FooService {
}
#Component
class BarService {
private final FooService fooService;
BarService(FooService fooService) {
this.fooService = fooService;
}
}
#SpringBootApplication
public class SpringApp {
public static void main(String[] args) {
new SpringApplicationBuilder()
.sources(SpringApp.class)
.initializers((ApplicationContextInitializer<GenericApplicationContext>) context -> {
context.registerBean(FooService.class);
})
.run(args);
}
}
SpringAppTest.java
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = SpringApp.class)
public class SpringAppTest {
#Autowired
ApplicationContext context;
#Test
public void barService() {
Assert.assertNotNull("The barService should not be null", context.getBean(BarService.class));
}
#Test
public void contextLoads() {
Assert.assertNotNull("The fooService should not be null", context.getBean(FooService.class));
}
}
First solution
The main error here is that the test does not have the initalization logic that the main method has. Solution is to extract the logic from the initializers method
class MyInitializer implements ApplicationContextInitializer<GenericApplicationContext> {
#Override
public void initialize(GenericApplicationContext context) {
System.out.println("Called initialize");
context.registerBean(FooService.class);
}
}
and use it in main
public static void main(String[] args) {
new SpringApplicationBuilder()
.sources(SpringApp.class)
.initializers(new MyInitializer())
.run(args);
}
and then use the MyInitializer in the test file through #ConextConfiguration
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = SpringApp.class, initializers = MyInitializer.class)
public class SpringAppTest {
// ...
}
Second (better) solution
Now, this can be cumbersome as we need to reference this initializer in every test, but there is an even better solution. We can create a specific Spring file resources/META-INF/spring.factories and put inside of it a reference to the initializer:
org.springframework.context.ApplicationContextInitializer=com.acme.orders.MyInitializer
After that, we can simplify both the main method
#SpringBootApplication
public class SpringApp {
public static void main(String[] args) {
SpringApplication.run(SpringApp.class, args);
}
}
and the tests, so that they don't need to always import the initializer.
#RunWith(SpringRunner.class)
#SpringBootTest
public class SpringAppTest {
// ...
}
Now both the main run process and the tests will have access to all the beans.

Spring Autowiring giving NPE. not manually creating beans

My service classes in my spring MVC program are still null upon running the application. Both classes are #Service and have #Autowired in the controller class but are still null. I've browsed around for a few days and all I've found is 2 causes, neither of which ( I believe) apply to my circumstance.
Spring boot app trying to create a discord bot, autowiring not working in controller or Junit test (NPE upon execution, and variable shows null while debugging).
Driver class:
package com.deth;
//imports
#SpringBootApplication
public class DethBotApplication {
private static Logger logger = Logger.getLogger(DethBotApplication.class);
#Autowired
private static BotCommandListener botListener;
public static void main(String[] args) {
SpringApplication.run(DethBotApplication.class, args);
try {
JDA jda = new JDABuilder(AccountType.BOT)
.setToken(TOKEN)
//.addEventListener(command controller)
.addEventListener(botListener)
.build(); //starts listening in discord server.
Relevant controller code:
package com.deth.controller;
//imports
#Component
public class BotCommandListener extends ListenerAdapter {
private static Logger logger = Logger.getLogger(BotCommandListener.class);
#Autowired
#Qualifier("raidAdminService")
private RaidAdminService raidAdminService;
#Autowired
private RaidRosterServiceImpl raidRosterService;
#Autowired
private RaidAttendanceService raidAttendanceService;
#Override
public void onMessageReceived(MessageReceivedEvent event) {
JDA jda = event.getJDA();
String msg = event.getMessage().getContentDisplay();
if(msg.startsWith("!")) {
String command = "";
if(!msg.contains(" ")) {
command = msg;
} else {
command = msg.subSequence(0, msg.indexOf(" ")).toString();
logger.trace("possible command: " + command);
}
try {
switch (command) {
//raid leader commands
case "!open":
raidAdminService.createRaid(event); //NPE here
logger.trace("!open detected");
break;
raidAdminService:
package com.deth.service;
//imports
#Service("raidAdminService")
public class RaidAdminServiceImpl extends CommandInfoService implements RaidAdminService {
String intRegex = "[0-9]+";
#Override
public void createRaid(MessageReceivedEvent event) {
// TODO Auto-generated method stub
package structure:
com
deth
DethBotApplication
Controller
DethBotCommandListner
Service
RaidAdminService (interface)
RaidAdminServiceImpl (class)
....
while program is up & running, send "!open" in discord server, correctly hitting the switch statement and trying to call createRaid method, but RaidAdminService wasn't autowired so its calling the method on null.
I think issue is in your DethBotApplication class. you can't autowire there. main class need to be executed first. after that app will look for #Componet, #Service, #Controller... annotations. below code might fix your issue.
package com.deth;
//imports
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.ConfigurableApplicationContext;
#SpringBootApplication
public class DethBotApplication extends SpringBootServletInitializer {
private static Logger logger = Logger.getLogger(DethBotApplication.class);
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(DethBotApplication.class, args);
BotCommandListener botListener = context.getBean(BotCommandListener.class);
try {
JDA jda = new JDABuilder(AccountType.BOT)
.setToken(TOKEN)
//.addEventListener(command controller)
.addEventListener(botListener)
.build(); //starts listening in discord server.
Look at that :
#Autowired
private static BotCommandListener botListener;
public static void main(String[] args) {
SpringApplication.run(DethBotApplication.class, args);
try {
JDA jda = new JDABuilder(AccountType.BOT)
.setToken(TOKEN)
.addEventListener(botListener)
//...
}
You annotate #Autowired a static field while Spring doesn't inject bean in static fields (but only in instance fields).
Your problem is indeed very common : you want to perform some tasks in the main() of the Spring Boot application class that depend on some beans.
The right approach is using the #PostConstruct annotation. A method annotated with that is (automatically) executed once the dependency injection was performed on the current bean : here the Spring Boot Application.
It would give :
#Autowired
private BotCommandListener botListener;
#PostConstruct
public void init(){
JDA jda = new JDABuilder(AccountType.BOT)
.setToken(TOKEN)
.addEventListener(botListener)
.build();
// ...
}
You instantiated the object manually, so Spring doesn't know about that bean. That's why your #Autowired field is null.
Spring boot has to load primary class first, then continue to the injected bean through these class.
Extend either an ApplicationRunner or CommandLineRunner class
package com.deth;
//imports
#SpringBootApplication
#Component
public class DethBotApplication implements CommandLineRunner {
private static Logger logger = Logger.getLogger(DethBotApplication.class);
#Autowired
private /*static*/ BotCommandListener botListener; // i remove "static" here
public static void main(String[] args) {
SpringApplication.run(DethBotApplication.class, args);
}
#Override
public void run(String args) {
try {
JDA jda = new JDABuilder(AccountType.BOT)
.setToken(TOKEN)
//.addEventListener(command controller)
.addEventListener(botListener)
.build(); //starts listening in discord server.
...
The key here is (as other answers point out) #Autowired only works if it is a #Component and not "static".

Unable to Autowire

Using Spring Initializer to create a simple Spring boot. I am only choosing DevTools under the options available.
After creating the project, without making any changes to it, able to run the program fine.
Now when I try to do some Autowiring in the project, it simply doesn't work. I don't get it. Been looking all over previous questions here which has resolutions for it but none works plus there is nothing complex about what I am doing in my case as follows. Please advice what I am missing.
#SpringBootApplication
public class DemoApplication {
// #Autowired
// private static Maker maker; // Stopped using this cos I wanted to check if the autowiring is not working in this class only or anywhere. Turns out it is anywhere.
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
Maker maker = new Maker();
maker.printName(); // Fails cos of ServiceHelper Autowiring
}
}
#Service
public class Maker {
#Autowired
private ServiceHelper serviceHelper;
public void printName(){
System.out.println("This is from the method body itself.");
System.out.println("Auto wiring works cos I got this -> " + serviceHelper.help());
}
}
#Component
public class ServiceHelper {
public String help(){
return "...FROM HELPER...";
}
}
StackTrace
Exception in thread "restartedMain"
java.lang.reflect.InvocationTargetException at
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497) at
org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49)
Caused by: java.lang.NullPointerException at
com.example.demo.services.Maker.printName(Maker.java:15) at
com.example.demo.DemoApplication.main(DemoApplication.java:17) ... 5
more
If you create any bean using new keyword that bean will not added to Spring Application Context, and this is one way to #Autowire static beans
#SpringBootApplication
public class DemoApplication {
#Autowired
private static Maker maker;
#Autowired
private Maker tMaker;
#PostConstruct
public void init() {
DemoApplication.maker = tMaker;
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
maker.printName();
}
}
or you can do Autowire using Constructor the instance of Maker is injected as an argument to the constructor when DemoApplication is created
#Autowired
public DemoApplication(Maker maker) {
DemoApplication.maker = maker;
}
or you can use #Autowired on setter method, the setter method is called with the instance of Maker when DemoApplication is created
#Autowired
public void setMaker(Maker maker) {
DemoApplication.maker = maker
}
You create yourself an instance, its ServiceHelper does not get autowired by Spring:
Maker maker = new Maker();
You can access bean via ApplicationContext:
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
ApplicationContext cts = SpringApplication.run(DemoApplication.class, args);
Maker maker = ctx.getBean(Maker.class);
maker.printName();
}
}
When you use new Maker() you are not using the bean Spring created for you and as a result the the object you have is uninitialized as dependencies are not injected.
You need to get the bean which the Spring framework created, like I did below as directly you cannot autowire static fields:
#SpringBootApplication
public class DemoApplication {
private static Maker maker;
#Autowired
public void setMaker(Maker maker) {
DemoApplication.maker = maker;
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
maker.printName();
}
}

Not able to load Application Context in Spring Boot app

I am working on a Spring Boot application wherein I am using that application to expose a SOAP webservice. I am using Apache CFX framework for SOAP impl in Spring boot app. I am using Annotation based approach.
I am facing issue in setting the Application Context from the Spring Boot Configuration file in one of the Beans. Below is my code.
#SpringBootApplication
#ComponentScan("com.test")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
The configuration file is as below.
#Configuration
public class WebServiceConfiguration {
//All individual bean definitions should go here
#Autowired
ApplicationContext appContext;
#Bean
public ServletRegistrationBean cxfServlet() {
return new ServletRegistrationBean(new CXFServlet(), "/soap-api/*");
}
#Bean(name = Bus.DEFAULT_BUS_ID)
public SpringBus springBus() {
return new SpringBus();
}
#Bean(name="IValidator")
public IValidator getValidator(){
return new Validator();
}
#Bean(name="SOAPprocessImpl")
public IPSoap getService() {
return new SOAPprocessImpl();
}
#Bean
public Endpoint endpoint() {
EndpointImpl endpoint = new EndpointImpl(springBus(), getService());
endpoint.publish("/WS_1.0");
endpoint.setWsdlLocation("process.wsdl");
return endpoint;
}
Now I have the bean SOAPprocessImpl implementation in which I need to get the Application Context so that I can get handle to the Validator bean. I have declared SOAPprocessImpl as a bean in the configuraton file. The code is as below
#javax.jws.WebService (endpointInterface="com.test.IPSoap")
public class SOAPprocessImpl implements IPSoap, ApplicationContextAware {
private static ApplicationContext context;
public static ApplicationContext getApplicationContext() {
return context;
}
#Override
public void setApplicationContext(ApplicationContext ac)
throws BeansException {
context = ac;
}
private static final Logger logger = Logger.getLogger(SOAPprocessImpl.class.getName());
private IValidator validator = (IValidator) context.getBean("IValidator"); // context is NULL here
public IRResponse GetBalance(TSSearchParams SearchParams) {
// Some processing logic
}
}
So the issue is that when I run the boot application by deploying to the embedded Tomcat then the Application Context is not getting set in the SOAPprocessImpl class even after implementing the ApplicationContextAware. I also tried Autowiring but that also is not working.
Strangely I tried to see if I can get the ApplicationContext in the Configuration file where all the bean are defined. Here it is getting setting properly.
Can anyone help me how to solve this issue. I am new to Spring Boot and may have missed some configutaion. Thanks in advance.
Option(1): To fix the issue, you need to use #Configuration to register your SOAPprocessImpl bean to the Spring container as shown below so that ApplicationContext object can be injected :
#Configuration
#javax.jws.WebService (endpointInterface="com.test.IPSoap")
public class SOAPprocessImpl implements IPSoap, ApplicationContextAware {
private static ApplicationContext context;
private IValidator validator;
public static ApplicationContext getApplicationContext() {
return context;
}
#Override
public void setApplicationContext(ApplicationContext ac)
throws BeansException {
SOAPprocessImpl.context = ac;
}
#PostConstruct//use PostConstruct
public void init() {
validator = (IValidator) context.getBean("IValidator");
}
//add your current code
}
The important point is that you can't use the context object until the bean is prepared by the container, so you need to use #PostConstruct method as shown above to initialise your variables.
Option2 (recommended):
The best approach is that you can use #Autowired to inject IValidator object into SOAPprocessImpl as shown below so that you don't need your SOAPprocessImpl bean to be aware of ApplicationContextAware. Spring container will inject the instance for the implementation provided for the IValidator class (provided it is under the packages of #Componentscan).
#Component
#javax.jws.WebService (endpointInterface="com.test.IPSoap")
public class SOAPprocessImpl implements IPSoap {
private static final Logger logger = Logger.getLogger(SOAPprocessImpl.class.getName());
#Autowired //spring directly injects this object
private IValidator validator;
public IRResponse GetBalance(TSSearchParams SearchParams) {
// Some processing logic
}
}

Java Spring application #autowired returns null pointer exception

Im fairly new to Java Spring IoC and here's my problem
I have a FactoryConfig class with all beans and annotation #Configuration and #ComponentScan written as below.
import org.springframwork.*
#Configuration
#ComponentScan(basePackages="package.name")
public class FactoryConfig {
public FactoryConfig() {
}
#Bean
public Test test(){
return new Test();
}
//And few more #Bean's
}
My Test class has a simple Print method
public class Test {
public void Print() {
System.out.println("Hello Test");
}
}
Now in my Main Class Ive created an ApplicationContentext of FactoryConfig. (I'm expecting all of my #Beans in Factory config will be initialised. However, it returns null when I access the Test class using #Autowired
My Main Class
public class Main {
#Autowired
protected static Test _autoTest;
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
ApplicationContext context =
new AnnotationConfigApplicationContext(FactoryConfig.class);
FactoryConfig config = context.getBean(FactoryConfig.class);
config.test().Print();
// _autoTest.Print(); <--- Im getting NULL Pointer Ex here
}
}
What is the correct way to #Autowire and use objects/beans? any clearer explanation would be much appreciated.
Only beans managed by Spring can have #Autowire annotations. Your main class is not managed by Spring: it's created by you and not declared in a Spring context: Spring doesn't known anything about your class, and doesn't inject this property.
You can just access in your main method the Test bean with :
context.getBean(Test.class).Print();
Usually, you get a "bootstrap" from the context, and call this bootstrap to start your application.
Moreover:
On Java, a method shouldn't start with an uppercase. Your Test class should have a print method, not Print.
If you start with Spring, you should maybe try Spring Boot
Spring does not manage your Main class, that's why you are getting Nullpointer Exception.
Using ApplicationContext to load beans, you can get your beans and access Methods as you are already doing -
ApplicationContext context =
new AnnotationConfigApplicationContext(FactoryConfig.class);
FactoryConfig config = context.getBean(FactoryConfig.class);
config.test().Print();
remove the static argument
protected Test _autoTest;
Your class
public class Test {
public void Print() {
System.out.println("Hello Test");
}
}
is not visible to Spring. Try adding an appropriate annotation to it, like #Component.
The reason is that your Main is not managed by Spring. Add it as bean in your configuration:
import org.springframwork.*
#Configuration
#ComponentScan(basePackages="package.name")
public class FactoryConfig {
public FactoryConfig() {
}
#Bean
public Test test(){
return new Test();
}
#Bean
public Main main(){
return new Main();
}
//And few more #Bean's
}
And then you can edit your main() as follows:
public class Main {
#Autowired
protected Test _autoTest;
public static void main(String[] args) throws InterruptedException {
ApplicationContext context =
new AnnotationConfigApplicationContext(FactoryConfig.class);
Test test = context.getBean(Test.class);
Main main = context.getBean(Main.class);
test.Print();
main._autoTest.Print();
}
}

Categories

Resources