How to create an aspect class that will implement the logging functionality. Logged classes/packages/methods need to be defined in the configuration file.
#Aspect
public class LoggingAspect {
private static final Logger log = LoggerFactory.getLogger(LoggingAspect.class);
#Autowired
private List<PackageProperties> packageProperties;
#Pointcut("execution(* org.epam.operations.Operations.removeStudentFromList())")
public void removeStudentLog() {
}
#After("removeStudentLog()")
public void applicationLogger() {
log.info("Student deleted");
}
}
application.properties
remove.packageName = org.epam.operations
remove.className = Operations
remove.methodName = removeStudentFromList
add.packageName = org.epam.operations
add.className = Operations
add.methodName = addStudent
For loading the value from properties file you have many ways, which mostly are different in initialization order in IOC container, two of them are as follow
Implement EnvironmentAware interface
public class YourAspectClass implements EnvironmentAware {
private Environment environment;
#Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
}
Using of #Value annotation to access property .
package org.springframework.beans.factory.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Documented
public #interface Value {
String value();
}
#Value("#{pointer_of_property}") String value
Related
In a Spring-Boot project, I use #ConditionalOnProperty to choose whether some Beans get loaded or not. It looks like the following:
#ConditionalOnProperty(
prefix = "myservice",
name = "implversion",
havingValue = "a"
)
#Service
public class MyServiceImplA implements MyService {
// ...
}
This allows me to choose with specific profiles which Bean should be loaded, for example different implementations of an interface, depending on the value of myservice.implversion being a or b or whatever other value.
I'd like to achieve the same effect with a user-friendlier annotation like such:
#OnMyServiceVersion(value = "a")
#Service
public class MyServiceImplA implements MyService {
// ...
}
How can one do this?
I've tried annotating my custom annotation with #Conditional and implementing the Condition interface but I don't understand how to check properties that way. The Spring-Boot OnPropertyCondition extends SpringBootCondition is not public so I cannot start from there, and extending annotations isn't allowed, so I'm kind of stuck.
I've also tried the following with no success:
// INVALID CODE, DO NOT USE
#Target({ElementType.TYPE, ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
#ConditionalOnProperty(
prefix = "myservice",
name = "implversion",
havingValue = OnMyServiceVersion.value()
)
public #interface OnMyServiceVersion {
String value();
}
You can annotate your #OnMyServiceVersion annotation with #ConditionalOnProperty and alias the value of your annotation to the havingValue attribute of #ConditionalOnProperty:
#Documented
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.TYPE, ElementType.METHOD })
#ConditionalOnProperty(prefix = "myservice", name = "implversion")
public #interface OnMyServiceVersion {
#AliasFor(annotation = ConditionalOnProperty.class, attribute = "havingValue")
String value() default "";
}
Here's a complete example that shows this in action:
package com.example.demo;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.core.annotation.AliasFor;
import org.springframework.stereotype.Service;
#SpringBootApplication
public class CustomPropertyConditionApplication {
public static void main(String[] args) {
SpringApplication.run(CustomPropertyConditionApplication.class, "--myservice.implversion=b");
}
#Service
#OnMyServiceVersion("a")
static class ServiceA {
ServiceA() {
System.out.println("Service A");
}
}
#Service
#OnMyServiceVersion("b")
static class ServiceB {
ServiceB() {
System.out.println("Service B");
}
}
#Documented
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.TYPE, ElementType.METHOD })
#ConditionalOnProperty(prefix = "myservice", name = "implversion")
static #interface OnMyServiceVersion {
#AliasFor(annotation = ConditionalOnProperty.class, attribute = "havingValue")
String value() default "";
}
}
This will output Service B when run. If you change the arguments in the main method to --myservice.implversion=a it will output Service A. If you remove the argument, it won't output either.
#Bean(name = "emailNotification")
#ConditionalOnProperty(prefix = "notification", name = "service")
public NotificationSender notificationSender() {
return new EmailNotification();
}
for reference
https://www.baeldung.com/spring-conditionalonproperty
I am trying to create an annotation and pass a variable as parameter to the impl method. Below is my code to create an annotation.
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.validation.Constraint;
#Documented
#Target(ElementType.PARAMETER)
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = Loggable1Impl.class)
public #interface Loggable1 {
String message() default "Invalid userId";
}
Below is my impl class
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class Loggable1Impl implements ConstraintValidator<Loggable, String> {
public void initialize(Loggable contactNumber) {
}
public boolean isValid(String generatedUserId, ConstraintValidatorContext cxt) {
System.out.println("generatedUserId in Loggable1 :: " + generatedUserId);
return true;
}
}
I am trying to use it in a spring-boot application
#CrossOrigin(origins = "*", allowedHeaders = "*")
#PostMapping("/api1/{user}")
public Topic regiterUser(#PathVariable String user ) {
String generatedUserId = user + "1";
return generatedUserId ;
}
I want to pass generatedUserId to my annotaion Loggable1
Please assist
I have a random class in a random package that is loaded through reflection after the app launches, is there a way for it to be registered as a component under springboot and have annotations such as #Autowired and #Value etc work for that class.
It works when it is in the same package at launch time, but if introduce it thorough another jar at runtime (same package or not) it doesn't work.
Below are samples that don't work even if it is in the same jar. I can't change the app's configuration - it would defeat the "random package/random class" objective.
Code in Spring boot application package
package sample.app
#SpringBootApplication
public class Application {
public static void main(String[] args) {
// Code that starts app
//
//
try {
Thread.sleep(7000);
Class test = Class.forName("test.Test", true, Application.class.getClassLoader());
System.out.println(test.getMethod("getName").invoke(null)); //NPE
System.out.println(test.getMethod("getProfiles").invoke(null)); //NPE
} catch (Throwable t) {
t.printStackTrace();
}
}
}
Test.java
package test;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.DependsOn;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Map;
#DependsOn("blaaaaaaaah")
#ComponentScan
public class Test {
#DependsOn("blaaaaaaaah")
public static String getName() {
return SpringGetter.instance.getApplicationName();
}
#DependsOn("blaaaaaaaah")
public static String[] getProfiles() {
String[] profiles = SpringGetter.instance.getEnv().getActiveProfiles();
if (profiles == null || profiles.length == 0) {
profiles = SpringGetter.instance.getEnv().getDefaultProfiles();
}
return profiles;
}
}
SpringGetter.java
package test;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
#Component("blaaaaaaaah")
public class SpringGetter implements InitializingBean {
public static SpringGetter instance;
#Value("${spring.application.name}")
private String applicationName;
#Autowired
private Environment env;
public SpringGetter() {
System.out.println("consASFJEFWEFJWDNFWJVNJSBVJWNCJWBVJNVJNVJSNJSNCSDJVNSVJtruct");
}
public String getApplicationName() {
return applicationName;
}
public void setApplicationName(String applicationName) {
this.applicationName = applicationName;
}
public Environment getEnv() {
return env;
}
public void setEnv(Environment env) {
this.env = env;
}
#PostConstruct
public void setInstance() {
instance = this;
}
#Override
public void afterPropertiesSet() throws Exception {
instance = this;
}
}
EDIT:
I managed to dynamically create the SpringGetter class as part of the same package as the Application class(the one with the #SpringBootApplication). I got Test.java to point to that dynamic class and yet no luck.
To simply inject fields into a POJO as if it were a Spring-managed bean, you can use something like the following:
#Component
public class BeanInitializer implements ApplicationContextAware {
private AutowireCapableBeanFactory beanFactory;
#Override
public void setApplicationContext(final ApplicationContext applicationContext) {
beanFactory = applicationContext.getAutowireCapableBeanFactory();
}
public void initializeObject(Object pojo) {
beanFactory.autowireBean(pojo);
}
}
Note, however, that this only injects fields marked as #Autowired or #Injected. It does not create proxies that honor method interception strategies based on e.g. #Transactional, #Async, etc.
If you're using Spring 5, have a look at the registerBean() method from GenericApplicationContext. You can find an example here: https://www.baeldung.com/spring-5-functional-beans
The issue in your Test class may also be that you're not loading the Spring Boot context from the main class. You can use the SpringBootTest annotation for this.
I wrote a custom validation to validate an id (Which is a path Param) as UUID in my #GET method of #RestController but this validation doesn't seem to be working. Even during the debug the control doesn't go to custom validation.
#RestController
#RequestMapping("/rateplan")
#Validated
public class RatePlanServiceController {
#RequestMapping(value = "/{ratePlanId}", method = RequestMethod.GET)
#ResponseStatus(HttpStatus.OK)
public void get(#UUID #PathVariable("ratePlanId") String ratePlanId) {
loggerFactory.warn("Get with Rate plan id {}", ratePlanId);
loggerFactory.info("Get with Rate plan id {}", ratePlanId);
loggerFactory.error("Get with Rate plan id {}", ratePlanId);
loggerFactory.debug("Get with Rate plan id {}", ratePlanId);
// return iRatePlanService.getRatePlan(ratePlanId);
}
}
I wrote the custom annotation for validation of UUID as follow.
import org.springframework.stereotype.Component;
import javax.validation.Constraint;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.regex.Pattern;
#Target(ElementType.PARAMETER)
#Constraint(validatedBy = {UUID.IdValidator.class})
#Retention(RetentionPolicy.RUNTIME)
public #interface UUID {
String message() default "{invalid.uuid}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
#Component
public class IdValidator implements ConstraintValidator<UUID, String> {
private static final Pattern id_PATTERN =
Pattern.compile("^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$");
public boolean isValid(String value, ConstraintValidatorContext context) {
if (!(id_PATTERN.matcher(value).matches())) {
return false;
}
return true;
}
public void initialize(UUID parameters) {
}
}
}
Can anyone let me know why is it not working. Even if I provide a garbage ratePlanId like '345%#7^34' it able to go inside GET method.
Solved this by adding a bean in Application configuration file. To validate a path Param in Spring you need to add this bean in your configuration class.
#Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
I try to access BeanParam, But not possible by my side because to many events access old version jar present on classpath so I can't remove jar files from classpath.
I tried to many way but finally,
I decided create own BeanParam using these links:-
javatpoint |
Annotation_Type_BeanParam_From_Oracle |
Source_code_of_the_class_BeanParam
There are code:-
My BeanParam Interface
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
#Documented
public #interface myBeanParam {
}
My Model Class
import javax.ws.rs.QueryParam;
public class ControllerFilterBean {
#QueryParam("id") String row_id;
#QueryParam("ot") String objectType;
public String getRow_id() {
return row_id;
}
public void setRow_id(String row_id) {
this.row_id = row_id;
}
public String getObjectType() {
return objectType;
}
public void setObjectType(String objectType) {
this.objectType = objectType;
}
}
my resources
public class MyResources {
#GET
#Path("/do/beanTest")
#Produces("application/json")
public Response beanTest(#myBeanParam ControllerFilterBean bean/*
#QueryParam("id") String row_id,
#QueryParam("ot") String objectType*/){
System.out.println(bean.objectType);
System.out.println(bean.row_id);
/*System.out.println(row_id);
System.out.println(objectType);*/
ResponseBuilder rBuild = Response.status(Response.Status.OK);
return Build.type(MediaType.APPLICATION_JSON).entity("OK").build();
}
}
then finally want access my resource class then its throw
WARNING: No message body reader has been found for request
I want help in my code where it's getting mistake.