Implementing custom factory pattern in Spring - 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.

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.

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();

Dependency Injection in 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

while using Spring ApplicationContext as in the example below, are annotations taken care of

public class SomeClass {
#Autowired
public WaterContainer wc;
private static int count = 0;
SomeClass(){ ++count;}
public static int testmethod() {
return count;
}
public static void main(String[] args) {
ApplicationContext context = new FileSystemXmlApplicationContext("spring-configuration\\beans.xml");
SomeClass obj = context.getBean(SomeClass.class);
//SomeClass obj2 = new SomeClass();
System.out.println("value of count "+SomeClass.testmethod());
if(obj.wc != null)
System.out.println("wc volume "+obj.wc.getCurrentVolume());
else
System.out.println("wc bean "+context.getBean(WaterContainer.class).getCurrentVolume()); //+(obj.wc==null)
}
}
The beans.xml file contains this :
<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 class="com.app.data.SomeClass"/>
<bean class="com.app.mechanism.WaterContainer">
<constructor-arg value="30"/>
</bean>
</beans>
the output that I got was as below but I expected the field wc in the SomeClass object to not be null, does that mean autowired annotations are not taken care of? or have I gone wrong somewhere?
value of count 1
wc bean 30
If so then how does
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"file:spring-configuration\\beans.xml"})
work for unit testing? how is the autowiring taken care of here
As M. Deinum stated, annotation configuration is built into some ApplicationContext implementations and not in others.
FileSystemXmlConfiguration is an implementation that does not have annotation configuration built into it. You can enable that annotation configuration with
<context:annotation-config />
(and the relevant namespace declarations).
Since that configuration was not enabled, #Autowired fields were not processed, and your SomeClass#wc field was left null.
The test configuration you've shown internally uses an AnnotationConfigApplicationContext where annotation configuration is built in.

How do I inject a bean in spring which is part of a class declared with #component tag

My spring config is set to use auto scanning for beans.
<context:annotation-config/>
<context:component-scan base-package="mypackage" />
I have a class that is declared with #Component
#Component
class A{
private InterfaceB b;
}
This InterfaceB is part of a jar file.
InterfaceBImpl implements InterfaceB{
// some contract here
}
I am getting an error from spring saying it cannot find a matching bean of type InterfaceB.
How can I inject that bean into the class?
private InterfaceB b in A has to be marked with #Autowired
But that doesn't enough, the InterfaceBImpl has to be a bean, too: or #Component, or as a <bean> in the xml config.
You have to declare a bean with the type B in your xml. Spring will automatically pick it up where you annotate a field with #Autowired.
Below is an example:
#Component
class A {
#Autowired
private B myB;
//other code
}
And in your xml:
<context:annotation-config/>
<context:component-scan base-package="mypackage" />
<bean id="myB" class="mypackage.InterfaceBImpl" />
Spring will see that A depends on B, looks in the context and sees that it has an implementation of B, which is InterfaceBImpl and inject it into A.
If you have control over the sources of InterfaceBImpl, add a #Component annotation to make Spring creating a bean be available for auto wiring.
If InterfaceBImpl cannot be changed, you can 1) extend it and annotate the extension or 2) you can create a configuration creating the bean:
1)
#Component
class MyInterfaceBImpl extends InterfaceBImpl {
}
2)
#Configuration
class MyAdditinalBeans {
#Bean
public InterfaceB interfaceB() {
return new InterfaceBImpl();
}
}

Resources