Spring interface injection example - java

No one so far was capable of providing a working correct example of interface injection in Spring Framework.
Martin Fowler article is not for mortals, everything else just words positioned in a very confusing way. I have surfed over THIRTY articles, where people either tell "Spring doesn't directly supports for interface injection"("and because I don't know exactly how I will only describe setter and constructor injections") or either "I will discuss it in my other threads" or either there will be few comments below saying that it is wrong example. I don't ask for explanation, I BEG for example.
There are three types of injection: Constructor, Setter and Interface. Spring doesn't support the latest directly(as I have observed people saying). So how is it done exactly?
Thank You,

According to Variants of DI in spring
DI exists in two major variants, Constructor-based dependency injection and Setter-based dependency injection.
Also see Interface injection is not implemented in Spring clearly states it.
So there are only two variants of DI. So if documentation says nothing about interface injection, its clear that its not there. Those who believe that interface injection is done by providing setter method in interface answer me:
Why spring ref doc left mention of interface injection?
Why can't interface injection via providing setter method NOT considered as setter injection itself. Why create special term for that when introduction of interface doesn't affect anything, I mean its still configured the same way. If they were different then how can one find it via seeing the config. Shouldn't it be transparent that in config and not seeing the impl that actually configured class implements some interface ?
Just like Instantiation using an instance factory method and Instantiation using an static factory method, some bean attributes should clarify the interface injection?

With interface injection an interface explicitly defines the point where a dependency can be set:
interface InjectPerson {
public void injectHere(Person p);
}
class Company implements InjectPerson {
Person injectedPerson;
public void injectHere(Person p) {
this.injectedPerson = p;
}
}

There are 3 types of dependency injections:-
1. Constructor Injection(E.g Pico Container, Spring supports it).
2. Setter Injection(E.g Spring supports it).
3. Interface Injection(E.g Avalon, Spring does not support it).
Spring supports only constructor and setter based injection.
Looks like you got confused in the different types(3) and what spring supports(2 of them).

Hi I tried with a very simple approach that may clarify your answer.
following is the code that i have built on using two interfaces and and two bean classes.
first interface with name Job.
public interface Job {
public void setmyJob(String myJob);
public String getmyJob();
}
and one class to implement this interface with name as MyJob
public class MyJob implements Job {
public String myJob;
public MyJob() {
System.out.println("From MyJob default Constructor and the ID= "+this);
}
public void setmyJob(String myJob) {
this.myJob=myJob;
}
public String getmyJob() {
return myJob;
}
}
In the next step i created another Interface with name as Service
public interface Service {
public void setJob(Job job);
public Job getJob();
}
and then again another class to implement this Service Interface.
public class MyService implements Service {
public Job job;
public void setJob(Job job) {
this.job=job;
System.out.println("Hello from Myservice: Job ID="+job);
}
public Job getJob() {
return job;
}
}
then i created on main class with the main function and written the code as follows:
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApplication {
public static void main(String...a) {
BeanFactory beanfactory=new ClassPathXmlApplicationContext("Beans.xml");
MyService myservice=(MyService)beanfactory.getBean("myservice");
System.out.println("Before print");
System.out.println(myservice.getJob().getmyJob());
}
}
in my Beans.xml file i mentioned the code as follows and it worked.
<?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="myjob" class="MyJob">
<property name="myJob" value="My First String"/>
</bean>
<bean id="myservice" class="MyService">
<property name="job" ref="myjob"/>
</bean>
</beans>
I have also reffered to another online tutorials and then got this kind of solution. please let me know if you have any problem with this code. it is working for me.

I think, that the confusion around interface injection is caused by missunderstanding what the term "interface injection" actually means. In my understanding, interface injection describes the ability of a bean contener to inject a new interface to the bean, no matter that the class definition of this bean is not implementing it.
All examples presented here show how to create a bean out of concrete class, and then how to inject it into another bean. The fact, that in all cases bean is injected into a field defined as an interface does not matter- all operations are done with beans created out of concrete instances.
I can provide also another catchy example:
package creditCards;
interface PaymentCard {
Boolean isDebitAllowed();
}
<bean id="card" class="creditCards.PaymentCard">
<lookup-method name="isDebitAllowed" bean="boolValue"/>
</bean>
<bean id="boolValue" class="java.lang.Boolean">
<constructor-arg type="boolean" value="true"/>
</bean>
As you see here, it is even possible to create a bean out of interface! Still, it is not a interface injection, as IoC contener initializes instanse of this bean by its own. In other words, card bean is an initialized object, not an interface, what makes, that the selected answer for this question is correct.

I think someone answered your questions here
I also didn't know what Interface injection is until I read this statement from "Pro Spring MVC with web flow book"
"Note that interface-based dependency injection isn’t supported by the Spring Framework. This
means that we need to specify which concrete implementation to inject for a certain interface."

Please check the below example for iterface injection.
There is a shape interface and 2 concrete classes which imiplements shape namely square and rectangle.
The interface
package di.interfaceinjection;
public interface Shape {
public String shapeName();
public void displayName();
}
2 Implemented classes
package di.interfaceinjection;
public class Square implements Shape {
#Override
public String shapeName() {
return "Square";
}
#Override
public void displayName() {
System.out.println("Square");
}
}
package di.interfaceinjection;
public class Rectangle implements Shape{
#Override
public String shapeName() {
return "Rectangle";
}
#Override
public void displayName() {
System.out.println("Rectangle");
}
}
Now, we have a class which sets the shape.
public class ShapeSetter {
private Shape shape;
public Shape getShape() {
return shape;
}
public void setShape(Shape shape) {
this.shape = shape;
}
}
and finally the configuration
<bean id="shape1" class="di.interfaceinjection.ShapeSetter">
<property name="shape" ref="square"></property>
</bean>
<bean id="shape2" class="di.interfaceinjection.ShapeSetter">
<property name="shape" ref="rectangle"></property>
</bean>
<bean id="square" class="di.interfaceinjection.Square"></bean>
<bean id="rectangle" class="di.interfaceinjection.Rectangle"></bean>
Here,
we are injecting different shapes.
package di.interfaceinjection;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class InterfaceInjeection {
/**
* #param args
*/
public static void main(String[] args) {
ApplicationContext appContext = new ClassPathXmlApplicationContext("intro.xml");
ShapeSetter shape = (ShapeSetter)appContext.getBean("shape2");
shape.getShape().displayName();
}
}

Related

Why is it not possible to have a generic bean when i have also an Aspect targeting the bean in Spring?

Hi i have a rather specific question regarding Spring Aspects that leaves me puzzled. I played around with Springs and Apsects and and tried out a very simple Example to see how it works:
#Component
public class Comment {
private String text;
private String author;
public String get() {
return text;
}
public void setText(String text) {
this.text = text;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
}
#Aspect
public class LoggingAspect {
#Around("execution(* aop.beans.*.*(..))")
public void log(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("Start Aspect for method: " + joinPoint.getSignature());
joinPoint.proceed();
}
}
My configuration file:
#Configuration
#ComponentScan("aop.beans")
#EnableAspectJAutoProxy
public class ProjectConfig {
#Bean
public LoggingAspect aspect() {
return new LoggingAspect();
}
}
With that i used a simple Main method to play around with the concept a little:
public class Main {
public static void main(String[] args) {
#SuppressWarnings("resource")
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ProjectConfig.class);
context.registerShutdownHook();
CommentService service = context.getBean(CommentService.class);
Comment comment = context.getBean(Comment.class);
comment.setAuthor("Andreas");
comment.setText("Hallo.");
service.publishComment(comment);
System.out.println(service.getClass());
}
}
This worked fine but something strange happens when ich change the hirarchy of the comment class. I wanted to see what happens if the class implements a generic interface so i changed it as follows
public class Comment implements Supplier<String>
I immediately get an error with the following stack trace:
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'aop.beans.Comment' available
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:351)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:342)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1172)
at aop.Main.main(Main.java:17)
This left me wondering why that is? If i remove the generic superinterface or the aspect bean everthing works fine but both things together do not seem to fare well. Can someone provide an explanation? Is Spring not able to create a Proxy object if the class has a generic superclass?
Edit: The solution is in the comments :) i found further documentation on the mechanism in Spring Proxy Mechanism Documentation
This happens because of the way Spring implements AOP.
When proxied class (Comment in your case) doesn't implement any interfaces, CGLib proxy is used. Basically, it generates a class which is subclass of proxied class. Thus, you can getBean by parent's class.
When there are implemented interfaces, Spring uses JDK dynamic proxies which don't extend the proxy class, but implement all its interfaces, thus you can't find a bean by it's class.
That's why it's always a good practice to autowire your beans by interface rather then by class.
Solution
You can force Spring to use CGLib proxies for AspectJ by annotating you configuration class #EnableAspectJAutoProxy(proxyTargetClass = true).
If you want to use JDK Dynamic proxies, create interface for Comment and then use context.getBean(YourInterfaceName.class).

Best way to override beans in Spring

I have an application that consists of 2 modules.
First of them is main one and it can work without second module installed.
I have beans with default implementation defined in beans.xml file of main module. And when installing second module I want to keep the ids of those beans but change the implementation to use new classes.
What is the best way to do that?
beans.xml of first module:
...
<bean id="myCoolService" class="com.blabla.defaultCoolServiceImpl">
...
and after the installation of second module I want to use the implementation of myCoolService that is defined in second module.
Upd:
Spring version is 3.2.4.
I need to make as little changes as possible so I need to continue using xml-driven configuration.
One way of doing this is introducing a common interface (I guess one should already be present):
public interface MyInterface {
//...
}
And then in the main module annotate the default implementation with #Service
#Service
public class DefaultImplementation implements MyInterface {
//...
}
Then, if one of your modules needs to override this implementation, use the #Primary-annotation:
#Service
#Primary
public class OverridingImplementation implements MyInterface {
//...
}
Then, the following code:
#Inject
private MyInterface myInterface;
will inject DefaultImplementation if OverridingImplementation is not scanned, and inject OverridingImplementation (without complaining about multiple beans) if it is scanned.
One way to achieve this is going through a proxy, that redirects to the proper implementation. The proxy would normally redirect to the default. It will redirect to module 2 if it is available.
To help the proxy figure out what is available, you may need to have
a member that always points to the default implementation using "name" property.
have a method to register a different bean as the alternate implementation.
For example
Inside MyProxy:
#Autowired
public void setDefaultWorker(Worker defaultWorker) {
this.defaultWorker = defaultWorker;
}
private Worker defaultWorker;
private Worker alternateWorker;
public void registerAlternateWorker(Worker alternateWorker) {
this.alternateWorker = alternateWorker;
}
//To use the worker
private Worker getWorker() {
return alternateWorker == null? defaultWorker : alternateWorker;
}
In Module 1, your default implementation bean should be declared as having the defaultWorker as name
<bean id="defaultWorker" class="MyDefaultWorkerImpl"/>
Module 2 can register itself to the proxy registry on startup using SmartLifeCycle.
if possible,use :
<bean id="myCoolService" class="${IMPL_CLASS_NAME}"/>
Define impl class in a property file.
IMPL_CLASS_NAME=com.blabla.SecondMduleCoolServiceImpl
OR other approach could be :
Lets say your defaultCoolServiceImpl and SecondMduleCoolServiceImpl implement ICoolService interface
You define these bean and an implementation of FactoryBean as below :
<bean id="mydefaultServiceimpl" class="com.blabla.defaultCoolServiceImpl">
<bean id="secondModuleCoolserviceimpl" class="com.blabla.SecondMduleCoolServiceImpl">
<bean id="myCoolService" class="com.blabla.ImplSelector"/>
public class ImplSelector implements FactoryBean<ICoolService>, ApplicationContextAware {
private ApplicationContext iApplicationContext;
// #Value("#{corePropertyConfigurer['defaultOrCool']}") you can injcet via property file.
private String defaultOrCool = "cool" ;
#Override
public ICoolService getObject() throws Exception {
if (StringUtils.equals(defaultOrCool, "default")) {
return iApplicationContext.getBean("mydefaultServiceimpl", ICoolService.class);
}
return iApplicationContext.getBean("secondModuleCoolserviceimpl", ICoolService.class);
}
#Override
public Class<?> getObjectType() {
return ICoolService.class;
}
#Override
public boolean isSingleton() {
return true;
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
iApplicationContext = applicationContext;
}
}
Then you can access myCoolService via autowiring or applicationContext.getBean("myCoolService", ICoolService.class);

Why i am not getting No unique bean of type in Autowiring in spring

Person.java
#Controller
public class Person
{
#Autowired
private Ability ability;
public void printMessage(){
ability.printMessasge();
}
public void setOutputGenerator( Ability ability) {
this.ability = ability;
}
}
Ability.java
#Controller
public class Ability
{
void printMessasge(){
System.out.println("I print message");
}
}
spring.xml
<bean id="invisible" class="com.mkyong.common.Ability" >
</bean>
<context:component-scan base-package="com.mkyong" />
App.java
public class App {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext(
"spring.xml");
Person person = (Person) context.getBean("person");
person.printMessage( );
}
In the above example i have defined two beans of Ability class one using #Controller and one in xml file. According to Autowire by type i should get
Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException:
...
No unique bean of type [com.mkyong.common.Ability] is defined:
But i am getting proper output. Why?
And if i create an interface which Ability class implements then i would get UnsatisfiedDependencyException exception. Like this:
Parent.java
public interface Parent{
public void printMessasge();
}
Ability.java
#Controller
public class Ability implements Parent
{
void printMessasge(){
System.out.println("I print message");
}
Person.java
#Controller
public class Person
{
#Autowired
Parent parent;
public void printMessage(){
parent.printMessasge();
}
public void setOutputGenerator( Parent parent) {
this.parent= parent;
}
}
By default Spring indeed matches by type, but in case of multiple matching beans, it falls back to matching by name. The reference (6.9.4 Fine-tuning annotation-based autowiring with qualifiers) says:
For a fallback match, the bean name is considered a default qualifier value.
Using the #Qualifier annotation makes the autowiring by type and additionally by name more explicit.
You are not going to get a UnsatisfiedDependencyException with this configuration because Spring is smart enough to also look at the name of your required dependency, as #Adam writes in his answer. So even if the default autowiring mode is by type and there are two beans of the same type for that dependency, Spring is able to solve the conflict.
This behavior is described in the official documentation, 6.9.4 Fine-tuning annotation-based autowiring with qualifiers section.
More details about autowiring modes can be found 6.4.5 Autowiring collaborators.
If you change the name of Person.ability field to something else, like Person.ability2, then Spring will throw a org.springframework.beans.factory.NoUniqueBeanDefinitionException. It cannot decide which bean it should wire for the Person.ability2 field because:
There are two beans of com.mkyong.common.Ability type in the context
Field name does not match with either bean names
EDIT: in the second case you mention (with the interface), Spring throws the exception for the same bean naming reason I explained above.
Official documentation references:
6.4.5 Autowiring collaborators
6.9.4 Fine-tuning annotation-based autowiring with qualifiers
you have two ambiguous beans defined, when spring try find the bean via type it finds two beans, one via scan other explicitly defined in spring.xml. use #Qualifier to mark the correct bean loaded in Person class. So probably you want like this,
#Controller
public class Person
{
#Autowired
#Qualifier("invisible")
private Ability ability;
public void printMessage(){
ability.printMessasge();
}
public void setOutputGenerator( Ability ability) {
this.ability = ability;
}
}

Ambiguous dependency while using #Produces annotation

I have been studying #Produces annotation of CDI dependency injection from here. I just created my own same example. However, I am facing with ambiguous dependency.
public interface Bank {
public void withdrawal();
public void deposit();
}
public class BankOfAmerica implements Bank {
#Override
public void withdrawal() {
System.out.println("Withdrawal from Bank of America");
}
#Override
public void deposit() {
System.out.println("Deposit to Bank of America");
}
}
public class BankFactory {
#Produces
public Bank createBank() {
return new BankOfAmerica();
}
}
And this is the class which bean is get injected.
public class ProducesExample {
#Inject
private Bank bankOfAmerica;
public void callBanksWithdrawal() {
bankOfAmerica.withdrawal();
}
}
I appreciate for any help.
EDIT: I know this a kind of duplicate of this question. However, in the tutorial which I shared, it says it should work. Moreover, there is only one type of bean so no need use #Default or #Alternatives but still get confused about why it is not working.
The tutorial is a bit ambiguous (pun intended) about which classes should be deployed simulataneously in each step, so I wouldn't worry about that too much.
The answer to the other question you linked does match your case. BankOfAmerica is a bean of type Bank (in CDI 1.0 or in CDI 1.1+ with explicit beans), and your producer method is another bean of the same type, hence the ambiguous resolution.
One thing that can be helpful is your beans.xml file.
If you want to have a factory (using #produces) you cannot have the bean-discovery-mode="all". If you have the all option than you will get Ambiguous dependencies exception cause all your implementations will be auto scanned as possible dependencies ( what in my opinion is a bad performance option ).
So put bean-discovery-mode="annotated" , leave your implementations cdi-annotation free and use #Dependent in the factory and #produces in the build method.
You have to add #BankProducer annotation like that :
public class BankFactory {
#Produces
#BankProducer
public Bank createBank() {
return new BankOfAmerica();
}
}

Strange behaviour of Spring DI

I am injecting dependency in one class. But not finding when I call method of that class.
public StudentValidator extends MyValidator implements Validator{
private StudentRepository studentRepository;
//setter for the same
public void validate(Object obj,Errors errors){
validateStudent(Obj)
}
}
public class MyValidator{
private StudentRepository studentRep;
public void setStudentRep(StudentRepository studentRep){
System.out.println("This is printing on Tomcat console");
this.studentRep=studentRep
System.out.println("This is also printing"+studentRep+" with hashcode");
}
public void validateStudent(Object obj){
studentRep.findStud(); getting here NullPointerException
}
}
No need of writing Spring servlet as I can see dependency has been injected in setters through Syso statements.
What would be problem for the same?
UPDATE
spring-servlet.xml
<beans>
<bean id="studentValidator" class="SyudentValidator" >
<property name="studentRepository" ref="studentRepository">
</bean>
<bean id="myValidator" class="MyValidator">
<property name="studentRep" ref="studentRepository">
</bean>
<bean id="studentRepository" class="StudentRepository">
</beans>
NullPointerException is not my problem. Problem is why I am getting the null pointer in this case as Syso statement is printing my dependencies hashcode.
Assuming both MyValidator and StudentValidator are spring beans, you should try:
bean id="myValidator" class="MyValidator.java">
bean id="studentValidator" class="StudentValidator.java" parent="myValidator>
You must notify spring that there is an inheritance.
You have declared a field of type StudentRepository twice, which is confusing but here's what I think is going on.
I assume you are creating an instance of StudentValidator, which will set the field StudentValidator.studentRepository. However your validateStudent method uses the field MyValidator.studentRep that will remain null, hence the NullPointerException.
Basically, only methods can be overridden in Java, not fields.
If you really need this unnecessary inheritance structure, the code should read:
public class MyValidator
{
private StudentRepository studentRepository;
public void setStudentRepository(StudentRepository studentRepository)
{
this.studentRepository = studentRepository;
}
public void validateStudent(Object obj)
{
studentRepository.findStud();
}
}
public StudentValidator extends MyValidator implements Validator
{
#Override
public void validate(Object obj, Errors errors)
{
validateStudent(Obj)
}
}
Though I'd be tempted to simplify it to:
public class StudentValidator implements Validator
{
private StudentRepository studentRepository;
public void setStudentRepository(StudentRepository studentRepository)
{
this.studentRepository = studentRepository;
}
#Override
public void validate(Object obj)
{
studentRepository.findStud();
}
}
UPDATE
Based on the Spring configuration above, I can see you are creating instances of both classes - not sure why you'd do that if you're not going to use the myValidator bean as a parent of the studentValidator bean, but either way, if you've only got one bean, using a parent just complicates the Spring configuration of no reason.
The reason you're getting a NullPointerException in the validateStudent is because you never set the studentRep on the studentValidator bean, you only set it on the myValidator bean (they're two different instances).
Given this, you're Spring config should read:
<beans>
<bean id="studentValidator" class="SyudentValidator" >
<property name="studentRepository" ref="studentRepository">
<property name="studentRep" ref="studentRepository">
</bean>
<bean id="studentRepository" class="StudentRepository">
Though as I said, your class design is confusing in the first place, which makes this Spring config look weird as you're setting a reference to the same bean, studentRepository, twice.
My advice would be to apply the class changes I originally suggested, which simply the design as a whole and make it harder to misconfigure you application as is currently happening.

Categories