Annotation and Pointcut on same class method - spring

I have two aspects but only TryCatchLog is working even when method annotated with CatchRedBanner.
One on all methods returning AssertionData in package pageActions
package com.abc.acceptance.b2b.aspects;
#Aspect
#Component
public class TryCatchLogAspect {
#Pointcut(
"execution(com.abc.acceptance.b2b.annotations.AssertionData com.abc.acceptance.b2b.pageActions..*(..))")
private void pageActionsTryCatchLog() {
}
#Around("pageActionsTryCatchLog()")
public Object tryCatchLog(ProceedingJoinPoint joinPoint) throws Throwable { ...
This one for methods with my annotation
import java.lang.annotation.*;
#Target({ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface CheckRedBanner {
}
-----
package com.abc.acceptance.b2b.annotations;
#Aspect
#Component
#Slf4j
public class CheckRedBannerAspect {
#Before("#annotation(CheckRedBanner)")
public void myAdviceForMethodAnnotation(JoinPoint joinPoint) {
handleBeforeExecution(joinPoint);
}
protected void handleBeforeExecution(JoinPoint joinPoint) {
System.out.println("Made iitttt !!! ");
}
}
My code is calling only TryCatchLog but not CheckRedBanner even when I annotate the method
package com.abc.acceptance.b2b.pageActions;
#Component
#Slf4j
#Scope(SCOPE_CUCUMBER_GLUE)
public class BroadbandPanelActions extends PageActions {
#CheckRedBanner
public AssertionData clickFindAddress() {
broadbandPanel.getFindAddressButton().click();
return new AssertionData();
}
...

From the reference docs regarding Advice Ordering
When two pieces of advice defined in different aspects both need to
run at the same join point, unless you specify otherwise, the order of
execution is undefined. You can control the order of execution by
specifying precedence.
For me the behaviour is reproducible when I order the Aspects as follows . I have ordered so that Around advice is executed before Before advice . Also note that I have commented the joinPoint.proceed(); call.
#Component
#Aspect
#Order(0)
public class TryCatchLogAspect {
#Pointcut("execution(sec2.aop.bean.AssertionData sec2.aop.bean..*(..))")
private void pageActionsTryCatchLog() {
}
#Around("pageActionsTryCatchLog()")
public Object tryCatchLog(ProceedingJoinPoint joinPoint) throws Throwable {
// Object result = joinPoint.proceed();
System.out.println("pageActionsTryCatchLog");
return new AssertionData();
}
}
and
#Component
#Aspect
#Order(1)
public class CheckRedBannerAspect {
#Before("#annotation(CheckRedBanner)")
public void myAdviceForMethodAnnotation(JoinPoint joinPoint) {
handleBeforeExecution(joinPoint);
}
protected void handleBeforeExecution(JoinPoint joinPoint) {
System.out.println("Made iitttt !!! ");
}
}
when I uncomment the proceed() call both the aspects work.
#Around("pageActionsTryCatchLog()")
public Object tryCatchLog(ProceedingJoinPoint joinPoint) throws Throwable {
Object result = joinPoint.proceed();
System.out.println("pageActionsTryCatchLog");
return new AssertionData();
}
When we are not explicitly calling joinPoint.proceed(); spring does that for us. But in the first case we are returning without calling the underlying method and the #Before advice fails to execute.

Related

Invoking PostConstruct for each bean in a list

I have the following pseudo code:
#Bean
public List<BeanB> beanB(
List<BeanA> beansA) {
List<BeanB> beansB = new ArrayList<>();
for (BeanA beanA : beansA) {
beansB.add(new BeanB(beanA))
}
return beansB;
}
#Bean
public BeanC beanC(
List<BeanB> beansB) {
return new BeanC(beansB);
}
Now the challenge is when the list of BeansB is constructed post construct is not invoked on those beans in the list. Is there any idiomatic way to trigger post construct invocation on those beans.
This does not work well as #PostConstruct is called by Spring. and from your code, for List<BeanB>, Spring will only try to find #PostConstruct in List. If it can find it, it will execute the #PostConstruct code.
I suspect you are writing beanA as follow in case you have #PostConstruct in beanA's class like below.
#Bean
public BeanA beanA_1() {
return new BeanA();
}
#Bean
public BeanA beanA_2() {
return new BeanA();
}
// which naming List of Bean B as beanB does not seems a good idea though
#Bean
public List<BeanB> beanB(List<BeanA> beansA) {
List<BeanB> beansB = new ArrayList<>();
for (BeanA beanA : beansA) {
beansB.add(new BeanB(beanA))
}
return beansB;
}
To make it work, there are multiple ways, I will just suggests some of them.
Creating a new Class, says BeanBCollectionWrappe to wrap the List of BeanB. And to make the code a bit more robust, I am implementing InitializingBean instead of #PostConstruct which is essentially the same, but allow me to ensure the method is afterPropertiesSet but not any methods.
public class BeanBCollectionWrapper implements InitializingBean {
private List<BeanB> beansB;
public void afterPropertiesSet() throws Exception {
for (BeanB beanB: beansB) {
beanB.afterPropertiesSet();
}
}
// getter and setter
}
public class BeanB implements InitializingBean {
public void afterPropertiesSet() throws Exception {
// ...
}
}
The Wrapper code can definitely be a bit better by use of Generics, like replacing BeanB to <T extends InitializingBean>.
And for the last Part,
#Bean
public BeanC beanC(BeanBCollectionWrapper wrapper) {
return new BeanC(wrapper.getBeansB());
}
Another method can be done by implementing BeanPostProcessor.
For example,
#Component
public class BeanBListBeanPostProcessor implements BeanPostProcessor{
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (beanName.equals("beanB")) {
List<BeanB> beanBList = (List) bean;
for (BeanB beanB : beanBList) {
try {
beanB.afterPropertiesSet();
} catch (Exception exception) {
exception.printStackTrace();
}
}
}
return null;
}
}
public class BeanB implements InitializingBean {
public void afterPropertiesSet() throws Exception {
// ...
}
}

Spring boot 1.5.12 Aspect not called

I know that there are a lot of similar questions in Stackoverflow but none of them helped me.
I have a controller like this:
com.mypkg.controller;
#RestController
public class MyController {
#RequestMapping(method = RequestMethod.POST,
......
public ResponseEntity<?> MyEndpoint(myParams) {
return this.myMethod(myParams, "myString");
}
public ResponseEntity<?> myMethod(myParams, String myString){
//do something
return myReponseEntity
}
}
I defined my aspect in this way:
com.mypkg.controller;
#Aspect
#Component
#Slf4j
public class MyAspect {
#Around("execution(* com.mypkg.controller.MyController.MyEndpoint(..)) && args(..,aParam)")
public ResponseEntity<?> endpointAround(ProceedingJoinPoint joinPoint, String aParam) throws Throwable {
// I am working fine
// do something
return
}
#Around("execution(* com.mypkg.controller.MyController.myMethod(..)) && args(..,myString)")
public ResponseEntity<?> myMethodAround(ProceedingJoinPoint joinPoint, String myString) throws Throwable {
// **** I AM NOT CALLED****
// do something
// return ...
}
}
I configured the AutoProxy
#Configuration
#EnableAspectJAutoProxy(proxyTargetClass=true)
public class AopConfig {}
The function endpointAround is called every time that I call MyEndpoint (throw the REST api).
The problem is the second #Around. it is not called. I need to call a method everytime MyEndpoint is exectued and another one eveytime that MyEndpoint call myMethod.
The problem is that your method myMethod is call from within your other method directly, and not as a someSpringBean.myMethod.
The way spring works is by wrapping any of your beans, and then on the 'wrapping' it can execute all the aspect or other spring related stuff. When you call one method from another one inside the same class, you don't go through the wrapping, thus the aspect related stuff can't happen
You just missed some code. Let us use below code snippet it will work.
Use this com.mypkg.controller.MyController.myMethod instead of
com.mypkg.controller.myMethod it will work
Controller
com.mypkg.controller;
#RestController
public class MyController {
#RequestMapping(method = RequestMethod.POST,
......
public ResponseEntity<?> MyEndpoint(myParams) {
return this.myMethod(myParams, "myString");
}
public ResponseEntity<?> myMethod(myParams, String myString){
//do something
return myReponseEntity
}
}
and in Aspect
com.mypkg.controller;
#Aspect
#Component
#Slf4j
public class MyAspect {
#Around("execution(* com.mypkg.controller.MyController.MyEndpoint(..)) && args(..,aParam)")
public ResponseEntity<?> endpointAround(ProceedingJoinPoint joinPoint, String aParam) throws Throwable {
// I am working fine
// do something
return
}
#Around("execution(* com.mypkg.controller.MyController.myMethod(..)) && args(..,myString)")
public ResponseEntity<?> myMethodAround(ProceedingJoinPoint joinPoint, String myString) throws Throwable {
// **** I AM NOT CALLED****
// do something
// return ...
}
}
You just missed the package path. Your method path should be like this...springbean.method

Why Spring Boot AOP pointcut not triggered

wanner test spring boot(1.5.20) aop with minimum code
class being aopped,
#Component
public class Test {
public Test() {
System.out.println("test constr");
}
public void print() {
System.out.println("test print");
}
}
aop class
#Aspect
#Component
public class LoggingAspect {
public LoggingAspect() {
System.out.println("aspect constr");
}
#After("execution(* *.Test.*(..))")
public void log(JoinPoint joinPoint) {
System.out.println("aspect print");
}
}
main class
#SpringBootApplication
#EnableAspectJAutoProxy(proxyTargetClass = true)
public class AopApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(AopApplication.class, args);
}
#Autowired
private Test test;
#Override
public void run(String... strings) throws Exception {
test.print();
}
}
both Test bean and LoggingAspect bean is created. Test.pring is executed. However, the pointcut log() is never triggered. I searched so and found no answer. I also tried #EnableAspectJAutoProxy with proxyTargetClass = True or False. In my understanding this params force to use cglib for Test class.
please let me know what I missed
figure out. change from .Test. to com.example.aop.Test.*, then works.

#Before #PostConstruct Spring AOP ineterception

I'm trying to write an aspect that can intercept PostConstruct methods. I've looked at related questions on SO and others, and following them, this is what I have so far:
The Spring configuration
#Configuration
#EnableAspectJAutoProxy
#EnableLoadTimeWeaving
#...//other config annotations
public class WebConfiguration {
#Bean
public CommonAnnotationBeanPostProcessor commonAnnotationBeanPostProcessor() {
return new CommonAnnotationBeanPostProcessor();
}
... // etc
}
The annotation:
#Retention(RetentionPolicy.RUNTIME)
public #interface Secured {
Permission[] permissions() default {};
}
The bean
#Component
#Scope("request")
public class SomeWebBean {
#Secured(permissions = Permission.SOME_PERMISSION)
#PostConstruct
public void secure() {
... // some stuff
}
}
The aspect
#Component
#Aspect
public class SecuredAspect {
#Before("#annotation(secured)")
public void doAccessCheck(Secured secured) {
... // actually do the access check
}
}
If I call someWebBean.secure() from a page, then the aspect is invoked. However, it is not invoked on bean creation.
So as a note to future me - this absolutely cannot be done in this way using Spring AOP.
However, the same effect can be achieved by implementing a BeanPostProcessor as below:
public class SecureBeanPostProcessor implements BeanPostProcessor {
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
Secured secured = bean.getClass().getAnnotation(Secured.class);
if (secured != null) {
// do your security test here, throw an exception, return false, however you like
}
return bean;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
You can extend CommonAnnotationBeanPostProcessor and override postProcessBeforeInitialization(Object bean, String beanName)
Then register replace the original CommonAnnotationBeanPostProcessor with a BeanFactoryPostProcessor .
public class InitCommonAnnotationBeanPostProcessor extends CommonAnnotationBeanPostProcessor {
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return super.postProcessBeforeInitialization(bean, beanName);
}
}
public class InitBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
RootBeanDefinition def = new RootBeanDefinition(InitCommonAnnotationBeanPostProcessor.class);
def.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(AnnotationConfigUtils.COMMON_ANNOTATION_PROCESSOR_BEAN_NAME, def);
}
}
#Configuration
public class InitialisationMonitoringConfig {
public static final String BEAN_INIT_MONITOR = "BEAN_INIT_MONITOR";
#Bean
public static InitBeanFactoryPostProcessor initBeanFactoryPostProcessor() {
return new InitBeanFactoryPostProcessor();
}
}
This is ugly, but I had to do that to analyse startup times in dev environment.
Maybe it's enough to just declare InitCommonAnnotationBeanPostProcessor as a bean, I didn't tried.

Run aspect on proxy object

I have following simple service:
#Service
public class TestServiceImpl implements TestService {
#Override
public void countExternal(Integer arg1) {
System.out.println("test - lock external");
count(arg1, new Integer(1));
}
public void count(Integer arg1, Integer arg2) {
System.out.println("test - lock internal");
}
}
that implements my simple interface:
public interface TestService {
void countExternal(Integer arg1);
}
Here's the aspect that I am using to do some validation etc. during count method:
#Aspect
#Component
public class TestAdvicer {
#Around("execution(* count(..))")
public Object advice(ProceedingJoinPoint joinPoint) throws Throwable {
// do som magic here
return joinPoint.proceed();
}
}
In my Spring configuration I have included autoproxying:
#EnableAspectJAutoProxy(proxyTargetClass = true)
Unfortunately, my TestAdvicer is never executed since count method is invoked from countExternal method. count method is executed on Proxy object and because of that advice didn't run.
Do you know how can I run my advice on Proxy object? What is the best way to solve this problem?

Resources