In my aspect method, i need get value of name (param of custom annotation) name = "unit test"
Method call by user:
#Service
#RequiredArgsConstructor
#Slf4j
public class Task {
#CronLogger(name = "unit test")
public void testCronLogger(String param) {
log.info("testCronLogger ...");
}
}
custom annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface CronLogger {
public String name() default "";
}
Aspect method:
#Aspect
#Component
#EnableAspectJAutoProxy
public class CronLoggerAspect {
private static final Logger log = LoggerFactory.getLogger(CronLoggerAspect.class);
#Around("#annotation(CronLogger)")
public Object trace(ProceedingJoinPoint joinPoint) throws Throwable {
Object[] tab = joinPoint.getArgs();
for (Object object : tab) {
log.debug("CronLogger: {}", object);
}
return joinPoint.proceed();
}
}
Console:
CronLogger: test
testCronLogger ...
How about this (untested, I simply modified your code)?
#Around("#annotation(cronLogger)")
public Object trace(ProceedingJoinPoint joinPoint, CronLogger cronLogger) throws Throwable {
log.debug("CronLogger: {}", cronLogger.name());
return joinPoint.proceed();
}
Please be careful with upper- and lower-case characters. One is an annotation class name, the other a method parameter name.
need get Method and get Annotation of this method.
#Around("#annotation(CronLogger)")
public Object trace(ProceedingJoinPoint joinPoint) throws Throwable {
String name = MethodSignature.class.cast(joinPoint.getSignature()).getMethod().getAnnotation(CronLogger.class)
.name();
log.debug("CronLogger: {}", name);
return joinPoint.proceed();
}
this is annotations Code:
#Target({ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Inherited
#Documented
public #interface PreVisit {
String value();
}
this use in the Controller #PreVisit("#pv.hasAccess('xxxxxx')")
#PreVisit("#pv.hasAccess('xxxxxx')")
#RequestMapping(value = "getUser")
public User getUser(Integer userId) {...some code...}
this is pv.hasAccess('xxxxxx') code:
#Service("pv")
public class PageVisit{
public boolean hasAccess(String par){
//return false or true;
}
}
My question:
In Aspect, how do you get methods in annotation parameters and get the result of execution
this is Aspect file codeļ¼
#Aspect
#Component
public class PreVisitAspect {
#Around("#annotation(PreVisit)")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
//How do I get the result of the method execution in the annotation parameter here
//boolean pvResult=#pv.hasAccess('xxxxxx'); pvResult=false or true
//Do something use the pvResult
}
}
You can second argument type PreVisit and you can access the annotation values in the method.
#Aspect
#Component
public class PreVisitAspect {
#Around("#annotation(PreVisit)")
public Object around(ProceedingJoinPoint joinPoint, PreVisit preVisit) throws Throwable {
//How do I get the result of the method execution in the annotation parameter here
//boolean pvResult=#pv.hasAccess('xxxxxx');
//Do something use the pvResult
String value = preVisit.value();
}
}
If you want to use the PageVisit#hasAccess(String), then inject PageVisit into your aspect and invoke the method.
For this you must modify you controller method as below.
#PreVisit("xxxxxx")
#RequestMapping(value = "getUser")
public User getUser(Integer userId) {...some code...}
and your aspect will be.
#Aspect
#Component
public class PreVisitAspect {
#Autowired
private PageVisit pv;
#Around("#annotation(PreVisit)")
public Object around(ProceedingJoinPoint joinPoint, PreVisit preVisit) throws Throwable {
//How do I get the result of the method execution in the annotation parameter here
//boolean pvResult=#pv.hasAccess('xxxxxx');
//Do something use the pvResult
String value = preVisit.value();
boolean hasAccess = pv.hasAccess(value);
}
}
I'm the questioner
I have solved this using Java reflection,as follows:
#Aspect
#Component
public class PreVisitAspect {
#Autowired
private PageVisit pv;
#Around("#annotation(preVisit)")
public Object around(ProceedingJoinPoint joinPoint, PreVisit preVisit) throws Throwable {
String value = preVisit.value();
//String value="#pas.hasAccess('xxxxxx')";
if(value.startsWith("#")){
String beanName=value.substring(value.indexOf("#")+1,value.indexOf("."));
String methodName=value.substring(value.indexOf(".")+1,value.indexOf("("));
String paramsStr=value.substring(value.indexOf("(")+2,value.lastIndexOf(")")-1);
Object[] paramsArr=paramsStr.split("','");
logger.info("beanName:"+beanName);
logger.info("methodName:"+methodName);
logger.info("paramsStr:"+paramsStr);
ServletContext servletContext = request.getSession().getServletContext();
ApplicationContext appContext = WebApplicationContextUtils.getWebApplicationContext(servletContext);
Method method = ReflectionUtils.findMethod(appContext.getBean(beanName).getClass(), methodName,new Class[]{String.class} );
Boolean result = (Boolean)ReflectionUtils.invokeMethod(method, appContext.getBean(beanName),paramsArr);
logger.info(result.toString());
}
.....other Code.....
}
}
tks #Karthikeyan Vaithilingam
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());
}
}
}
}
Is there any way to force the use of producer method if using #Qualifier? I have a #Produces factory method but the constructor is still being called, which is an issue because I need the InjectionPoint to read parameters. Using the code below RRRRRRRR is always printed out.
#ProductTypeA
public class ProductA implements Product
{
public String test="testA";
private ProductA()
{
System.out.println("RRRRRRRRRRRRRRRRRRRR");
this.test = "testB";
}
private ProductA(InjectionPoint injectionpoint)
{
System.out.println("TTTTTTTTTTTTTTTT");
this.test="testC";
}
#Produces
public ProductA getProductA(InjectionPoint injectionpoint)
{
this.test="testD";
System.out.println("-----------------------------");
System.out.println("injectionpoint.getAnnotated() = "+injectionpoint.getAnnotated());
return new ProductA(injectionpoint);
}
#Override
public LinkedList<Feature> getFeatures()
{
LinkedList<Feature> rtn = new LinkedList<Feature>();
rtn.add( new Feature("AAA","111") );
return rtn;
}
#Override
public String toString()
{
return "ProductA []";
}
}
Qualifier:
#Qualifier
#Retention(RUNTIME)
#Target({TYPE, METHOD, FIELD, PARAMETER})
public #interface ProductTypeA
{
#Nonbinding
String testfield() default "23";
}
The Qualifier should be in the producer method:
#Produces
#ProductTypeA
public ProductA getProductA(InjectionPoint injectionpoint)
{
this.test="testD";
System.out.println("-----------------------------");
System.out.println("injectionpoint.getAnnotated() = "+injectionpoint.getAnnotated());
return new ProductA(injectionpoint);
}
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;
}
}
}