Episode Summary:

  • What did the code look like before dependency injection, and what were its drawbacks?
  • What is dependency injection? Why use dependency injection?
  • How does Spring use XML configuration for dependency injection?

Nobita was born after 1995 and loves programming. After graduation, he joined an Internet company and was responsible for the development of an e-commerce project in the company. In order to let nobita faster growth, the company arranged DORA as nobita’s mentor.

The spring breeze complacent

Under Dorra’s guidance, Nobita soon had a rough understanding of the project’s code, so Dorra was ready to give Nobita some tasks.

“Big male, our project lacks log printing now, in case we find a bug when we go online, it will be difficult to locate. See if you can print some of the necessary information to the log file.” “Sure! Nobita readily agreed.

Nobita used to surf the Internet for resources when he was in school, and quickly settled on a tool called PerfectLogger. “The data is very good, many gods recommended it, well, use it.”

PerfectLogger provides a variety of log printing functions, including printing to files, printing to console, and printing to remote servers. All of these classes implement an interface called ILogger:

  • ILogger
    • FileLogger
    • ConsoleLogger
    • ServerLogger

“Dorra said to print to a file, use FileLogger!” So Nobita first added log printing to the payment interface code (the code used in this article can be downloaded from SpringNovel) :

public class PaymentAction {
	
	private ILogger logger = new FileLogger(a);
	
	public void pay(BigDecimal payValue) {
		logger.log("pay begin, payValue is " + payValue);
		
		// do otherthing
		// ...
		
		logger.log("pay end");
	}
}
Copy the code

Then, Big male and login, authentication, refund, return and other interfaces, add and pay interface similar to the log function, to add a lot of places, big male added two days and two nights, finally finished, done! Think of their first task is successfully completed, big male can not help but have a little satisfaction…

Changed requirements

Soon the company upgraded the system, and Nobita’s logging function would be tested in a production environment for the first time.

Two days later, Dorra found Nobita. “Nobita, the test side said that there are too many log files, can not be printed to the local directory, we need to print the log to a log server, you see whether the change is big.” “This is easy, I just need to do a global substitution, replace all the Fileloggers with the ServerLogger and be done.” DORA frowned and asked, “What if the next time the company asks us to print logs to the console, or suddenly wants us to print logs to a local file, will you continue with global replacement?” Big male listen to, feel is a little inappropriate…

How does the code decouple

“I looked at your current code, and each Action created the Logger itself, so if you want to change the Logger implementation class, you have to change a lot of things. Ever thought you could outsource the creation of logger objects?” Nobita listened and felt that this seemed to be some kind of design pattern he had learned before. “Factory mode!” Nobita suddenly saw the light.

Soon, Nobita refactored the code:

public class PaymentAction {
	
	private ILogger logger = LoggerFactory.createLogger(a);
	
	public void pay(BigDecimal payValue) {
		logger.log("pay begin, payValue is " + payValue);
		
		// do otherthing
		// ...
		
		logger.log("pay end");
	}
}
Copy the code

public class LoggerFactory {
	public static ILogger createLogger(a) {
		return new ServerLogger(a);
	}
}
Copy the code

With the LoggerFactory, if you want to switch to logging in the future, all you need to do is change the factory class.

Pa! A plate of cold water

Nobita happily to DORA mention code review request, but, soon, a dish of cold water poured over, DORA’s reply is this:

  • Factory class every time a new object, is it very wasteful, can be made singleton, or even made singleton and multiple cases can be configured;
  • If there is such a demand: payment information is more and more sensitive, the log should be printed to the remote server, other information is printed to the local, how to achieve;

Nobita felt 2Young2Simple, ready to stay tonight to work overtime…

Spring! Spring!

Just as big male depressed of time, the screen bottom right corner duo la’s head suddenly jumped out.

“It’s not just the factory model. It’s also known as the Inverse of Control, and it’s also known by a more common name, Dependency Injection. There is already a mature implementation of this mechanism in the industry. It is the Spring Framework. Go home early tonight, have a look at Spring and come back tomorrow.”

That night, Nobita looked for Spring’s information on the Internet, and he seemed to find another world…

Use Spring to transform the code

Nobita arrived early the next day, eager to transform the original code using Spring.

After using Gradle to introduce the necessary jar packages, Nobita changed the original PaymentAction to no longer create logger objects inside the class, and added a constructor to the PaymentAction to make it easy for Spring to inject:

public class PaymentAction {
	
	private ILogger logger;
	
	public PaymentAction(ILogger logger) {
		super(a);
		this.logger = logger;
	}

	public void pay(BigDecimal payValue) {
		logger.log("pay begin, payValue is " + payValue);
		
		// do otherthing
		// ...
		
		logger.log("pay end");
	}
}
Copy the code

We then create an XML file with <beans> as the root node, import the necessary XSD files, and configure two bean objects using the <constructor-arg> tag, specifying the ServerLogger as an entry to the PaymentAction constructor:


        
<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.xsd">

  <bean id="paymentAction" class="com.springnovel.paymentwithspringxml.PaymentAction">
  	<constructor-arg ref="serverLogger" />
  </bean>
        
  <bean id="serverLogger" class="com.springnovel.perfectlogger.ServerLogger" />

</beans>
Copy the code

That’s it. Now test it out:

ApplicationContext context = new ClassPathXmlApplicationContext("payment.xml");
PaymentAction paymentAction = (PaymentAction) context.getBean("paymentAction");
paymentAction.pay(new BigDecimal(2));
Copy the code

Output:

ServerLogger: pay begin, payValue is 2
ServerLogger: pay end
Copy the code

Great! The ServerLogger object is already injected into the PaymentAction. In this way, Nobita quickly used Spring to implement the functionality of the factory class he wrote yesterday, fixing the problem of too much coupling in the previous code.

Put this to use

Here Nobita is happy, suddenly found next to the test sister Static incense frown, so the past about a. Originally shizuka was testing a function to delete orders, but now the test database suddenly hangs, so shizuka can’t test.

Nobita looked at the code for the order deletion interface:

public class OrderAction {
	public void deleteOrder(String orderId) {
		/ / authentication
		// Ten thousand words omitted here...
		
		IOrderDao orderDao = new OrderDao(a);
		orderDao.deleteOrder(orderId);
	}
}
Copy the code

“This is another code coupling problem!” Nobita blurted out. “This delete order interface has several logic: Authentication, delete, and roll back, but here the delete database operations and OrderDao binding is dead, and this requires test this interface must be connected to the database, but as a unit test, we just want to delete the order logic is reasonable, and the order is really deleted, should belong to another unit test the male is very exciting,” Spit flying from his mouth. “Let me change it for you.”

OrderAction after “reverse of control” :

public class OrderAction {
	
	private IOrderDao orderDao;
	
	public OrderAction(IOrderDao orderDao) {
		super(a);
		this.orderDao = orderDao;
	}

	public void deleteOrder(String orderId) {
		/ / authentication
		// Ten thousand words omitted here...
		
		orderDao.deleteOrder(orderId);
	}
}
Copy the code

The new OrderAction is no longer coupled to the OrderDao implementation class. When unit testing, you can write a “Mock” test like this:

public void mockDeleteOrderTest(a) {
	IOrderDao orderDao = new MockOrderDao(a);
	OrderAction orderAction = new OrderAction(orderDao);
	orderAction.deleteOrder("@ # % ^ $1234567");
}
Copy the code

The MockOrderDao does not need to connect to the database, so you can unit test even if the database dies.

Side duo la has been quietly looking at, and then patted the big male shoulder, “evening please and static incense to lu string ah”, say that finish, the ghost of the big male picked eyebrows.

Nobita’s notes

It’s been a rewarding two days, seeing the need for dependency injection and learning how to implement it using Spring. After the masturbation, back home, Nobita wrote down in his notepad:

  • Why use dependency injection
    • In traditional code, each object is responsible for managing the objects it needs to depend on, resulting in multiple changes if it needs to switch the implementation classes of dependent objects. At the same time, excessive coupling makes the object difficult to unit test.
    • Dependency injection (DI) gives the creation of objects to external management, which is a good solution to the problem of tight couple of code. It is a mechanism for code to achieve loose couple.
    • Loose coupling makes code more flexible, better able to respond to changing requirements, and easier to unit test.
  • Why use Spring
    • Using the Spring framework primarily to simplify Java development (as most frameworks do), it helps us encapsulate a lot of great functionality, and the Spring ecosystem is very large.
    • Xml-based configuration is the original dependency injection configuration provided by Spring, and it’s been around since Spring was born, and it’s the most full-featured (but there seems to be a better one, check it out tomorrow!). .

To be continued

After writing his notes, Nobita moved on to the Spring guide, which he had only read a small part of before, and found that in addition to constructor injection, there was another type of injection called set injection; In addition to XML configuration, annotations and even Java can be used for configuration. Spring is so powerful that it gives users so many choices, but which injection and configuration methods to use in a given situation, Nobita mused…

Refer to the content

  • Spring in Action
  • tutorialspoint – Spring Tutorial
  • javatpoint – Spring Tutorial
  • Why does one use dependency injection?
  • Dependency Injection and Unit Testing