Spring NullPointerException with #Autowired Object with #EnableAspectJAutoProxy - java

i am new to spring framework and i am getting NullPointerException when i try to use the injected dependency. Here is my code:
DemoConfig:
#Configuration
#EnableAspectJAutoProxy
#ComponentScan("com.trial.classes")
public class DemoConfig {
}
AccountDAO:
#Component
public class AccountDAO {
#Autowired
TestClass test; // the problem: the object is null!
}
TestClass:
#Component
public class TestClass {
public void testMethod()
{
System.out.println("this is a test method");
}
}
Main:
public static void main(String[] args) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(DemoConfig.class);
AccountDAO theAccountDAO = context.getBean("accountDAO", AccountDAO.class);
theAccountDAO.test.testMethod(); //NullPointerException
context.close();
}

Related

JUNIT Autowired instance is always null

I am trying to write test case for the below class where everytime myConfig instance is coming as null. Is there any way to pass the autowired instance.
public class MyClass {
#Autowired
MyConfig myConfig ;
public Properties getUnAckMessage(String queueName) {
Properties prop=new Properties()
URL url = new URL(StringUtils.join(myConfig.getQueueHost(),
myConfig.getQueueURL(),myConfig.getQueueVm(),queueName));
return prop;
}
public Properties request(String queue) {
return getUnAckMessage(queue);
}
}
public class Main {
public void method() {
MyClass myClass=new MyClass();
myClass.getUnAckMessage("test");
}
}
Test case
#RunWith(MockitoJUnitRunner.class)
public class MyClassTest {
#MockBean
MyConfig myConfigReader;
#Test
public void testMyClass() {
MyClass propertiesExchangeManager1 = new MyClass ();
propertiesExchangeManager1.request("test");
}
}
You must activate Spring for your test if you want Spring to autowire. For example:
#RunWith(SpringRunner.class)
public class Test {
#Autowired private MyClass myClass
#Test
public void test() {
///...
}
}
If you instantiate the class MyClass by yourself, Spring cannot inject the needed classes. You should modify your test like this:
#RunWith(MockitoJUnitRunner.class)
public class MyClassTest {
#MockBean
MyConfig myConfigReader;
#Autowired
MyClass propertiesExchangeManager1;
#Test
public void testMyClass() {
propertiesExchangeManager1.request("test");
}
}

I cannot use Autowired inside my main application

I am trying to instantiate the customer class inside the Running class. Although I ANNOTATED IT AS Component, there is a null pointer exception
#Component
public class Running {
#Autowired
CustomerRepository cRep;
#Autowired InvoiceRepository iRep;
public void run() {
Customer c1=new Customer().setAge(25).setBalance(100).setName("xyz");
Customer c2=cRep.save(c1);
}
#SpringBootApplication
#EnableJpaRepositories
public class HelloWorldSpringBootApp {
#Autowired static
Running r1;
public static void main (String[] args ) {
SpringApplication.run(HelloWorldSpringBootApp.class, args);
r1.run();
}
I am getting a null pointer exception on r1.run().
Spring does not allow you to autowire static fields (see this question). Here is the right way to get a Running instance:
#SpringBootApplication
#EnableJpaRepositories
public class HelloWorldSpringBootApp {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(HelloWorldSpringBootApp.class, args);
Running r1 = applicationContext.getBean(Running.class);
r1.run();
}
}

Clean code - Where should #Autowired be applied?

I'll start with a simple example. You have a Spring boot application that runs a CommandLineRunner class on initialization.
// MyCommandLineRunner.java
public class MyCommandLineRunner implements CommandLineRunner {
private final Log logger = LogFactory.getLog(getClass());
#Autowired //IntelliJ Warning
private DataSource ds;
#Override
public void run(String... args) throws Exception {
logger.info("DataSource: " + ds.toString());
}
}
// Application.java
#SpringBootApplication
public class Application {
public static void main(String... args) {
SpringApplication.run(Application.class, args);
}
#Bean
public MyCommandLineRunner schedulerRunner() {
return new MyCommandLineRunner();
}
}
Now, like this, this works, everything is OK. However, IntelliJ reports a warning where #Autowired is located (I marked where in the comment)
Spring team recommends: Always use constructor based dependency injection in your beans. Always use assertions for mandatory dependencies.
Now if I follow this, I have a constructor based dependency injection
#Autowired
public MyCommandLineRunner(DataSource ds) { ... }
This also means that I have to edit Application.java as well, since the constructor needs an argument. In Application.java if I try to use the setter injection, I'll get the same warning. If I refactor that as well, I'll end up with some, in my opinion, nasty code.
// MyCommandLineRunner.java
public class MyCommandLineRunner implements CommandLineRunner {
private final Log logger = LogFactory.getLog(getClass());
private DataSource ds;
#Autowired // Note that this line is practically useless now, since we're getting this value as a parameter from Application.java anyway.
public MyCommandLineRunner(DataSource ds) { this.ds = ds; }
#Override
public void run(String... args) throws Exception {
logger.info("DataSource: " + ds.toString());
}
}
// Application.java
#SpringBootApplication
public class Application {
private DataSource ds;
#Autowired
public Application(DataSource ds) { this.ds = ds; }
public static void main(String... args) {
SpringApplication.run(Application.class, args);
}
#Bean
public MyCommandLineRunner schedulerRunner() {
return new MyCommandLineRunner(ds);
}
}
The above code yields the same result, but doesn't report any warnings in IntelliJ.
I'm confused, how is the 2nd code better than the first one? Am I following an incorrect logic? Should this be wired differently?
In short, what's the correct way to do this?
note DataSource is just a pure example, this question applies to anything being autowired.
note 2 Just to say that MyCommandLineRunner.java can't have another, empty, constructor, since DataSource needs to be autowired/initialized. It will report an error and will not be compiled.
There are several ways to improve it.
You can remove #Autowired from your MyCommandLineRunner as you are letting a #Bean method construct an instance of it. Inject the DataSource directly into the method as an argument.
Or remove #Autowired and remove the #Bean and slap a #Component annotation on your MyCommandLineRunner to have it detected and remove factory method.
Inline your MyCommandLineRunner inside your #Bean method as a lambda.
No Autowiring in the MyCommandLineRunner
public class MyCommandLineRunner implements CommandLineRunner {
private final Log logger = LogFactory.getLog(getClass());
private final DataSource ds;
public MyCommandLineRunner(DataSource ds) { this.ds = ds; }
#Override
public void run(String... args) throws Exception {
logger.info("DataSource: " + ds.toString());
}
}
And the application class.
#SpringBootApplication
public class Application {
public static void main(String... args) {
SpringApplication.run(Application.class, args);
}
#Bean
public MyCommandLineRunner schedulerRunner(DataSource ds) {
return new MyCommandLineRunner(ds);
}
}
Usage of #Component
#Component
public class MyCommandLineRunner implements CommandLineRunner {
private final Log logger = LogFactory.getLog(getClass());
private final DataSource ds;
public MyCommandLineRunner(DataSource ds) { this.ds = ds; }
#Override
public void run(String... args) throws Exception {
logger.info("DataSource: " + ds.toString());
}
}
And the application class.
#SpringBootApplication
public class Application {
public static void main(String... args) {
SpringApplication.run(Application.class, args);
}
}
Inline CommandLineRunner
#SpringBootApplication
public class Application {
private static final Logger logger = LoggerFactory.getLogger(Application.class)
public static void main(String... args) {
SpringApplication.run(Application.class, args);
}
#Bean
public MyCommandLineRunner schedulerRunner(DataSource ds) {
return (args) -> (logger.info("DataSource: {}", ds);
}
}
All of these are valid ways of constructing your instances. Which one to use, use the one that you feel comfortable with. There are more options (all variations on the ones mentioned here).
Consider making the field ds final, then you don't need #Autowired. See more about dependency injection http://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-spring-beans-and-dependency-injection.html#using-boot-spring-beans-and-dependency-injection
To keep the code clean, have you considered using Lombok annotations? #RequiredArgsConstructor(onConstructor = #__(#Autowired))
would generate the constructor with #Autowired annotations. See more here
https://projectlombok.org/features/Constructor.html
Your code could look like this:
#Slf4j
#RequiredArgsConstructor
// MyCommandLineRunner.java
public class MyCommandLineRunner implements CommandLineRunner {
//final fields are included in the constructor generated by Lombok
private final DataSource ds;
#Override
public void run(String... args) throws Exception {
log.info("DataSource: {} ", ds.toString());
}
}
// Application.java
#SpringBootApplication
#RequiredArgsConstructor(onConstructor_={#Autowired}) // from JDK 8
// #RequiredArgsConstructor(onConstructor = #__(#Autowired)) // up to JDK 7
public class Application {
private final Datasource ds;
public static void main(String... args) {
SpringApplication.run(Application.class, args);
}
#Bean
public MyCommandLineRunner schedulerRunner() {
return new MyCommandLineRunner(ds);
}
}
Later edit
Solution without Lombok relies on Spring to inject dependency when the bean is created
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
/**
* dependency ds is injected by Spring
*/
public MyCommandLineRunner schedulerRunner(DataSource ds) {
return new MyCommandLineRunner(ds);
}
}
// MyCommandLineRunner.java
public class MyCommandLineRunner implements CommandLineRunner {
private final Log logger = LogFactory.getLog(getClass());
private final DataSource ds;
public MyCommandLineRunner(DataSource ds){
this.ds = ds;
}
#Override
public void run(String... args) throws Exception {
logger.info("DataSource: "+ ds.toString());
}
}

Mock Prototype Spring Bean

I am trying to mock the bean having scope prototype.
package com.aks.operation;
#Scope(value="prototype")
public class Operation{
public int sum(int a,int b){
return a+b;
}
}
#Service
public class UseOperation{
#Autowired
private ApplicationContext context;
public void doOperation() throws Exception{
Operation opr = context.getBean(Operation.class); // this will return the prototype instance
..
..
}
}
//Junit
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:/app-context.xml" })
#ActiveProfiles("DEV")
#TestExecutionListeners(listeners = { DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class, TransactionalTestExecutionListener.class })
#ComponentScan(basePackages = { "com.aks.operation" })
public class TestOperation{
#InjectMocks
private Operation operation;
#Autowired
private UseOperation useOperation;
#Before
public void before() throws Exception{
MockitoAnnotations.initMocks(this);
when(operation.sum(1,2)).thenReturn(3);
}
#Test
public void doOperation() throws Exception{
useOperation.doOperation();// this function will create the prototype instance, and that instance should be mine for which I did mocking
}
}
Please suggest what approach should I take to test the prototype instances.
It's the context.getBean(Class<?>) which is always returning me the new instances.
Thanks.

Using properties files in spring-boot tests

I have simple spring boot web service, where for configuration I use .properties files. As example for spring-mail configuration, I have separate file mailing.properties located in src/main/resources/config/ folder.
in main application I include it using:
#PropertySource(value = { "config/mailing.properties" })
The problem appears when it comes to tests, I would like to use the same properties from this file, but when i try to use it, I get fileNotFaundExeption.
Question is:
Should I have separate resources in my src/test folder, or it is possible to access resources from src/main folder, if yes, how?
UPDATE added sources
test class:
#RunWith(SpringRunner.class)
#SpringBootTest
#TestPropertySource("classpath:config/mailing.properties")
public class DemoApplicationTests {
#Autowired
private TestService testService;
#Test
public void contextLoads() {
testService.printing();
}
}
service class:
#Service
public class TestService
{
#Value("${str.pt}")
private int pt;
public void printing()
{
System.out.println(pt);
}
}
main app class:
#SpringBootApplication
#PropertySource(value = { "config/mailing.properties" })
public class DemoApplication {
public static void main(String[] args)
{
SpringApplication.run(DemoApplication.class, args);
}
}
You can use #TestPropertySource annotation in your test class.
For example you have this attribute in your mailing.properties file:
mailFrom=fromMe#mail.com
Just annotate #TestPropertySource("classpath:config/mailing.properties") on your test class.
You should be able to read out the property for example with the #Value annotation.
#Value("${fromMail}")
private String fromMail;
To avoid annotate this annotation on multiple test classes you can implement a superclass or meta-annotations.
EDIT1:
#SpringBootApplication
#PropertySource("classpath:config/mailing.properties")
public class DemoApplication implements CommandLineRunner {
#Autowired
private MailService mailService;
public static void main(String[] args) throws Exception {
SpringApplication.run(DemoApplication.class, args);
}
#Override
public void run(String... arg0) throws Exception {
String s = mailService.getMailFrom();
System.out.println(s);
}
MailService:
#Service
public class MailService {
#Value("${mailFrom}")
private String mailFrom;
public String getMailFrom() {
return mailFrom;
}
public void setMailFrom(String mailFrom) {
this.mailFrom = mailFrom;
}
}
DemoTestFile:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = DemoApplication.class)
#TestPropertySource("classpath:config/mailing.properties")
public class DemoApplicationTests {
#Autowired
MailService mailService;
#Test
public void contextLoads() {
String s = mailService.getMailFrom();
System.out.println(s);
}
}

Categories