Dependency Injection in Spring? - spring

How can we know if spring framework is using constructor based dependency injection or setter method based dependency injection, when both the constructor and setter method is defined?
for example.. I have two java classes as follows..
TextEditor.java
public class TextEditor {
private SpellChecker spellChecker;
TextEditor(SpellChecker spellChecker){
this.spellChecker = spellChecker;
}
public void setSpellChecker(SpellChecker spellChecker) {
this.spellChecker = spellChecker;
}
// a getter method to return spellChecker
public SpellChecker getSpellChecker() {
return spellChecker;
}
public void spellCheck() {
spellChecker.checkSpelling();
}
}
and
SpellChecker.java
public class SpellChecker {
public SpellChecker(){
System.out.println("Inside SpellChecker constructor." );
}
public void checkSpelling(){
System.out.println("Inside checkSpelling." );
}
}
in configuration file, pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!-- Definition for textEditor bean using inner bean -->
<bean id="textEditor" class="com.tutorialspoint.TextEditor">
<property name="spellChecker">
<bean id="spellChecker" class="SpellChecker"/>
</property>
</bean>
</beans>
now, how can we know that spring has added dependency using Constructor or using Setter method?

When using a <property>, spring injects dependencies via setter.
If you want to inject it via constructor, you use <constructor-arg>.
Also see: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.html#beans-factory-collaborators

It is all about developer choice how does he/she configure spring to inject dependency.
Since everyone could write big wall of text about how Dependency Injection works you should see/read this:
http://www.journaldev.com/2410/spring-dependency-injection-example-with-annotations-and-xml-configuration

Related

How to pass base package as a variable inside pointcut expression in Spring AOP?

I am creating a java library for logging purpose so that if any application uses my library then spring AOP's advices are applied to each method of the application. But in my library, I don't know the base package of the applications that will be using it. So, the applications should specify their base package in the application.properties file and my library will use it and put it in my pointcut as a variable. But seems like pointcut expressions only accepts contants inside it.
I don't want to go for annotation based approach.
#Component
#Aspect
#Slf4j
public class LoggingAop {
#Value("${base.package}")
String basePackage;
#Pointcut("within("+basePackage+"..*)") //error
public void logAllMethods()
{
}
}
Answers for the following questions explains why this is not possible with annotation based Spring AOP configuration
Get rid of "The value for annotation attribute must be a constant expression" message
How to supply value to an annotation from a Constant java
Spring boot supports xml based configurations and the requirement can be achieved as follows.
Assuming we have to intercept all the method calls within base package com.app.service
Have an entry in resources/application.properties.
base.package=com.app.service
An Aspect to log method calls within the base package can be as follows
package com.app.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LogAspect {
Logger log = LoggerFactory.getLogger(LogAspect.class);
public Object logAllMethods(ProceedingJoinPoint pjp) throws Throwable {
log.info("Log before method call");
try {
return pjp.proceed();
}finally {
log.info("Log after method call");
}
}
}
The aop configuration can be defined as follows .
File : resources/aop-application-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="logAspect" class="com.app.aspect.LogAspect"></bean>
<aop:config>
<aop:aspect id="logallaspect" ref="logAspect" >
<!-- #Around -->
<aop:pointcut id="logAllMethodsAround" expression="within(${base.package}..*)" />
<aop:around method="logAllMethods" pointcut-ref="logAllMethodsAround" />
</aop:aspect>
</aop:config>
</beans>
Do note that the pointcut expression is constructed with the application.properties entry.
Remember to load the aop-application-context.xml as follows
#SpringBootApplication
#ImportResource("classpath:aop-application-context.xml")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
The above code will advice all the interceptable Spring bean method calls within the defined base package and logs them .
Hope this helps.

Spring Autowiring a list into a bean results in a NoSuchBeanDefinitionException

In a Spring 3.1.3.RELEASE project I want to create and autowire a list that contains some enum to some service.
Unfortunately the autowiring fails (NoSuchBeanDefinitionException), while I can retrieve the bean in the context and manually wire the dependency.
Here is a small testcase showcasing the issue (using Spring 3.1.3 and JUnit):
The XML context (int package /junk):
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="junk"/>
<util:list id="myList" value-type="junk.GreatThings">
<value>SUPER</value>
<value>COOL</value>
</util:list>
</beans>
The enum:
package junk;
public enum GreatThings {AMAZING, GREAT, SUPER, COOL}
The test class (in package junk - I've removed the import for clarity):
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath*:junkcontext.xml"})
public class TestAutowiringSupport {
#Autowired #Qualifier("myList") List<GreatThings> greatThings;
#Test public void testSuperCool() {
Assert.assertThat(greatThings, hasItem(SUPER));
Assert.assertThat(greatThings, hasItem(COOL));
}
}
This result in a NoSuchBeanDefinitionException.
I have try to put a #Qualifier with my bean id to help Spring to perform the wiring without any success.
My IDE is however able to detect the wiring by itself:
And if I use the Spring lifecycle callbacks to get back the bean and wire it manually then, it is fine.
Version that compiles and runs fine:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath*:junkcontext.xml"})
public class TestAutowiringSupport implements ApplicationContextAware
{
ApplicationContext ctx;
List<GreatThings> greatThings;
#Override
public void setApplicationContext(ApplicationContext ctx) throws BeansException {this.ctx = ctx;}
#PostConstruct
public void manualWiring() {greatThings = (List<GreatThings>) ctx.getBean("myList");}
#Test public void testSuperCool() {
Assert.assertThat(greatThings, hasItem(SUPER));
Assert.assertThat(greatThings, hasItem(COOL));
}
}
What's wrong with the autowiring in this case ?
Looks like some problem with generics.
With Spring 4.1 I am able to execute this code: where greatThings is of type List<Object>
#Qualifier("myList")
#Autowired List<Object> greatThings;
#Test
public void testSuperCool() {
Assert.assertThat(greatThings, Matchers.hasSize(2));
Assert.assertThat(greatThings, Matchers.hasItem(GreatThings.SUPER));
Assert.assertThat(greatThings, Matchers.hasItem(GreatThings.COOL));
}
As stated in the reference documentation, placing the #Autowired annotation over a typed collection means "find all beans of the given type (GreatThings in your case), put them in a collection and inject that collection". You are getting the exception because there are no beans of type GreatThings declared.
The problem is that there is no easy way to declare enum values as beans. Then again I don't see the use case to be honest.

bean scope in xml not working

singleton bean scope in XML spring not working.only prototype working.Even without any scope tag prototype is working.
XML snippet:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="restaurant" class="bean_scope.Restaurant1" scope="singleton"> <!-- scope="singleton" -->
</bean>
</beans>
Java class for setter methods:
package bean_scope;
public class Restaurant1 {
private String welcomeNote;
public void setWelcomeNote(String welcomeNote) {
this.welcomeNote = welcomeNote;
}
public void greetCustomer(){
System.out.println(welcomeNote);
}
}
Java Spring Test class:
package bean_scope;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Restaurant1Test {
public static void main(String[] args) {
Restaurant1 restaurantOb1=(Restaurant1) new ClassPathXmlApplicationContext("bean_scope/SpringConfig1.xml")
.getBean("restaurant");
restaurantOb1.setWelcomeNote("Welcome");
restaurantOb1.greetCustomer();
Restaurant1 restaurantOb2=(Restaurant1) new ClassPathXmlApplicationContext("bean_scope/SpringConfig1.xml")
.getBean("restaurant");
//restaurantOb2.setWelcomeNote("Hello");
restaurantOb2.greetCustomer();
}
}
Output:
Welcome
null
Please help me with this why singleton scope is not working
Since you create two independent instances of ClassPathXmlApplicationContext, each one will have its own singleton instance of the bean. The singleton scope means there will only be one instance of the bean within the Spring context - but you have two contexts there.
This would produce the result you're expecting:
ApplicationContext ctx = new ClassPathXmlApplicationContext("bean_scope/SpringConfig1.xml");
Restaurant1 restaurantOb1=(Restaurant1) ctx.getBean("restaurant");
restaurantOb1.setWelcomeNote("Welcome");
restaurantOb1.greetCustomer();
Restaurant1 restaurantOb2=(Restaurant1) ctx.getBean("restaurant");
//restaurantOb2.setWelcomeNote("Hello");
restaurantOb2.greetCustomer();

Converting spring xml to java configuration with implicit setter autowiring and ComponentScan

I have two classes: vehicle.Tire and vehicle.Car.
package vehicle;
#Named
public class Tire {
private String age;
}
package vehicle;
#Named
public class Car {
private Tire tire;
// Spring calls this setter because default-autowire="byName" of xml configuration
public void setTire(Tire newTire) {
this.tire = newTire;
}
public Tire getTire() {
return this.tire;
}
}
The following spring xml works fine.
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
default-autowire="byName">
<context:component-scan base-package="vehicle" />
</beans>
I tried to create a java configuration above:
#Configuration
#ComponentScan(basePackages={"vehicle"})
public VehicleConfig {
}
I'm not using #Inject nor #Autowired annotations in the classes, but spring autowires and it works with xml.
Using java configuration, the method setTire of Car is not called :(
What is missing? How can I change java configuration using component scan without modifying Car and Tire classes? Is there default-autowire="byName" xml tag attribute equivalent in java?
I used the class above to test java configuration
#Test
public class VehicleConfigTest {
public void testTire() {
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext();
applicationContext.register(VehicleConfig.class);
applicationContext.refresh();
Car car = applicationContext.getBean(Car.class);
Assert.assertNotNull(car.getTire());
}
}
Thanks in advance.
There is no equivalent in sprig java configuration to default-autowire="byName" global xml tag attribute.
More, the right way to have injection is using #Inject annotation by cdi or #Autowired annotation by spring. So, the class Car should be modified:
package vehicle;
#Named
public class Car {
#Inject
private Tire tire;
public Tire getTire() {
return this.tire;
}
}
Now, both xml or java configuration works.

Implementing custom factory pattern in Spring

Normally if I want to implement a factory pattern I will do it like this.
public class CustomFactory(){
// pay attention: parameter is not a string
public MyService getMyService(Object obj){
/* depending on different combinations of fields in an obj the return
type will be MyServiceOne, MyServiceTwo, MyServiceThree
*/
}
}
MyServiceOne, MyServiceTwo, MyServiceThree are implementations of the interface MyService.
That will work perfectly fine.
But the issue is that I would like to have my objects instanciated by Spring container.
I've looked through some examples and I know how to make Spring container create my objects depending on a string.
The queston is: can I include implemenations of objects by Spring Container in this example or should I make all my manipulations with Object obj in some other place and write a method public MyService getMyService(String string) in my CumtomFactory?
Well what do you think about following way? :
public class CustomFactory {
// Autowire all MyService implementation classes, i.e. MyServiceOne, MyServiceTwo, MyServiceThree
#Autowired
#Qualifier("myServiceBeanOne")
private MyService myServiceOne; // with getter, setter
#Autowired
#Qualifier("myServiceBeanTwo")
private MyService myServiceTwo; // with getter, setter
#Autowired
#Qualifier("myServiceBeanThree")
private MyService myServiceThree; // with getter, setter
public MyService getMyService(){
// return appropriate MyService implementation bean
/*
if(condition_for_myServiceBeanOne) {
return myServiceOne;
}
else if(condition_for_myServiceBeanTwo) {
return myServiceTwo;
} else {
return myServiceThree;
}
*/
}
}
EDIT :
Answers to your questions in comment :
Isn't it the same with getting by String?
--> Yes definitely, you are getting those beans from Spring.
I mean how should my spring.xml look like?
--> See below xml :
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!-- services -->
<bean id="myServiceBeanOne"
class="com.comp.pkg.MyServiceOne">
<!-- additional collaborators and configuration for this bean go here -->
</bean>
<bean id="myServiceBeanTwo"
class="com.comp.pkg.MyServiceTwo">
<!-- additional collaborators and configuration for this bean go here -->
</bean>
<bean id="myServiceBeanThree"
class="com.comp.pkg.MyServiceThree">
<!-- additional collaborators and configuration for this bean go here -->
</bean>
<!-- more bean definitions for services go here -->
</beans>
And what should i do inside getMyService method? just return new MyServiceOne() and so on or what?
--> See getMyService() method in above code, it is updated.

Resources