define two beans of same class in single application context - spring

if I am defining two beans of same class and not giving any scope. Then how many instance of class will get created. for example
in applicationContext.xml
<bean name="testBean" class="com.test.Example"/>
<bean name="myBean" class="com.test.Example"/>

Spring will create two beans of the type com.test.Example and the autowiring will be for the type or method name (or Qualifiers), see Spring IOC
See this simple test:
With this class
public static class TestBean {
static int INT = 1;
public int test;
public TestBean() {
test = INT++;
}
}
Configuration xml:
<bean name="testBean" class="com.test.TestBean"/>
<bean name="myBean" class="com.test.TestBean"/>
JUnit4 with spring container test:
#Resource
TestBean testBean;
#Resource
TestBean myBean;
#Test
public void test() {
assertNotNull(testBean);
assertNotNull(myBean);
assertFalse(testBean == myBean);
assertFalse(testBean.test == myBean.test);
}
This test dont fail, as you see, two beans of type TestBean are created.
See this part in the Spring Doc:
byName
Autowiring by property name. Spring looks for a bean with the same name as the property that needs to be autowired. For example, if a bean definition is set to autowire by name, and it contains a master property (that is, it has a setMaster(..) method), Spring looks for a bean definition named master, and uses it to set the property.
byType
Allows a property to be autowired if exactly one bean of the property type exists in the container. If more than one exists, a fatal exception is thrown, which indicates that you may not use byType autowiring for that bean. If there are no matching beans, nothing happens; the property is not set.
constructor
Analogous to byType, but applies to constructor arguments. If there is not exactly one bean of the constructor argument type in the container, a fatal error is raised.

Spring will create two instances in this scenario. Spring container creates a singleton instance per bean definition.
When you call getContext.getBean("testBean") will always give you same instance for testBean bean definition
When you call getContext.getBean("myBean") will always give you same instance for myBean bean definition.

Yes. Spring will create two instances in this scenario. Spring container creates a singleton instance per bean definition.
EX:
public class Test {
#SuppressWarnings("resource")
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("ws.xml");
TestBean teatBean = (TestBean) ac.getBean(TestBean.class);
TestBean myBean1 = (TestBean) ac.getBean(TestBean.class);
System.out.println("a : " + teatBean.test + " : "
+ teatBean.getName());
teatBean.setName("a TEST BEAN 1");
System.out.println("uPdate : " + teatBean.test + " : "
+ teatBean.getName());
System.out.println("a1 : " + myBean1.test + " : " + myBean1.getName());
myBean1.setName(" a1 TEST BEAN 10");
System.out.println("a1 update : " + teatBean.test + " : "
+ myBean1.getName());
}
}
<bean class="com.spring4hibernate4.TestBean">
<constructor-arg name="i" value="1"></constructor-arg>
<property name="name" value="1-name"></property>
</bean>
<bean class="com.spring4hibernate4.TestBean">
<constructor-arg name="i" value="10"></constructor-arg>
<property name="name" value="10-name"></property>
</bean>
</beans>
public class Test {
#SuppressWarnings("resource")
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("ws.xml");
TestBean teatBean = (TestBean) ac.getBean(TestBean.class);
TestBean myBean1 = (TestBean) ac.getBean(TestBean.class);
System.out.println("a : " + teatBean.test + " : "
+ teatBean.getName());
teatBean.setName("a TEST BEAN 1");
System.out.println("uPdate : " + teatBean.test + " : "
+ teatBean.getName());
System.out.println("a1 : " + myBean1.test + " : " + myBean1.getName());
myBean1.setName(" a1 TEST BEAN 10");
System.out.println("a1 update : " + teatBean.test + " : "
+ myBean1.getName());
}
}

Related

about spring applicationContext

i learned that when spring applicationContext is created,
context itself will be registered as bean.
so i made a simple code and expect applicationContext as a bean.
However, when i create applicationContext with java-code like below,
i couldn't see applicationContext as a bean..
====code====
ApplicationContext parent = new GenericXmlApplicationContext(basePath + "parentContext.xml");
GenericApplicationContext child = new GenericApplicationContext(parent);
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(child);
reader.loadBeanDefinitions(basePath+"childContext.xml");
child.refresh();
Printer printer = child.getBean("printer",Printer.class);
assertNotNull(printer);
for(String bean : parent.getBeanDefinitionNames()) {
System.out.println("TTTT : "+ bean +" : "+parent.getBean(bean).getClass().getName());
}
=====================
i both tried with parent and child. could anyone can explain why applicationContext itself is not
registered as a bean?
I would try adding this line to parentContext.xml:
<import resource="contextSub.xml"/>
and in the java code add this annotation
#Before
public void setup(){
ApplicationContext parent = new GenericXmlApplicationContext(basePath + "parentContext.xml");
GenericApplicationContext child = new GenericApplicationContext(parent);
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(child);
reader.loadBeanDefinitions(basePath+"childContext.xml");
child.refresh();
Printer printer = child.getBean("printer",Printer.class);
assertNotNull(printer);
for(String bean : parent.getBeanDefinitionNames()) {
System.out.println("TTTT : "+ bean +" : "+parent.getBean(bean).getClass().getName());
}
Let me know if this helps.

Spring boot custom Servlet doesn't map to bean name

I am trying to register a custom servlet.
I used this code in a #Configuration class:
#Bean (name="probe")
public PingServlet probe(){
return new PingServlet();
}
I thought this would be mapped to /probe, but it doesn't. I maps to '/' and the reason is that in class ServletContextInitializerBeans, there this method:
private <T, B extends T> void addAsRegistrationBean(ListableBeanFactory beanFactory,
Class<T> type, Class<B> beanType, RegistrationBeanAdapter<T> adapter) {
List<Map.Entry<String, B>> beans = getOrderedBeansOfType(beanFactory, beanType,
this.seen);
for (Entry<String, B> bean : beans) {
if (this.seen.add(bean.getValue())) {
int order = getOrder(bean.getValue());
String beanName = bean.getKey();
// One that we haven't already seen
RegistrationBean registration = adapter.createRegistrationBean(beanName,
bean.getValue(), beans.size());
registration.setName(beanName);
registration.setOrder(order);
this.initializers.add(type, registration);
if (this.log.isDebugEnabled()) {
this.log.debug(
"Created " + type.getSimpleName() + " initializer for bean '"
+ beanName + "'; order=" + order + ", resource="
+ getResourceDescription(beanName, beanFactory));
}
}
}
}
The line List<Map.Entry<String, B>> beans = getOrderedBeansOfType(beanFactory, beanType, this.seen);, return list of 1 bean only (my servlet) although beanType is javax Servlet and I would expect DispatcherServlet to be there as well (I'm also using Spring MVC).
This results to an error in the following method (in class ServletRegistrationBeanAdapter):
#Override
public RegistrationBean createRegistrationBean(String name, Servlet source,
int totalNumberOfSourceBeans) {
String url = (totalNumberOfSourceBeans == 1 ? "/" : "/" + name + "/");
if (name.equals(DISPATCHER_SERVLET_NAME)) {
url = "/"; // always map the main dispatcherServlet to "/"
}
ServletRegistrationBean bean = new ServletRegistrationBean(source, url);
bean.setMultipartConfig(this.multipartConfig);
return bean;
}
Since the beans list is of size 1, in the createRegistrationBean it hard codes the mapping to '/'.
This in turn causes they embedded jetty to fail starting as there are 2 mappings to '/' (DispatcherServlet and my PingServlet).
Any ideas what's going wrong here?
Thanks to #M. Deinum This works:
#Bean
public ServletRegistrationBean pingRegistration(PingServlet pingServlet) {
ServletRegistrationBean registration = new ServletRegistrationBean(
pingServlet);
registration.addUrlMappings("/probe/*");
return registration;
}

Bean property 'feedId' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?

Below is code Batch snippet:
XML :
</beans:property> -->
<beans:bean id="RDFieldSetMapper" class="in.gov.tds.batch.mapper.RDFieldSetMapper"
autowire="byName" scope="step">
<!-- <beans:property name="feedId" value="429717"></beans:property> -->
<beans:property name="feedId" value="#{jobParameters[feedId]}"></beans:property>
</beans:bean>
setter method in Java Class:
recordDetail.setFeedId(new Long(feedId));
Please provide the resolution as I am getting Invalid setter method.
More Mapper detail:
public class RDFieldSetMapper implements FieldSetMapper {
private Long feedId;
private int batchCounter;
#Override
public RecordDetail mapFieldSet(FieldSet fieldSet) throws BindException {
if (LOGGER.isDebugEnabled())
LOGGER.debug("Record Detail Mapper:-- " + " " + fieldSet);
RecordDetail recordDetail = new RecordDetail();
// feedId = FeedReader.feedId;
recordDetail.setFeedId(new Long(feedId));
}
}
solved the problem. Issue : setter and getter methods are not present in the mapper class.
public Long getFeedId() {
return feedId;
}
public void setFeedId(Long feedId) {
this.feedId = feedId;
}

Is there a difference in order of bean creation between dispatch servlet and context Loader Listener

I faced an issue when accessing an instance variable when configuration xml is loaded through context Loader Listener.
Below is a configuration xml for creating beans
<bean id="A" class"org.sample.A">
</bean>
<bean id="B" class"org.sample.B">
<property name="cobj" ref="C"/>
</bean>
<bean id="C" class"org.sample.C"/>
Below is the java code
Class A{
C cobj = null;
public A(){
cobj = B.getInstance().getCobj();
}
void display(){
System.out.println(cobj);
}
}
Class B{
private static bobj = null;
C cobj = null;
public static B getInstance(){
if (bobj == null) {
return new B();
}
return bobj;
}
public setCobj(C obj){
this.cobj = obj
}
public C getCobj(){
return cobj;
}
}
Class C{
}
When the configuration xml is loaded through context Loader Listener , sysout prints null.
But when the same configuration is loaded through Dispatcher servlet the value is available.
Moreover if the display method is modified as below it shows result
public void display(){
B stackObj = new B();
System.out.println(stackObj.cobj);
}
Bean B is initialized later when configured through context Loader.
But its initialized earlier of Class A if configured through dispatcher servlet.
Can this order of creation be determined ?
You dont create objects by yourself. This is what Spring is supposed to do; provide objects to you. Load all the objects by making a call to xmlBeanFactory and then you make actual use of spring.

Spring init-method params

I am new to spring and I wanted to ask whether or not it is possible to pass params to the init and destroy methods of a bean.
Thanks.
No, you can't. If you need parameters, you will have to inject them as fields beforehand.
Sample Bean
public class Foo{
#Autowired
private Bar bar;
public void init(){
bar.doSomething();
}
}
Sample XML:
<bean class="Foo" init-method="init" />
This method is especially useful when you cannot change the class you are trying to create like in the previous answer but you are rather working with an API and must use the provided bean as it is.
You could always create a class (MyObjectFactory) that implements FactoryBean and inside the getObject() method you should write :
#Autowired
private MyReferenceObject myRef;
public Object getObject()
{
MyObject myObj = new MyObject();
myObj.init(myRef);
return myObj;
}
And in the spring context.xml you would have a simple :
<bean id="myObject" class="MyObjectFactory"/>
protected void invokeCustomInitMethod(String beanName, Object bean, String initMethodName)
throws Throwable {
if (logger.isDebugEnabled()) {
logger.debug("Invoking custom init method '" + initMethodName +
"' on bean with beanName '" + beanName + "'");
}
try {
Method initMethod = BeanUtils.findMethod(bean.getClass(), initMethodName, null);
if (initMethod == null) {
throw new NoSuchMethodException("Couldn't find an init method named '" + initMethodName +
"' on bean with name '" + beanName + "'");
}
if (!Modifier.isPublic(initMethod.getModifiers())) {
initMethod.setAccessible(true);
}
initMethod.invoke(bean, (Object[]) null);
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
see spring soruce code in Method initMethod = BeanUtils.findMethod(bean.getClass(), initMethodName, null);
the init method is find and param is null
You cannot pass params to init-method but you can still achieve the same effect using this way:
<bean id="beanToInitialize" class="com.xyz.Test"/>
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject" ref="beanToInitialize" />
<property name="targetMethod" value="init"/> <!-- you can use any name -->
<property name="arguments" ref="parameter" /> <!-- reference to init parameter, can be value as well -->
</bean>
Note: you can also pass multiple arguments as a list using this
<property name="arguments">
<list>
<ref local="param1" />
<ref local="param2" />
</list>
</property>

Resources