I have an application.yml file which contains properties as below
NAME:
CLASS:
ID: ABC123456
and here is my spring boot component class
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
#Component
#Slf4j
public class ProcessMe {
#Value("${NAME.CLASS.ID}")
String StuId;
public boolean IsRightOrWrong(){
System.out.println(StuId);
}
}
In above componenent System.out.println(StuId); is always coming as null. When i am tying to call this function using Junit test class. What's wrong in the above code?
I suppose this solution to define in constructor like
#Component
#Slf4j
public class ProcessMe {
String StuId;
#Autowired
ProcessMe(#Value("${NAME.CLASS.ID}") String StuId) {
this.StuId = StuId;
}
public boolean IsRightOrWrong(){
System.out.println(this.StuId);
}
}
Hope useful
If you can start the application then I believe that your configuration is correct. I mean that StuId have some value.
The problem is how you testing When i am tying to call this function using Junit test class. What's wrong in the above code?
It seems that you're calling from the unit test. Then you need to clone application.yaml to test profile then your test container can read data, otherwise you have to mock your configuration.
btw: Java Object property should using camel case lol
Related
I want to prepare for a migration from Couchbase 6.X to 7.X. For this, I want to configure the scope and collection for my cluster. In the documentation of spring-data-couchbase, I saw that I just need to add #Scope and #Collection on my repository.
This configuration works:
package xxx.couchbase;
import xxx.MyDocument;
import org.springframework.data.couchbase.repository.Collection;
import org.springframework.data.couchbase.repository.CouchbaseRepository;
import org.springframework.data.couchbase.repository.Scope;
import org.springframework.stereotype.Repository;
#Repository
#Scope("_default")
#Collection("_default")
public interface MyDocumentRepository extends CouchbaseRepository<MyDocument, String> {
}
What I want to do:
package xxx.couchbase;
import xxx.MyDocument;
import org.springframework.data.couchbase.repository.Collection;
import org.springframework.data.couchbase.repository.CouchbaseRepository;
import org.springframework.data.couchbase.repository.Scope;
import org.springframework.stereotype.Repository;
#Repository
#Scope("${couchbase.scope}")
#Collection("${couchbase.collection}")
public interface MyDocumentRepository extends CouchbaseRepository<MyDocument, String> {
}
application.properties:
couchbase.scope=_default
couchbase.collection=_default
This latter configuration doesn't work, because the string value in the annotation is not parsed and is taken literally.
I'm working with Spring Boot version 2.7.3, spring-boot-starter-data-couchbase version 2.7.3.
Is there another way to pass the value from the application.properties to the annotations ?
I don't see a way for spring boot to populate arguments of an annotation with values from application.properties.
But you can populate variables from values in application.properties like this:
#Value("${scopeName}")
private String scopeName;
#Value("${collectionName}")
private String collectionName;
And then define your repository to extend DynamicProxyable
public interface AirportRepository extends CouchbaseRepository<Airport, String>, DynamicProxyable<AirportRepository>{...}
And then get a repository object with that scope and collection as the defaults:
List<Airport> airports = airportRepository.withScope(scopeName).withCollection(collectionName).findByName(iata);
see https://docs.spring.io/spring-data/couchbase/docs/4.4.6/reference/html/#couchbase.collections
Using Springboot 2.5.7 with bundled Junit5 (via spring-boot-starter-test), I'm trying to test the constraints that I have put in place all over the bean via custom an standard annotations.
Most of the documentation I found was about junit4 and I cannot find a way to make it work in springboot's junit5.
Also, I'm a bit puzzled because, ideally, I would like to test contraint per constraint and only found documentation on how to test it globally (using junit4).
Does anyone already fiddled with that kind of testing?
Just for the sample, here I have a bean for my inbound SQS event:
package com.application.sqs.dto.inbound;
import com.application.sqs.dto.validators.ZoneDateTime;
import lombok.Data;
import java.util.List;
#Data
public class SqsEvent {
private String version;
private String id;
private String source;
private String account;
#ZoneDateTime(
message = "Wrong time."
)
private String time;
private String region;
private List<String> resources;
private SqsPayload detail;
}
And an example of a payload (that has lots of child objects, edited):
package com.application.sqs.dto.inbound;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.application.sqs.dto.validators.LocalDate;
import lombok.Data;
import javax.validation.constraints.Digits;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;
import java.util.List;
#Data
#JsonIgnoreProperties(ignoreUnknown = true)
public class SqsPayload {
#JsonProperty("primkey")
#NotBlank(
message = "primkey is blank"
)
private String primarykey;
#JsonProperty("numb")
#Pattern(
regexp="^(A|B)$",
message = "numb is invalid"
)
private String number;
}
I'd like to test that numb only accepts A or B, that primkey must not be blank and that my custom validator (#ZonedDateTime) is working as intended for example.
Thanks a lot for your help and guidance!
Best,
Haven't done per individual constraint, but below are 2 possibilities.
Option 1
If you run an instance of that class through the javax validator, then you will get one ConstraintViolation per invalid constraint. That way, you could just validate that you get all expected ConstaintViolation's for each constraint.
Option 2
define a valid mock object, and then in each test case, you just change a value of a constrained field into an invalid case, and ensure that it catches it
Details on programmatic javax validation
For programmatic javax validation, you could have a look here on step 5.
Also suggest you have a deeper look at the javax.validation.Validator interface on available methods. You also have a method called validateProperty to target specific properties as well.
I'm trying to bind a list of objects to my entity class using #ConfigurationProperties annotation. Spring Boot framework seems to ignore that annotation, and it literally does nothing.
Here is my application.yml properties file:
spring:
datasource:
url: jdbc:mysql://localhost:3306/search-engine
username: landsreyk
password: 12345678
jpa:
database-platform: org.hibernate.dialect.MySQLDialect
show-sql: false
hibernate:
ddl-auto: none
sites:
- url: http://someurl1.com
name: somename1
- url: https://someurl2.com
name: somename2
- url: https://someurl3.com
name: somename3
And here is my entity class:
package main.model;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import javax.persistence.*;
import java.sql.Timestamp;
#Getter
#Setter
#ToString
#Entity
#Table(name = "_site")
public class Site {
#Column(nullable = false)
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#Enumerated(EnumType.STRING)
#Column(name = "status")
private Status status;
#Column(name = "status_time")
private Timestamp statusTime;
#Column(name = "last_error")
private String lastError;
private String url;
private String name;
public enum Status {
INDEXING, INDEXED, FAILED
}
}
Binding is pretty straight forward:
package main.utilities;
import lombok.Getter;
import lombok.Setter;
import main.model.Site;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.List;
#ConfigurationProperties
public class ApplicationProperties {
#Getter
#Setter
private List<Site> sites;
}
Somewhere in application I'm testing that binding, let's say I created end point in my API to test that binding, i.e. my controller calls a method which just prints all objects in a list:
package main.application.indexer;
import main.utilities.ApplicationProperties;
import org.springframework.beans.factory.annotation.Autowired;
public class IndexBuilder {
#Autowired
private ApplicationProperties properties;
public void run() {
System.out.println(properties.getSites());
}
}
Expected:
After launch, ApplicationProperties instance is not null. Calling properties.getSites() returns a list of Site objects. Each Site object has url and name fields initialized from yaml source.
Actual:
After launch ApplicationProperties instance is null.
I was shocked to realize that Spring wasn't able to accomplish such a simple binding.
Knowing that just parsing a yaml file is not such a hard task to accomplish, I thought that Spring Framework should have this feature build-in.
How do I bind my list?
By the way, here is my project structure.
UPDATE:
I edited IndexBuilder class. Added #Configuration and #Bean annotations.
Now it looks like this:
package main.application.indexer;
import main.utilities.ApplicationProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class IndexBuilder {
#Autowired
private ApplicationProperties properties;
#Bean
public void run() {
System.out.println(properties.getSites());
}
}
That fixed the problem with initialization, but Spring Framework just call run() immideately after launch. That isn't an expected behavior.
UPDATE 2:
Answer of this user is completely authentic.
He is right.
Instead of using #Configuration you should use #Component on your IndexBuilder class.
#Configuration should only be used for configuration classes that define the application‘s beans. Here it is actually expected that the methods annotated with #Bean are executed immediately on application start. This is the phase where spring creates all needed beans. However I’m wondering spring accepts a void method with the #Bean annotation.
What’s your expected time when the run method should be executed? With a normal bean/component/service you could for example use the #PostConstruct annotation on methods. These will however be executed in the very same phase as the #Bean methods or at least not much later.
Turns out, Spring isn't that smart as I thought.
To access #ConfigurationProperties class, that is: accessing a bean from a non-managed-by-spring class, - I had to go through this article:
Autowiring Spring Beans Into Classes Not Managed by Spring
https://dzone.com/articles/autowiring-spring-beans-into-classes-not-managed-by-spring
It solved the problem.
I would like to use custom Java annotation to insert a value in a private class property using Spring AOP (and/or AspectJ). Quick example:
MyAnnotation.java:
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.FIELD })
public #interface MyAnnotation {
}
MyController.java:
public class MyControllerImpl implements MyController {
...
#MyAnnotation
private String var1;
#Override
public String getVarExample() {
// imagine this is a REST API that gets called on #GET
// request and returns a string
System.out.println(this.var1); // <-- I'd like this to be "helloworld"
// this is just for illustration
// of course, I will want to do
// something more meaningful with
// the 'var1' variable
return "ok"; <- unimportant for this example
}
...
MyAspect.java:
#Aspect
#Component
public class MyAspect {
#Pointcut("#annotation(com.mypackage.annotation.MyAnnotation)")
public void fieldAnnotatedWithMyAnnotation() {
}
#Around("fieldAnnotatedWithMyAnnotation()")
public Object enrichVar1(ProceedingJoinPoint pjp) throws Throwable {
// problem #1 - the program never enters here
// problem #2 - I need to figure out how to set up the var1 here
// to "helloworld" , how?
return pjp.proceed();
}
...
}
What would I like to happen?
I will call and get into the getVarExample() and after it returns I would like to see "helloworld" in console or log. I would like to somehow set the var1 to a custom value using AOP. Any property variable that will be annotated with #MyAnnotation will be set to "helloworld". I hope the example above is clear.
What have I tried?
I made sure there is no typo in the package names, also fiddled with different AOP advice annotations like #Around and #Before. I also tried different targets in the MyAnnotation and ended up with ElementType.FIELD which should be correct.
Can you help me to get it working?
I know this can be done, but couldn't find any working example online. Again, I would like to see 2 answers:
1. How to get the pointcut to trigger on MyController entrance? I want to catch a breakpoint inside the enrichVar1(..) method of the MyAspect class.
2. How can I modify the annotated var1 value inenrichVar1(..) method of the MyAspect class?
I don't know what I am doing wrong. Any help will be greatly appreciated. Thank you!
The AOP is set up correctly in my project. I know that because I am already using AOP for different things (logging for example).
Update #1:
Please, note there are not getters or setters for the var1 private variable. The variable will be only used within the MyControllerImpl. To illustrate this better I changed the return value of the getVarExample.
Like I said in my comment:
The pointcut designator #annotation() intercepts annotated methods, not annotated fields. For that, native AspectJ has get() and set(). I.e., the pointcut would also need to be changed if migrating to AspectJ. But I agree that sticking to Spring AOP and annotating getter methods instead of fields is probably enough here.
But because you insist that you want to keep the controller class unchanged, here is the native AspectJ solution. Please read chapter Using AspectJ with Spring Applications for how to configure that with #EnableLoadTimeWeaving and JVM parameter -javaagent:/path/to/aspectjweaver.jar.
In order to demonstrate that this solution really does work independently of Spring, I am using no Spring classes or annotations at all, only POJOs and native AspectJ. You can simply do the same within your Spring application. Please note that native AspectJ aspects do not need #Component annotations, in contrast to Spring AOP aspects.
package de.scrum_master.app;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.FIELD })
public #interface MyAnnotation {}
package de.scrum_master.app;
public interface MyController {
String getVarExample();
}
package de.scrum_master.app;
public class MyControllerImpl implements MyController {
#MyAnnotation
private String var1;
#Override
public String getVarExample() {
System.out.println(this.var1);
return "ok";
}
}
package de.scrum_master.app;
public class Application {
public static void main(String[] args) {
MyController myController = new MyControllerImpl();
myController.getVarExample();
}
}
package de.scrum_master.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
#Aspect
public class MyAspect {
#Pointcut("get(#de.scrum_master.app.MyAnnotation * *)")
public void fieldAnnotatedWithMyAnnotation() {}
#Around("fieldAnnotatedWithMyAnnotation()")
public Object enrichVar1(ProceedingJoinPoint pjp) throws Throwable {
System.out.println(pjp);
return "helloworld";
}
}
When running Application, the console log is going to be:
get(String de.scrum_master.app.MyControllerImpl.var1)
helloworld
The AspectJ manual explains the syntax of field get and set join point signatures and field patterns.
Note: I think that your use case might be a hack rather than a valid application design. You ought to refactor rather than hack into an application like this.
As it goes from Spring docs Spring AOP does support Spring beans' method execution join points. To make field access join points work you need to use AspectJ's backend with load time weaving for AOP.
But for your case it's not required to use field join points, you can put your annotation on the getter and this should work.
I am working on an application that uses a variable that is declared in the application.yml file. In my application.yml file it is defined as so:
lwt:
application:
five-minute-jobs: ${ENABLE_FIVE_MINUTE_JOBS:true}
In my controller file it is declared this way but it is always returning false whenever I log it in in the console. Here is the shortened version:
import org.springframework.beans.factory.annotation.Value;
public class EmailJobSchedulerController {
#Value("${lwt.application.five-minute-jobs}")
private boolean fiveMinuteJobsEnabled;
Am I declaring it correctly in the file? Been searching on other threads but haven't been able to find a clear answer for this. Thanks!
Have you tried with code below?
lwt:
application:
five-minute-jobs: true
May be you are not setting the env variable correctly. Can you do the following in your main #SpringBootApplication class and tell me what it is printing?
#SpringBootApplication
public class AccessingDataJpaApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(AccessingDataJpaApplication.class);
System.out.println(context.getEnvironment().getProperty("ENABLE_FIVE_MINUTE_JOBS"));
System.out.println(context.getEnvironment().getProperty("lwt.application.five-minute-jobs"));
}
}
#value annotation will get the value if it's under bean life cycle else you need to take from ConfigureEnviornment class, is below code is registered with bean ? I feel you might be missed adding #RestController on top of this class
import org.springframework.beans.factory.annotation.Value;
#RestController
public class EmailJobSchedulerController {
#Value("${lwt.application.five-minute-jobs}")
private boolean fiveMinuteJobsEnabled;