Can we calculate Spring bean initialization time - java

I would like to develop a spring AOP feature where we can put a point cut/within during the spring bean initialization so as to calculate some statistics as required for business.
I would like to know if its possible using spring AOP module?

You can measure initialization time using this component:
#Component
public class MyBeanPostProcessor implements BeanPostProcessor, Ordered {
private Map<String, Long> start;
private Map<String, Long> end;
public MyBeanPostProcessor() {
start = new HashMap<>();
end = new HashMap<>();
}
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
start.put(beanName, System.currentTimeMillis());
return bean;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
end.put(beanName, System.currentTimeMillis());
return bean;
}
#Override
public int getOrder() {
return Integer.MAX_VALUE;
}
//this method returns initialization time of the bean.
public long initializationTime(String beanName) {
return end.get(beanName) - start.get(beanName);
}
}
But this time doesn't include time of running constructor.
But you can chronicle a moment after reading bean definition before all bean constructors are run. Use BeanFactoryPostProccessor for it:
#Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
private long launchTime;
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
launchTime = System.currentTimeMillis();
}
public long getLaunchTime() {
return launchTime;
}
}
The lauchTime is a moment when the spring just finished reading the configuration (for example, xml file) and ready to create beans.
So, the full initialization time can be calculated use this two components like: max(end) - launchTime. (The difference between the time last bean was initialized and bean configuration was read)

Related

How to intercept configuration of all ThreadPoolTaskExecutors?

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;
}
}

Init BeanPostProcessor at a later point

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 to additionally configure autocreated Spring Boot beans?

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;
}
}

Programmatically adding Beans to Spring application Context

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;
}

Getting BeanNotOfRequiredTypeException in Spring while using BeanPostProcessor

I am trying to run a Spring example using BeanPostProcessor.
Below is the bean post processor
public class DisplayNamePostProcessor implements BeanPostProcessor{
DisplayNamePostProcessor(){
System.out.println("DisplayNamePostProcessor instantiated");
}
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
System.out.println("postProcessBeforeInitialization for bean "+beanName);
return this;
}
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
System.out.println("postProcessAfterInitialization for bean "+beanName);
return this;
}
}
here, is the spring configuration file
<bean id="car" class="com.core.Car" >
<property name="wheel" value="four" />
</bean>
<bean class="com.core.DisplayNamePostProcessor"></bean>
Here, is my bean class
public class Car {
private String wheel;
public String getWheel() {
return wheel;
}
public void setWheel(String wheel) {
this.wheel = wheel;
}
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
System.out.println("application context loaded");
Car car = context.getBean("car", Car.class);
}
}
On running the above main method, I am getting the below exception
Exception in thread "main" org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'car' must be of type [com.core.Car], but was actually of type [com.core.DisplayNamePostProcessor]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:361)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:198)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1121)
Can someone please let me know what Ia m doing wrong and how to resolve this exception. Also, what is the root cause of it?
Any BeanPostProcessor beans you declare will be picked up by the ApplicationContext bean factory and used. Your implementation does this
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
System.out.println("postProcessBeforeInitialization for bean "+beanName);
return this;
}
Instead of doing anything to the target bean, it simply returns itself. It thus overrides all beans it processes with a DisplayNamePostProcessor bean.

Categories