How can we log each bean instantiation in Spring?
I wish to log a message every time a bean is initialized.
I have N number of bean and I don't want to put any logger on init method for each bean.
I see this as a cross-cutting concern, but not sure how to achieve this.
Is there a way.?
You can use a BeanPostProcessor
#Component
public class LogBeanPostProcessor implements BeanPostProcessor {
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
return bean;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
LOGGER.log(String.format("Bean instantiated with name %s and class %s", beanName, bean.getClass().getSimpleName()));
return bean;
}
}
Try setting logging level of org.springframework.beans.factory to TRACE or DEBUG.
I use log4j2 with xml configuration:
<logger name="org.springframework.beans.factory" level="trace"/>
You can use Spring's event listener (explained here) to listen for event. I believe the event you need to listen to is ContextRefreshedEvent, e.g.:
#Component
public class MyListener
implements ApplicationListener<ContextRefreshedEvent> {
public void onApplicationEvent(ContextRefreshedEvent event) {
...
}
}
Related
I'd like to intercept the creation of all ThreadPoolTaskExecutors inside the application context, and add all of them a custom TaskDecorator.
Pseudocode:
public void interceptTaskExecutors(List<ThreadPoolTaskExecutor> executors) {
var decorator = new MyTaskDecorator();
executors.stream().forEach(executor -> executor.setTaskDecorator(decorator));
}
But how can I actually intercept the bean initialization process of all TaskExecutors to apply this?
#M. Deinum probably means as follows:
#Configuration
public class ThreadPoolCustomizer implements BeanPostProcessor {
#Autowired
private TaskDecorator decorator;
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof ThreadPoolTaskExecutor)
((ThreadPoolTaskExecutor) bean).setTaskDecorator(decorator);
return bean;
}
}
On application close, each singleton bean is destroyed by DefaultListableBeanFactory.destroySingletons().
If the bean has a public void no-arg method called shutdown, then it will be invoked.
I have some 3rd party beans, all of which implement a certain interface, let's call it DoNotDestroy, that I do not want Spring to destroy.
I can specify a blank string destroy method on each bean, like so:
#Bean(destroyMethod = "")
public MyBean myBean() {
return new MyBean();
}
However, I would prefer to somehow configure Spring to not destroy any beans that implement DoNotDestroy. Is there a good way to do this?
Rather than blanking out the destroy method in each #Bean method, I can implement a BeanFactoryPostProcessor to do the same thing:
#Component
public class DoNotDestroyPostProcessor implements BeanFactoryPostProcessor {
private final Logger log = LoggerFactory.getLogger(DoNotDestroyPostProcessor.class);
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
String[] doNotDestroys = beanFactory.getBeanNamesForType(DoNotDestroy.class);
for (String doNotDestroy : doNotDestroys) {
BeanDefinition bean = beanFactory.getBeanDefinition(doNotDestroy);
log.info("Don't destroy bean {} {}", bean.getFactoryMethodName(), bean.getDestroyMethodName());
bean.setDestroyMethodName("");
}
}
}
This avoids the problem of someone adding a bean and forgetting to blank out the destroy method.
Could anybody explain me it? After reading the documentation I didn't understand.
Can Spring init Bean(Factory)PostProcessor lazily or not?
https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-factory-extension-factory-postprocessors
There is a such block that confuse me:
As with BeanPostProcessors , you typically do not want to configure BeanFactoryPostProcessors for lazy initialization. If no other bean references a Bean(Factory)PostProcessor, that post-processor will not get instantiated at all. Thus, marking it for lazy initialization will be ignored, and the Bean(Factory)PostProcessor will be instantiated eagerly even if you set the default-lazy-init attribute to true on the declaration of your element.
The correct answer to the question: "Can Spring init Bean(Factory)PostProcessor lazily?" is "NO". I checked it by myself. I created 2 classes:
#Lazy
#Component
public class CustomBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("bean factory!");
}
}
and
#Lazy
#Component
public class CustomBeanPostProcessor implements BeanPostProcessor {
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("before init!");
return bean;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
And run spring application. So, in console it was printed: "bean factory" and several times "before init", although I put #Lazy annotation on these classes.
Currently I'm having a spring boot application and for each class like SampleClass that I would like to have a log I need to initialize the logger like this:
private static Logger log = LoggerFactory.getLogger(SampleClass.class);
Which means that we need statically send the classname to the getLogger method.
I was thinking of creating a loggable interface and everytime a class implements this interface, it dynamically finds the class name and properly write the log to the outputstream with proper classname.
I googled to find a proper solution but every example is specifically sending the classname in the compile time. Does spring have this ability?
I recommend to use Lombok #log varient in your project, it will automatically set at compile time
If you are using slf4j use #Slf4j
It's best approach and even spring uses internally in some projects
Create a new #Log annotation.
#Documented
#Target(ElementType.FIELD)
#Retention(RetentionPolicy.RUNTIME)
public #interface Log {}
Now implement the BeanPostProcessor which gives us the postProcessBeforeInitialization method which allows us to manage a bean before initialization. We search for the #Log annotation and inject an implementation with the LoggerFactory.
#Component
public class LoggerPostProcessor implements BeanPostProcessor {
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
ReflectionUtils.doWithFields(bean.getClass(), new ReflectionUtils.FieldCallback() {
public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
Logger log = LoggerFactory.getLogger(bean.getClass());
ReflectionUtils.makeAccessible(field);
field.set(bean, log);
}
}, new ReflectionUtils.FieldFilter() {
#Override
public boolean matches(Field field) {
return field.isAnnotationPresent(Log.class);
}
});
return bean;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
And use the #Log annotation at field level to describe that we want to inject a logger.
#Log
private Logger logger;
I want to execute custom action after a Spring container will create the instance of some type of bean e.g. bean which is annotated with #MyAnnotation.
I do not want to use #PostConstructno init-method because the required custom action should not be saved in created bean but outside of it.
So I hope that Spring has some interface with method like newBeanCreated and I hope that I wolud implement it like this:
public void newBeanCreated(Object newObjReference){
if (newObjReference.getClass().isAnnotationPresent(MyAnnotation.class)) {
System.out.println("Here I am!");
}
}
I think you ar looking for the BeanPostProcessor interface.
Below example might also help you:
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class MyBeanPostProcessor implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if(beanName.equals("com.x.y.z")) //Fully Classified Bean Class Name
{
//You can place your logic here
}
return bean;
}
}