1

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.

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
ginglymus
  • 51
  • 7
  • 2
    And it probably never will. Spring uses proxies to apply AOP, proxies may or may not be created at the moment `@PostConstruct` is being invoked. – M. Deinum Feb 16 '17 at 09:58
  • Is there an alternative then that I can hook into on bean creation? – ginglymus Feb 16 '17 at 10:36
  • Your only change is to use full blown aspects with either compile or load time weaving. However do you really want security on your initializing method... – M. Deinum Feb 16 '17 at 10:43
  • The idea was for the initialiser to throw an exception to be caught and handled upstream (redirect to Permission denied page, or display message, as appropriate). I could do this all without AOP, it just felt like AOP was the more elegant and reusable solution here...ie, can use it on methods and constructors... – ginglymus Feb 16 '17 at 10:51
  • BTW, "more is better" is usually not the right answer when it comes to medicine or software development. You either use `@EnableAspectJAutoProxy` for Spring AOP or `@EnableLoadTimeWeaving` for AspectJ LTW, but not both, unless you are interested in real chaos and the same aspects being woven twice with different technolofgies. – kriegaex Feb 16 '17 at 11:14
  • Yeah, I thought I might be overdoing it a bit - was trying to follow advice from here: http://stackoverflow.com/a/34696719/1791174 – ginglymus Feb 16 '17 at 11:20

2 Answers2

2

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;
    }
}
ginglymus
  • 51
  • 7
0

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.

Mirak
  • 59
  • 9