I am coding in spring-boot. I tried to get the value of properties.properties in other packages without success. For example in the classe ClassUtils.java, the value of testValue is always null
This is my project
This is my code:
package com.plugins.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import com.plugins.domain.ClassUtils;
#RestController
public class SearchContactController {
#Value("${environnement.test}")
String testValue;
#Value("${environnement.url}")
String urlValue;
#RequestMapping(value = "/test")
public String pingRequest() {
System.out.println("value ===> " + testValue + " /// " + urlValue);
return "test !" + ClassUtils.getTestValue();
}
}
This is my second class, where I can't get the value of testValue variable:
package com.plugins.domain;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
#Component
public class ClassUtils {
#Value("${environnement.test}")
static String testValue;
public static String getTestValue(){
return "The return "+testValue;
}
}
This is my springApp.java
package com.plugins;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class SpringBootVideApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootVideApplication.class, args);
}
}
Enable #ComponentScan({"com.plugins"}) , in Application
To access the properties defined in application.properties
myapp.url="xxxxxx"
in your class
#Value("${myapp.url}")
private String testValue;
but this cannot be a static variable, if it is a static variable you do some hack like this, by defining setter method
private static String testValue;
#Value("${myapp.url}")
public void testValue(String value) {
testValue = value;
}
I resolve this issue by addin #Autowired in the class which use the method of the other class this is a snippet
// Class: SearchContactController.java
#Autowired
ClassUtils cd;
#RequestMapping(value = "/ping")
public String pingRequest() {
return "Ping OK !" + cd.getTestValue();
}
Related
I am building a REST API to access a database and having trouble / consistently getting a whitepage error. Running in circles trying to find my error and/or my error in the flow or logic of the program.
Here is my application:
package com.skilldistillery.myRest;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
#SpringBootApplication
#ComponentScan(basePackages= {"com.skilldistillery.edgemarketing"})
#EntityScan("com.skilldistillery.edgemarketing")
#EnableJpaRepositories("com.skilldistillery.myRest.repositories")
public class MyRestApplication extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(MyRestApplication.class);
}
public static void main(String[] args) {
SpringApplication.run(MyRestApplication.class, args);
}
}
My controller:
package com.skilldistillery.myRest.controllers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.skilldistillery.edgemarketing.entities.House;
import com.skilldistillery.myRest.services.HouseService;
#RestController
#RequestMapping("api")
#CrossOrigin({ "*", "http://localhost:4200" })
public class HouseController {
#Autowired
HouseService houseServ;
#GetMapping("index/{id}")
public House show(#PathVariable("id") Integer id) {
return houseServ.show(id);
}
}
My repo:
package com.skilldistillery.myRest.repositories;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.skilldistillery.edgemarketing.entities.House;
#Repository
public interface HouseRepo extends JpaRepository<House, Integer> {
}
My service:
package com.skilldistillery.myRest.services;
import java.util.List;
import org.springframework.stereotype.Service;
import com.skilldistillery.edgemarketing.entities.House;
#Service
public interface HouseService {
List<House> index();
House show(Integer id);
}
And my ServiceImpl:
package com.skilldistillery.myRest.services;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.skilldistillery.edgemarketing.entities.House;
import com.skilldistillery.myRest.repositories.HouseRepo;
#Service
public class HouseServiceImpl {
#Autowired
HouseRepo hRepo;
public House show(Integer id) {
Optional<House> opt = hRepo.findById(id);
House house = null;
if (opt.isPresent()) {
house = opt.get();
}
return house;
}
}
It compiles and launches but via postman and browser, I am getting whitepage errors. I've scoured the internets trying to understand where I'm going wrong but not finding it. Please advise.
You can use the following solution.
Change your main class to the following code
#SpringBootApplication
public class MyrestapplicationApplication {
public static void main(String[] args) {
SpringApplication.run(MyrestapplicationApplication.class, args);
}
}
Then create a separate class for your configurations.As well as running away from tight coupled architecture.
#Configuration
#EntityScan("com.skilldistillery.edgemarketing.entities")
#EnableJpaRepositories("com.skilldistillery.myRest.repositories")
public class BusinessConfig {
#Bean
public HouseService houseService(final HouseRepo houseRepo){
return new HouseServiceImpl(houseRepo);
}
}
Your controller will then change to the following.Utilising Dependency Injection
#RestController
#RequestMapping("api")
#CrossOrigin({ "*", "http://localhost:4200" })
public class HouseController {
private HouseService houseServ;
public HouseController(HouseService houseServ) {
this.houseServ = houseServ;
}
#GetMapping(value = "index/{id}",produces = MediaType.APPLICATION_JSON_VALUE,consumes = MediaType.APPLICATION_JSON_VALUE)
public House show(#PathVariable("id") Integer id) {
return houseServ.show(id);
}
}
HouseServiceImpl should also implement HouseService
public class HouseServiceImpl implements HouseService{
private HouseRepo hRepo;
public HouseServiceImpl(HouseRepo hRepo) {
this.hRepo = hRepo;
}
#Override
public List<House> index() {
return null;
}
public House show(Integer id) {
Optional<House> opt = hRepo.findById(id);
House house = new House();
if (opt.isPresent()) {
house = opt.get();
}
return house;
}
}
*NB - don't forget to remove the following configs #Autowired,#Repository as they are now handled within the BusinessConfig class.More Beans can be defined in the BusinessConfig Class
I have encountered a very strange exception when I was learning AOP in Spring.
Here are my codes:
CompactDisc Interface:
public interface CompactDisc {
void play();
}
BlankDisc class:
import org.springframework.stereotype.Component;
import java.util.List;
#Component
public class BlankDisc implements CompactDisc {
private String title;
private String artist;
private List<String> tracks;
public BlankDisc(String title, String artist, List<String> tracks) {
this.title = title;
this.artist = artist;
this.tracks = tracks;
}
#Override
public void play() {
System.out.println("Playing "+title+" by "+artist);
for (String track : tracks)
System.out.println("-Track "+track);
}
public void playTrack(int trackNumber) {
System.out.println("-Track "+tracks.get(trackNumber));
}
TrackCounter class
package soundsystem;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import java.util.HashMap;
import java.util.Map;
#Aspect
public class TrackCounter {
private Map<Integer,Integer> trackCounts = new HashMap<>();
#Pointcut("execution(* BlankDisc.playTrack(int)) " +
"&& args(trackNumber)")
public void trackPlayed(int trackNumber) {}
#Before("trackPlayed(trackNumber)")
public void countTrack(int trackNumber) {
int currentCount = getPlayCount(trackNumber);
trackCounts.put(trackNumber, currentCount + 1);
}
public int getPlayCount(int trackNumber) {
return trackCounts.getOrDefault(trackNumber, 0);
}
}
TrackCounterConfig class
package soundsystem;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import java.util.ArrayList;
import java.util.List;
#Configuration
#EnableAspectJAutoProxy
public class TrackCounterConfig {
#Bean(name = "compactDisc")
public BlankDisc blankDisc() {
List<String> list= new ArrayList<>(5);
list.add("Sgt.Pepper's Lonely Hearts Club Band");
list.add("With a Little Help from My Friends");
list.add("Lucy in the Sky with Diamonds");
list.add("Getting Better");
list.add("Fixing a Hole");
return new BlankDisc("Sgt.Pepper's Lonely Hearts Club Band","The Beatles",list);
}
#Bean
public TrackCounter trackCounter() {
return new TrackCounter();
}
}
TrackCounterTest class:
import static org.junit.Assert.*;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import soundsystem.*;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = TrackCounterConfig.class)
public class TrackCounterTest {
#Autowired
private BlankDisc cd;
#Autowired
private TrackCounter counter;
#Test
public void testTrackCounter() {
//Question: cd must be CompactDisc not BlankDisc,
//otherwise will cause type exception, why?
cd.playTrack(0);
cd.playTrack(1);
cd.playTrack(2);
cd.playTrack(3);
cd.playTrack(3);
cd.playTrack(3);
cd.playTrack(3);
cd.playTrack(4);
assertEquals(1,counter.getPlayCount(1));
assertEquals(1,counter.getPlayCount(0));
assertEquals(1,counter.getPlayCount(2));
assertEquals(4,counter.getPlayCount(3));
assertEquals(1,counter.getPlayCount(4));
}
}
I ran the test,then I had the exception:
Caused by: org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'compactDisc' is expected to be of type 'soundsystem.BlankDisc' but was actually of type 'com.sun.proxy.$Proxy22'
at org.springframework.beans.factory.support.DefaultListableBeanFactory.checkBeanNotOfRequiredType(DefaultListableBeanFactory.java:1510)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1489)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1104)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1066)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:585)
The problems are:
1.If I change the type of cd in TrackCounterTest from BlankDisc to CompactDisc, the exception will not occur, why?
2.After the first problem being solved, if I haven't declared the method void playTrack(int) in the interface CompactDisc, the assertEquals() in test class will fail. That is, AOP does not work, why?
I have been spent a whole day working on this problem and after searching on the google I still could not be able to find the answer. Since I am a rookie in Spring, I sincerely hope that someone could help me to figure this out.
Thank you!!!
This issue is because of autowiring the bean of type class instead do for implemented interface:
Please check this stackoverflow link which explains the same issue:
BeanNotOfRequiredTypeException issue stackoverflow explanation
This question already has answers here:
Why java classes do not inherit annotations from implemented interfaces?
(2 answers)
Closed 5 years ago.
Simple application - Application.java
package hello;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Simple interface - ThingApi.java
package hello;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
public interface ThingApi {
// get a vendor
#RequestMapping(value = "/vendor/{vendorName}", method = RequestMethod.GET)
String getContact(#PathVariable String vendorName);
}
Simple controller - ThingController.java
package hello;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class ThingController implements ThingApi {
#Override
public String getContact(String vendorName) {
System.out.println("Got: " + vendorName);
return "Hello " + vendorName;
}
}
Run this, with your favorite SpringBoot starter-parent.
Hit it with GET /vendor/foobar
and you will see:
Hello null
Spring thinks that 'vendorName' is a query parameter!
If you replace the controller with a version that does not implement an interface and move the annotations into it like so:
package hello;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class ThingController {
#RequestMapping(value = "/vendor/{vendorName}", method = RequestMethod.GET)
public String getContact(#PathVariable String vendorName) {
System.out.println("Got: " + vendorName);
return "Hello " + vendorName;
}
}
It works fine.
So is this a feature? Or a bug?
You just missed #PathVariable in your implement
#Override
public String getContact(#PathVariable String vendorName) {
System.out.println("Got: " + vendorName);
return "Hello " + vendorName;
}
I'm logging method input and output parameters by a simple Aspect.
package com.mk.cache;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.util.Arrays;
#Aspect
#Component
public class LoggingAspect {
#Around("within(#com.mk.cache.LoggedIO *) && execution(* *(..))")
public Object logAroundPublicMethod(ProceedingJoinPoint joinPoint) throws Throwable {
String wrappedClassName = joinPoint.getSignature().getDeclaringTypeName();
Logger LOGGER = LoggerFactory.getLogger(wrappedClassName);
String methodName = joinPoint.getSignature().getName();
LOGGER.info("LOG by AOP - invoking {}({})", methodName, Arrays.toString(joinPoint.getArgs()));
Object result = joinPoint.proceed();
LOGGER.info("LOG by AOP - result of {}={}", methodName, result);
return result;
}
}
which is attached by this Annotation.
package com.mk.cache;
public #interface LoggedIO {
}
I use this mechanism to log inputs and outputs of methods like this (notice #LoggedIO):
package com.mk.cache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
#Service
#LoggedIO
public class CachedService {
private static final Logger LOGGER = LoggerFactory.getLogger(CachedService.class);
#Cacheable("myCacheGet")
public int getInt(int input) {
LOGGER.info("Doing real work");
return input;
}
}
I also use Spring Cache. Here is the example application.
package com.mk.cache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
#SpringBootApplication
#EnableCaching
public class CacheApplication implements CommandLineRunner {
private static final Logger LOGGER = LoggerFactory.getLogger(CacheApplication.class);
public static void main(String[] args) {
SpringApplication.run(CacheApplication.class, args);
}
#Autowired
private CachedService cachedService;
#Override
public void run(String... args) throws Exception {
LOGGER.info("cachedService.getInt(1)={}", cachedService.getInt(1));
LOGGER.info("cachedService.getInt(1)={}", cachedService.getInt(1));
}
}
The output looks like this:
LOG by AOP - invoking getInt([1])
Doing real work
LOG by AOP - result of getInt=1
cachedService.getInt(1)=1
cachedService.getInt(1)=1
My problem is, that when I call LOGGER.info("cachedService.getInt(1)={}", cachedService.getInt(1)); for the second time, the cached value is used, but the input and output parameters are not logged, as the cache is the first wrapper. Is it possible to somehow configure the LoggingAspect to be the first wrapper, so I will be able to use both AOP logging and both Spring Cache?
Just implement spring Ordered interface and in getOrder() method return 1.
#Aspect
#Component
public class LoggingAspect implements Ordered {
#Around("within(#com.mk.cache.LoggedIO *) && execution(* *(..))")
public Object logAroundPublicMethod(ProceedingJoinPoint joinPoint) throws Throwable {
String wrappedClassName = joinPoint.getSignature().getDeclaringTypeName();
Logger LOGGER = LoggerFactory.getLogger(wrappedClassName);
String methodName = joinPoint.getSignature().getName();
LOGGER.info("LOG by AOP - invoking {}({})", methodName, Arrays.toString(joinPoint.getArgs()));
Object result = joinPoint.proceed();
LOGGER.info("LOG by AOP - result of {}={}", methodName, result);
return result;
}
#Override
public int getOrder() {
return 1;
}
}
Read more here. Chapter 11.2.7
I am vainly trying to read an array of strings from the application.yml.
Both the Environment and the #Value annotation, always return null.
Everything works if I read an item, instead of the entire array.
Here the code:
Sources
Boot Application and Rest Controllers
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
#RestController
class WithEnvCtrl {
#Autowired
private Environment env;
#RequestMapping(value = "/with_env", method = { RequestMethod.GET, RequestMethod.POST }, produces = "application/json")
public String test() {
System.err.println(env.getProperty("this.is.array[0]"));
System.err.println(env.getProperty("this.is.array", List.class));
System.err.println(env.getProperty("this.is.array", String[].class));
return env.getProperty("this.is.array[0]");
}
}
#RestController
class WithValueAnnotation {
#Value("${this.is.array[0]}")
private String first;
#Value("${this.is.array}")
private List<String> list;
#Value("${this.is.array}")
private String[] array;
#RequestMapping(value = "/with_value_annotation", method = { RequestMethod.GET, RequestMethod.POST }, produces = "application/json")
public String test() {
System.err.println(first);
System.err.println(list);
System.err.println(array);
return first;
}
}
application.yml file
this:
is:
array:
- "casa"
- "pesenna"
Results
The WithEnvCtrl.test method prints:
casa
null
null
null
The WithValueAnnotation.test method correctly sets the variable first with the first element of the array (casa). However, the annotations #Value on the attributes list and array cause the exception:
java.lang.IllegalArgumentException: Could not resolve placeholder 'this.is.array' in string value "${this.is.array}"
Here is an example project: property-array.
Many thanks in advance!
Solved by:
using the annotation #ConfigurationProperties;
declaring an attribute with the same name as the yml property;
defining the get method for the attribute;
initializing the attribute or defining the set method.
Here the code:
import java.util.List;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
#RestController
#ConfigurationProperties(prefix="this.is")
class WithValueAnnotation {
private List<String> array;
public List<String> getArray(){
return this.array;
}
public void setArray(List<String> array){
this.array = array;
}
#RequestMapping(value = "/test_cfg", method = { RequestMethod.GET,
RequestMethod.POST }, produces = "application/json")
public String test() {
System.err.println(array);
return array.toString();
}
}
Thanks #Quagaar.