Spring #configurable NullPointerException, #autowired service is null - java

I'm trying to use #configurable in spring to use a #autowired service in a non bean class I create.
It doesn't want to work anymore whatever I try.
Can someone tell me what I'm doing wrong? (I did some research but I'm totally clueless now)
Here is a very basic code example I made :
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.5</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo2</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo2</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Configuration ComponentScan class
package com.example.demo2;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.aspectj.EnableSpringConfigured;
#Configuration
#ComponentScan
#EnableSpringConfigured
public class AspectJConfig
{
}
#SpringBootApplication class
package com.example.demo2;
import javax.annotation.PostConstruct;
//import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
#SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
public class Demo2Application
{
//#Autowired
//private HelloWorldService helloWorldService;
public static void main(String[] args)
{
SpringApplication.run(Demo2Application.class, args);
}
#PostConstruct
public void doSomethingIProbablyShouldNotBeDoing()
{
//helloWorldService.sayHello();
HelloWorldClient client = new HelloWorldClient();
client.sayHello();
}
}
class with #Configurable and #Autowired service
package com.example.demo2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
#Configurable
public class HelloWorldClient
{
#Autowired
private HelloWorldService service;
public void sayHello()
{
// Used injected instance of service
service.sayHello();
}
}
#Service class
package com.example.demo2;
import org.springframework.stereotype.Service;
#Service
public class HelloWorldService
{
public void sayHello()
{
System.out.println("Hello world!");
}
}
Also here is a link to my previous post on that subject. I did received an answer to my question that was working. But for whatever reason it doesn't work anymore on my side.
Spring #configurable NullPointerException

#Configurable should be used in connection with native AspectJ, not with Spring AOP, see here. Because the annotation is meant to be used with POJOs rather than Spring beans, this makes sense.
In my answer to your first question, we used AspectJ Maven Plugin to do compile-time weaving (CTW). Either you need to do the same here or configure load-time weaving (LTW) instead.
When using LTW, in order for org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect to work, which is responsible for achieving the POJO dependency injection, you need either of
-javaagent:/path/to/aspectjweaver.jar on the JVM command line or
-javaagent:/path/to/spring-instrument.jar on the JVM command line and #EnableLoadTimeWeaving in your Spring configuration or
de.invesdwin:invesdwin-instrument in the dependency list plus a code snippet initialising the weaver in your application. On more recent Java versions, you probably also need --add-opens java.base/java.lang=ALL-UNNAMED on the JVM command line to enable invesdwin-instrument to work correctly, using reflection.
You also need spring-aspects for the AnnotationBeanConfigurerAspect to be found. Optionally, you might want to add your own META-INF/aop.xml (or org/aspectj/aop.xml) file in the resources directory, if you want to configure certain weaving options or deactivate unneeded aspects from spring-aspects, if the corresponding warning messages on the console get on your nerves.
I know that this is not trivial, which is why I think that the CTW approach is easier to set up. But if you want to apply aspects dynamically via LTW, you need to use one of the approaches mentioned above. It is not rocket science, just a bit complicated when doing it the first time. But like Miyamoto Musashi said: "It may seem difficult at first, but all things are difficult at first."
Update: Here is an MCVE for you, i.e. a full, minimal example:
https://github.com/kriegaex/SO_AJ_SpringMavenConfigurableNPE_74184130
Feel free to play with it and remove the code using invesdwin-instrument and the corresponding dependency, if you want to use -javaagent and Spring on-board means. I actually recommend that.

You are creating service manually using new HelloWorldClient() #Autowired works only when bean/service is created by spring
try autowiring HelloWorldClient in Demo2Application and not creating it manually
Maybe You could try Spring #Autowired on a class new instance maybe it works

Related

Why REST Controlle in Spring Boot is returning HTTP Status 404 – Not Found

I have created simple spring Boot Application.I have tried so many times but each time it is throwing error:404 Error when I run this on Pivotal tc server.It would be really great if any one can help me this.I have created Controller class with Spring Boot Starter Class.Below the image for the Error I am getting.Pivotal Server is up and running at port 8082 but as soon as I enter /hello to localhost:8082,I am getting Error 404(I have shared image of Pivotal server too in the last).I have almost tried everything that is available on google.I am starter in Spring Boot and would really appreciate if any one can give suggestion to resolve this.I have also shared pom.xml.
404 Error Screenshot-->Error Image.
I am running this on pivotal tc server with url: http://localhost:8082/hello
Link to Pivotal Server url Screenshot--->Pivotal server with controller return url Image
Pivotal server is up and running but as soon as I enter http://localhost:8082/hello,
I am getting 404 error.Can anyone please help.I have tried everything that I can search for.
Link to Pivotal server up and Running Screenshot-->Pivotal server up and running image
Code for Spring Boot Starter Class:
package io.javabrains.springbootstarter;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class CourseApiApp {
public static void main(String[] args) {
SpringApplication.run(CourseApiApp.class,args);
}
}
Code For controller Class:
package io.javabrains.springbootstarter.hello;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class HelloController {
#RequestMapping("/hello")
public String sayHi()
{
return("Hello");
}
}
Pom.xml:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-
instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-
4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>io.javacheck.springbootquickstart</groupId>
<artifactId>course-api-test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>Java Brains Course Api</name>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
<!-- WEB -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<properties>
<java.version>1.8</java.version>
</properties>
</project>
use this :
#RestController
#RequestMapping("/test")
public class HelloController {
#GetMapping("/hello")
public String sayHi()
{
return("Hello");
}
}
and then send a request to this API /test/hello
Could you try wrapping the String inside Response Entity.
#RestController
public class HelloController {
#RequestMapping("/hello")
public ResponseEntity<String> sayHi()
{
return ResponseEntity.ok("Hello");
}
}

spring bean default Priority

In my simplified code sample I have 2 spring beans implementing the same interface (in real life the 2 components reside in different jars). One component (BeanImplBusiness in my sample) must be annotated with only #Component. The second one (BeanImplTest) can have any annotations.
The desired behavior is when both beans are in the classpath, spring to inject the "Business" bean in all places.
I try to achieve this using the javax.annotation.Priority annotation but it doesn't work the way I expect. Let me explain:
put #Priority annotation only in "Test" component: spring will inject only the "Test" bean no matter what value I give to #Priority. It looks like the "Business" bean doesn't exists.
put #Priority annotation in both beans and the lowest precedence in "Test" bean ( #Priority(Ordered.LOWEST_PRECEDENCE) ) it works perfectly, spring inject the "Business" component.
Spring version is 4.3.20, but I try also with 5.2.5 and I have same results.
So, my question is how can I configure the spring bean to give a default "Priority" on Business bean even if I don't declare it with annotation. Any solution with annotations, xml is good.
Why do I need to specify Priority annotations on both components? Is this a bug?
import javax.annotation.Priority;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
public class SpringPriorityMain {
public static void main(String[] args) {
try (AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class)) {
Bean bean = ctx.getBean(Bean.class);
System.out.println();
System.out.println(bean.getName());
System.out.println();
}
}
public static interface Bean {
String getName();
}
#Component
// #Priority(0)
public static class BeanImplBusiness implements Bean {
#Override
public String getName() {
return "Business";
}
}
#Component
#Priority(Ordered.LOWEST_PRECEDENCE)
public static class BeanImplTest implements Bean {
#Override
public String getName() {
return "Test";
}
}
#Configuration
#ComponentScan("ro.catta.spring")
public static class SpringConfig {
}
}
The sample project is a maven project, here is the pom file to see exactly the dependencies
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ro.catta.poc</groupId>
<artifactId>spring-priority</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<mvn.spring.version>4.3.20.RELEASE</mvn.spring.version>
<!-- <mvn.spring.version>5.2.5.RELEASE</mvn.spring.version> -->
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${mvn.spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${mvn.spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${mvn.spring.version}</version>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

How to get the version of Spring Boot in the /info actuator endpoint

It's possible to include the version of Spring Boot in the banner.txt by adding ${spring-boot.version}.
How can I do the same for the /info actuator endpoint?
I've tried adding both of the following to my application properties, but no luck:
info.app.spring-boot.version=${spring-boot.version}
info.app.spring-boot.version=#spring-boot.version#
The /info endpoint will print "${spring-boot.version}" (or "#spring-boot.version#" ), doesn't resolve the placeholder variable
My next idea was to create a Maven property for the Spring Boot version and reference it in the parent section like so
<properties>
<spring.boot.version>1.5.3.RELEASE</spring.boot.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>${spring.boot.version}</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
But this didn't work because Maven resolves placeholder variables AFTER handling the <parent> section
UPDATE
The following does work, but it's not ideal:
#Component
public class MyInfoContributor implements InfoContributor {
#Override
public void contribute(Info.Builder builder) {
builder.withDetail("spring-boot.version", SpringBootVersion.getVersion());
}
}
This should work:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<additionalProperties>
<spring-boot-version>${spring.boot.version}</spring-boot-version>
</additionalProperties>
</configuration>
</plugin>
You then don't need the app.info properties.
As you mentioned in your update, an InfoContributor can be used to add this property based on the value in SpringBootVersion:
#Component
public class SpringBootVersionInfoContributor implements InfoContributor {
#Override
public void contribute(Info.Builder builder) {
builder.withDetail("spring-boot.version", SpringBootVersion.getVersion());
}
}
This seems a pretty solid idiomatic approach to adding this detail.

Is the proxy issue handled gracefully in Spring 4?

I can't find any documentation answering this question.
To summarize:
I have a bean that implements an interface.
I set proxy-target-class to false.
I toggle between Spring 3.2.17 and 4.3.3 versions.
When I use Spring 3.2.17, the main class throws this:
java.lang.ClassCastException: com.sun.proxy.$Proxy7 cannot be cast to
com.package1.Camera
at com.package1.App.main(App.java:8)
This is what I expected. This is the correct behaviour because the JDK proxy class was generated.
But when I use Spring 4.3.3, no exception is thrown and main class runs properly.
The CGLIB proxy class was generated. But why was it generated when I set the proxy-target-class to false?
Here are the source files:
App.java
package com.package1;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Camera camera = (Camera) context.getBean("camera");
camera.snap();
context.close();
}
}
ICamera.java
package com.package1;
public interface ICamera {}
Camera.java
package com.package1;
import org.springframework.stereotype.Component;
#Component("camera")
public class Camera implements ICamera {
public void snap() {
System.out.println("SNAP!");
}
}
Logger.java
package com.package1;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
#Aspect
#Component
public class Logger {
#Pointcut("execution(void com.package1.Camera.snap())")
public void cameraSnap() {
}
#Before("cameraSnap()")
public void beforeAdvice() {
System.out.println("Before advice...");
}
}
beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
<context:component-scan
base-package="com.package1">
</context:component-scan>
<aop:aspectj-autoproxy proxy-target-class="false"></aop:aspectj-autoproxy>
</beans>
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.package1</groupId>
<artifactId>spring-test-app</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.3.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.3.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.3.3.RELEASE</version>
</dependency>
</dependencies>
</project>
When you set proxy-target-class = false, CGLIB Proxy still creates with same formula JDK Proxy.
In Spring docs, they said:
Spring AOP uses either JDK dynamic proxies or CGLIB to create the proxy for a given target object. (JDK dynamic proxies are preferred whenever you have a choice).
If the target object to be proxied implements at least one interface then a JDK dynamic proxy will be used. All of the interfaces implemented by the target type will be proxied. If the target object does not implement any interfaces then a CGLIB proxy will be created.
See more : Proxying mechanisms
Thru trial and error I had come up with the answer.
App.java
package com.package1;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Object o = context.getBean("camera");
System.out.println("class=" + o.getClass());
Camera camera = (Camera) context.getBean("camera");
camera.snap();
context.close();
}
}
proxy-target-class still defaults to false.
But now, Spring 4 is smart enough what proxy will it generate (JDK or CGLIB).
If you have an empty interface, it will generate a CGLIB proxy.
ICamera.java
package com.package1;
public interface ICamera {}
Otherwise, JDK proxy will be generated.
ICamera.java
package com.package1;
public interface ICamera {
void snap();
}
HTH.

Spring #Value annotation not working with mockito mock

I am using spring #Value annotation and setting values for some fields in class A.
I am writing unit tests for this class A. In the test class, I am annotating the reference for class A with Mockito #Spy. I am setting the values as system properties and then invoking MockitoAnnotations.initMocks(this).
My expectation is the spied object will have the fields initialized with the values from system properties via #Value annotation. But this is not happening.
Please anybody could explain?
I have a similar test and I'm using the following relevant code:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations="/context.xml")
public class ContextTest {
private boolean started = false;
#Spy
#Autowired
private Baz baz;
#Before
public void before() {
if (!started) {
MockitoAnnotations.initMocks(this);
started = true;
}
}
#Test
public void spy() {
Assert.assertEquals("value", baz.getProperty());
Mockito.verify(baz).getProperty();
}
}
Basically it will let spring process the test annotations (due to SpringJUnitRunner) and afterwards let Mockito process them (explicitly invoked MockitoAnnotations.initMocks(instanceOfTestClass)).
Other files to have a complete test
simple Baz.java spring class:
package foo.bar;
import org.springframework.beans.factory.annotation.Value;
public class Baz {
#Value("${property:test}")
private String property;
public String getProperty() {
return property;
}
public void setProperty(String property) {
this.property = property;
}
}
the context.xml file:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>my.properties</value>
</property>
</bean>
<bean id="baz" class="foo.bar.Baz" />
</beans>
my.property file:
property=value
and the maven (pom.xml) file:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>spring-test</groupId>
<artifactId>my.spring.test</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>3.2.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>3.2.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>3.2.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.9.5</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
the file structure is:
+ spring-test
- pom.xml
+ src/main/java
+ foo.bar
- Baz.java
+ src/main/resources
- context.xml
- my.properties
+ src/test/java
+ foo.bar
- ContextTest.java
Mockito is not Spring aware! And will never be! You'll always have to init these kind of injection yourself as it is not pure java.
However you can take a look at springockito, it is a spring extension that enable some interesting usage of Mockito with Spring. But you'll have to create a Spring context for the test.
Note that using a spring context in a JUnit test is like crafting an integration test.

Categories

Resources