Is there a standard way of configuring already created bean in Spring Boot by NOT creating providing this bean myself but instead somehow injecting this bean in a hook method in a configuration class and do additional class?
For example I would like to have Thymeleaf's TemplateResolver as created by its autoconfiguration but I would like to change one property.
What is the best way to do this (again, not by providing my own TemplateResolver ) ?
You could #Autowired the auto-configured TemplateResolver into your configuration class and then use a #PostConstruct method to set the property.
public class ExampleConfiguration {
#Autowired
private TemplateResolver templateResolver;
#PostConstruct
void customize() {
templateResolver.setFoo("bar");
}
}
I was facing a similar issue, I needed to customize an autoconfigured bean. Thanks to M. Deinum and Andy Wilkinson, I coded a BPP based on your points. Hope it can be a more general solution for this kind of issue.
public class TemplateResolverCustomizationBeanPostProcessor implements BeanPostProcessor, Ordered {
private Logger logger = LoggerFactory.getLogger(getClass());
private int order = Ordered.LOWEST_PRECEDENCE;
public TemplateResolverCustomizationBeanPostProcessor() {
logger.info("Created TemplateResolverCustomizationBeanPostProcessor instance");
}
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
logger.info("postProcessBeforeInitialization method invoked");
return bean;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
logger.info("postProcessAfterInitialization method invoked");
if (bean instanceof TemplateResolver) {
((TemplateResolver) bean).setFoor("bar");
}
return bean;
}
public void setOrder(int order) {
this.order = order;
}
#Override
public int getOrder() {
return order;
}
}
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;
}
}
I'm developing an external component for applications which contains functionality to inject Jersey Client filters into lazy-loaded clients. Ive implemented a BeanPostProcessor that does this:
public class ClientFilterInjector implements BeanPostProcessor, Ordered {
private ClientTraceInterceptor clientTraceInterceptor;
public ClientFilterInjector(ClientTraceInterceptor clientTraceInterceptor) {
this.clientTraceInterceptor = clientTraceInterceptor;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if(bean instanceof JerseyWebTarget) {
((JerseyWebTarget) bean).register(clientTraceInterceptor);
}
return bean;
}
#Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
}
However, Spring Boot apparently auto-scans for BeanPostProcessor implementations regardless of whether or not they're annotated or have a bean creation method. Because of this, it screws up the order for which beans are created in the application. Is there a way to defer the instantiation of a BeanPostProcessor?
Did you try to add lazy annotation?
import org.springframework.context.annotation.Lazy;
By using this annotation it will effect on the first call but later on it will exactly the same.
public class ClientFilterInjector implements BeanPostProcessor, Ordered {
private ClientTraceInterceptor clientTraceInterceptor;
public ClientFilterInjector(ClientTraceInterceptor clientTraceInterceptor) {
this.clientTraceInterceptor = clientTraceInterceptor;
}
#Override
public Object postProcessAfterInitialization(#Lazy Object bean, String beanName) {
if(bean instanceof JerseyWebTarget) {
((JerseyWebTarget) bean).register(clientTraceInterceptor);
}
return bean;
}
#Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
}
It looks like if you implement BeanProcessor in the form of an anonymous class it will not get auto-scanned in the Spring Boot application init and you can defer its instantiation whenever you want by adding it to the bean factory of the application context.
((ConfigurableApplicationContext) appContext).getBeanFactory().addBeanPostProcessor(new BeanPostProcessor() {
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if(bean instanceof WebTarget) {
((WebTarget) bean).register(instance);
}
return bean;
}
});
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) {
...
}
}
I am trying to add a simple String to my Spring Application Context, and then autowire this to a different existing bean (A) within the application context. I know this is not the usual way to go, yet I need to add many beans programmatically, which would otherwise make my xml configuration huge.
public class MyPostProcessor implements BeanFactoryPostProcessor, Ordered {
#Override
public int getOrder() {
return 0;
}
#Override
public void postProcessBeanFactory(
ConfigurableListableBeanFactory beanFactory) throws BeansException {
beanFactory.registerSingleton("myString", "this is the String");
A a = beanFactory.getBean(A.class);
beanFactory.autowireBean(a);
}
}
public class A {
#Autowired
public transient String message;
}
When running this, the property message of the instance of A is null. What am I missing?
EDIT: this is my application context:
#Configuration
class TestConfig {
#Bean
public A a() {
return new A();
}
#Bean
public MyPostProcessor postProcessor() {
return new MyPostProcessor();
}
}
And this is my test:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = TestConfig.class)
public class MyTest {
#Autowired
private transient A a;
#Test
public void test() throws Exception {
System.err.println("Running");
System.err.println("This is the autowired String: " + a.message);
Thread.sleep(1000);
}
}
Thanks
You should not instantiate beans from BeanFactoryPostprocessors.
From BeanFactoryPostProcessor JavaDoc:
A BeanFactoryPostProcessor may interact with and modify bean
definitions, but never bean instances. Doing so may cause premature
bean instantiation, violating the container and causing unintended
side-effects.
In your case, the A bean is instantiated before BeanPostProcessors and therefore not autowired.
Remove the lines:
A a = beanFactory.getBean(A.class);
beanFactory.autowireBean(a);
And will work.
Try using the #Qualifier to specific which bean you want to Auto wire.
public class A {
#Autowired
#Qualifier("myString")
public transient String message;
}
I have a controller which is supposed to create version dependend instances (currently not implemented).
#Controller
public class ReportController {
#Autowired
private ReportCompFactory reportCompFactory;
public ModelAndView getReport() {
I_Report report = reportCompFactory.getObject();
^^^^^<- no autowiring in this instance
}
...
}
The Factory looks like this:
#Component
public class ReportCompFactory implements FactoryBean<I_Report> {
#Override
public I_Report getObject() throws BeansException {
return new ReportComp();
}
#Override
public Class<?> getObjectType() {
return I_Report.class;
}
#Override
public boolean isSingleton() {
return false;
}
}
The created instances fields (#Autowired annotated ) are not set.
What should I do, is FactoryBean the right interface to implement?
I would prefer a solution which doesn't involve xml-configurations.
The component itself:
ReportComp implements I_Report {
#Autowired
private ReportDao reportDao;
^^^^^^^<- not set after creation
...
}
}
Spring doesn't perform autowiring if you create your objects. Here are a few options
define the bean to be of scope prototype - this will make the factory redundant (this is applicable in case you simply want instantiation in the factory)
inject the ReportDao in the factory, and set it to the ReportComp via a setter
inject ApplicationContext in the factory and do ctx.getAutowireCapableBeanFactory().autowireBean(instance)