Episode Summary:

  • What are the disadvantages of constructor injection?
  • How do I use setter injection?
  • Why does setter injection cause null pointer exceptions?

Previously: A novel on Spring (1) — Why dependency Injection


He introduced the Spring framework to the project and solved the problem of over-coupling code. Of course, this is just the tip of the iceberg of Spring’s power.

Bigger and bigger constructors

One day, after a morning meeting, Dorra said to Nobita, “Nobita, our order interface and payment interface have been very perfect, now we need to update the order status when the payment is completed, please see how this requirement is realized.” “This is easy to do, just add a new dependency IOrderDao to the payment interface and inject the OrderDao into it.” “Good boy, open your mouth a ‘dependency’, shut up a ‘injection’, the terminology is quite slip ah.” “That is, you wait, immediately deal with this requirement,” said Nobita, then rushed to write code.

Add a member variable orderDao to the PaymentAction, create a new constructor, inject orderDao into the PaymentAction, and write an updateOrderAfterPayment method. Call the orderDao method to update the order (the code used in this article can be downloaded from the SpringNovel, stars welcome) :

public class PaymentAction {

    private ILogger logger;
    
    // new proprety !!!
    private IOrderDao orderDao;

    public PaymentAction(ILogger logger) {
        super(a);
        this.logger = logger;
    }
    
    // new constructor !!!
    public PaymentAction(ILogger logger. IOrderDao orderDao) {
        super(a);
        this.logger = logger;
        this.orderDao = orderDao;
    }

    public void pay(BigDecimal payValue) {
        logger.log("pay begin, payValue is " + payValue);

        // do otherthing
        // ...

        logger.log("pay end");
    }
    
    // new method !!!
    public void updateOrderAfterPayment(String orderId) {
        orderDao.updateOrderAfterPayment(orderId);
    }

}
Copy the code

Finally, we modify payment. XML to inject orderDao:

<bean id="paymentAction" class="com.springnovel.payment.springxml.PaymentAction">
	<constructor-arg ref="serverLogger">
	</constructor-arg>
	<constructor-arg ref="orderDao">
	</constructor-arg>
</bean>

<bean id="serverLogger" class="com.springnovel.perfectlogger.ServerLogger" />
<bean id="orderDao" class="com.springnovel.dao.OrderDao" />
Copy the code

In this way, Nobita soon realized the payment interface to add the order update function requirements, excitedly to DORA submitted the code Review request…

Soon, the Review results came back:

  • If later PaymentAction needs to rely on more interfaces, such as SMS sending interface, Alipay interface, wechat pay interface, etc., do you still add constructors? If you rely on 20 interfaces, your constructor will take 20 arguments, as in the following code. Do you think this code is elegant?
public PaymentAction(ILogger logger. IOrderDao orderDao. ISMSUtil smsUtil. IPaybal paybal. IWechatPay wechatPay. ...). {
    super(a);
    this.logger = logger;
    this.orderDao = orderDao;
    this.smsUtil = smsUtil;
    this.paybal = paybal;
    this.wechatPay = wechatPay;
    .
}
Copy the code

Dorra’s words once again poured a dish of cold water to Nobita, “why can’t review every time……”

Setter injection

How do you solve the problem of growing constructors? Nobita suddenly thought of an example he had seen in the first chapter of Effective Java called the Builder pattern, which simplifies a large constructor into a small constructor with many set functions. “Ah, it doesn’t have to be injected with a constructor! And setter injection!” Nobita then remembered another injection method he had seen during Spring learning — setter injection.

Next, we modify the PaymentAction with setter injection by removing the two-argument constructor and adding a setOrderDao method:

public class PaymentAction_SetInjection {

    private ILogger logger;
    private IOrderDao orderDao;

    public PaymentAction_SetInjection(ILogger logger) {
        super(a);
        this.logger = logger;
    }
    
    // setter injection !!!
    public void setOrderDao(IOrderDao orderDao) {
        this.orderDao = orderDao;
    }

    public void pay(BigDecimal payValue) {
        logger.log("pay begin, payValue is " + payValue);

        // do otherthing
        // ...

        logger.log("pay end");
    }

    public void updateOrderAfterPayment(String orderId) {
        orderDao.updateOrderAfterPayment(orderId);
    }

}
Copy the code

Then modify the payment. XML to use the <property> tag and inject orderDao:

<bean id="paymentAction_setInjection" class="com.springnovel.payment.springxml.PaymentAction_SetInjection">
	<constructor-arg ref="serverLogger">
	</constructor-arg>
	<property name="orderDao" ref="orderDao"></property>
</bean>
Copy the code

Test it out:

public void test_PaymentAction_UpdateOrder_XML_SetInjection(a) {
	ApplicationContext context = new ClassPathXmlApplicationContext("payment.xml");
	PaymentAction_SetInjection paymentAction = (PaymentAction_SetInjection) context.getBean("paymentAction_setInjection");
	paymentAction.updateOrderAfterPayment("123456");
}
Copy the code

Output:

real update order after payment, orderId is 123456
Copy the code

“Perfect! Setter injection is nothing!” , big male shout way, secretly took a look at Duo la, Duo la at this time is focused on looking at his screen, it seems that there is no awareness of this side of the car excited big male.

Null pointer exception!

Nobita once again ready to submit a review request to DORA, in the index finger is about to press enter that moment, he seems to have the ability to peer into the future, he saw DORA with a basin filled with cold water, toward him spill over… “Oh, no, doesn’t constructor injection fail setter injection? Unscientific… There must be some limitations to setter injection that I haven’t discovered yet… ..”

“When the Spring container initializes an object, it calls the object’s constructor. If constructor injection is used and the corresponding

tag is not configured in the XML, injection should fail because there is no matching constructor.” If is not configured, will initialization fail or will it not be injected at all?” “, Big male’s head turned quickly.

“Modify the code and verify it!” <constructor> comment out the constructor tag:

<bean id="paymentAction_setInjection" class="com.springnovel.payment.springxml.PaymentAction_SetInjection">
	<! --<constructor-arg ref="serverLogger">-->
	<! --</constructor-arg>-->
	<property name="orderDao" ref="orderDao"></property>
</bean>
Copy the code

Error: Execute test case

org.springframework.beans.factory.BeanCreationException: 
...
No default constructor found; 
Copy the code

The PaymentAction class does not have a constructor for empty parameters. This error is easy to detect because it will cause container initialization to fail.

Nobita then undoes the operation and comments out the <property> tag:

<bean id="paymentAction_setInjection" class="com.springnovel.payment.springxml.PaymentAction_SetInjection">
	<constructor-arg ref="serverLogger">
	</constructor-arg>
	<! --<property name="orderDao" ref="orderDao"></property>-->
</bean>
Copy the code

Re-execute the test case, ah, error! Null pointer exception! :

java.lang.NullPointerException at com.springnovel.payment.springxml.PaymentAction_SetInjection.updateOrderAfterPayment(PaymentAction_SetInjection.java:34)  at com.springnovel.test.PaymentTest.test_PaymentAction_UpdateOrder_XML_SetInjection(PaymentTest.java:46)Copy the code

Set injection does not fail if the object to be injected is not specified in the XML. The dependent object is not injected into any object, so it defaults to NULL. “This is not good, in case you are careless and forget to specify the object to inject in the XML, the container can start successfully, but the runtime will hang…” “Is there a way to make setter-injected properties mandatory?”

Nobita decides to do some research on the Internet, open Google, and type in “Spring setter Bitian…” “Oh no. What the hell… What do you call it in English…” “Ow, required, it’s a mandatory check attribute is required” villager, big male input “spring setter required” soon, he found Spring2.0 provides a @ the required notes…

Reuqired annotations

“That’s easy! , Nobita contrast with the tutorial, modify the code. Start by turning on Spring annotations and adding these configurations to payment.xml:

<beans
       .    
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
.
        http://www.springframework.org/schema/context
	    http://www.springframework.org/schema/context/spring-context-2.5.xsd">

    <context:annotation-config/>
Copy the code

Then add the @required annotation to the PaymentAction setOrderDao method:

@Required
public void setOrderDao(IOrderDao orderDao) {
	this.orderDao = orderDao;
}
Copy the code

Executing the test case again, of course, results in an error, but this time after the container is initialized:

org.springframework.beans.factory.BeanCreationException: 
...
Property 'orderDao' is required for bean 'paymentAction_setInjection'
Copy the code

“This is a great way to save a careless programmer from throwing a null pointer exception when creating objects in the Spring container.

Nobita carefully checks the code and finally hits Enter to submit a Review request to Dorra.

“Ding, pass!” After about half an hour, the screen pops up a notification that Nobita’s code has finally passed doraand team members’ review, successfully submitted to the code base!

Nobita’s notes

Today Nobita learned about setter injection, an alternative to constructor injection. Before going to bed, Nobita made a habit of summarizing what he learned today:

  • Constructor injection vs Setter injection
    • Constructor injection can force the caller to inject all arguments in the Constructor, otherwise it will fail at container initialization; But if there are too many objects to inject, the constructor becomes too large.
    • Setter injection, similar to the Builder pattern, breaks down a large constructor into one small constructor and many set methods. Setter injection does not guarantee that the object will be injected, but you can use the @required annotation to force the user to inject the object or an error will be reported when the container is initialized.

After summing up, The big male jumped to his feet and landed on Simmons with a snap. The whole man became “big”. He closed his eyes and opened his mouth and quickly fell asleep…

Male dream

In his sleep, Nobita saw a very strange “three-eyed crow”, “three-eyed crow” silent wood on the branch, such as nobita approached, quickly flew to another tree, nobita chased for a long time… Suddenly, Nobita saw a Bridge, at the end of which stood a sign that said “Bridge for You” “Bridge for me? Give me the bridge…” One side of the “three-eyed crow” really can not look down, curse way, “is for you to prepare the bridge! Idiot!” “For me? Then I must go and see!” “Nobita walked into the Bridge for You…

On the opposite side of the bridge was a thatched hut, the door was open, Nobita looked at it, as if no one was there, went in, and found a boy in the back room, typing on the keyboard. Nobita leans over to peek. The boy seems to be writing a novel. The title of the novel is “Explaining Spring in The Form of a Novel…” “Big male wondered how anyone could have such a bad title. “Big hero, big hero, I have a new department recently, I am very busy, I can only come to see you once a week…”

To be continued

The next day early in the morning, the phone suddenly rang, sleep nobita answered the phone in a daze…

“Feed! Big male, still sleeping?? Get up, there’s a need to change!” It was that pesky project manager, Fat Tiger… “Change.. To change the requirements? !” “Didn’t I tell you to print your logs to the log server? The client just said, to change, to call the console! It’s going to be finished this morning!” “Wow… .” Big male blurt out, but soon he secretly pleased, because he knew that the adoption of dependency injection, now he only need to change a configuration, “oh, this customer so much at first sight, you wait, I now change, to change a lot, change after you have to invite I to have a meal” “rabbit, project launched how much you want to eat it!”

Nobita immediately gets up, opens the portable, and changes the Payment.xml ServerLogger to ConsoleLogger:

<bean id="serverLogger" class="com.springnovel.perfectlogger.CosoleLogger"/>
Copy the code

“Want to test it? Ah, calculate, measure what measure, certainly no problem “, say that finish big male submitted the code to fat tiger, then send a message to fat tiger, let him review. Chubby soon submitted nobita’s code to the code base…

“Really, I didn’t sleep well…” Nobita is getting ready to go back to sleep… The phone rings again… “Big male, how did you do! Changed your code and now the server doesn’t even start!” “Huh?? How could…” , Big male a face meng force……

Refer to the content

  • Spring in Action
  • Explain why constructor inject is better than other options
  • Setter injection versus constructor injection and the use of @Required
  • Spring dependency checking with @Required Annotation