#Before #PostConstruct Spring AOP ineterception - spring

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.

Related

About register "BeanPostProcessor" use"BeanFactoryPostProcessor"

When I use "BeanFactoryPostProcessor" to register "BeanPostProcessor" and at the same time use AutoConfiguration to wire up my defined "BeanFactoryPostProcessor", I found that my "BeanPostProcessor" execution order is after "#PostConstruct", I am curious why, and please what is good way to deal with
The key code is as follows
JDK17\SpringBoot3.0.0-M3
#RequiredArgsConstructor
public class MapstructFactory implements BeanPostProcessor {
private final MapstructRegistry registry;
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof Converter) {
registry.addConverter((Converter<?, ?>) bean);
}
return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
}
}
#RequiredArgsConstructor
public class MapstructFactoryRegister implements BeanFactoryPostProcessor {
private final MapstructRegistry registry;
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
beanFactory.addBeanPostProcessor(new MapstructFactory(registry));
}
}
#AutoConfiguration
public class MapstructAutoConfiguration {
#Bean
public MapstructFactoryRegister mapstructFactoryRegister(MapstructRegistry registry) {
return new MapstructFactoryRegister(registry);
}
}

WebSocketMessageBrokerStats - how to set loggingPeriod

How to set loggingPeriod in WebSocketMessageBrokerStats to decrease value (default is 30')
WebSocketMessageBrokerStats is loaded by #Bean in WebSocketMessageBrokerConfigurationSupport
version : Spring 4.2.0.RELEASE
My current Config :
#Configuration
#EnableWebSocketMessageBroker
#EnableScheduling
public class AppWebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(final MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
}
#Override
public void registerStompEndpoints(final StompEndpointRegistry registry) {
registry.addEndpoint("/entry")
.setAllowedOrigins("*")
.withSockJS()
.setDisconnectDelay(10000);
}
}
According to the WebSocketMessageBrokerStats javadoc:
This class is declared as a Spring bean by the above configuration with the name "webSocketMessageBrokerStats"
So you can add this to your configuration class:
#Autowired
private WebSocketMessageBrokerStats webSocketMessageBrokerStats;
#PostConstruct
public void init() {
webSocketMessageBrokerStats.setLoggingPeriod(10 * 1000); // desired time in millis
}
If you want to setup the logging period before Spring has put the bean into service, you can use a PostBeanProcessor:
#Bean
public BeanPostProcessor beanPostProcessor() {
return new BeanPostProcessor() {
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof WebSocketMessageBrokerStats) {
WebSocketMessageBrokerStats webSocketMessageBrokerStats = (WebSocketMessageBrokerStats) bean;
webSocketMessageBrokerStats.setLoggingPeriod(30 * 1000); // your customization
}
return bean;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
};
}
With WebSocketMessageBrokerStats it is not much of a difference to Toyo's way. However if you have a bean which won't let you change a setting after initialization, this way will always work.

Spring proxy beans from Interface

What is the correct way to create proxy beans by interfaces?
public class JdbiRepositoryAnnotationBeanPostProcessorTest {
private DBI dbi = mock(DBI.class);
#org.junit.Test
public void testIncompleteBeanDefinition() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(JdbiRepositoryAnnotationBeanPostProcessor.class);
ctx.register(MyConfig.class);
ctx.refresh();
ITest bean = ctx.getBean(ITest.class);
assertNotNull(bean);
}
#JdbiRepository
public static interface ITest {
}
#Configuration
#ComponentScan(basePackageClasses = {ITest.class},
includeFilters = {#ComponentScan.Filter(type = FilterType.ANNOTATION, value = JdbiRepository.class)})
public static class MyConfig {
}
}
I have tried bean post processor but It did not help me.
Edit:
I wanted to use component scanning by including annotation filter but it did not help me too.
Edit:
I want to create instances by another library which is creating proxy beans as this:
TestInterface proxy = factory.onDemand(TestInterface.class);
Edit:
I have extended InstantiationAwareBeanPostProcessorAdapter for JdbiRepositoryAnnotationBeanPostProcessor.
I have been just logging currently. But I can not see my interfaces as a bean.
Please note that I have also changed my test code above.
public class JdbiRepositoryAnnotationBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
private ConfigurableListableBeanFactory beanFactory;
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
#Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
if(!(beanFactory instanceof ConfigurableListableBeanFactory)) {
throw new IllegalArgumentException("AutowiredAnnotationBeanPostProcessor requires a ConfigurableListableBeanFactory");
}
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
// this.dbiMap = this.beanFactory.getBeansOfType(DBI.class);
}
}
The problem was related to ComponentScanning not PostBeanProcessor. ComponentScan is scanning only concrete classes that is why my processor did not work. I had to create a custom importer for interfaces.

Can I access a annotation on the instance of a bean to be injected

Given a class :
public ClassA {
#Autowired
#SomeAnnotation("foo")
private ClassB bar;
}
#Component
#Scope(prototype)
public ClassB {
private String someString;
}
I would like to write some bean processor (post construct...) that can at ClassB construction time can access the #SomeAnnotation on the intances that ClassB is getting injected into so that I can set the value of someString to "foo".
I know this isn't very IoC and I'm going to guess it cannot be done.
You might be able to do something like this with a #PostConstruct of ClassA:
#PostConstruct
public void postConstruct(){
SomeAnnoation someAnnotation = this.getClass().getField("bar").getAnnotation(SomeAnnotation.class);
bar.someString(someAnnotation.value());
}
Update: - General solution using a BeanPostProcessor :
public class SomeAnnotationFieldInitalizer implements BeanPostProcessor{
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
Field[] fields = bean.getClass().getFields();
if (fields!=null){
for (Field field:fields){
SomeAnnotation someAnnotation = field.getAnnotation(SomeAnnotation.class);
if (someAnnotation!=null){
try {
ReflectionUtils.makeAccessible(field);
field.set(bean, someAnnotation.value());
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
return bean;
}
}

What is the Spring DI equivalent of CDI's InjectionPoint?

I would like to create a Spring's bean producer method which is aware who invoked it, so I've started with the following code:
#Configuration
public class LoggerProvider {
#Bean
#Scope("prototype")
public Logger produceLogger() {
// get known WHAT bean/component invoked this producer
Class<?> clazz = ...
return LoggerFactory.getLogger(clazz);
}
}
How can I get the information who wants to get the bean injected?
I'm looking for some equivalent of CDI's InjectionPoint in Spring world.
Spring 4.3.0 enables InjectionPoint and DependencyDescriptor parameters for bean producing methods:
#Configuration
public class LoggerProvider {
#Bean
#Scope("prototype")
public Logger produceLogger(InjectionPoint injectionPoint) {
Class<?> clazz = injectionPoint.getMember().getDeclaringClass();
return LoggerFactory.getLogger(clazz);
}
}
By the way, the issue for this feature SPR-14033 links to a comment on a blog post which links to this question.
As far as I know, Spring does not have such a concept.
Then only thing that is aware of the point that is processed is a BeanPostProcessor.
Example:
#Target(PARAMETER)
#Retention(RUNTIME)
#Documented
public #interface Logger {}
public class LoggerInjectBeanPostProcessor implements BeanPostProcessor {
public Logger produceLogger() {
// get known WHAT bean/component invoked this producer
Class<?> clazz = ...
return LoggerFactory.getLogger(clazz);
}
#Override
public Object postProcessBeforeInitialization(final Object bean,
final String beanName) throws BeansException {
return bean;
}
#Override
public Object postProcessAfterInitialization(final Object bean,
final String beanName) throws BeansException {
ReflectionUtils.doWithFields(bean.getClass(),
new FieldCallback() {
#Override
public void doWith(final Field field) throws IllegalArgumentException, IllegalAccessException {
field.set(bean, produceLogger());
}
},
new ReflectionUtils.FieldFilter() {
#Override
public boolean matches(final Field field) {
return field.getAnnotation(Logger.class) != null;
}
});
return bean;
}
}

Resources