spring bean default Priority - java

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>

Related

Spring #configurable NullPointerException, #autowired service is null

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

Spring singleton bean creates new instance/bean on a each method call when proxied

Bellow is the code. When you run it and go in the browser to localhost:8080/hello you will see sequences "1,2", "2,3"...,because Spring CGLIB proxy (which is actually inserted into prototype field) creates new bean on every method call.
I think the prototype bean should be created only once per http call so the output should be "1,1", "2,2"..
I can solve this with ObjectFactory, but then I am loosing proxy along with all AOP spring capabilities.
How is this supposed to behave? Do I have st wrong? Is this really limitation of Spring? Can it be somehow solved with Java-based config?
This is the whole code, you need only 2 files:
Application.java
#SpringBootApplication
#RestController
public class Application {
#Autowired //one solution is ObjectFactory<PrototypeExample>
private PrototypeExample prototype;
#GetMapping("/hello")
public String hello() {
return (prototype.getCounter() + ", " + prototype.getCounter());
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
#Component
#Scope(value = SCOPE_PROTOTYPE, proxyMode = TARGET_CLASS)
class PrototypeExample {
private static AtomicInteger counter = new AtomicInteger();
public PrototypeExample() {
counter.getAndIncrement();
}
public int getCounter() {
return counter.get();
}
}
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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.neco</groupId>
<artifactId>spring-core_IV</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.3.RELEASE</version>
<relativePath/>
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>
Your bean PrototypeExample has a SCOPE_PROTOTYPE scope attribute.
According to the official documentation :
The non-singleton, prototype scope of bean deployment results in the
creation of a new bean instance every time a request for that specific
bean is made. That is, the bean is injected into another bean or you
request it through a getBean() method call on the container. As a
rule, use the prototype scope for all stateful beans and the singleton
scope for stateless beans.
If you need a singleton, just remove the #Scope annotation or set it to singleton (which is the default one). All scopes available are listed here.

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 Boot, #Autowire into an unmanaged class using #Configurable and load time weaving

I have a collection of unmanaged classes that I are instantiated outside of Spring. I've been attempting to use Spring AOP with load time weaving to #Autowire a bean into these classes but have so far not had any luck.
I've been testing using Tomcat 8 and Spring Boot 1.2.0.
My #Configuration where I attempt to set up class looks like this:
#Configuration
#PropertySource("classpath:application.properties")
#EnableSpringConfigured
#EnableLoadTimeWeaving
public class Config
Inside Config I define the bean I want to #Auotwire into my unmanaged classes:
#Bean
public StateProvider stateProvider() {
//setup bean
return new DynamoStateProviderImpl( );
}
The unmanaged bean looks like this:
#Configurable(autowire = Autowire.BY_TYPE, dependencyCheck = true, preConstruction = true)
public class StateOutput implements UnifiedOutput {
#Autowired
private StateProvider stateProvider;
And I have the following deps inside my pom
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-agent</artifactId>
<version>2.5.6.SEC03</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<version>3.0.0</version>
</dependency>
So far, I have not been able to see anything injected into stateProvider or been able to pull any info from the logs. I've also attempted setter style injection using
#Autowired
public void setStateProvider(StateProvider stateProvider){
this.stateProvider = stateProvider;
}
Thanks
In order to instrument LTW you'll need to either use the javaagent or place spring-tomcat-weaver.jar in the \lib folder and set up TomcatInstrumentableClassLoader in context.xml.
javaagent example:
-javaagent:"${settings.localRepository}/org/springframework/spring-agent/2.5.6.SEC03/spring-agent-2.5.6.SEC03".jar
ClassLoader example:
<Context>
<Loader loaderClass="org.springframework.instrument.classl oading.tomcat.TomcatInstrumentableClassLoader" />
</Context>

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