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.
Related
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
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>
I'm trying to intercept rest service calls with an aspect in the following manner
package mypackage.services.Service;
#Component
public class Service {
#Override
public Response helloService() {
return handleResult("Hello test " + new Date());
}
}
#Component
#Aspect
public class AuditLog {
#Before("execution(* mypackage.services.Service.*(..))")
public void beforeServcie(JoinPoint jp){
log.info("Before ",jp.getSignature().getName());
}
}
I'm using the following maven dependencies
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.6</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.10</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.10</version>
</dependency>
This maven plugin
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.0</version>
</plugin>
And my configuration xml contains
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="mypackage"/>
<aop:aspectj-autoproxy proxy-target-class="true" />
also in the Application class I've added the following annotation
#Configuration
#EnableAspectJAutoProxy(proxyTargetClass=true)
public class Configuration{
...
}
On startup, by logging beans in the ApplicationContext, I can see that the aspect class "AuditLog" is being created.
I've set 2 breakpoints, but the debugger does not stop at the "beforeServcie" method but it does stop at the "helloService".
What am I missing?
Try this
execution(* mypackage.services.Service.*.*(..))
instead of
execution(* mypackage.services.Service.*(..))
If you are using spring-boot then instead of automatically adding dependency jars you can do
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
If you're using XML config <aop:aspectj-autoproxy ... /> then no need to have #EnableAspectJAutoProxy. It probably doesn't matter since AFAIK XML config wins over annotation config but better to avoid duplication
I am not quite sure why do you need aspectj-maven-plugin since Spring implements AOP by proxy and AFAIK this plugin is only needed for compile-time, post compile-time or load time weaving which are different concepts, see Spring AOP vs AspectJ
Now all the above mentioned points may not resolve your issue but the following might
execution(* mypackage.services.Service.Service.*(..))
And, don't set proxyTargetClass=true, let it be default false.
Explanation
The format is execution(<return type> <package name>.<class name>.<method name>(..)
The package name here is mypackage.services.Service and the class name is Service.
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.
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>