have have these two apps which actually do the same (if I am correct)
#SpringBootApplication
public class DemoApplication {
#Autowired
HelloWorld helloWorld;
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#Bean
public CommandLineRunner run() {
helloWorld.setMessage("wow");
return (load) -> {
helloWorld.getMessage();
};
}
}
and
public class MainApp {
public static void main(String[] args) {
ApplicationContext context = new
ClassPathXmlApplicationContext("Beans.xml");
HelloWorld obj = (HelloWorld) context.getBean("helloWorld");
obj.getMessage();
}
}
both uses
#Component
public class HelloWorld {
private String message;
public void setMessage(String message) {
this.message = message;
}
public void getMessage() {
System.out.println("Your Message : " + message);
}
}
The only difference at the helloWord obj is, that if I use the MainApp-class in my program, then the helloWorld class doesn't need the #Component annotation.
My Question:
If I am correct the SpringBoot annotation makes it unnecessary to define a ClassPathXMLApplicationContext. #Autowire does that for me.
I am now interested if I AutoWire lets say 100 objects at the beginning, all these objects are now in the IoC container correct?
If so: Is not possible to just hand out that container in a CTOR of another class and have access to all saved objects there like:
(HelloWorld) context.getBean("helloWorld"); or
(someRandomClass) context.getBean("someRandomClass")
public CTOR(IOCContainer container) {
this.container = container;
}
Instead of that implementation
public CTOR(HelloWorld helloWorld, SomeRandomClass someRandomClass) {
this.helloWorld = helloWorld;
this.someRandomClass = someRandomClass;
}
And if that is possible, how can I do that?
(There is no use case/task behind my question, i am just interested if that is possible)
The XML'ish way of configuration where you define your bean and wiring via
<bean ... etc. pp.
can be completely replaced by either using
#Component
public class MyClass ....
or by
#Bean
public MyClass myClass() {return new MyClass();}
definition in a configuration class. Both ways place the entity in the IoC container of Spring.
The #Autowire just informs the IoC container of Spring that you would like to have a bean fulfilling the contract of the entity marked with #Autowire injected into this place.
In order to get access to the container you just need to inject the ApplicationContext where you would like to have it.
There are two ways of creating beans in Spring. One is through XML config and the other is through annotation config. Annotation config is the preferred approach as it has lot of advantages over xml config.
Spring boot doesnt have any thing to do with annotation or xml config. Its just a easy way to boot spring application. #Component creates the object of the annotated bean in the application context. #Import or #ImportResource are the annotations used to load the configs from Annotations or through XML configs in Spring boot. With Spring boot u need not create ClassPathXMlCOntext or AnnotationContext objects, but its created internally by spring boot.
#Autowired is a way of getting the beans into any object by injecting rather than tight coupling to the code. Spring container(Application context) do this job of injecting. Just autowiring any class wont create the objects in Spring context. Its just an indication for the Spring context to set the object in the Application context here. You need to create them explicitly inside a xml config/ or annotations like #Component #Service others.
There is no need of hand out of container anywhere. U can just #Autowire ApplicationContext context; in any other spring bean object. With which you can call getBean(YourBean.class) to get that bean.
Related
This question already has answers here:
Injecting beans into a class outside the Spring managed context
(8 answers)
Closed 3 years ago.
Introduction
I have some business logic properties in the application.yml file.
They are loaded into the application via a #ConfigurationProperties annotated class.
How could I use these properties in a class which is not a Spring Bean? It cannot be a singleton, because many objects of it must be created during run-time.
Example
application.yml
business.foo: 2
BusinessProperties.java
#ConfigurationProperties("business")
#Getter // lombok
#Setter // lombok
public class BusinessProperties {
private int foo;
}
TypicalBean.java
#Component
public class TypicalBean {
private final BusinessProperties properties;
#Autowired
public TypicalBean(BusinessProperties properties) {
this.properties = properties;
}
#PostConstruct
public void printFoo() {
System.out.println("Foo: " + properties.getFoo()); // "Foo: 2"
}
}
NonBean.java
public class NonBean {
public void printFoo() {
System.out.println("Foo: ???"); // How to access the property?
}
}
Is there some way to create a non-singleton class, which can have access to configuration (or even other Spring beans) but otherwise works the same as a regular java class? Meaning that I can control its creation, it is collected by the garbage collector if not used anymore, etc.
You can still define the NonBean.class as a Component with Scope.Prototype
#Component
#Scope(value = BeanDefinition.SCOPE_PROTOTYPE)
public class NonBean {
#Autowired
public TypicalBean(BusinessProperties properties) {
this.properties = properties;
}
public void printFoo() {
System.out.println("Foo: " + properties.getFoo());
}
}
The trick is how you create an instance of NonBean.class. In the code where you'll be creating an instance of NonBean.class, use Spring's ObjectFactory<T>
private final ObjectFactory<NonBean> nonBeanFactory;
...
NonBean nonBean = nonBeanFactory.getObject();
The instantiated nonBean object will have been autowired.
All spring-beans creates by SpringApplicationContext. Bean - it's simple POJO-object, but created by Spring and saved in his container. If you want to get access to bean from outside of container - see this:
Getting Spring Application Context
Spring beans are really meant to be used within the application context but you might be able to achieve what you want by autowiring the properties to a static field in a Spring bean.
#Component
public class BusinessPropertiesUtils {
public static BusinessProperties INSTANCE;
#Autowired
public setBusinessProperties(BusinessProperties properties) {
this.INSTANCE = properties;
}
}
And then:
public class NonBean {
public void printFoo() {
System.out.println("Foo: " + BusinessPropertiesUtils.INSTANCE.getFoo());
}
}
PS: this is very hacky and definitely not the "Spring way".
You can configure beans with the prototype scope, which will give you a new instance of the bean every time it's requested.
From the Spring documentation:
In contrast to the other scopes, Spring does not manage the complete lifecycle of a prototype bean. The container instantiates, configures, and otherwise assembles a prototype object and hands it to the client, with no further record of that prototype instance.
...
In some respects, the Spring container’s role in regard to a prototype-scoped bean is a replacement for the Java new operator. All lifecycle management past that point must be handled by the client.
Example of how you can convert the TypicalBean class to a prototype scoped bean:
#Component
#Scope("prototype")
public class TypicalBean {
...
}
Another alternative is to manually instantiate the bean class (or any POJO) and injecting the dependencies (configuration, spring beans, etc.) through the constructor or setter methods, if you have them available or can get them from the Spring Context.
new TypicalBean(properties);
I have a Hibernate Search ClassBridge where I want to use #Inject to inject a Spring 4.1 managed DAO/Service class. I have annotated the ClassBridge with #Configurable. I noticed that Spring 4.2 adds some additional lifecycle methods that might do the trick, but I'm on Spring 4.1
The goal of this is to store a custom field into the index document based on a query result.
However, since the DAO, depends on the SessionFactory getting initialized, it doesn't get injected because it doesn't exist yet when the #Configurable bean gets processed.
Any suggestions on how to achieve this?
You might try to create a custom field bridge provider, which could get hold of the Spring application context through some static method. When provideFieldBridge() is called you may return a Spring-ified instance of that from the application context, assuming the timing is better and the DAO bean is available by then.
Not sure whether it'd fly, but it may be worth trying.
Hibernate Search 5.8.0 includes support for bean injection. You can see the issue https://hibernate.atlassian.net/browse/HSEARCH-1316.
However I couldn't make it work in my application and I had implemented a workaround.
I have created an application context provider to obtain the Spring application context.
public class ApplicationContextProvider implements ApplicationContextAware {
private static ApplicationContext context;
public static ApplicationContext getApplicationContext() {
return context;
}
#Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
ApplicationContextProvider.context = context;
}
}
I have added it to the configuration class.
#Configuration
public class RootConfig {
#Bean
public ApplicationContextProvider applicationContextProvider() {
return new ApplicationContextProvider();
}
}
Finally I have used it in a bridge to retrieve the spring beans.
public class AttachmentTikaBridge extends TikaBridge {
#Override
public void set(String name, Object value, Document document, LuceneOptions luceneOptions) {
// get service bean from the application context provider (to be replaced when HS bridges support beans injection)
ApplicationContext applicationContext = ApplicationContextProvider.getApplicationContext();
ExampleService exampleService = applicationContext.getBean(ExampleService .class);
// use exampleService ...
super.set(name, content, document, luceneOptions);
}
}
I think this workaround it's quite simple in comparision with other solutions and it doesn't have any big side effect except the bean injection happens in runtime.
I've a Spring Boot application created as follows, that runs as a Java standalone process
#Configuration
#ComponentScan(basePackages = {"com.abc.def.ghi"})
public class Application {
public static void main(String[] args) throws Exception {
SpringApplication springApplication = new SpringApplication(Application.class);
springApplication.setWebEnvironment(false);
springApplication.run(args);
}
#Bean
public static PropertyPlaceholderConfigurer propertyPlaceholderConfigurer () {
PropertyPlaceholderConfigurer propertyPlaceholderConfigurer = new MyConfigPropertyPlaceholderConfigurer();
return propertyPlaceholderConfigurer;
}
I can see that MyConfigPropertyPlaceholderConfigurer implementation is successfully loading the properties into its Properties member.
However, when MyErrorHandler is being initialized any of the #Value annotated members are not being loaded, even though they do exist in the Properties object
#Component
public class MyErrorHandler {
#Value("${max.retries}")
private Integer maxRetries;
#Value("${backoff.multiplier}")
private Integer backOffMultiplier;
public MyErrorHandler() {
super();
maximumRedeliveries(maxRetries);
backOffMultiplier(backOffMultiplier);
}
What am I missing here?
Injection, of other beans or properties into annotated fields can occur only after the object has been instantiated because #Autowired, #Inject, #Resource, and #Value are handled by BeanPostProcessors . Hence, when the constructor is invoked, all such fields will NOT be populated. Nonetheless you can use a (private) method annotated with #Postcosnstruct to run post-init logic, when it's guaranteed that the injection has taken place.
Alternatively, you could also use constructor injection to have spring call a dedicated constructor with appropriate parameters.
In a spring application, we write like this to get a bean through manually loading spring application context.
ApplicationContext context = new ClassPathXmlApplicationContext("path/to/applicationContext.xml");
JobLauncher launcher=(JobLauncher)context.getBean("launcher");
How to do the similar thing in spring boot ?
Being a newbie...need help
#SpringBootApplication
public class Application {
public static void main(String[] args) throws Exception {
ApplicationContext app = SpringApplication.run(Application .class, args);//init the context
SomeClass myBean = app.getBean(SomeClass.class);//get the bean by type
}
#Bean // this method is the equivalent of the <bean/> tag in xml
public SomeClass getBean(){
return new SomeClass();
}
#Bean
public MyUtilClass myUtil(SomeClass sc){
MyUtilClass uc = new MyUtilClass();
uc.setSomeClassProp(sc);
return uc;
}
}
You can also your xml file to declare the beans instead of the java config, just use #ImportResource({"classpath*:applicationContext.xml"})
Edit: To answer the comment: Make the util class a spring bean(using #Component annotation and component scan or the same as SomeClass shown above) and then you can #Autowire the bean you like. Then when you want to use the Util class just get it from the context.
What exactly is the advantages of autowiring is Spring?
An example of autowiring in spring would be like
public class TestClass {
testMethod() {
// .....
};
}
public class MainClass {
public static void main(String[] args) {
ApplicationContext ctx = new ClasspathXmlApplicationContext("test.xml");
TestMethod obj = (TestClass) ctx.getBean("test");
obj.testMethod();
}
}
test.xml
<bean id="test" class="TestClass">
same in a normal operation could be done using:
public class MainClass {
public static void main(String[] args) {
TestClass obj = new TestClass();
obj.testMethod();
}
}
What is the advantage of Spring, I mean I have heard about terms Inversion of control and Dependency Injection.
In both the examples a reference of TestClass is used once through Spring XML again through new oerator. So can someone in simple terms explain what is the advantage.
Spring is taking care of creating of the objects. Let's say in spring boot you are creating a service:
#Service
public class CreditService { ....
with this you are saying to spring boot that he needs to create an object from type CreditService and whenever you want to use it you don't need to create it you can just say:
#Autowired
private CreditService creditService;
With that you are getting an reference: creditService , that will point to the object that spring boot created for you and call the methods (services). So basically spring is taking care of creation of the object and you are just calling it, not to worry about creating new object anywhere.