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
Related
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.
Through #Autowired i am not able to access the #Component/#Service/#Respository/#Controller class objects in other java files which has #Component annotation (Step 1: Approach) with the Step 1 approach getting Null pointer Exception, but same i could achieve using (Step 2: Approach).
Can anyone please tell me why i am not able to achieve using Step 1 approach:
FYI- I've searched in my entire project i have not used/called/initialized the #Component classes using new method for the autowired class still i getting the issue as "Null Pointer Exception"
Step 1: Using #Autowired Annotation
#Component
public class Processor {
#Autowired
PropertyConfigurator propconfigrator; --> Getting here as null pointer Exception
public void getDetails(){
System.out.println ("Application URL +propconfigrator.getProperties().getProperty("appURL"));
}
}
Step 2: Using ApplicationContext Interface with/without #AutoWired annotation . I am able to get the property value from PropertyConfigurator java file
#Component
public class Processor {
#Autowired
PropertyConfigurator propconfigrator = ApplicationContextHolder.getContext().getBean(PropertyConfigurator.class);
public void getDetails(){
System.out.println ("Application URL +propconfigrator.getProperties().getProperty("appURL"));
}
}
ApplicationContextHolder.java
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
#Component
public class ApplicationContextHolder implements ApplicationContextAware {
private static ApplicationContext context;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
public static ApplicationContext getContext() {
return context;
}
}
PropertyConfigurator.java file
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
#Service
#Configurable
public class PropertyConfigurator {
private final Properties properties;
public Properties getProperties () {
return properties;
}
public PropertyConfigurator(){
properties = new Properties();
try {
properties.load(getClass().getClassLoader().getResourceAsStream("dbconfig.properties"));
} catch (IOException e) {
Logger.getLogger(getClass().getName()).log(Level.SEVERE, e.getMessage(), e);
}
}
}
Why did you use #Configurable annotation? In the code you postet, it doesn't make sense. #Configurable is only needed in cases when instances of this class are not createt by spring.
I have changed into Constructor Injection Autowiring as below with the step 1 approach of above (Not using Step 2. It resolved my issue finally.
Not sure why Spring is not able to inject the bean without using the Constructor Autowiring.
Step 1: Using #Autowired Annotation with Constructor
#Component
public class Processor {
#Autowired
public Processor (PropertyConfigurator propconfigrator) {
this.propconfigrator = propconfigrator;
}
public void getDetails(){
System.out.println ("Application URL +propconfigrator.getProperties().getProperty("appURL"));
}
}
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!
I am learning the scope dependency of injected beans in Spring framework. I was learning to solve the narrow scope bean dependency from websites. But I am not able to see the resolution as explained in the websites. I have tried using the method of Inject ApplicationContext bean into MySingletonBean to get instance of MyPrototypeBean. However I do not see any difference in the bean creation.
#SpringBootApplication
#ComponentScan("com.learning.spring.basics.scope.proxy")
public class SpringScopeProxyApplication {
public static void main(String...strings) throws InterruptedException {
ApplicationContext ctx = SpringApplication.run(SpringScopeProxyApplication.class, strings);
SingletonBean bean1=ctx.getBean(SingletonBean.class);
/**
* Singleton bean is wired with a proototypeBean. But since a singleton bean is created only once by the container
* even the autowired proptotyeBean is also created once.
*/
bean1.display();
SingletonBean bean2=ctx.getBean(SingletonBean.class);
bean2.display();
}
}
#Component
public class SingletonBean {
#Autowired
// prototypeBean is of scope prototype injected into singleton bean
private PrototypeBean prototypeBean;
//Fix 1 - Inject ApplicationContext and make the singletonbean applicationcontextaware
#Autowired
private ApplicationContext applicationContext;
public ApplicationContext getApplicationContext() {
return applicationContext;
}
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
public PrototypeBean getPrototypeBean() {
return prototypeBean;
}
public void setPrototypeBean(PrototypeBean prototypeBean) {
this.prototypeBean = prototypeBean;
}
// Fix -1 - get the prototypeBean object from applicationContext everytime the method is called
public void display() {
applicationContext.getBean(PrototypeBean.class).showTime();
//prototypeBean.showTime();
}
PrototypeBean
package com.learning.spring.basics.scope.proxy;
import java.time.LocalDateTime;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
#Component
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class PrototypeBean {
public void showTime() {
System.out.println("Time is "+LocalDateTime.now().toString());
}
}
Expected Results:The time stamp should be different as the prototype scoped bean should be a new instance
Actual results: The timestamps are same with no difference
This is not right way to validate SCOPE_PROTOTYPE bean because showTime is an instance methods that gets executed when you invoke it through object so you will always get the different timestamp.But i'm not sure how you are getting same timestamp, but below is an example
#SpringBootApplication
#ComponentScan("com.learning.spring.basics.scope.proxy")
public class SpringScopeProxyApplication {
public static void main(String...strings) throws InterruptedException {
ApplicationContext ctx = SpringApplication.run(SpringScopeProxyApplication.class, strings);
SingletonBean bean1=ctx.getBean(PrototypeBean.class);
System.out.println(bean1);
SingletonBean bean2=ctx.getBean(PrototypeBean.class);
System.out.println(bean2);
}
}
You will get the two different object references
com.learning.spring.basics.scope.proxy.PrototypeBean#48976e6d
com.learning.spring.basics.scope.proxy.PrototypeBean#2a367e93
I'm learing Spring Boot and I have probably very simple question, but it's not clear enough for me. I'm facing some problem with #Value annotation - I would like to know why apprication property cannot be injected to class parameter.
I prepared some very basic project using Spring Initializr and I added one property to my "application.properties" resource. Moreover, I created two additional classes: "YellowCar" (which works fine) and "RedCar" (which does not work - the parameter cannot be properly injected).
"application.properties" file:
car.age=15
The main class of my application:
package com.example.helper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.core.env.Environment;
#SpringBootApplication
public class HelperApplication implements CommandLineRunner {
#Autowired
private Environment env;
public static void main(String[] args) {
SpringApplication.run(HelperApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
System.out.println (new RedCar(env));
System.out.println (new YellowCar());
}
}
RedCar is build by passing the Environment variable to constructor:
package com.example.helper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
#Component
public class RedCar {
private int age;
#Autowired
public RedCar (Environment env) {
this.age = new Integer(env.getRequiredProperty("car.age")).intValue();
}
#Override
public String toString() {
return "Car [age=" + age + "]";
}
}
YellowCar is build without passing the Environment variable to the constructor, but using the #Value annotation:
package com.example.helper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
#Component
public class YellowCar {
#Value("${car.age}")
private int age;
#Override
public String toString() {
return "YellowCar [age=" + age + "]";
}
}
This is the program output:
Car [age=15]
YellowCar [age=0]
As you can see, age of YellowCar was not properly injected (it equals 0).
My goal: I'd like not to pass everywhere Environment object to the constructor of other classes... I'd like to use #Value annotatnio instead. Could somebody explain me:
1) why my code is not working?
2) how this code should be updated in order to get the following output?
Car [age=15]
YellowCar [age=15]
Thanks!
This happens, because you instantiate YellowCar directly. In order to make thing work, #Autowire YellowCar inside HelperApplication and use this injected instance like this(not tested):
#Autowired
private YellowCar yellowCar;
public static void main(String[] args) {
SpringApplication.run(HelperApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
System.out.println (new RedCar(env));
System.out.println (yellowCar);
}
Explanation:
When you creating class using new, Spring knows nothing about this new instance, hence, it cannot inject anything. But when instance is created through Spring infrastructure (I'm trying not use a lot of slang words here), it will have all fields injected: when you mark class with #Component annotation or create method with #Bean annotation, you tell Spring, that you want it to be a Bean. On application startup, spring creates instances of this beans - by default only one instance per context - and inject this instance into all other beans, where requested. In this case, this instance is being processed by spring infrastructure (exact class which do it - AutowiredAnnotationBeanPostProcessor), and instances of other beans will be injected into our bean.
UPD: You can do the same thing with RedCar:
#Autowired
private YellowCar yellowCar;
#Autowired
private RedCar redCar;
public static void main(String[] args) {
SpringApplication.run(HelperApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
System.out.println (redCar);
System.out.println (yellowCar);
}