Hello everyone, I am Wolf King, a programmer who loves playing ball

If normal refactoring is about removing the bad smell of code, high-level refactoring is about removing the bad smell of architecture

Recently, the components of the company’s infrastructure need to be compatible, adapted and split, so it is often necessary to reconstruct the components. When you get the old project and code of the company and need to split or rebuild, you will feel confused and have no way to start. I have always been in such a state before. But as you get familiar with some of the ideas and methods of refactoring, and get a little more comfortable with it, I’m going to start talking about refactoring, and then I’m going to focus on SPI interfacing in refactoring.

Let me show you a component that has recently been refactored through the use of SPI interfaces – distributed storage.

The code structure before refactoring

Boy, all third party storage is written in one module, variousAlibaba Cloud, Tencent Cloud, Huawei cloudWait, such a code architecture may exist in the early stagesIt doesn't need to be extended very oftenIt still works.

But when a new demand to such as I met: need to support multiple accounts to upload download function of multiple cloud, this is because in the different cloud, different account permissions, safety certification is not same, so at some point, this requirement was proposed, which is you want to upload which cloud which account is ok.

Then I got the code and looked at the architecture. It may be no problem to complete the requirements on such a basis, but it is troublesome to expand, and the code will be more and more heavy, and the architecture will be more and more complex and unclear.

So I just took this opportunity to reconstruct, and after discussing with other colleagues, I decided to divide modules into SPI. The advantage is to introduce corresponding dependencies according to what you want to use, which makes the code architecture clearer and easier to expand later! Here is the general architecture after refactoring:

That makes it easier to add new features to a cloud storage, or to accommodate more clouds.

Ok, let’s start with refactoring


refactoring

What is refactoring?

Refactoring refers to adjusting program code to improve software quality and performance, rationalize the program design pattern and architecture, and improve software scalability and maintainability.

The most important idea of refactoring is to allow ordinary programmers to write good programs.

By breaking down the process of optimizing code quality into small steps, the enormous effort of refactoring a project can be reduced to simple goals such as changing variable names, extracting functions, extracting interfaces, and so on.

The average programmer can improve his coding skills, deepen his knowledge of the project, and lay the foundation for the highest level of refactoring by achieving these easy-to-accomplish goals.

And high-level refactoring is still made up of numerous small goals, rather than being done over a long period of time and on a large scale.

The essence of refactoring is a part of extreme programming, and only complete extreme programming can maximize the value of refactoring. Extreme programming itself advocates embracing change and enhancing adaptability, so it is the best operation mode to decompose the functions in extreme programming to adapt to the needs of the project and the current situation of the team.

Focus of refactoring

Repetitive code, too long functions, too large classes, too long argument columns, divergent variations, shotgun modifications, attachment complexes, data mud, basic type paranoia, parallel inheritance systems, redundant classes, etc

Here are some common or basic examples:

There are some basic principles that I think need to be understood

  1. Try to avoid creating too many Java objects for too long
  2. Use local variables whenever possible
  3. Use StringBuilder and StringBuffer whenever possible for string concatenation
  4. Minimize double counting of variables
  5. Try to free resources ina finally block
  6. Cache frequently used objects as much as possible
  7. Objects that are not used are set to NULL immediately
  8. Consider using static methods whenever possible
  9. Use singletons when appropriate
  10. Use final modifiers whenever possible

The following is about class and method optimization:

  1. Extraction of duplicate code
  2. Verbose method segmentation
  3. Optimization of nested conditional branching or cyclic recursion
  4. Extract constants from a class or inheritance system
  5. Extract repeated properties and methods from the inheritance system to the parent class

Here’s a brief introduction to some of the more general refactoring ideas, principles and methods. After all, today’s hero is SPI.

SPI

What is the SPI?

SPI stands for Service Provider Interface. It is a set of apis provided by Java that can be implemented or extended by third parties. It can be used to enable framework extensions and replace components.

It is a service discovery mechanism that automatically loads classes defined in files by looking for them in the META-INF/services folder in the ClassPath path.

This mechanism provides the possibility for many framework extensions, such as the USE of SPI mechanisms in Dubbo and JDBC.

The following is the mechanism of SPI

SPIIs actually based onInterface programming + policy pattern + configuration fileCombinatorial realizedDynamic loading mechanism.

Each abstraction of system design, there are often many different implementation schemes. In object-oriented design, it is generally recommended to program based on interface between modules, not hard coding between modules.

Once a specific implementation class is involved in the code, it violates the principle of pluggability, and if an implementation needs to be replaced, the code needs to be changed. A service discovery mechanism is needed in order to realize the dynamic specification during module assembly.

SPI provides a mechanism for finding service implementations for an interface. Similar to the IOC idea of moving control of assembly out of the program, this mechanism is especially important in modular design. So the core idea of SPI is decoupling.

Introduction to SPI

To use the Java SPI, you generally follow the following conventions:

  1. When the service provider provides a concrete implementation of the interface, the jar package’sMETA-INF/servicesDirectory to create a fully qualified interface name named ‘file, the content of the implementation class fully qualified name;
  2. The jar package that implements the interface class is placed in the main programclasspath;
  3. Main program passjava.util.ServiceLoderDynamically load the implementation module, which passes the scanMETA-INF/servicesThe configuration file in the directory finds the fully qualified name of the implementation class and loads the class into the JVM;
  4. The IMPLEMENTATION class of SPI must carry a constructor that takes no arguments;

SPI Usage Scenarios

In a nutshell, this applies to the implementation policies of the framework that the caller enables, extends, or replaces, depending on actual usage needs

Here are some more common examples:

  1. JDBC loads drivers for different types of databases
  2. SLF4J loads logging implementation classes from different vendors
  3. SPI is used extensively in Spring, such as: For implementation of ServletContainerInitializer servlet3.0 specification, automatic Type Conversion Type Conversion SPI (Converter SPI, the Formatter SPI), etc
  4. Dubbo also makes extensive use of SPI extensions to the framework, but it encapsulates the native SPI provided by Java and allows users to extend the Filter interface

SPI simple example

Define the interface class first

package com.test.spi.learn;
import java.util.List;

public interface Search {
    public List<String> searchDoc(String keyword);   
}
Copy the code

File search implementation

package com.test.spi.learn;
import java.util.List;

public class FileSearch implements Search{
    @Override
    public List<String> searchDoc(String keyword) {
        System.out.println("File Search"+keyword);
        returnnull; }}Copy the code

Database search implementation

package com.test.spi.learn;
import java.util.List;

public class DBSearch implements Search{
    @Override
    public List<String> searchDoc(String keyword) {
        System.out.println("Database Search"+keyword);
        returnnull; }}Copy the code

You can under the resources new meta-inf/services/directory, then the new interface fully qualified name of the file: com. Test. The spi. Learn. The Search

Plus the implementation classes that we need to use

com.test.spi.learn.FileSearch
com.test.spi.learn.DBSearch
Copy the code

Then write a test method

package com.test.spi.learn;
import java.util.Iterator;
import java.util.ServiceLoader;

public class TestCase {
    public static void main(String[] args) {
        ServiceLoader<Search> s = ServiceLoader.load(Search.class);
        Iterator<Search> iterator = s.iterator();
        while (iterator.hasNext()) {
           Search search =  iterator.next();
           search.searchDoc("hello world"); }}}Copy the code

You can see the output:

File search Hello World Database search Hello WorldCopy the code

SPI Principle analysis

By looking at the source code of ServiceLoader, the implementation process is as follows:

  1. The serviceloader. load method is used to create a new ServiceLoader and instantiate the class’s member variables, including the following:

Loader (ClassLoader) ACC (AccessControlContext) Providers (LinkedHashMap<String,S> LookupIterator (implements iterator functionality)

  1. Providers (LinkedHashMap

    ) : {ServiceLoader: {ServiceLoader: {ServiceLoader: {ServiceLoader: {ServiceLoader: {ServiceLoader: {ServiceLoader: {ServiceLoader: {ServiceLoader: {ServiceLoader:}}}
    ,s>

If there is a cache, return it directly. If there is no cache, the class is loaded as follows:

META-INF/services/ config file () The ServiceLoader can fetch the meta-INF configuration file across jar packages. (2) Load Class objects using reflection class.forname () and instantiate the Class using instance(). (3) Cache the instantiated class into providers (LinkedHashMap

) and return the instance object.
,s>

conclusion

advantages

The advantage of using the SPI mechanism is decoupling, so that the definition of the interface is separated from the concrete business implementation rather than coupled together. Application processes can enable or replace specific components based on actual services.

disadvantages

  1. Cannot load on demand. althoughServiceLoaderLazy loading is done, but basically it can only be done by traversing all fetching, that is, the implementation classes of the interface are all loaded and instantiated once. If you don’t want to use some implementation class, or if instantiation of some class is time consuming, it gets loaded and instantiated, and that’s a waste.
  2. The way to get an implementation class is not flexible enoughIteratorFormal fetch, not based on a parameter to get the corresponding implementation class.
  3. Multiple concurrent multithreadingServiceLoaderInstances of the class are not secure.
  4. When an implementation class is not loaded, an exception is thrown for no real reason, and the error is difficult to locate.

To see so many of the above shortcomings, you will think, why have these disadvantages to use, it is true that in the process of reconstruction, SPI interface is a very useful way, when you need to extend, adaptation, the use of the sooner you will be the earlier, at an appropriate time, the right opportunity, summon up courage, refactoring!!!!


All right. That’s all for today. I will continue to share what I have learned and thought. I hope we can walk on the road to success together!

Willing to output dry Java technology public number: Wolf king programming. The public number has a large number of technical articles, massive video resources, beautiful brain map, might as well pay attention to it! Get lots of learning resources and free books!

Forwarding moments is the biggest support for me!

\

Click “like and watching” if you feel something! Thank you for your support!

\