I implemented this springboot Component that is supposed to return the value of some properties at runtime
#Component
public class ConnectionService {
#Value("${spring.datasource.url}")
private String url;
#Value("${spring.datasource.username}")
private String username;
#Value("${spring.datasource.password}")
private String password;
#Value("{spring.liquibase.change-log}")
private String changelog;
public ConnectionService() {
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getChangelog() {
return changelog;
}
public void setChangelog(String changelog) {
this.changelog = changelog;
}
}
But for some reason all the properties are being returned as null
This is how I call my component
#SpringBootApplication
public class MigrationsApplication {
private static final Logger logger = LoggerFactory.getLogger(MigrationsApplication.class);
#Autowired
static
ConnectionService cs;
public static void main(String[] args) throws DatabaseException {
try{
cs=new ConnectionService();
String dbUrl=cs.getUrl();
logger.info("URL:"+dbUrl);
String dbUsername=cs.getUsername();
logger.info("USERNAME:"+dbUsername);
String dbPassword=cs.getPassword();
logger.info("PASSWORD:"+dbPassword);
String changelogPath=cs.getChangelog();
}
What am I doing wrong? Do I need a full constructor for the Component?It's because cs is static? Wasn't springboot meant to fill the values automatically?
Component means this is a bean, so Spring will handle its lifecycle. When the bean is created it will fill it with the requested data. However, you try to fill static values with it which is not a part of the lifecycle.
Add a full arg constructor to the class and let Spring inject it to the place where you want to use it and it will work fine.
Edit:
I just realized you not using static variables so the field injection should work when you properly let Spring to initialize your bean
Edit2:
Based on your snippet you using it wrong.
Do not put business logic into the main void, use SpringApplication.run, also, if you want a CLI then use Spring's CommandLineRunner interface like that:
#SpringBootApplication
public class MigrationsApplication.class implements CommandLineRunner {
private static final Logger logger = LoggerFactory.getLogger(MigrationsApplication.class);
#Autowired
ConnectionService cs;
public static void main(String[] args) {
SpringApplication.run(MigrationsApplication.class, args);
}
#Override
public void run(String... args) {
String dbUrl=cs.getUrl();
logger.info("URL:"+dbUrl);
String dbUsername=cs.getUsername();
logger.info("USERNAME:"+dbUsername);
String dbPassword=cs.getPassword();
logger.info("PASSWORD:"+dbPassword);
String changelogPath=cs.getChangelog();
}
}
Related
I have some method which use final String arguments in ServiceClassA.
#Service
public class ServiceClassA {
public String callingMethod(final String argOne, final String argTwo) {
//Implementation here
}
}
And its call within another method in another class ServiceClassB.
#Service
public class ServiceClassB {
private final ServiceClassA serviceClassA;
public MyResponse methodB() {
String inputOne = "111";
String inputTwo = MyEnum.AAA.getDescription();
final String result = serviceClassA.callingMethod(inputOne,inputTwo);
//some implementations
MyResponse response = new MyResponse();
//set values to response
return response;
}
}
Also there is a enum.
public enum MyEnum {
AAA(1, "AAA"), BBB(2,"BBB");
private static final Map<Integer, MyEnum> MYENUM_MAP = new HashMap<>();
static {
for (MyEnum myEnum : values()) {
MYENUM_MAP.put(myEnum.getValue(), myEnum);
}
}
private final int value;
private final String description;
private MyEnum(int value, String description) {
this.value = value;
this.description = description;
}
public String getDescription() {
return description;
}
public int getValue() {
return value;
}
}
I'm testing ServiceClassB using mockito and JUnit. When I try to mock callingMethod in ServiceClassA it returns null instead of the "SUCCESS".
#RunWith(MockitoJUnitRunner.class)
public class ServiceClassBTest {
#InjectMocks
private ServiceClassB serviceClassB;
#Mock
private ServiceClassA serviceClassA;
#Before
public void init() {
MockitoAnnotations.initMocks(this);
}
#Test
public void methodB_success(){
String result = "SUCCESS";
when(serviceClassA.callingMethod(Mockito.anyString(), Mockito.anyString())).thenReturn(result);
}
}
I tried several ways as below and always it returns null. But I want to get SUCCESS as the result.
when(myService.callingMethod(Mockito.anyString(), Mockito.anyString())).thenReturn(result); //return null
when(myService.callingMethod(Mockito.any(), Mockito.any())).thenReturn(result); //return null
when(myService.callingMethod(ArgumentMatchers.anyString(), ArgumentMatchers.anyString())).thenReturn(result); //return null
doReturn(result).when(myService).callingMethod(Mockito.anyString(), Mockito.anyString()); //return null
The problem there is that you are using #RunWith(MockitoJUnitRunner.class) together with MockitoAnnotations.initMocks(this). They are conflicting.
Use something one MockitoJUnitRunner or MockitoAnnotations.initMocks.
MockitoJUnitRunner Initializes mocks annotated with Mock, so that
explicit usage of MockitoAnnotations.initMocks(Object) is not
necessary. Mocks are initialized before each test method.
Working test:
#RunWith(MockitoJUnitRunner.class)
public class ServiceClassBTest {
#InjectMocks
private ServiceClassB serviceClassB;
#Mock
private ServiceClassA serviceClassA;
#Test
public void methodB_success(){
String result = "SUCCESS";
when(serviceClassA.callingMethod(Mockito.anyString(), Mockito.anyString())).thenReturn(result);
serviceClassB.methodB();
}
}
I would like to read the array in the YAML file in java spring. I don't know if it's possible to use an array of objects.
Here is the content of the YAML:
main:
nodes:
-
label: "My label"
id: "My label id"
uri: ""
-
label: "My label"
id: "My label id"
uri: ""
And here is my component which loads the file:
public class MyNode {
String label, id, uri;
}
#ConfigurationProperties(prefix = "main")
#PropertySource(value = "classpath:mycustomfile.yml", factory = YamlPropertyLoaderFactory.class)
#Component
public class CustomProperties {
private List<MyNode> nodes;
}
And the YamlPropertySourceFactory
public class YamlPropertySourceFactory implements PropertySourceFactory {
#Override
public PropertySource<?> createPropertySource(String name, EncodedResource encodedResource)
throws IOException {
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
factory.setResources(encodedResource.getResource());
Properties properties = factory.getObject();
return new PropertiesPropertySource(encodedResource.getResource().getFilename(), properties);
}
}
When I run my application, nodes are null. I followed Baeldun tutorial
I solved it by adding #Data (Lombok) and make MyNode as inner static class. You can also add getter and setter if you don't use Lombok.
#ConfigurationProperties(prefix = "main")
#PropertySource(value = "classpath:mycustomfile.yml", factory = YamlPropertyLoaderFactory.class)
#Component
public class CustomProperties {
private List<MyNode> nodes;
#Data
public static class MyNode {
private String label, id, uri;
}
}
It seems that the problem comes from the visibility of your MyNode class members.
If you define public setters for all the members, everything will work fine.
So just try to modify
public class MyNode {
String label, id, uri;
}
to
public class MyNode {
private String label, id, uri;
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUri() {
return uri;
}
public void setUri(String uri) {
this.uri = uri;
}
}
I am working on a microservice in Spring Boot, and I have a common configuration class where I store all the config variabes that I got from application.properties file. It looks something like this:
Config.java
package programming
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
#Configuration
#Data
#NoArgsConstructor
#AllArgsConstructor
public class CommonConfiguration {
#Value("${user-pool.id}")
private String userPoolId;
#Value("${client.id}")
private String clientId;
...
...
...
}
Then whenever I need these variables in other classes, I autowire Config.java class, and simply use it like this:
#Autowired
private Config config;
public void method1() {
...
String key = items.get(config.getClientId()).toString();
}
Would having a common configuration class that stores all the autowired variables and use these when needed in other classes a good practice in spring boot? If not, what would be the best way to go about this? Any help would be greatly appreicated. Thank you!
The better way is to do something ging like this
#ConfigurationProperties(prefix = "optional.key")
Public class MyProperties{
Int example;
String example2;
Getters/setters
}
In your application.properties you can now type
optional.key.example=5
You can the Autowerire MyProperties where ever you need them
Edit
It’s also good to add the following dependency to maven, it generates a helper file so that your idea can recognise the defined properties
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
Small addition to replay on the question in the comments
You can leave the prefix empty and just annotate you class with #ConfigurationProperties if you don't want to have a general key
Subkeys can be easily handled with subclasses I will post an example of one of my projects here
#ConfigurationProperties(prefix = "swagger")
public class SpringFoxProperties {
private Info info = new Info();
private Config config = new Config();
public Info getInfo() {
return info;
}
public void setInfo(Info info) {
this.info = info;
}
public Config getConfig(){
return config;
}
public void setConfig(Config config) {
this.config = config;
}
public static class Config {
private String paths = "";
private String documentationType = "openApi";
public String getPaths() {
return paths;
}
public void setPaths(String paths) {
this.paths = paths;
}
public String getDocumentationType() {
return documentationType;
}
public void setDocumentationType(String documentationType) {
this.documentationType = documentationType;
}
}
public static class Info {
private String title = "";
private String description = "";
private String version = "";
private String termsOfServiceUrl = "";
private Contact contact = new Contact();
private License license = new License();
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public String getTermsOfServiceUrl() {
return termsOfServiceUrl;
}
public void setTermsOfServiceUrl(String termsOfServiceUrl) {
this.termsOfServiceUrl = termsOfServiceUrl;
}
public Contact getContact() {
return contact;
}
public void setContact(Contact contact) {
this.contact = contact;
}
public License getLicense() {
return license;
}
public void setLicense(License license) {
this.license = license;
}
public static class Contact {
private String name = "";
private String url = "";
private String email = "";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
public static class License {
private String name = "";
private String url = "";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
}
}
example application.yml
swagger:
info:
title: title
description: description
version: version
termsOfServiceUrl: termsOfServiceUrl
contact:
name: name
url: url
email: email
license:
name: name
url: url
config:
paths: /api/.*
documentation-type: swagger
Would having a common configuration class that stores all the autowired variables and use these when needed in other classes a good practice in spring boot? If not, what would be the best way to go about this? Any help would be greatly appreicated.
My answer to that part is: definitely no!
You are introducing a real class dependency for all classes that need configuration. With that you are actually reversing the configuration injection that spring offers.
The whole point is to be able to inject only that specific part of the configuration you need in fields of your target class directly (e.g. by using #Value("${foo.bar}" private String fooBar;))
With that you also disable use of specific features (e.g. using a different profile in different classes with #Profile), and i'm sure there are more examples that you are actually limiting the flexibility.
I don't see any benefit in your approach - you may aswell explain why you need that :)
I have the following mapping on my spring mvc controller:
#RequestMapping(value = "/servers", method = RequestMethod.GET)
public #ResponseBody Server getServers(#RequestParam(value="password", required=false) String password)
{
Server server = new Server();
server.setIpaddress("192.168.0.4");
server.setServername("serverOne");
List<Service> list = new ArrayList<Service>();
Service s1 = new Service();
s1.setServiceName("telnet");
s1.setServicePort(21);
list.add(s1);
s1= new Service();
s1.setServiceName("SSH");
s1.setServicePort(22);
list.add(s1);
server.SetServices(list);
return server;
}
It should return a server class in json , with one filled property matching List
but is doesn't show anything. This are the involved classes:
Class Server:
package es.landesoft.mvctesting.JavaBeans;
import java.util.List;
public class Server {
private String ipaddress;
private String serverName;
private List<Service> services;
public void setIpaddress(String value)
{
this.ipaddress = value;
}
public String getIpAddress()
{
return this.ipaddress;
}
public void setServername (String value)
{
this.serverName= value;
}
public String getServername()
{
return this.serverName;
}
public void SetServices(List<Service> services)
{
this.services= services;
}
public List<Service> GetServices()
{
return this.services;
}
}
Class service:
package es.landesoft.mvctesting.JavaBeans;
public class Service
{
private String serviceName;
private int servicePort;
public void setServiceName(String value)
{
this.serviceName= value;
}
public String getServiceName(){
return this.serviceName;
}
public void setServicePort(int value)
{
this.servicePort=value;
}
public int getServicePort()
{
return this.servicePort;
}
}
The Json Output is:
{"servername":"serverOne","ipAddress":"192.168.0.4"}
No trace of the List property.
What am I doing wrong.
do like this
Change your Server model like this.
public class Server {
private List<Service> services = new ArrayList<Service>();
}
And Add like this.
Server server = new Server();
server.setIpaddress("192.168.0.4");
server.setServername("serverOne");
Service s1 = new Service();
s1.setServiceName("telnet");
s1.setServicePort(21);
server.GetServices().add(s1); //Add like this.
Note: Maintain the java naming conventions.
public List<Service> GetServices(){}
should be public List<Service> getServices()
The same for setters too.
I'm using OpenPojo for validating getter and setter methods of a class.
But while trying to validate my getter and setter methods I'm having following exception:
java.lang.IncompatibleClassChangeError: class com.openpojo.reflection.java.bytecode.asm.SubClassCreator has
interface org.objectweb.asm.ClassVisitor as super class
Here is my code:
public final class ClientToken implements RememberMeAuthenticationToken {
private static final long serialVersionUID = 3141878022445836151L;
private final String clientName;
private final Credentials credentials;
private String userId;
private boolean isRememberMe;
public ClientToken(final String clientName, final Credentials credentials) {
this.clientName = clientName;
this.credentials = credentials;
}
public void setUserId(final String userId) {
this.userId = userId;
}
public void setRememberMe(boolean isRememberMe) {
this.isRememberMe = isRememberMe;
}
public String getClientName() {
return this.clientName;
}
public Object getCredentials() {
return this.credentials;
}
public Object getPrincipal() {
return this.userId;
}
#Override
public String toString() {
return CommonHelper.toString(ClientToken.class, "clientName", this.clientName, "credentials", this.credentials,
"userId", this.userId);
}
public boolean isRememberMe() {
return isRememberMe;
}
}
Does anybody help me why this problem occurs.
Have you tried to add the ASM 5.0.3 or higher as a dependency?
It seems an older version for ASM is getting pulled in.