when I use a aspect on an annotation, I cannot use AnnotationUtils.getAnnotation,
//here, I cannot not find PulsarListener
PulsarListener annotation = AnnotationUtils.getAnnotation(method, PulsarListener.class);
and when i remove #Aspect ,then it's ok.
#Target({ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
#Documented
public #interface PulsarListener {
String[] topics() default {};
}
#Aspect
#Slf4j
#Component
#Order(0)
public class MDCAspect {
#Around("#annotation(PulsarListener)")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
try {
String requestUUID = MDC.get("requestUUID");
if (StringUtils.isEmpty(requestUUID)) {
String uid = ObjectId.get().toHexString();
MDC.put("requestUUID", uid);
}
return joinPoint.proceed();
} finally {
MDC.clear();
}
}
}
#Component
#Slf4j
public class PulsarConsumer {
#PulsarListener(topics = "${topics}")
public void listen(Message<byte[]> receive) {
//doSomething
}
}
public class PulsarPostProcessor implements BeanPostProcessor {
#Value("${pulsar.service.url}")
private String pulsar_service_url;
#Autowired
private ApplicationContext applicationContext;
#Override
public Object postProcessAfterInitialization(final Object bean, final String beanName) throws BeansException {
Method[] methods = bean.getClass().getDeclaredMethods();
for (Method method : methods) {
//here , i canot not found PulsarListener
//here is the problem
PulsarListener annotation = AnnotationUtils.getAnnotation(method, PulsarListener.class);
if (annotation != null) {
if (log.isDebugEnabled()) {
log.debug("bean :{},method:{}", beanName, method.getName());
}
}
}
}
I am using ValdationSchemaFactoryWarapper class to generate validation properties in JSON schema and I need to add my own custom properties to the schema too. If I develop my custom SchemaFactoryWrapper, I can only use one or the other but not the two at the same time.
I was able to achieve what I wanted by extending ValidationSchemaFactoryWrapper by using the below code, however, I was wondering whether there is perhaps a simpler way to do it, i.e. to use two separate instances of SchemaFactoryWrapper instances.
public class ConfigSchemaExtension {
public static class ConfigSchemaFactoryWrapper extends ValidationSchemaFactoryWrapper {
private static class SchemaFactoryWrapperFactory extends WrapperFactory {
private SchemaFactoryWrapperFactory() {
}
public SchemaFactoryWrapper getWrapper(SerializerProvider p) {
SchemaFactoryWrapper wrapper = new ConfigSchemaFactoryWrapper();
wrapper.setProvider(p);
return wrapper;
}
public SchemaFactoryWrapper getWrapper(SerializerProvider p, VisitorContext rvc) {
SchemaFactoryWrapper wrapper = new ConfigSchemaFactoryWrapper();
wrapper.setProvider(p);
wrapper.setVisitorContext(rvc);
return wrapper;
}
}
public ConfigSchemaFactoryWrapper() {
super(new AnnotationConstraintResolver());
schemaProvider = new ConfigJsonSchemaFactory();
visitorFactory = new FormatVisitorFactory(new SchemaFactoryWrapperFactory());
}
}
public static class ConfigJsonSchemaFactory extends JsonSchemaFactory {
#Override
public com.fasterxml.jackson.module.jsonSchema.types.StringSchema stringSchema() {
return new ConfigStringSchema();
}
}
public static class ConfigStringSchema extends StringSchema {
#JsonProperty
private String myLink;
#Override
public void enrichWithBeanProperty(BeanProperty beanProperty) {
super.enrichWithBeanProperty(beanProperty);
Reference ref = beanProperty.getAnnotation(Reference.class);
if(ref != null) this.myLink = ref.value();
}
}
#Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#JacksonAnnotation
public #interface Reference
{
String value() default "";
}
}```
I have myannotation and whenever my method(which has myannotation) is executed then AOP should be called but which is not working in my spring boot controller.But which is working for methods which has other annotations.Please help me to understand about what happens.
Update: MyAnnotation
#Retention(RUNTIME)
#Target({ METHOD, CONSTRUCTOR })
public #interface MyAnnotation {
}
#Aspect
#Component
public class AnnotationAspect {
private static final String POINTCUT_METHOD1 = "#annotation(com.somepackage.MyAnnotation)";
#Around(POINTCUT_METHOD1)
public Object weaveJoinPoint(ProceedingJoinPoint joinPoint) throws Throwable {
try {
System.out.println("Beforeee " + joinPoint);
joinPoint.proceed();
} finally {
System.out.println("Afterrr " + joinPoint);
}
return null;
}
}
Scenario 1:(Working)
#Controller
#RequestMapping("user")
public class ArticleController {
#GetMapping("article/{id}")
#MyAnnotation // here it is
public ResponseEntity<String> getArticleById(#PathVariable("id") Integer id)
{
return new ResponseEntity<String>(dummyMethod(), HttpStatus.OK);
}
public String dummyMethod() {
System.out.println("Dummy method with MyAnnotation");
return "HelloWorld!";
}
}
Log:(Working)
Beforeee execution(ResponseEntity com.mypackage.getArticleById(Integer))
Dummy method with MyAnnotation
Afterrr execution(ResponseEntity com.mypackage.getArticleById(Integer))
Scenario 2:(Not Working)
#Controller
#RequestMapping("user")
public class ArticleController {
#GetMapping("article/{id}")
public ResponseEntity<String> getArticleById(#PathVariable("id") Integer id)
{
return new ResponseEntity<String>(dummyMethod(), HttpStatus.OK);
}
#MyAnnotation //here it is
public String dummyMethod() {
System.out.println("Dummy method with MyAnnotation");
return "HelloWorld!";
}
}
Log:(Not Working)
Dummy method with MyAnnotation
Scenario 3: (Not Working)
#Service
public class ArticleService {
#MyAnnotation //here it is
public String dummyMethod() {
System.out.println("Dummy method with MyAnnotation");
return "HelloWorld!";
}
}
It might not work because you call dummyMethod() from the same class. Try moving dummyMethod() to another service class. The reason is that calls within the same class does not go though the Spring proxy. The call to getArticleById() is proxied and will be handled by AOP but dummyMethod() might as well be a private method.
I have managed to successfully implement a custom injection annotation with target PARAMETER. I do not understand how I make my the annotation support target METHOD as well though?
Here is my sample code:
Hello annotation:
#Retention(RUNTIME)
#Target({METHOD, PARAMETER})
public #interface Hello {
}
Hello annotation resolver:
#Singleton
public class HelloResolver {
public static class HelloInjectionResolver extends ParamInjectionResolver<Hello> {
public HelloInjectionResolver() {
super(HelloValueFactoryProvider.class);
}
}
#Singleton
public static class HelloValueFactoryProvider extends AbstractValueFactoryProvider {
#Inject
public HelloValueFactoryProvider(final MultivaluedParameterExtractorProvider extractorProvider,
final ServiceLocator injector) {
super(extractorProvider, injector, UNKNOWN);
}
#Override
protected Factory<?> createValueFactory(final Parameter parameter) {
if (!String.class.equals(parameter.getRawType())) return null;
if (parameter.getAnnotation(Hello.class) == null) return null;
return new AbstractContainerRequestValueFactory<String>() {
#Override
public String provide() {
final DateTime now = DateTime.now();
if (22 < now.getHourOfDay() || now.getHourOfDay() < 6) {
throw new WebApplicationException(FORBIDDEN);
} else {
return format("Hello, it is %s o'clock so I am awake! :)", forPattern("HH:mm").print(now));
}
}
};
}
}
public static class Binder extends AbstractBinder {
#Override
protected void configure() {
bind(HelloValueFactoryProvider.class).to(ValueFactoryProvider.class).in(Singleton.class);
bind(HelloInjectionResolver.class).to(
new TypeLiteral<InjectionResolver<Hello>>() {
}
).in(Singleton.class);
}
}
}
Hello resources:
#Path("hello")
public class HelloResource {
#GET
#Path("method")
#Produces(APPLICATION_JSON)
#Hello
public String method() {
return "Hello!";
}
#GET
#Path("param")
#Produces(APPLICATION_JSON)
public String param(#Hello final String hello) {
return hello;
}
}
When I hit
http://localhost:8080/hello/method
I get a Hello! back no matter if the hour is within the forbidden interval.
I am not sure this will work, but you could try this:
public static class HelloInjectionResolver extends ParamInjectionResolver<Hello> {
public HelloInjectionResolver() {
super(HelloValueFactoryProvider.class);
}
public boolean isMethodParameterIndicator() {
return true;
}
}
Warning: I have not tried this myself but in theory that should allow your resolver to work as a parameter in the method.
I want to monitor all public methods of all Classes with specified annotation (say #Monitor) (note: Annotation is at class level). What could be a possible pointcut for this?
Note: I am using #AspectJ style Spring AOP.
You should combine a type pointcut with a method pointcut.
These pointcuts will do the work to find all public methods inside a class marked with an #Monitor annotation:
#Pointcut("within(#org.rejeev.Monitor *)")
public void beanAnnotatedWithMonitor() {}
#Pointcut("execution(public * *(..))")
public void publicMethod() {}
#Pointcut("publicMethod() && beanAnnotatedWithMonitor()")
public void publicMethodInsideAClassMarkedWithAtMonitor() {}
Advice the last pointcut that combines the first two and you're done!
If you're interested, I have written a cheat sheet with #AspectJ style here with a corresponding example document here.
Using annotations, as described in the question.
Annotation: #Monitor
Annotation on class, app/PagesController.java:
package app;
#Controller
#Monitor
public class PagesController {
#RequestMapping(value = "/", method = RequestMethod.GET)
public #ResponseBody String home() {
return "w00t!";
}
}
Annotation on method, app/PagesController.java:
package app;
#Controller
public class PagesController {
#Monitor
#RequestMapping(value = "/", method = RequestMethod.GET)
public #ResponseBody String home() {
return "w00t!";
}
}
Custom annotation, app/Monitor.java:
package app;
#Component
#Target(value = {ElementType.METHOD, ElementType.TYPE})
#Retention(value = RetentionPolicy.RUNTIME)
public #interface Monitor {
}
Aspect for annotation, app/MonitorAspect.java:
package app;
#Component
#Aspect
public class MonitorAspect {
#Before(value = "#within(app.Monitor) || #annotation(app.Monitor)")
public void before(JoinPoint joinPoint) throws Throwable {
LogFactory.getLog(MonitorAspect.class).info("monitor.before, class: " + joinPoint.getSignature().getDeclaringType().getSimpleName() + ", method: " + joinPoint.getSignature().getName());
}
#After(value = "#within(app.Monitor) || #annotation(app.Monitor)")
public void after(JoinPoint joinPoint) throws Throwable {
LogFactory.getLog(MonitorAspect.class).info("monitor.after, class: " + joinPoint.getSignature().getDeclaringType().getSimpleName() + ", method: " + joinPoint.getSignature().getName());
}
}
Enable AspectJ, servlet-context.xml:
<aop:aspectj-autoproxy />
Include AspectJ libraries, pom.xml:
<artifactId>spring-aop</artifactId>
<artifactId>aspectjrt</artifactId>
<artifactId>aspectjweaver</artifactId>
<artifactId>cglib</artifactId>
Something like that:
#Before("execution(* com.yourpackage..*.*(..))")
public void monitor(JoinPoint jp) {
if (jp.getTarget().getClass().isAnnotationPresent(Monitor.class)) {
// perform the monitoring actions
}
}
Note that you must not have any other advice on the same class before this one, because the annotations will be lost after proxying.
Use
#Before("execution(* (#YourAnnotationAtClassLevel *).*(..))")
public void beforeYourAnnotation(JoinPoint proceedingJoinPoint) throws Throwable {
}
it should be enough to mark your aspect method like this:
#After("#annotation(com.marcot.CommitTransaction)")
public void after() {
have a look at this for a step by step guide on this.
You can also define the pointcut as
public pointcut publicMethodInsideAClassMarkedWithAtMonitor() : execution(public * (#Monitor *).*(..));
The simplest way seems to be :
#Around("execution(#MyHandling * com.exemple.YourService.*(..))")
public Object aroundServiceMethodAdvice(final ProceedingJoinPoint pjp)
throws Throwable {
// perform actions before
return pjp.proceed();
// perform actions after
}
It will intercept execution of all methods specifically annotated with '#MyHandling' in 'YourService' class. To intercept all methods without exception, just put the annotation directly on the class.
No matter of the private / public scope here, but keep in mind that spring-aop cannot use aspect for method calls in same instance (typically private ones), because it doesn't use the proxy class in this case.
We use #Around advice here, but it's basically the same syntax with #Before, #After or any advice.
By the way, #MyHandling annotation must be configured like this :
#Retention(RetentionPolicy.RUNTIME)
#Target( { ElementType.METHOD, ElementType.TYPE })
public #interface MyHandling {
}
I share with you a code that can be useful, it is to create an annotation that can be used either in a class or a method.
#Target({TYPE, METHOD})
#Retention(RUNTIME)
#Documented
public #interface AnnotationLogger {
/**
* It is the parameter is to show arguments in the method or the class.
*/
boolean showArguments() default false;
}
#Aspect
#Component
public class AnnotationLoggerAspect {
#Autowired
private Logger logger;
private static final String METHOD_NAME = "METHOD NAME: {} ";
private static final String ARGUMENTS = "ARGS: {} ";
#Before(value = "#within(com.org.example.annotations.AnnotationLogger) || #annotation(com.org.example.annotations.AnnotationLogger)")
public void logAdviceExecutionBefore(JoinPoint joinPoint){
CodeSignature codeSignature = (CodeSignature) joinPoint.getSignature();
AnnotationLogger annotationLogger = getAnnotationLogger(joinPoint);
if(annotationLogger!= null) {
StringBuilder annotationLoggerFormat = new StringBuilder();
List<Object> annotationLoggerArguments = new ArrayList<>();
annotationLoggerFormat.append(METHOD_NAME);
annotationLoggerArguments.add(codeSignature.getName());
if (annotationLogger.showArguments()) {
annotationLoggerFormat.append(ARGUMENTS);
List<?> argumentList = Arrays.asList(joinPoint.getArgs());
annotationLoggerArguments.add(argumentList.toString());
}
logger.error(annotationLoggerFormat.toString(), annotationLoggerArguments.toArray());
}
}
private AnnotationLogger getAnnotationLogger(JoinPoint joinPoint) {
AnnotationLogger annotationLogger = null;
try {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = joinPoint.getTarget().getClass().
getMethod(signature.getMethod().getName(), signature.getMethod().getParameterTypes());
if (method.isAnnotationPresent(AnnotationLogger.class)){
annotationLogger = method.getAnnotation(AnnotationLoggerAspect.class);
}else if (joinPoint.getTarget().getClass().isAnnotationPresent(AnnotationLoggerAspect.class)){
annotationLogger = joinPoint.getTarget().getClass().getAnnotation(AnnotationLoggerAspect.class);
}
return annotationLogger;
}catch(Exception e) {
return annotationLogger;
}
}
}
From Spring's AnnotationTransactionAspect:
/**
* Matches the execution of any public method in a type with the Transactional
* annotation, or any subtype of a type with the Transactional annotation.
*/
private pointcut executionOfAnyPublicMethodInAtTransactionalType() :
execution(public * ((#Transactional *)+).*(..)) && within(#Transactional *);
You could use Spring's PerformanceMonitoringInterceptor and programmatically register the advice using a beanpostprocessor.
#Target({ ElementType.TYPE, ElementType.METHOD })
#Retention(RetentionPolicy.RUNTIME)
#Inherited
#Documented
public #interface Monitorable
{
}
public class PerformanceMonitorBeanPostProcessor extends ProxyConfig implements BeanPostProcessor, BeanClassLoaderAware, Ordered,
InitializingBean
{
private Class<? extends Annotation> annotationType = Monitorable.class;
private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
private Advisor advisor;
public void setBeanClassLoader(ClassLoader classLoader)
{
this.beanClassLoader = classLoader;
}
public int getOrder()
{
return LOWEST_PRECEDENCE;
}
public void afterPropertiesSet()
{
Pointcut pointcut = new AnnotationMatchingPointcut(this.annotationType, true);
Advice advice = getInterceptor();
this.advisor = new DefaultPointcutAdvisor(pointcut, advice);
}
private Advice getInterceptor()
{
return new PerformanceMonitoringInterceptor();
}
public Object postProcessBeforeInitialization(Object bean, String beanName)
{
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName)
{
if(bean instanceof AopInfrastructureBean)
{
return bean;
}
Class<?> targetClass = AopUtils.getTargetClass(bean);
if(AopUtils.canApply(this.advisor, targetClass))
{
if(bean instanceof Advised)
{
((Advised)bean).addAdvisor(this.advisor);
return bean;
}
else
{
ProxyFactory proxyFactory = new ProxyFactory(bean);
proxyFactory.copyFrom(this);
proxyFactory.addAdvisor(this.advisor);
return proxyFactory.getProxy(this.beanClassLoader);
}
}
else
{
return bean;
}
}
}