Simple Implementation of Spring Automatic Injection

Keywords: Java Spring github

Statement: This article is not about how Spring uses annotations. It's just a simple implementation to understand how Spring injects an object.

_Students who have used Spring know that Spring uses annotations to implement dependency injection, which greatly reduces the coupling between classes. But just using it can't understand how Spring implements itself. I haven't seen Spring's source code. I can only talk about Spring from my own point of view. Interested students can learn more about Spring after reading this article.
Often, we have such application scenarios. For example, in the DAO layer, you declare an interface, such as IUserDao, to represent an interface used to process User, then write an implementation class, UserDaoImpl, to implement methods in IUserDao, and then inject them into the upper service layer. Spring automatically injects instantiated objects into our scans after startup, so that we don't care about the life cycle of each object. Next, let's talk about how to inject it.
Assume that there are now the following classes:

public interface IUserDao {
    
    public void setData(String data);
    public String getData();
}

public class UserDaoImpl implements IUserDao{

    @Override
    public void setData(String data) {
        System.out.println("data is : " + data);
    }

    @Override
    public String getData() {
        return "just test";
    }

}

FieldInject is a note imitated by the author, which is defined as follows.

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FieldInject {
    //Suppose there are some variables for the control strategy.
}

_For the meaning of the meta-annotation above the annotation, you can see another article. Blog . There is no explanation here.
This is the preparation work, and then we will explain the real initialization method.
Assuming that we now have a Class object of a class, we can find out which member variables are annotated according to the Class object. The code is as follows

//Now start injecting
            for(int i=0; i<field.length; i++){
                FieldInject annotation = field[i].getAnnotation(FieldInject.class);
                if(annotation != null){//Explain that the member variable is annotated
                    //Gets the fully qualified name of the member variable
                    String fullClassName = field[i].getType().getName(); 
                    Class sub = findSubClass(fullClassName);
                    if(sub == null) continue; //Can't find it, skip it
                    String lowCase = "set"+field[i].getName();
                    
                    //Use the setter method to select the magnitude from the following code
//                    injectMethod(method,obj, sub, lowCase.toLowerCase()); 
                    
                    //Here is the direct magnitude
                    injectField(field[i],obj,sub);
                }
            }

_In this code, the annotation I queried is a Field Inject annotation implemented by myself, and the annotation itself does not affect the execution of the code. By judging whether it is empty, it can be concluded whether a member variable has been annotated. If you find a member variable annotated, you can inject instantiated objects into that member variable.
Question 1: How do you know which object to inject?
Question 2: How to inject?
Problem 2 is a good solution. If the original class has setter methods, then method.invoke() method can be used to call and inject. Or it can be injected directly through field. So the main problem is 1, how to find the appropriate injection object.
Spring has a variety of injection strategies, such as injection according to assembly name, or default implementation of interface or subclass instance object of abstract class. In short, different strategies are just different choices, and we can assume that we inject them using the instance object of the first suitable subclass found.

    //Find a subclass of a class [involving Spring's selection strategy]
    private Class findSubClass(String fullClassName){
        try {
            Class target = Class.forName(fullClassName);
            
            //It's not an abstract class, it's not an interface, that's all.
            if(!target.isInterface()){
                boolean isAbs = Modifier.isAbstract(target.getModifiers());
                if(!isAbs) return target;
            }
            
            int size = clazzList.size();
            for(int i=0; i<size; i++){
                Class p = clazzList.get(i);
                if(p.getSuperclass() != null && p.getSuperclass().getName().equals(fullClassName)) return p;
                Class[] inter = p.getInterfaces();
                for(Class c : inter){
                    if(c.getName().equals(fullClassName)) return p;
                }
            }
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }

_findSubClass is used to find the appropriate subclasses of a class, similar to the strategy used in Spring, where a relatively simple method is used. Find the first appropriate subclass. In this method, some simple judgments are made. If the class itself is not an abstract class or an interface, then the class is the first suitable class. If the class is an interface or an abstract class, find the appropriate class in the classList of the global scan. After finding the appropriate class, the next step is an injection. The author uses the method of setter injection. If we want to assign the member variables directly, it is very simple. Just replace the method injectMethod with the following two lines of code.

field[i].setAccessible(true);
field[i].set(target, obj);

_injectMethod is also relatively simple to implement. By comparing the methods in Method, we can find the appropriate setter method (which is judged by the name of field) and assign the instance object to it. This is the implementation of a simple injection process. I write in a hurry, and may not be able to withstand deliberation on some details. But it's also good to provide an idea for confused beginners. I upload this code to github, and if I want to download it and run it, I can move it around. My github.

Posted by dmcke5 on Sat, 13 Apr 2019 16:54:33 -0700