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;
}
});
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;
}
}
This is a contrived example, but it illustrates my question. I want to create a custom DynamoDBMarshaller<T> to marshall/unmarshall my objects to/from the database. It requires the CustomMarshaller Spring bean, which needs to be injected into the MyAttributeMarshaller class after it's instantiated. However, since this class is created through the #DynamoDBMarshalling() annotation, it's not managed by Spring. Is there a way to make my CustomMarshaller spring bean available in the MyAttributeMarshaller class?
#DynamoDBTable(tableName = "MyTable")
public class ObjectInTable
{
#DynamoDBAttribute(attributeName = "myAttribute")
#DynamoDBMarshalling(marshallerClass = MyAttributeMarshaller.class)
public MyAttribute getMyAttribute() { ... }
public MyAttribute setMyAttribute(MyAttribute o) { ... }
}
And my marsher class..
#Named
public class MyAttributeMarshaller implements DynamoDBMarshaller<MyAttribute>
{
#Override
public String marshall(MyAttribute o)
{
return marshaller.marshall(o);
}
#Override
public ProviderContentReference unmarshall(Class<ProviderContentReference> clazz, String obj)
{
return marshaller.unmarshall(o);
}
#Inject
private CustomMarshaller marshaller; // ERROR: null
}
** UPDATE **
I found a solution that seems to work by first creating a Spring bean:
#Named
public class SpringContext implements ApplicationContextAware
{
private static ApplicationContext context;
public void setApplicationContext(ApplicationContext context) throws BeansException
{
this.context = context;
}
public static ApplicationContext getApplicationContext() {
return context;
}
}
And then retrieving my Custom Marshaller in the Non-Spring managed MyAttributeMarshaller by:
private CustomMarshaller marshaller = (CustomMarshaller)
SpringContext.getApplicationContext().getBean("customMarshaller");
I am afraid not. You are asking DynamoDB to use an specific instance of that marshaller class which has been injected by Spring with a CustomMarshaller.
You could put a CustomMarshaller instance in an static place/field and pick it up from MyAttributeMarshaller but this is a shot in the dark since the posted code is not enough to say for sure
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)
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;
}
}
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)