I am having some difficulties to do something I thought would be easy.
I have a service:
#Service("tr_v1_mapper")
#RequiredArgsConstructor
#Slf4j
public class VoneMapper implements GenericMapper{
// Mapping code ...
// Service name
String serviceName = "tr_v1_mapper"
}
Basically, I want to stock my service name in a String variable inside my class VoneMapper without harcoding it.
is it something possible ?
Thanks by advance for your help.
PM
What about using a constant in the annotation? More info here: https://www.baeldung.com/java-annotation-attribute-value-restrictions
#Service(VoneMapper.SERVICE_NAME)
#RequiredArgsConstructor
#Slf4j
public class VoneMapper implements GenericMapper {
// Mapping code ...
// Service name
static final String SERVICE_NAME = "tr_v1_mapper";
}
Related
I am currently using Spring Cloud Function, and I want to deploy my GET function on AWS Lambda, using the adapter for AWS.
Till now all the Spring Cloud Function implemented was a single POST function with the following structure.
This was done using the
SpringBootRequestHandler.
The DemoRequest class was a POJO class defined to accept the request body and DemoResponse is for the response.
I understand that this part of the code is Depricated.
Interface to define the Endpoint:
#SuppressWarnings("hiding")
#FunctionalInterface
public interface Demoapi<DemoRequest, DemoResponse>{
#PostMapping(value = "/v1/demo")
public ResponseEntity<DemoResponse> demo(#RequestBody DemoRequest demoInfo);
}
Followed by the controller where the #RestController was defined
Part of the controller:
#RestController
public class Democontroller implements Demoapi\<DemoRequest, DemoResponse\>{
static final Logger LOGGER = LogManager.getLogger(Democontroller.class);
#Autowired
private DemoService demoService;
#Override
public ResponseEntity<DemoResponse> demo(#RequestBody DemoRequest demoInfo) {
DemoResponse demoResponse=new DemoResponse();
try {
demoResponse = demoService.demofun(demoInfo);
//logic
}
Now I want to modify this for the GET call and this is the modification I am using.
#SuppressWarnings("hiding")
#FunctionalInterfacepublic interface Demoapi<Void, DemoResponse>{ // as there is no request #GetMapping(value = "/v1/demo")public ResponseEntity<DemoResponse> demo(#RequestParam(name="q", required = false defaultValue="...") String name);// the get call has an optional query param}
Followed by the controller where the #RestController was defined
Part of the controller:
#RestControllerpublic class Democontroller implements Demoapi<Void, DemoResponse>{static final Logger LOGGER = LogManager.getLogger(Democontroller.class);
#Autowiredprivate DemoService demoService;
#Overridepublic ResponseEntity<DemoResponse> demo(#RequestParam(name="q", required = false , defaultValue="...") String name) {DemoResponse demoResponse=new DemoResponse();try {demoResponse = demoService.demofun(name);//logic}
This set up works fine when tested in local but when deployed as Lambda ( I provide Handler as com.demo.DemoHandler in the AWS console, and in the Environment Variable under FUNCTION_NAME I give the DemoController class with the Starting letter in small caps i.e demoController ) and the lambda throws the following error when tested in the console as well as directed from an API Gateway:
\*2023-02-17 11:24:04.080 INFO 10 --- \[ main\] lambdainternal.AWSLambda : Started AWSLambda in 2.649 seconds (JVM running for 3.349)
2023-02-17 11:24:04.329 ERROR 10 --- \[ main\] c.f.c.c.BeanFactoryAwareFunctionRegistry : Failed to invoke function 'domainController'
java.lang.IllegalArgumentException: argument type mismatch
\*
As per error seems like Handler going to find #RestController 'domainController'
In your code there is all Democontroller
The bean for the demoController not defined properly.
I have a simple problem - SpringBootApplication doesn't see my controller - what's more weird - only one of three.
I have UserController, WalletController and DashboardController - this one is not visible for my application.
What I have already done is:
Every package with controller is under the main package, where my SpringBootApplication.class is,
I tried annotate main SpringBootApplication.class with #ComponentScan both with basePackages and basePackageClasses,
There is no other beans - which should be annotated #Component, I removed them and moved methods to my DashboardService.class
This is my controller, which is not visible:
DashboardController
And this is my package structure(seems to be right): Package Structure
Thank You for help!
EDIT:
It might be important, that I use the third-party api to get the data I need
In that methods I use url:
private String getNbpJson(String url) {
return new RestTemplate().getForObject(url, String.class);
}
private CurrentRateDTO getCurrentExchangeRate(String json) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readTree(json);
String code = node.get("code").toString();
String date = node.get("rates").get(0).get("effectiveDate").toString();
double bid = node.get("rates").get(0).get("bid").asDouble();
double ask = node.get("rates").get(0).get("ask").asDouble();
return new CurrentRateDTO(code, date, bid, ask);
}
And then in ExchangeService this is my url
#Service
public class ExchangeRateService implements IExchangeRateService {
private static final Logger logger = LoggerFactory.getLogger(ExchangeRateService.class);
private String NBP_CURRENT_RATE_URL = "http://api.nbp.pl/api/exchangerates/rates/c/eur/2020-12-11/?format=json";
What's more... when I move methods from DashboardController to WalletController (which works)
Another thing that I have found out is that only methods, which make use of the third-party api don't work.
Basically, I retrieve data from the url above - I get the specific fields, create an objects with filled fields.
May it be a problem with retrieving data from the third-party and then implementing it in my app?
I have no more ideas for now...
Maybe your controller is registered but you type a slightly different url. Try this property logging.level.org.springframework.web.servlet.mvc.method.annotation: TRACE
and check on application startup if the controller is registered under some other url.
Try to add this to your controller
#RestController()
#RequestMapping("exchangerates")
Okay, I probably found out the problem... Before I have 3 controllers:
#RestController("/api")
public class UserController {}
#RestController("/wallets")
public class WalletController {}
#RestController("/exchangerates")
public class DashboardController {}
I changed the above to
#RestController("/api")
public class UserController {}
#RestController
public class WalletController {}
#RestController
public class DashboardController {}
So, basically I removed base ?endpoint? for each controller and now it works... It seems like basic endpoints in three RestControllers is too much and the third one is not available, but I don't know is it truth (I bet that it's not).
Why that happened then, can anybody explain that behaviour of controllers?
Thank You for explanantion.
Given two (or more) implementations of a particular service API, what's the best way to pick which one to use at runtime in my app based on an application property?
Example API:
public interface Greeting {
String sayHello(String username);
}
Implementations:
public class FriendlyGreeting implements Greeting {
public String sayHello(String username) {
return "Hello, " + username;
}
}
public class HostileGreeting implements Greeting {
public String sayHello(String username) {
return "Go away, " + username;
}
}
I've got a separate service class with an #Autowired constructor that takes an instance of Greeting. What I want, is based upon a configuration property, to decide which greeting implementation gets injected and used. I came up with using a configuration class to make that decision:
#Configuration
public class GreetingConfiguration {
private String selection;
#Autowired
public GreetingConfiguration(#Value("${greeting.type}") String type) {
this.selection = type;
}
#Bean
public Greeting provideGreeting() {
if ("friendly".equals(selection)) {
return new FriendlyGreeting();
} else {
return new HostileGreeting();
}
}
}
Is this the right way to do what I want? I went down the road of using #Qualifier on the implementations, and ended up with a mess where Spring saw 3 instances of my Greeting API, and I needed a configuration anyway to pick which implementation to use and return it with a unique qualifier name on it, and that feels worse than what I settled on.
You can mark both Greeting as #Service and select the chosen one with #Qualifier("yourServiceHere") like this:
#Autowired
#Qualifier("friendlyGreeting")
private Greeting greeting;
Another way you can do it is with profile. You can mark your FriendlyGreeting service with #Service and #Profile("friendly") and the HostileGreeting service with #Service and #Profile("hostileGreeting") and just put in the application.properties the following:
spring.profiles.active=friendly
Answering my own question.
#Compass and #user268396 were correct - using Profiles got this working as expected.
I created both implementations, annotated with #Service and #Profile("friendly") or #Profile("hostile"), and could change the property spring.profiles.active to dev,friendly for example, and get what I wanted.
You can use #Conditional annotations described at https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/Conditional.html and https://reflectoring.io/spring-boot-conditionals/
#Profile annotations mentioned above are based upon #Conditional(from Spring Framework); see also Spring Boot: org.springframework.boot.autoconfigure.condition
Here is a full solution using ideas mentioned by David and Vitor above with #Profile and #Qualifer annotations.
Two beans with same name but Only one is activated based on which profile is defined.
#Profile("profile1")
#Bean("greeting")
public class FriendlyGreeting implements Greeting {
---
#Profile("profile2")
#Bean("greeting")
public class HostileGreeting implements Greeting {
---
#Configuration
public class GreetingConfiguration {
private Greeting greeting;
#Autowired
public GreetingConfiguration(#Qualifier("greeting") Greeting greeting) {
this.greeting = greeting;
}
}
Notes:
you can remove the intermediate class GreetingConfiguration and stick the "greeting" bean wherever you need
i prefer the #Autowired on the constructor instead of the class member to make it easier for unit testing.
Please find the code below for application.yml
decrypt: /Users/Blahblah/Bleh
The above property we're trying to read into a Class please find the code for PropertyLoader.java
#Configuration
#Component
public class PropertyLoader implements InitializingBean{
#Value("${decrypt}")
private String decryptPath;
<--->
}
the value decryptPath is always null. Can anyone tell me what is wrong with the code?
Firstly application.yml should be under the src/main/resources/application.yml.
If you want to use this variables in constructor, you don't. Because spring inject #Value annotated variables after the construction.But if you want to do in constructer you can do like :
public class PropertyLoader implements InitializingBean{
private String decryptPath;
public PropertyLoader(#Value("${decrypt}") decrypPath) {
this.decryptPath = decryptPath;
}
}
It turns out since this class is implementing InitializingBean, the properties object won't be initialized until this class has completed execution. The #Value will always return null.
Most of my classes wired up look something like:
#Component
public class MyClassImpl implements MyClass {
private MyService service;
#Autowired
public MyClass(MyService service) {
this.service = service;
}
}
So that makes sense to me, but if I want to do something like this:
#Component
public class MyClassImpl implements MyClass {
private MyService service;
private String id; // this is what I need
#Autowired
public MyClass(MyService service, String id) {
this.service = service;
this.id = id;
}
}
But the problem is String id is not known until runtime. Is there any way to do this? From what I can tell, Spring checks all the dependencies by default at runtime so if I try the second example, it complains about the constructor arguments.
I've seen some examples where you can use a factory to create the actual value later down the line. Or I can create getters and setters in order to set the id when I need it, but that would also mean I'd need to add those getters and setters to the interface MyClass as well. Is there a cleaner way to do this?
You can create a #Bean method for id with logic needed to calculate it.
#Configuration
public class Config {
#Bean
public String idForService() {
return calculateId();
}
}
And then your service constructor will be look like this
#Autowired
public MyClass(MyService service, #Qualifier("idForService") String id) {
this.service = service;
this.id = id;
}
But as others have already mentioned it looks like a bad practice so you'd better consider to redesign your service class.
I am afraid what you are trying to do is not in the spirit of what Spring was created for. Please do have a look at: Runtime dependency injection with Spring
Having said that, there might be a workaround to your problem.
If you have some class that calculates that ID for you, just inject that class in a constructor and bind the result of its calculation to the field id in your class.