NullPointerException when using customized Autowired - spring

I customized a Annotation #CustomizedAutowired like #Autowired by using BeanPostProcessor (InjectBeanPostProcessor.java), but I got a NullPointerException when AOP is used.
Why it is null when using AOP?
Why DemoController seems to be proxied twice when using AOP?
what should I do, so that #CustomizedAutowired can work just like #Autowired?
#Documented
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
#Inherited
public #interface CustomizedAutowired {}
#RestController
#RequestMapping("/hello")
public class DemoController {
#CustomizedAutowired
private InjectBean injectBean;
#GetMapping("/world")
public LocalDateTime hello() {
injectBean.hello(); // injectBean is null
return LocalDateTime.now();
}
}
#Aspect
#Component
public class AopDemo {
#Pointcut("execution(public java.time.LocalDateTime *(..))")
public void pointcut() {}
#AfterReturning(pointcut = "pointcut()")
public void round() {
System.out.println("after returning");
}
}
#Component
public class InjectBeanPostProcessor implements BeanPostProcessor {
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
Class<?> targetClass = bean.getClass();
while (targetClass != null) {
Field[] fields = targetClass.getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(CustomizedAutowired.class)) {
field.setAccessible(true);
try {
field.set(bean, new InjectBean());
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
targetClass = targetClass.getSuperclass();
}
return bean;
}
}
#SpringBootApplication
public class DemoApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#CustomizedAutowired
private InjectBean injectBean;
#Override
public void run(String... args) throws Exception {
System.out.println("instance -> " + this);
injectBean.hello(); // works fine here
}
}
Here is the result:

You cannot do field.set(bean, new InjectBean()) on the proxy bean, because it does not inherit any private fields. You need to unwrap the proxy and set the field on the original object.
I am not going to comment on the idea of using all that ugly reflection in order to implement your custom injection idea, just help you make it work. You can use AopTestUtils.getTargetObject(bean) instead of your while-loop in order to get the original object and then easily its class afterwards.
How about this?
package de.scrum_master.spring.q70408968;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
import org.springframework.test.util.AopTestUtils;
import java.lang.reflect.Field;
#Component
public class InjectBeanPostProcessor implements BeanPostProcessor {
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// Unwrap bean, in case it is a proxy
Object targetObject = AopTestUtils.getTargetObject(bean);
Class<?> targetClass = targetObject.getClass();
Field[] fields = targetClass.getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(CustomizedAutowired.class)) {
field.setAccessible(true);
try {
field.set(targetObject, new InjectBean());
}
catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
return bean;
}
}
This is going to work both with and without AOP proxies. Note how I am injecting the field into targetObject, but returning the original bean instance (i.e. the proxy in the AOP case).
Then, get rid of the annotated member in the application class, because the application is not a normal Spring component.
package de.scrum_master.spring.q70408968;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
try (ConfigurableApplicationContext context = SpringApplication.run(DemoApplication.class, args)) {
context.getBean(DemoController.class).hello();
}
}
}
Now the application runs just fine.
o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
d.s.spring.q70408968.DemoApplication : Started DemoApplication in 7.457 seconds (JVM running for 10.318)
Hello from InjectBean
after returning
Or maybe you prefer a Java streams approach, but that is just cosmetics:
package de.scrum_master.spring.q70408968;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
import org.springframework.test.util.AopTestUtils;
import java.util.Arrays;
#Component
public class InjectBeanPostProcessor implements BeanPostProcessor {
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// Unwrap bean, in case it is a proxy
Object targetObject = AopTestUtils.getTargetObject(bean);
Arrays.stream(targetObject.getClass().getDeclaredFields())
.filter(field -> field.isAnnotationPresent(CustomizedAutowired.class))
.forEach(field -> {
field.setAccessible(true);
try {
field.set(targetObject, new InjectBean());
}
catch (IllegalAccessException e) { e.printStackTrace(); }
});
return bean;
}
}

After searching from google, I found that the reason why NPE happens is that we got the wrong targetObject, BeanPostProcessor.postProcessAfterInitialization(Object bean, String beanName) gives us a proxied bean, and if we use AOP, it will be proxied twice, replace postProcessAfterInitialization with postProcessBeforeInitialization can solve this problem, or another solution that can do the Injection Operation before being proxied by AOP.

Related

Component bean is not injected (autowired) by spring when running AWS lambda function using Spring Cloud function AWS Adapter

I am implementing AWS lambda function creating handler using Spring Cloud function AWS Adapter SpringBootRequestHandler. The functional bean registered in GenericApplicationContext is invoked, but autowiring of component class is giving NullPointer Exception.
I have tried #ComponentScan for base package at Spring Application.
Application class:
#Slf4j
#SpringBootApplication
#ComponentScan({ "com.poc.evthub" })
#EnableConfigurationProperties(EventHubProperties.class)
public class EventHubServerlessApplication implements ApplicationContextInitializer<GenericApplicationContext> {
public EventHubServerlessApplication() {
}
public static void main(String[] args) throws Exception {
FunctionalSpringApplication.run(EventHubServerlessApplication.class, args);
}
#Bean
public KinesisEventFunction ingestEvents() {
return new KinesisEventFunction();
}
#Override
public void initialize(GenericApplicationContext context) {
log.debug("======== initialize ========");
context.registerBean("ingestEvents", FunctionRegistration.class,
() -> new FunctionRegistration<Function<KinesisEvent, ApiResponse>>(ingestEvents())
.type(FunctionType.from(KinesisEvent.class).to(ApiResponse.class).getType()));
}
}
Handler:
public class KinesisEventHandler extends SpringBootRequestHandler<KinesisEvent, ApiResponse> {
}
Functional Bean:
package com.poc.evthub.function;
import com.amazonaws.services.lambda.runtime.events.KinesisEvent;
import com.poc.evthub.beans.ApiResponse;
import com.poc.evthub.constant.Constants;
import com.poc.evthub.service.IngestionServiceFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.function.Function;
#Slf4j
#Component
public class KinesisEventFunction implements Function<KinesisEvent, ApiResponse> {
private Context context = null;
#Autowired
private IngestionServiceFactory ingestionServiceFactory;
#Autowired
#Qualifier("targetExecutionContext")
public void setContext(Context context) {
log.info("Context: {}", context);
this.context = context;
}
#Override
public ApiResponse apply(final KinesisEvent kinesisEvent) {
log.info("KinesisEventFunction apply called...");
String sourceDomain = System.getenv(Constants.SYSENV.SOURCE_DOMAIN);
log.info("Source Domain = {}", sourceDomain);
if(null == kinesisEvent || null == kinesisEvent.getRecords()) {
log.error("Event contains no data. {}", System.lineSeparator());
//TODO build response NOT FOUND
return null;
}
else
log.info("Received {} records from {}. {}",
kinesisEvent.getRecords().size(),
kinesisEvent.getRecords().get(0).getEventSourceARN(),
System.lineSeparator());
log.info("ingestionServiceFactory = {}",ingestionServiceFactory);
ingestionServiceFactory.ingest();
return null;
}
}
Complete code and pom is uploaded at:
https://github.com/rjavaria/eventhub-serverless
KinesisEventFunction apply called...
Also able to read the environment value(source_domain) from lambda, and receiving Kinesis event record.
But #Autowired ingestionServiceFactory is null. I am injecting this component bean to delegate the business logic.
What is missed here, so spring is not able to inject this component bean?
Thanks in advance!
You could try manual injection of your IngestionServiceFactory bean into your function class via constructor or setter injection.
In your function class, add a constructor and remove #Autowired from your IngestionServiceFactory field, like:
...
public class KinesisEventFunction implements Function<KinesisEvent, ApiResponse> {
...
// No #Autowired here...
private final IngestionServiceFactory ingestionServiceFactory;
...
// The new constructor here...
public KinesisEventFunction(final IngestionServiceFactory pIngestionServiceFactory) {
this.ingestionServiceFactory = pIngestionServiceFactory;
}
...
}
Then in your main class (the one implementing ApplicationContextInitializer<GenericApplicationContext>), pass the reference to the factory when registering the function bean, like:
...
public class EventHubServerlessApplication implements ApplicationContextInitializer<GenericApplicationContext> {
...
#Autowired
private IngestionServiceFactory ingestionServiceFactory;
...
#Bean
public KinesisEventFunction ingestEvents() {
return new KinesisEventFunction(this.ingestionServiceFactory);
}
#Override
public void initialize(GenericApplicationContext context) {
log.debug("======== initialize ========");
context.registerBean("ingestEvents", FunctionRegistration.class,
() -> new FunctionRegistration<>(ingestEvents())
.type(FunctionType.from(KinesisEvent.class).to(ApiResponse.class).getType()));
}
}
Or, as you are already manually registering the function bean with context.registerBean(), you could just:
...
public class EventHubServerlessApplication implements ApplicationContextInitializer<GenericApplicationContext> {
...
#Autowired
private IngestionServiceFactory ingestionServiceFactory;
...
#Override
public void initialize(GenericApplicationContext context) {
log.debug("======== initialize ========");
context.registerBean("ingestEvents", FunctionRegistration.class,
() -> new FunctionRegistration<>(new KinesisEventFunction(this.ingestionServiceFactory))
.type(FunctionType.from(KinesisEvent.class).to(ApiResponse.class).getType()));
}
}
Please, let me know if it works!

SFTP : BeanPostProcessor interfere with #ServiceActivator and #MessagingGateway

It seems BeanPostProcessor interface implementation is having impact on #ServiceActivator. What should be the way to use BeanPostProcessor with #ServiceActivator. Thanks.
Complete logs are available here logs
Following is Java Config used for SFTP -
package com.ftp.example;
import java.io.File;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.expression.common.LiteralExpression;
import org.springframework.integration.annotation.Gateway;
import org.springframework.integration.annotation.IntegrationComponentScan;
import org.springframework.integration.annotation.MessagingGateway;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.config.EnableIntegration;
import org.springframework.integration.file.FileNameGenerator;
import org.springframework.integration.file.remote.session.CachingSessionFactory;
import org.springframework.integration.file.remote.session.SessionFactory;
import org.springframework.integration.sftp.outbound.SftpMessageHandler;
import org.springframework.integration.sftp.session.DefaultSftpSessionFactory;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageHandler;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import com.jcraft.jsch.ChannelSftp.LsEntry;
#Configuration
#EnableScheduling
#EnableAspectJAutoProxy
#EnableAsync
#IntegrationComponentScan
#EnableIntegration
#EnableBatchProcessing
#PropertySource("file:C:\\DEV\\workspace_oxygen\\ftp-example\\ftp-example.properties")
public class DependencySpringConfiguration {
private Logger LOG = LoggerFactory.getLogger(DependencySpringConfiguration.class);
#Value("${project.name}")
private String applicationName;
#Value("${${project.name}.ftp.server}")
private String server;
#Value("${${project.name}.ftp.port}")
int port;
#Value("${${project.name}.ftp.username}")
private String username;
#Value("${${project.name}.ftp.password}")
private String password;
#Value("${${project.name}.ftp.remote.directory}")
private String remoteDirectory;
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
#Bean
public ProcessStarter processStarter() {
return new ProcessStarter();
}
/* #Bean
public LogInjector logInjector() {
return new LogInjector();
}*/
#Bean
public FTPOutService fTPOutService() {
return new FTPOutService();
}
#Bean
public SessionFactory<LsEntry> sftpSessionFactory() {
DefaultSftpSessionFactory sf = new DefaultSftpSessionFactory();
sf.setHost(server);
sf.setPort(port);
sf.setUser(username);
sf.setPassword(password);
sf.setAllowUnknownKeys(true);
return new CachingSessionFactory<LsEntry>(sf);
}
#Bean
#ServiceActivator(inputChannel = "toSftpChannel")
public MessageHandler handler() {
SftpMessageHandler handler = new SftpMessageHandler(sftpSessionFactory());
handler.setRemoteDirectoryExpression(new LiteralExpression(remoteDirectory));
handler.setFileNameGenerator(new FileNameGenerator() {
#Override
public String generateFileName(Message<?> message) {
return "fileNameToBeFtp.txt";
}
});
return handler;
}
#MessagingGateway
public interface MyGateway {
#Gateway(requestChannel = "toSftpChannel")
void sendToSftp(File file);
}
}
And We are calling gateway object like this while doing SFTP
Main class
public class FtpExample {
public static String[] ARGS;
private static final Logger LOG = LoggerFactory.getLogger(FtpExample.class);
public static void main(String[] args) throws Exception {
ARGS = args;
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(DependencySpringConfiguration.class);
ProcessStarter processStarter = ctx.getBean(ProcessStarter.class);
processStarter.startService();
}
}
Other classes -
public class ProcessStarter {
#Inject
private FTPOutService ftpOutService;
public void startService() {
ftpOutService.ftpToBbg();
}
}
public class FTPOutService {
private static Logger log = LoggerFactory.getLogger(FTPOutService.class);
#Inject
private ApplicationContext appContext;
public void ftpToBbg() {
log.info("Starting FTP out process...");
File file = null;
try {
file = new File("C:\\Temp\\log\\debug\\ftp\\priceindex\\for-upload\\ftp-example.txt.REQ");
MyGateway gateway = appContext.getBean(MyGateway.class);
gateway.sendToSftp(file);
log.info("File {} written successfully on remote server", file);
} catch (Exception e) {
log.error("Error while uploading file {}", file, e);
}
}
}
Above code is working fine unless I am not adding following bean declaration in above defined Java Config -
public LogInjector logInjector() {
return new LogInjector();
}
Above bean definition is having following implementation -
public class LogInjector implements BeanPostProcessor {
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
#Override
public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
ReflectionUtils.doWithFields(bean.getClass(), new FieldCallback() {
#Override
public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
// make the field accessible if defined private
ReflectionUtils.makeAccessible(field);
if (field.getAnnotation(Log.class) != null) {
if (org.slf4j.Logger.class == field.getType()) {
org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(bean.getClass());
field.set(bean, log);
} else if (java.util.logging.Logger.class == field.getType()) {
java.util.logging.Logger log = java.util.logging.Logger.getLogger(bean.getClass().toString());
field.set(bean, log);
}
}
}
});
return bean;
}
}
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
#Documented
public #interface Log {
}
Once any BeanPostProcessor implementation is added in Java Config, it creates problem and application not able to see toSftpChannel -
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
bean named 'toSftpChannel' available at
org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:685)
at
org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1199)
at
org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:284)
at
org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
at
org.springframework.integration.support.channel.BeanFactoryChannelResolver.resolveDestination(BeanFactoryChannelResolver.java:88)
at
org.springframework.integration.support.channel.BeanFactoryChannelResolver.resolveDestination(BeanFactoryChannelResolver.java:45)
at
org.springframework.integration.gateway.MessagingGatewaySupport.getRequestChannel(MessagingGatewaySupport.java:327)
at
org.springframework.integration.gateway.MessagingGatewaySupport.send(MessagingGatewaySupport.java:368)
at
org.springframework.integration.gateway.GatewayProxyFactoryBean.invokeGatewayMethod(GatewayProxyFactoryBean.java:477)
at
org.springframework.integration.gateway.GatewayProxyFactoryBean.doInvoke(GatewayProxyFactoryBean.java:429)
at
org.springframework.integration.gateway.GatewayProxyFactoryBean.invoke(GatewayProxyFactoryBean.java:420)
at
org.springframework.integration.gateway.GatewayCompletableFutureProxyFactoryBean.invoke(GatewayCompletableFutureProxyFactoryBean.java:65)
at
org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at
org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213)
at com.sun.proxy.$Proxy57.sendToSftp(Unknown Source)
Looks what you have:
#Bean
public LogInjector logInjector() {
return new LogInjector();
}
If you declare BeanPostProcessors as #Bean you have to specify them with the static modifier: https://docs.spring.io/spring/docs/5.0.0.RELEASE/spring-framework-reference/core.html#beans-factorybeans-annotations
You may declare #Bean methods as static, allowing for them to be called without creating their containing configuration class as an instance. This makes particular sense when defining post-processor beans, e.g. of type BeanFactoryPostProcessor or BeanPostProcessor, since such beans will get initialized early in the container lifecycle and should avoid triggering other parts of the configuration at that point.

#Before #PostConstruct Spring AOP ineterception

I'm trying to write an aspect that can intercept PostConstruct methods. I've looked at related questions on SO and others, and following them, this is what I have so far:
The Spring configuration
#Configuration
#EnableAspectJAutoProxy
#EnableLoadTimeWeaving
#...//other config annotations
public class WebConfiguration {
#Bean
public CommonAnnotationBeanPostProcessor commonAnnotationBeanPostProcessor() {
return new CommonAnnotationBeanPostProcessor();
}
... // etc
}
The annotation:
#Retention(RetentionPolicy.RUNTIME)
public #interface Secured {
Permission[] permissions() default {};
}
The bean
#Component
#Scope("request")
public class SomeWebBean {
#Secured(permissions = Permission.SOME_PERMISSION)
#PostConstruct
public void secure() {
... // some stuff
}
}
The aspect
#Component
#Aspect
public class SecuredAspect {
#Before("#annotation(secured)")
public void doAccessCheck(Secured secured) {
... // actually do the access check
}
}
If I call someWebBean.secure() from a page, then the aspect is invoked. However, it is not invoked on bean creation.
So as a note to future me - this absolutely cannot be done in this way using Spring AOP.
However, the same effect can be achieved by implementing a BeanPostProcessor as below:
public class SecureBeanPostProcessor implements BeanPostProcessor {
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
Secured secured = bean.getClass().getAnnotation(Secured.class);
if (secured != null) {
// do your security test here, throw an exception, return false, however you like
}
return bean;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
You can extend CommonAnnotationBeanPostProcessor and override postProcessBeforeInitialization(Object bean, String beanName)
Then register replace the original CommonAnnotationBeanPostProcessor with a BeanFactoryPostProcessor .
public class InitCommonAnnotationBeanPostProcessor extends CommonAnnotationBeanPostProcessor {
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return super.postProcessBeforeInitialization(bean, beanName);
}
}
public class InitBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
RootBeanDefinition def = new RootBeanDefinition(InitCommonAnnotationBeanPostProcessor.class);
def.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(AnnotationConfigUtils.COMMON_ANNOTATION_PROCESSOR_BEAN_NAME, def);
}
}
#Configuration
public class InitialisationMonitoringConfig {
public static final String BEAN_INIT_MONITOR = "BEAN_INIT_MONITOR";
#Bean
public static InitBeanFactoryPostProcessor initBeanFactoryPostProcessor() {
return new InitBeanFactoryPostProcessor();
}
}
This is ugly, but I had to do that to analyse startup times in dev environment.
Maybe it's enough to just declare InitCommonAnnotationBeanPostProcessor as a bean, I didn't tried.

Spring proxy beans from Interface

What is the correct way to create proxy beans by interfaces?
public class JdbiRepositoryAnnotationBeanPostProcessorTest {
private DBI dbi = mock(DBI.class);
#org.junit.Test
public void testIncompleteBeanDefinition() {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(JdbiRepositoryAnnotationBeanPostProcessor.class);
ctx.register(MyConfig.class);
ctx.refresh();
ITest bean = ctx.getBean(ITest.class);
assertNotNull(bean);
}
#JdbiRepository
public static interface ITest {
}
#Configuration
#ComponentScan(basePackageClasses = {ITest.class},
includeFilters = {#ComponentScan.Filter(type = FilterType.ANNOTATION, value = JdbiRepository.class)})
public static class MyConfig {
}
}
I have tried bean post processor but It did not help me.
Edit:
I wanted to use component scanning by including annotation filter but it did not help me too.
Edit:
I want to create instances by another library which is creating proxy beans as this:
TestInterface proxy = factory.onDemand(TestInterface.class);
Edit:
I have extended InstantiationAwareBeanPostProcessorAdapter for JdbiRepositoryAnnotationBeanPostProcessor.
I have been just logging currently. But I can not see my interfaces as a bean.
Please note that I have also changed my test code above.
public class JdbiRepositoryAnnotationBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
private ConfigurableListableBeanFactory beanFactory;
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
#Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
if(!(beanFactory instanceof ConfigurableListableBeanFactory)) {
throw new IllegalArgumentException("AutowiredAnnotationBeanPostProcessor requires a ConfigurableListableBeanFactory");
}
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
// this.dbiMap = this.beanFactory.getBeansOfType(DBI.class);
}
}
The problem was related to ComponentScanning not PostBeanProcessor. ComponentScan is scanning only concrete classes that is why my processor did not work. I had to create a custom importer for interfaces.

How do I enumerate all the BeanFactories in Spring?

Is there a way to discover all the BeanFactories defined by Spring programmatically. I want to create a status debug page which prints out the names and class types of every bean in the spring application context, however I don't know how to obtain a list of all the ApplicationContexts.
You can wire a BeanFactoryPostProcessor with your ApplicationContext that will allow you to traverse the BeanDefinition's of a passed ConfigurableListableBeanFactory which will represent all of the beans from your ApplicationContext.
With this instance of ConfigurableListableBeanFactory, you can find all beans of a type (getBeansOfType()), or all beans with a given annotation (getBeansWithAnnotation()), among other things.
You can use the ApplicationContext aware to do this.
#Component
public class PrintSpringBeansInContext implements ApplicationContextAware
{
private ApplicationContext applicationContext;
#Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException
{
this.applicationContext = applicationContext;
}
public void print()
{
String[] beanNames = this.applicationContext.getBeanDefinitionNames();
StringBuilder printBuilder = new StringBuilder("Spring Beans In Context: ");;
for(String beanName : beanNames)
{
printBuilder.append("\n");
printBuilder.append(" Bean Name: ");
printBuilder.append(beanName);
printBuilder.append(" Bean Class: ");
printBuilder.append(this.applicationContext.getBean(beanName).getClass());
}
System.out.println(printBuilder.toString());
}
}
You can test this
#ContextConfiguration(locations={"classpath:context.xml"})
#RunWith(SpringJUnit4ClassRunner.class)
public class PrintContextTest
{
#Autowired
private PrintSpringBeansInContext service;
#Test
public void printBeans()
{
Assert.assertNotNull(service);
service.print();
}
}
Code below is a Spring listener which can be registered with the main spring.xml file for the web application, it builds a map of all the child application contexts and exposes this map as a property. The class below can be injected into any spring bean that needs it using #Autowired.
import java.util.Hashtable;
import java.util.Map;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ApplicationContextEvent;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.ContextStartedEvent;
public class ContextsApplicationListener implements ApplicationListener<ApplicationContextEvent> {
private Map<String,ApplicationContext> contextMap = new Hashtable<String,ApplicationContext>();
#Override
public void onApplicationEvent(ApplicationContextEvent event) {
if( event instanceof ContextStartedEvent || event instanceof ContextRefreshedEvent){
this.getContextMap().put(event.getApplicationContext().getDisplayName(), event.getApplicationContext());
}
}
public Map<String,ApplicationContext> getContextMap() {
return contextMap;
}
}
enter code here

Resources