I cannot get the bean by adding #Component on it.
I have created a class as below for getting bean in cases where instances are not injected by #autowired.
#Component
public class SpringApplicationContext implements ApplicationContextAware{
private static ApplicationContext CONTEXT;
public static Object getBean(String beanName) {
return CONTEXT.getBean(beanName);
}
#Override
public void setApplicationContext(ApplicationContext context)
throws BeansException {
CONTEXT = context;
}
}
Then I created a AppProperties class for reading the decrypt token from application.properties.
#Component
public class AppProperties {
#Autowired
private Environment env;
public String getTokenSecret() {
return env.getProperty("tokenSecret");
}
}
Then, I try to get the AppProperties instance as bean like this, which SecurityConstants.getTokenSecret() are used as parameter by manually inject in another method like this:
public class SecurityConstants {
...
public static String getTokenSecret() {
//Fail to get bean in this line
AppProperties appProperties = (AppProperties)SpringApplicationContext.getBean("AppProperties");
return appProperties.getTokenSecret();
}
}
but fail with below exception:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'AppProperties' available
After that, I add getAppProperties() with #Bean to make it register as a bean and it worked.
#SpringBootApplication
public class RegAppApplication {
public static void main(String[] args) {
SpringApplication.run(RegAppApplication.class, args);
}
#Bean(name="AppProperties")
public AppProperties getAppProperties() {
return new AppProperties();
}
}
The question is:
Why SpringApplicationContext is run on application start and AppProperties does not?
Why cannot get AppProperties as bean? Isn't adding #Component above the class will make it a component scan target, thus being treated as a bean?
How adding getAppProperties() with #Bean making it different with adding #Component above the class?
Thank you.
Related
I have a custom spring-boot starter project which is used by rest controller, the auto configuration class is used for creating several(according to config value in application.yml) Storage Context instance as spring singleton, so I have to create them dynamically in setBeanFactory method of BeanFactoryAware by :
#Import(StorageContextProperties.class)
public class StorageAutoConfiguration implements BeanFactoryAware {
......
#Override
public void setBeanFactory(BeanFactory factory) throws BeansException {
......
StorageContextProperties config = beanFactory.getBean(StorageContextProperties.class);
config.getProfiles().stream().forEach(p -> {
......
((SingletonBeanRegistry) beanFactory).registerSingleton(profile.name, instance);
the problem is that the method is not been called before the controller #autowired event, so it will complain there is no StorageContext instance, I have also tried BeanFactoryPostProcessor and InitializingBean interface, neither of them works.
But, I notice if I just add some special #Bean method into the auto config class, let's say :
#Import(StorageContextProperties.class)
public class StorageAutoConfiguration implements BeanFactoryAware{
#Bean
public MethodValidationPostProcessor validationPostProcessor2() {
return new MethodValidationPostProcessor();
}
......
#Override
public void setBeanFactory(BeanFactory factory) throws BeansException {
then the setBeanFactory() will be called before the controller, it seems that spring needs MethodValidationPostProcessor instance, so it created shared instance of singleton bean: StorageAutoConfiguration, also create StorageContextProperties instance and call setBeanFactory().
the code works if I add above #Bean method. well things also goes well in this way, but I don't like the style since I actually have no need for MethodValidationPostProcessor.
is there any elegant(without #Bean method) way to achieve it?
my requirements are :
before the controller creating event.
1 create StorageContextProperties ( it's a #ConfigurationProperties class by #Import)
2 some callback I can call registerSingleton() to create my Storage Context instances
Thanks!
[UPDATED1]
I still don't find any way to make it works, but I changed the code as :
#Configuration
#Import(StorageContextProperties.class)
#ConditionalOnProperty(prefix = "avalon.thiton.storage.config", name = "masterKey")
public class StorageAutoConfiguration implements BeanFactoryAware {
private static Logger log = LoggerFactory.getLogger(StorageAutoConfiguration.class);
#Bean
#Order(Ordered.LOWEST_PRECEDENCE)
#ConditionalOnMissingBean(MethodValidationPostProcessor.class)
public MethodValidationPostProcessor placeholder() {
return new MethodValidationPostProcessor();
}
......
It makes me feel..... better.
Hi I've had similar problem when I was trying to initialize custom beans using BeanFactoryAware interface - The #Configuration annotated class was like:
#Configuration
#EnableConfigurationProperties(SomeProps.class)
class MyConfiguration implements BeanFactoryAware {
#Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
...
...
}
#Bean
MyBean myBean(){
return new MyBean();
}
}
It did not run this configuration class code before MyBean was needed and NoSuchBeanDefinitionException were thrown.
To fix this I've changed BeanFactoryAware interface to extending AbstractBeanFactoryAwareAdvisingPostProcessor like that:
#Configuration
#EnableConfigurationProperties(SomeProps.class)
class MyConfiguration extends AbstractBeanFactoryAwareAdvisingPostProcessor {
#Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
...
...
}
#Bean
MyBean myBean(){
return new MyBean();
}
}
I am trying to #Autowire a #Configuration class inside a #Service class. basically my #Configuration class contains mapping to my custom .properties file. When i try to autowire my configuration class inside my service class, BeanCreationException occurs. I am not sure what happen. Just followed the guide on creating Property classes from spring. There must be something i missed out.
Also, when i try to autowire #Configuration class to another #Configuration class, it runs smoothly
Currently, i know that, prop is always null because when i remove prop.getUploadFileLocation() call, everything will be fine. There must be something wrong during autowiring.
Here is my Service class
#Service
public class ImageService {
public static Logger logger = Logger.getLogger(ImageService.class.getName());
#Autowired
MyProperties prop;
private final String FILE_UPLOAD_LOCATION = prop.getUploadFileLocation() +"uploads/images/";
public void upload(String base64ImageFIle) throws IOException {
logger.info(FILE_UPLOAD_LOCATION);
}
}
Here is my Configuration class
#Data
#Configuration
#ConfigurationProperties (prefix = "my")
public class MyProperties {
private String resourceLocation;
private String resourceUrl;
public String getUploadFileLocation() {
return getResourceLocation().replace("file:///", "");
}
public String getBaseResourceUrl() {
return getResourceUrl().replace("**", "");
}
}
And here is where i can successfully use MyProperties
#Configuration
public class StaticResourceConfiguration implements WebMvcConfigurer {
#Autowired
MyProperties prop;
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler(prop.getResourceUrl())
.addResourceLocations(prop.getResourceLocation());
}
}
The issue is that you are trying to use an autowired field to set the value in an inline field assignment.
That means
private final String FILE_UPLOAD_LOCATION = prop.getUploadFileLocation() +"uploads/images/";
is executed before the prop is autowired, meaning it will always be null
The way to mitigate this would be to use constructor injection instead.
#Service
public class ImageService {
//Fine since you are using static method
public static Logger logger = Logger.getLogger(ImageService.class.getName());
//Not needed if you are only using it to set FILE_UPLOAD_LOCATION
//Allows field to be final
private final MyProperties prop;
//Still final
private final String FILE_UPLOAD_LOCATION;
//No need for #Autowired since implicit on component constructors
ImageService(MyProperties prop){
//Again not needed if you aren't going to use anywhere else in the class
this.prop = prop;
FILE_UPLOAD_LOCATION = prop.getUploadFileLocation() +"uploads/images/";
}
public void upload(String base64ImageFIle) throws IOException {
logger.info(FILE_UPLOAD_LOCATION);
}
}
See this question for why constructor is preferred over #autowired in general
If you need MyProperties bean to be created before StaticResourceConfiguration bean, you can put #ConditionalOnBean(MyProperties.class) as following. Spring will make sure MyProperties is there before processing StaticResourceConfiguration.
#Configuration
#ConditionalOnBean(MyProperties.class)
public class StaticResourceConfiguration implements WebMvcConfigurer {
I need to create multiple instances of a spring bean (let's call it MainPrototypeBean), which I can do with the prototype scope. It depends on some other beans, and I want to create new instances of them each time the main bean is created. However, there is a shared dependency between some of the beans, let's call it SharedPrototypeBean. How do I inject the same instance of SharedPrototypeBean in each of the dependent beans, while also creating a new instance for each MainPrototypeBean?
I'm looking into implementing a custom scope, but I'm hoping to find a cleaner way. Making any of the beans singletons is not an option, as they need to be isolated between different instances of MainPrototypeBean.
Here's an example of what I'm trying to do:
#SpringBootApplication
public class DIDemo {
public static void main(String[]args){
ConfigurableApplicationContext context = SpringApplication.run(DIDemo.class, args);
context.getBean(MainPrototypeBean.class);
}
#Component #Scope("prototype") static class SharedPrototypeBean {}
#Component #Scope("prototype") static class FirstPrototypeBean {
#Autowired SharedPrototypeBean shared;
#PostConstruct public void init() {
System.out.println("FirstPrototypeBean.init() with shared " + shared);
}
}
#Component #Scope("prototype") static class SecondPrototypeBean {
#Autowired SharedPrototypeBean shared;
#PostConstruct public void init() {
System.out.println("SecondPrototypeBean.init() with shared " + shared);
}
}
#Component #Scope("prototype") static class MainPrototypeBean {
#Autowired FirstPrototypeBean first;
#Autowired SecondPrototypeBean second;
}
}
And the output of executing it is:
FirstPrototypeBean.init() with shared DIDemo$SharedPrototypeBean#1b84f475
SecondPrototypeBean.init() with shared DIDemo$SharedPrototypeBean#539d019
You can use the FactoryBean for complex construction logic. Implement its abstract subclass AbstractFactoryBean for creating a MainPrototypeBean, and inject all three dependent beans into it. You can then wire them together in the createInstance method.
The FactoryBean implementation:
public class MainFactoryBean extends AbstractFactoryBean<MainPrototypeBean> implements FactoryBean<MainPrototypeBean> {
private FirstPrototypeBean firstPrototype;
private SecondPrototypeBean secondPrototpye;
private SharedPrototypeBean sharedPrototype;
public MainFactoryBean(FirstPrototypeBean firstPrototype, SecondPrototypeBean secondPrototype, SharedPrototypeBean sharedPrototype) {
this.firstPrototype = firstPrototype;
this.secondPrototpye = secondPrototype;
this.sharedPrototype = sharedPrototype;
}
#Override
protected MainPrototypeBean createInstance() throws Exception {
MainPrototypeBean mainPrototype = new MainPrototypeBean();
firstPrototype.setSharedPrototypeBean(sharedPrototype);
secondPrototpye.setSharedPrototypeBean(sharedPrototype);
mainPrototype.first = firstPrototype;
mainPrototype.second = secondPrototpye;
//call post construct methods on first and second prototype beans manually
firstPrototype.init();
secondPrototpye.init();
return mainPrototype;
}
#Override
public Class<?> getObjectType() {
return MainPrototypeBean.class;
}
}
Note: sharedPrototype is injected after the post-construct phase in the lifecycle of the first and second prototype. So, if you have post-construction logic in these beans that require the sharedPrototype, you need to manually call the init-method when creating the MainPrototypeBean.
Your annotation - configuration changes as as a consequence. The sharedPrototype attributes are no longer autowired (they are set inside FactoryBean), and MainPrototypeBean is not annotated anymore. Instead you need to create the MainFactoryBean.
#Configuration
public class JavaConfig {
//method name is the name refers to MainPrototypeBean, not to the factory
#Bean
#Scope("prototype")
public MainFactoryBean mainPrototypeBean(FirstPrototypeBean firstPrototype, SecondPrototypeBean secondPrototype, SharedPrototypeBean sharedPrototype) {
return new MainFactoryBean(firstPrototype, secondPrototype, sharedPrototype);
}
//Annotations are not needed anymore
static class MainPrototypeBean {
FirstPrototypeBean first;
SecondPrototypeBean second;
}
#Component
#Scope("prototype")
static class SharedPrototypeBean {
}
#Component
#Scope("prototype")
static class FirstPrototypeBean {
private SharedPrototypeBean shared;
//no autowiring required
public void setSharedPrototypeBean(SharedPrototypeBean shared) {
this.shared = shared;
}
#PostConstruct
public void init() {//reference to shared will be null in post construction phase
System.out.println("FirstPrototypeBean.init() with shared " + shared);
}
}
#Component
#Scope("prototype")
static class SecondPrototypeBean {
private SharedPrototypeBean shared;
public void setSharedPrototypeBean(SharedPrototypeBean shared) {
this.shared = shared;
}
#PostConstruct
public void init() {
System.out.println("SecondPrototypeBean.init() with shared " + shared);
}
}
}
After reading the comments and the other answer, I realized that the design is indeed too complex. I made SharedPrototypeBean, FirstPrototypeBean and SecondPrototypeBean regular POJOs, not managed by Spring. I then create all of the objects in a #Bean annotated method.
#Bean
public MainPrototypeBean mainPrototypeBean() {
Shared shared = new Shared();
First first = new First(shared);
Second second = new Second(shared);
return new MainPrototypeBean(first, second);
}
I am developing a SpringBoot project and I want to get the bean by its name using applicationContext. I have tried many solution from web but could not succeed. My Requirement is that I have a controller
ControllerA
and inside the controller I have a method getBean(String className). I want to get instance of registered bean. I have hibernate entities and I want to get an instance of the bean by passing the name of class only in getBean method.
Please help if someone know the solution.
You can Autowire the ApplicationContext, either as a field
#Autowired
private ApplicationContext context;
or a method
#Autowired
public void context(ApplicationContext context) { this.context = context; }
Finally use
context.getBean(SomeClass.class)
You can use ApplicationContextAware.
ApplicationContextAware:
Interface to be implemented by any object that wishes to be notified
of the ApplicationContext that it runs in. Implementing this interface
makes sense for example when an object requires access to a set of
collaborating beans.
There are a few methods for obtaining a reference to the application context. You can implement ApplicationContextAware as in the following example:
package hello;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
#Component
public class ApplicationContextProvider implements ApplicationContextAware {
private ApplicationContext applicationContext;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public ApplicationContext getContext() {
return applicationContext;
}
}
Update:
When Spring instantiates beans, it looks for ApplicationContextAware implementations, If they are found, the setApplicationContext() methods will be invoked.
In this way, Spring is setting current applicationcontext.
Code snippet from Spring's source code:
private void invokeAwareInterfaces(Object bean) {
.....
.....
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware)bean).setApplicationContext(this.applicationContext);
}
}
Once you get the reference to Application context, you get fetch the bean whichever you want by using getBean().
actually you want to get the object from the Spring engine, where the engine already maintaining the object of your required class at that starting of the spring application(Initialization of the Spring engine).Now the thing is you just have to get that object to a reference.
in a service class
#Autowired
private ApplicationContext context;
SomeClass sc = (SomeClass)context.getBean(SomeClass.class);
now in the reference of the sc you are having the object.
Hope explained well. If any doubt please let me know.
Even after adding #Autowire if your class is not a RestController or Configuration Class, the applicationContext object was coming as null. Tried Creating new class with below and it is working fine:
#Component
public class SpringContext implements ApplicationContextAware{
private static ApplicationContext applicationContext;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws
BeansException {
this.applicationContext=applicationContext;
}
}
you can then implement a getter method in the same class as per your need to get the bean. Like:
applicationContext.getBean(String serviceName,Interface.Class)
Using SpringApplication.run(Class<?> primarySource, String... arg) worked for me. E.g.:
#SpringBootApplication
public class YourApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(YourApplication.class, args);
}
}
As an alternative approach you can use ConfigurableApplicationContext to get bean of any class which is annotated with #Component, #Repository or #Service.
Let's say you want to get a bean of the class BaseComponent :
#Service
public class BaseComponent {
public String getMessage() {
return "hello world";
}
}
Now you can use ConfigurableApplicationContext to get the bean:
#Component
public class DemoComponent {
#Autowired
ConfigurableApplicationContext applicationContext;
public BaseComponent getBeanOfBaseComponent() {
return applicationContext.getBean(BaseComponent.class);
}
}
You can use the ApplicationContextAware class that can provide the application context.
public class ApplicationContextProvider implements ApplicationContextAware {
private static ApplicationContext ctx = null;
public static ApplicationContext getApplicationContext() {
return ctx;
}
#Override
public void setApplicationContext(final ApplicationContext ctx) throws BeansException {
ApplicationContextProvider.ctx = ctx;
}
/**
* Tries to autowire the specified instance of the class if one of the specified
* beans which need to be autowired are null.
*
* #param classToAutowire the instance of the class which holds #Autowire
* annotations
* #param beansToAutowireInClass the beans which have the #Autowire annotation
* in the specified {#classToAutowire}
*/
public static void autowire(Object classToAutowire, Object... beansToAutowireInClass) {
for (Object bean : beansToAutowireInClass) {
if (bean == null) {
ctx.getAutowireCapableBeanFactory().autowireBean(classToAutowire);
}
}
}
}
If you are inside of Spring bean (in this case #Controller bean) you shouldn't use Spring context instance at all. Just autowire className bean directly.
BTW, avoid using field injection as it's considered as bad practice.
One API method I use when I'm not sure what the bean name is org.springframework.beans.factory.ListableBeanFactory#getBeanNamesForType(java.lang.Class<?>). I simple pass it the class type and it retrieves a list of beans for me. You can be as specific or general as you'd like to retrieve all the beans associated with that type and its subtypes, example
#Autowired
ApplicationContext ctx
...
SomeController controller = ctx.getBeanNamesForType(SomeController)
Easy way in configration class call the BEAN annoted method . Yes u heard it right---- :P calling SpringBoot #Bean annoted method return the same bean from config .I was trying to call a logout in #predestroy method in config class from a bean and direcltly called the method to get the same bean .
P.S. : I added debug in the #bean annotated method but it didn't entered the method even when i called it.Sure to blame -----> Spring Magic <----
You can use ServiceLocatorFactoryBean. First you need to create an interface for your class
public interface YourClassFactory {
YourClass getClassByName(String name);
}
Then you have to create a config file for ServiceLocatorBean
#Configuration
#Component
public class ServiceLocatorFactoryBeanConfig {
#Bean
public ServiceLocatorFactoryBean serviceLocatorBean(){
ServiceLocatorFactoryBean bean = new ServiceLocatorFactoryBean();
bean.setServiceLocatorInterface(YourClassFactory.class);
return bean;
}
}
Now you can find your class by name like that
#Autowired
private YourClassfactory factory;
YourClass getYourClass(String name){
return factory.getClassByName(name);
}
Just use:
org.springframework.beans.factory.BeanFactory#getBean(java.lang.Class)
Example:
#Component
public class Example {
#Autowired
private ApplicationContext context;
public MyService getMyServiceBean() {
return context.getBean(MyService.class);
}
// your code uses getMyServiceBean()
}
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;
}