5
votes

I am learning spring and I was just playing around with ApplicationContextAware and bean scopes.

I will be attaching the code and then describe what I want to do.

So far, I have created a

Point class:

public class Point {
private int x;
private int y;

public int getX() {
    return x;
}
public void setX(int x) {
    this.x = x;
}
public int getY() {
    return y;
}
public void setY(int y) {
    this.y = y;
}

@Override
public String toString() {
    return "x: "+this.x+",y: "+this.y;
}

}

I have created a Triangle class which has 3 instances of this Point class:

public class Triangle implements ApplicationContextAware, BeanNameAware{

private Point pointA;
private Point pointB;
private Point pointC;
private String beanName;
private ApplicationContext context=null;

public Point getPointA() {
    return pointA;
}

public void setPointA(Point pointA) {
    this.pointA = (Point)this.context.getBean("point1");
}

public Point getPointB() {
    return pointB;
}

public void setPointB(Point pointB) {
    this.pointB = (Point)this.context.getBean("point2");
}

public Point getPointC() {
    return pointC;
}

public void setPointC(Point pointC) {
    this.pointC = (Point)this.context.getBean("point3");
}

public void draw() {
    System.out.println("BeanName is: "+this.beanName);
    System.out.println(pointA);
    System.out.println(pointB);
    System.out.println(pointC);
}

@Override
public void setApplicationContext(ApplicationContext arg0) throws BeansException {
    this.context = arg0;
}

@Override
public void setBeanName(String arg0) {
    this.beanName = arg0;
}
}

Below is spring.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 5.1.1//EN" "http://www.springframework.org/dtd/spring-beans-5.1.1.dtd">

<beans>
<bean id="triangle" class="org.java.learning.Triangle" >
    <property name="pointA" ref="point1" />
    <property name="pointB" ref="point2" />
    <property name="pointC" ref="point3" />
</bean>

<bean id="point1" class="org.java.learning.Point" scope="prototype">
    <property name="x" value="0" />
    <property name="y" value="0" />
</bean>

<bean id="point2" class="org.java.learning.Point" scope="prototype">
    <property name="x" value="20" />
    <property name="y" value="20" />
</bean>

<bean id="point3" class="org.java.learning.Point" scope="prototype">
    <property name="x" value="-20" />
    <property name="y" value="20" />
</bean>

And this is the Main class:

public class DrawingApp {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
        Triangle t = (Triangle) context.getBean("triangle");

        t.draw();
    }
}

So, you would have seen that bean scope of three beans, point1, point2 and point3 is prototype, but they are members of bean triangle, whose scope by default is singleton in spring.xml

So, my assumption is, this should not work, unless I can somehow get ApplicationContext in my Triangle class and set pointA, pointB, pointC respectively like I have done in their setter methods using ApplicationContext.

Not sure if this is correct way or not, or how useful is this thing in live applications.

So, if it all above code works, I should get three lines output like:

x: 0,y: 0
x: 20,y: 20
x: -20,y: 20

But when I run the main method I get following error:

WARNING: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'triangle' defined in class path resource [spring.xml]: Error setting property values; nested exception is org.springframework.beans.PropertyBatchUpdateException; nested PropertyAccessExceptions (3) are: PropertyAccessException 1: org.springframework.beans.MethodInvocationException: Property 'pointA' threw exception; nested exception is java.lang.NullPointerException PropertyAccessException 2: org.springframework.beans.MethodInvocationException: Property 'pointB' threw exception; nested exception is java.lang.NullPointerException PropertyAccessException 3: org.springframework.beans.MethodInvocationException: Property 'pointC' threw exception; nested exception is java.lang.NullPointerException Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'triangle' defined in class path resource [spring.xml]: Error setting property values; nested exception is org.springframework.beans.PropertyBatchUpdateException; nested PropertyAccessExceptions (3) are: PropertyAccessException 1: org.springframework.beans.MethodInvocationException: Property 'pointA' threw exception; nested exception is java.lang.NullPointerException PropertyAccessException 2: org.springframework.beans.MethodInvocationException: Property 'pointB' threw exception; nested exception is java.lang.NullPointerException PropertyAccessException 3: org.springframework.beans.MethodInvocationException: Property 'pointC' threw exception; nested exception is java.lang.NullPointerException at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1685) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1400) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:575) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:498) at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:846) at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:863) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:546) at org.springframework.context.support.ClassPathXmlApplicationContext.(ClassPathXmlApplicationContext.java:144) at org.springframework.context.support.ClassPathXmlApplicationContext.(ClassPathXmlApplicationContext.java:85) at org.java.learning.DrawingApp.main(DrawingApp.java:9) Caused by: org.springframework.beans.PropertyBatchUpdateException; nested PropertyAccessExceptions (3) are: PropertyAccessException 1: org.springframework.beans.MethodInvocationException: Property 'pointA' threw exception; nested exception is java.lang.NullPointerException PropertyAccessException 2: org.springframework.beans.MethodInvocationException: Property 'pointB' threw exception; nested exception is java.lang.NullPointerException PropertyAccessException 3: org.springframework.beans.MethodInvocationException: Property 'pointC' threw exception; nested exception is java.lang.NullPointerException at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:122) at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:77) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.app lyPropertyValues(AbstractAutowireCapableBeanFactory.java:1681) ... 13 more

I believe I have done something wrong while setting the values of pointA, pointB and pointC member variables

Please let me know if any details required from my side, to get rid of this error and achieve what I am trying to do.

3
You seem to be using very old materials. At the very least, use the generic form of getBean: getBean(Triangle.class). - chrylis -cautiouslyoptimistic-
Why are you doing this public void setPointB(Point pointB) { this.pointB = (Point)this.context.getBean("point2"); }. If you are injecting via setter you should have to have "bean-standard" setter - smsnheck
I was injecting via setter earlier, but then I tried to set values using context object and not using the ones passed in setter method. - Akki

3 Answers

1
votes

Independent to your error you are facing: Let's have a look at your classes setup:

You want your points to be prototype. So each time you request a new Bean by injection Spring gives you a new instance.

And now you inject your prototype beans in your Triangle bean which is by default singleton (which means it is requested and created only once). So the prototype beans (Point) are also requested once to inject them into the singleton Triangle bean.

From now on your prototype beans doesn't act like prototype beans, which means they inherit the singleton scope from your Triangle bean.

But on this setup you're fine, but you should keep that pitfall in mind: Spring creates new instances of prototype beans only if the beans are requested from the container/context.

1
votes

You are getting NPE because context object is null in below and similar methods

public void setPointC(Point pointC) {
    this.pointC = (Point)this.context.getBean("point3");
}

context Object would be set in ApplicationContextAware beans only when all the beans are initialized and context object is ready.

Your code should look like below

public void setPointC(Point pointC) {
        this.pointC = pointC;
    }
0
votes

Two reasons cause you code throws exception:

  • new ClassPathXmlApplicationContext("spring.xml") method can't Aware the class context is ready.
  • A loop dependency. create ApplicationContext need success create all beans,but the Triangle need a context.

You can use IOC to solve your problem.