Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can Spring life cycle methods be prevented when using Java Config?

How can I prevent the @PostConstruct method on the ProblematicSerivce from being invoked by Spring after I return the object?

@Configuration
class MyConfig {
    @Bean
    public ProblematicService problematicService() {
        ProblematicService service = someMethodOutsideMyControl();
        // ProblematicService is constructed for me by other code (outside of Spring)
        // and it happens to have a @PostConstruct method. The @PostConstruct method
        // cannot be invoked here or by Spring once this method returns.
        return service;
    }
}

I believe wrapping the result in a FactoryBean would have the desired effect, but I need to repeat this code in several places, so I'm looking for a more elegant solution.

like image 859
Ed Thomas Avatar asked Sep 30 '22 18:09

Ed Thomas


1 Answers

This is a non-trivial change. A @Configuration class (or rather the AnnotationConfigApplicationContext) registers a CommonAnnotationBeanPostProcessor which is in charge of invoking the @PostConstruct method of a bean. Changing that would mean changing almost the whole Spring IoC stack.

Actually, you could just declare a CommonAnnotationBeanPostProcessor with a bean name org.springframework.context.annotation.internalCommonAnnotationProcessor which will override the default one. You can sett the init annotation type to null so that it ignores @PostConstruct.

@Bean(name = "org.springframework.context.annotation.internalCommonAnnotationProcessor")
public CommonAnnotationBeanPostProcessor commonAnnotationBeanPostProcessor() {
    CommonAnnotationBeanPostProcessor bean = new CommonAnnotationBeanPostProcessor();
    bean.setInitAnnotationType(null);;
    return bean;
}

Careful while using this as it might break other things.

I'm going to first recommend to try and find a way around that. For example, return a wrapper object which can give you access to the ProblematicService.

@Bean
public ServiceProvider provider() {
    ProblematicService service = ...;
    ServiceProvider provider = new ServiceProvider(service);
    return provider;
}

Or similarly the FactoryBean you suggested.

Another, cooler, but uglier way to do it is to wrap the object in a CGLIB proxy.

@Bean
public ProblematicService service() {
    ProblematicService service = ...;
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(service.getClass());
    enhancer.setCallback(new MethodInterceptor() {
        ProblematicService inner = service;
        @Override
        public Object intercept(Object obj, Method method, Object[] args,
                    MethodProxy proxy) throws Throwable {
            if (!method.getName().equals("initMethodName"))
                return method.invoke(inner, args);
            return null;
        }
    });
    return (ProblematicService) enhancer.create();
}

Basically the init-method can never be called.

like image 92
Sotirios Delimanolis Avatar answered Oct 12 '22 16:10

Sotirios Delimanolis