auto- instantiated bean parameters? - java

I'm new to spring and spring mvc
I'm going over a course where they present the following #Configuration class:
#Configuration
public class MailConfig {
#Bean
#ConditionalOnProperty(name="spring.mail.host",
havingValue="foo",
matchIfMissing=true)
public MailSender mockMailSender() {
return new MockMailSender();
}
#Bean
#ConditionalOnProperty(name="spring.mail.host")
public MailSender smtpMailSender(**JavaMailSender javaMailSender**) {
SmtpMailSender mailSender = new SmtpMailSender();
mailSender.setJavaMailSender(javaMailSender);
return mailSender;
}
}
in the second bean (smtpMailSender) - there's a parameter :
JavaMailSender javaMailSender
but the parameter is not passed by the caller.
the instructors says : "inside bean methods if we pass parameter like this one , the parameters will be injected by spring"
My question is - how could I know that this is the expected behavior of Spring ? what is the instructor basing this on ?
is there a specific trait of JavaMailSender that is part of spring and therefor treated as a component or is something else in play here ?

That, simply said, is the way Spring works when using Java based configuration.
When a method annotated with #Bean is detected and it has parameters Spring will auto wire them by default. It does so by type.
#Bean
#ConditionalOnProperty(name="spring.mail.host")
public MailSender smtpMailSender(JavaMailSender javaMailSender) {
SmtpMailSender mailSender = new SmtpMailSender();
mailSender.setJavaMailSender(javaMailSender);
return mailSender;
}
In this case it will inject a bean of type JavaMailSender into this method. As you are using Spring Boot that is configured by default and will be injected. For a more information see the reference guide.

Related

Groovy Bean configuration

I have two beans, one of type scope request and the other is the factory to create Create a bean for each request from the first one. I did it with annotations and it works, however I need to create them with XML or with DSL and I haven't been able to do it. Any idea how to do it? I'm using Grails 2.5.6
These are the two Java Beans with annotations
#Configuration
public class HeadersConfiguration {
#Bean
public Function<HttpServletRequest, MelHeaders> headerHandlerFactory() {
return { request -> melHeaders(request) };
}
#Bean
#Scope(value = WebApplicationContext.SCOPE_REQUEST)
public MelHeaders melHeaders(HttpServletRequest request) {
return new MelHeaders(request);
}
}
Thank's

Spring creating prototype bean twice?

I have a simple spring boot app with the following config:
#Configuration
public class MyConfig {
#Bean
#Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
public HashMap<String, BidAskPrice> beanyBoi() {
System.out.println("creating a new one");
return dataFetcher().getPairPricesBidAsk();
}
}
and i have a service
#Service
public class ProfitCalculatorService {
#Autowired
private HashMap<String, BidAskPrice> prices;
public HashMap<String, BidAskPrice> getPrices() {
return prices;
}
}
and i have a controller
#RestController
public class TestController {
#Autowired
ProfitCalculatorService profitCalculatorService;
#GetMapping("/getprices")
public HashMap<String, BidAskPrice> someprices() {
return profitCalculatorService.getPrices();
}
}
}
now when i hit the /getprices endpoint, i was seeing some odd behaviour. The following message is logged twice: "creating a new one".
Your beans have a prototype scope, it means that every time that you asked for this bean a new instance will be created.
Official explanation:
The non-singleton prototype scope of bean deployment results in the
creation of a new bean instance every time a request for that specific
bean is made. That is, the bean is injected into another bean or you
request it through a getBean() method call on the container. As a
rule, you should use the prototype scope for all stateful beans and
the singleton scope for stateless beans.
https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-factory-scopes-prototype
Another thing is the proxyMode with TARGET_CLASS value. It means that the bean injected into the ProfitCalculatorService is not the BidAskPrice itself, but a proxy to the bean (created using CGLIB) and this proxy understands the scope and returns instances based on the requirements of the scope (in your case prototype).
So, my suggestion is: You don't need to create an explicitly bean of HashMap<String, BidAskPrice>. Since BidAskPrice is a bean, you can inject the HashMap<String, BidAskPrice> directly in your service, Spring will manage this list for you!

how to override custom auto-configurations?

I have made a auto-configuration class for my project to connect to AWS Sqs. This class is working fine but when I try to override auto-configuration functionality, I am getting autowire error from calling code. Please guide if my implementation of auto-configuration class is right?
I tried different #Conditional annotations to find the solution but its not working out.
#Configuration
#ConditionalOnClass(AwsSqsAsyncClient.class)
public class AwsSqsAsyncAutoConfiguration {
#Configuration
#ConditionalOnProperty(name = "aws.sqs.queue-type", havingValue = "fifo")
#EnableConfigurationProperties(AwsSqsProperties.class)
static class AwsFifoSqsAsyncAutoConfigurationBuilder{
private final AwsSqsProperties awsSqsProperties; //another class defined by me for properties
#Inject
public AwsFifoSqsAsyncAutoConfigurationBuilder(AwsSqsProperties awsSqsProperties) {
this.awsSqsProperties = awsSqsProperties;
}
#Bean
#ConditionalOnMissingBean
public AwsSqsAsyncClient fifoAsyncClient() {
return AwsSqsFactory.createAwsSqsAsyncClient(AwsSqsMessageRequestFactory
.createAwsSqsFifoRequestFactory("producer-application-name", awsSqsProperties.getQueueUrl()),
awsSqsProperties.getAccessKey(), awsSqsProperties.getSecretKey());
}
}
}
This is where I am trying to override auto-configuration functionality
#Configuration
public class AmazonSQSConfig {
#Bean
public AwsSqsAsyncClient amazonSqsAsyncClient(){
return AwsSqsFactory
.createAwsSqsAsyncClient(AwsSqsMessageRequestFactory
.createAwsSqsFifoRequestFactory("someother-producer-application-name",
"some amazon url"), "access key",
"secret key");
}
}
calling code, this is where I am trying to autowire and getting error
"Could not autowire. there is more than one bean of type AwsSqsAsyncClient
Beans: amazonSqsAsyncClient
and fifoAsyncClient "
private final AwsSqsAsyncClient awsSqsClient;
#Autowired
public SQSPublisherImpl(AwsSqsAsyncClient awsSqsClient) {
this.awsSqsClient = awsSqsClient;
}
Spring boot factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.aws.starter.sqs.AwsSqsAsyncAutoConfiguration
I expect autoconfiguration to get disabled when I define my bean for AwsSqsAsyncClient in calling code

Autowiring class with non SpringBoot managed constructor arguments

As the question suggests, how do you Autowire a class with non SpringBoot managed class as constructor args.
The following is a code block illustrating this:
#Component
class Prototype
{
#Autowired
private Repository repository;
private NonSpringBootManagedBean bean;
Prototype(NonSpringBootManagedBean bean)
{
this.bean = bean;
}
}
#Component
class PrototypeClient
{
#Autowired
private ApplicationContext context;
private void createNewPrototype(NonSpringBootManagedBean bean)
{
// This throws an error saying no bean of type NonSpringBootManangedBean found
Prototype prototype = context.getBean(Prototype.class, bean);
}
}
The reason I am using ApplicationContext to obtain an instance of Prototype instead of using #Autowired is because I need a new instance of Prototype within the method createNewPrototype() every time it's invoked and not a singleton instance (Also, please advise if this way obtaining a new instance is incorrect).
Update:
As others have stated to move my creation of bean to a Java configuration class and adding method annotated by #Bean and instantiating the NonSpringBootManagedBean in the #Bean method. But I think this is not possible as this NonSpringBootManagedBean is passed by caller of PrototypeClient.createNewPrototype().
Update
I have updated my above code example with a more clarity. Please refer this now.
#Component
class Prototype
{
#Autowired
private Repository repository;
// Here Session is part of javx.websocket package and cannot be added as part of
// Java configuration class with a #Bean annotation
// In this case how can I use constructor injection?
private Session session;
Prototype(Session session)
{
this.session = session;
}
}
#Component
class PrototypeClient
{
#Autowired
private ApplicationContext context;
private void createNewPrototype(Session session)
{
Prototype prototype = context.getBean(Prototype.class, session);
}
}
#ServerEndpoint(value = "/resources")
class WebSocketController
{
private PrototypeClient client = ApplicationContext.getBean(PrototypeClient.class);
#OnMessage
void handleMessage(Session session, String message)
{
client.createNewPrototype(session);
}
}
Did you know that you can change your bean scope to be a prototype reference instead of a singleton. That way you can scope a single bean definition to any number of object instances.
https://docs.spring.io/spring/docs/3.0.0.M3/reference/html/ch04s04.html
private NonSpringBootManagedBean bean = new NonSpringBootManagedBean();
#Bean
public Prototype getPrototype(){
return new Prototype(bean);
}
Spring can not Autowire an Object if it is not aware of it. Some where there need to be #Component or #Bean or some other annotation like #Service etc to tell spring to manage the instance .
Also it is suggested that if you are using a private variable in Autowire it should be part of constructor(for constructor injection ) or a setter method must be provided(setter injection)
To solve your error : you can create a java config class and place it in you base pkg (same as #SpringBootApplication or add #ComponentScan("pkg in which config is present") on class with #SpringBootApplication)
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;
#Configuration
public class myconfig {
#Bean
public NonSpringBootManagedBean nonSpringBootManagedBean()
{
return new NonSpringBootManagedBean();
}
}
Define a bean with scope prototype
That is each time injected as new instance.
In SpringBoot you can use the annotation #Scope("prototype") to your bean class Prototype.
#Component
#Scope("prototype")
class Prototype {
#Autowired
private Repository repository;
private NonSpringBootManagedBean bean;
Prototype() {
// you can only modify this 'NonSpringBootManagedBean' later
// because Spring calls constructor without knowing NonSpringBootManagedBean
this.bean = new NonSpringBootManagedBean();
// do something with 'repository' because its defined
}
public void setNonSpringBootManagedBean(NonSpringBootManagedBean bean) {
this.bean = bean;
}
}
Use instances of this bean
Via injection (e.g. #Autowired to constructor) you can use different instances of this prototypical bean within other beans.
#Component
class PrototypeClient {
// ApplicationContext still used?
#Autowired
private ApplicationContext context;
private Prototype prototypeInstance;
#Autowired // injects the new instance of Prototype
public PrototypeClient(Prototype p)
this.prototypeInstance = p;
// here you can change the NSBMB
modifyPrototype();
}
private void modifyPrototype(NonSpringBootManagedBean bean) {
this.prototypeInstance.setNonSpringBootManagedBean( new NonSpringBootManagedBean() );
}
}
Why is your exception thrown?
no bean of type NonSpringBootManangedBean found
Spring complains when trying to instantiate the bean of type Prototype
Prototype prototype = context.getBean(Prototype.class, bean);
because for calling its constructor it needs to pass an argument of type NonSpringBootManagedBean. Since all this bean-instantiating is done internally by Spring(Boot), you can not intercept and tell Spring: "Hey, use this bean of class NonSpringBootManagedBean" like you tried in method createNewPrototype(NonSpringBootManagedBean bean).
Why could'nt the NonSpringBootManagedBean be managed as bean by Spring(Boot)?
Autowiring in SpringBoot is a way of dependency-injection. This means a bean has been previously instantiated by SpringBoot, automatically at startup (when Spring boots). And this bean is now injected as dependency into another bean, etc. because this other bean depends on it.
If you tell us some more background, we could possibly bring light into your situation. This can be some answers to:
What is NonSpringBootManagedBean and why is it no managed bean?
What is Prototype and for which purpose does it use NonSpringBootManagedBean?
What is PrototypeClient and why does it create its own Prototype ?
I am not sure if I have understood the relationship and purpose between your objects/classes.

Eliminating Spring.xml from Spring Framework

In Spring Framework is it possible to eliminate the entire Spring.xml and use a configuration class with #Configuration and #Bean annotation for creating bean, and for all other purpose use a spring.xml?
Yes, you can have pure java configuration in Spring. You have to create a class and annotate it with #Configuration. We annotate methods with #Bean and instantiate the Spring bean and return it from that method.
#Configuration
public class SomeClass {
#Bean
public SomeBean someBean() {
return new SomeBean();
}
}
If you want to enable component scanning, then you can give #ComponentScan(basePackages="specify_your_package") under the #Configuration. Also the method name as someBean serves as bean id. Also if you have to inject a dependency, you can use constructor injection and do as following:
#Configuration
public class SomeClass {
#Bean
public SomeDependency someDependency() {
return new SomeDependency();
}
#Bean
public SomeBean someBean() {
return new SomeBean(someDependency());
}
}
Yes,most of (maybe all of)official guides uses absolutely no xml configuration file,just annotations.

Categories

Resources