I guess with spring-boot: bean injection

Goal lead &

Benpian is "now I guess Spring-Boot" series, the first two (Oh, I actually have written a 10, really is not easy).

In the last article, we achieved Bean created, but only to create it, did not really realize the injection of Bean. So this today, we are going to achieve the bean automatically injected.

We have previously defined in the project  SimpleServiceand  SimpleControllerthese two categories, then this article, we want to  SimpleServiceautomatically injected into the  SimpleControllermiddle;

SimpleController.java

@Service
public class SimpleController {
    @Autowired
    private SimpleService simpleService;
    public SimpleController(){
        System.out.println("the controller is created!");
    }
}

Because there is just a console program, no way to call and show genuine, so I  SimpleServiceadd an output that represents the uniqueness of this class. In this way, SimpleServiceit becomes like this:

SimpleService.java

@Service
public class SimpleService {
    private String serviceId ;
    public SimpleService(){
        serviceId= UUID.randomUUID().toString();
        System.out.println("the service :"+serviceId+"is created!");
    }
    public String getServiceId(){
        return this.serviceId;
    }
}

Although now have to call, there is no way to verify our ideas.

Altogether, we will count on the meter, we add a  PostContructcomment now :)

SimpleController.java

public class SimpleController{
    // other code
    @PostContruct
    public void init(){
        System.out.println("the service id is :"+this.simpleService.getServiceId());
    }   
}

Dependency injection needs analysis

Our goal by the change program, we can see that the pair  of major changes in the following points:mini-boot

  1. Definitions  PostConstructand Autowired

  2. In  Autowiredmarked, dependency injection

  3. With  PostContructmarked realize Bean created in automatic initialization

3 is intuitive to target this time we want to achieve, however, due between us is too simple design, we have a problem to solve, namely:

We need a place to store, find has already raw bean!

Then the problem is clearly more important than 1,2,3 strip, so

The foregoing digging, later filled pit

Our goal becomes:

  1. Achieve storage and management of Bean

  2. Definitions  PostConstructand Autowired

  3. In  Autowiredmarked, dependency injection

  4. With  PostContructmarked realize Bean created in automatic initialization

Step 0 Bean storage and management of

In Spring, obtained from bean next external mode:

  1. Dependency injection or by other means to give ApplicationContext

  2. By  obtaining the corresponding bean.ApplicationContext.getBean

By these two, it is obvious:

  1. We also like a photo ApplicationContext

  2. And by this logic to get the bean class name or class, obviously a map like this, us.  ApplicationContextBecome very good realization:

ApplicationContext.java

package com.github.yfge.miniboot.context;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
public class ApplicationContext {
    private Map<String,Object> beanMap ;
    public ApplicationContext(){
        beanMap=new LinkedHashMap<>();
    }
    public void addBean( Object ob){
        this.beanMap.put(ob.getClass().getName(),ob);
    }
    public Object getBean(String beanName){
        return this.beanMap.get(beanName);
    }
    public Object getBean(Class beanClass){
        return this.beanMap.get(beanClass.getName());
    }
}

Ah, with the  ApplicationContextlater, we can use a applicationContext all the bean are saved while generating bean. At this time, our  function has a little bit of change:Application.loadBeans

Application.loadBeans

public class Application{
// other code
  private static void LoadBeans(Class source) {
        ClassUtils util = new ClassUtils();
        List<String> classNames = util.loadClass(source);
        /** 实例化一个context **/
        ApplicationContext applicationContext = new ApplicationContext();
        for (String name : classNames) {
            try {
                var classInfo = Class.forName(name);
                /**
                 * 检查是否声明了@Service
                 **/
                if (classInfo.getDeclaredAnnotation(Service.class) != null) {
                    /**
                     * 得到默认构造函数
                     */
                    var constructor = classInfo.getConstructor();
                    if (constructor != null) {
                        /**
                         * 创建实例
                         */
                        var obj = constructor.newInstance();
                        /** 保存bean**/
                        applicationContext.addBean(obj);
                    }
                }
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }
  }
//other code
}

It can be seen at this time of change is still small, we are just at the beginning of the initialization of a context, then after the bean is generated, stored in the context of the bean

OK, hold bean functional complete (just previous pit filled it), our next step is to begin formal work.

Step1 defined Annotation

Previously analyzed, we need to define  Autowiredand  PostContructtwo notes are very simple, you can do some statement:

Autowired.java

package com.github.yfge.miniboot.autoconfigure;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Autowired {
}

PostConstruct.java

package com.github.yfge.miniboot.autoconfigure;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface PostConstruct {
}

Also note that we are here to add  this line, the specific features and differences, we will look for opportunities to explain (again digging, not digging uncomfortable group)@Retention(RetentionPolicy.RUNTIME)

Step2 to @Autowired marked, dependency injection

Ah, we want to be the most exciting part, and dependency injection, that is, we want to  SimpleServiceautomatically injected into the  SimpleControllermiddle.

But ....

Everything has a very matter of course, but what good double Cheese Li excited? We have to do nothing more than:

  1. Put all taken out of context in the bean;

  2. To give a reflection of each field bean;

  3. Check this field has not added autowird comment;

  4. If so, check this field type is not a bean;

  5. If so, the assignment taken out by reflection;

Because we are using a storage Map performed, it can be combined into two steps 4 and 5:

4-5 Press this field to get the type of bean, if not empty, it is assigned.

Because we want to clear, coherent nature codes, i.e.  increasing injection logic behind this:Application.loadBeans

Application.loadBeans

public class Application {
    /**
     * 加载相应的bean(Service)
     *
     * @param source
     */
    private static void LoadBeans(Class source) {
        //other code
        //上面是之前的逻辑
        /** 注入bean **/
        for (Object ob : applicationContext.getAllBeans()) {
            var classInfo = ob.getClass();
            for (var field : classInfo.getDeclaredFields()) {
                if (field.getDeclaredAnnotation(Autowired.class) != null) {
                    field.setAccessible(true);
                    var filedBean = applicationContext.getBean(field.getType());
                    if (filedBean != null) {
                        try {
                            field.set(ob, filedBean);
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
    //other code
}

At the same time, we added a new method for the ApplicationContext, to get all the bean

ApplicationContext.java

public class ApplicationContext {
    //other code
    public Collection<Object> getAllBeans(){
        return this.beanMap.values();
    }
}

Note that there is a point that we Applicaton.loadBeans cycle twice:

  • The first is to generate and store the bean

  • The second is to assign all bean taken out, dried in the form of injection for each dependency bean

The reason for this are obvious:

In order to ensure that when injected can get what we want bean, must be processed after all the bean generates

Step3 @PostConstruct marked to achieve the initialization process automatically run

To the previous step, in fact, our dependency injection has been completed, but due to the current of our  framework is too simple, there is no way to validate our success.mini-boot

So, here we are passing mechanism will also realize what @PostContruct, after automatic initialization class that is loaded.

With the step of the work, you will find that this time we work very stylized and tasteless, namely:

  1. Put all taken out of context in the bean;

  2. Each of the bean to give a reflection method ;

  3. Check this method has not added PostContruct comment;

  4. If so, using this method of performing reflective ;

You will have noticed, here I have some standard fonts are thick, the reason is because the standard thick: 1-4 Here is my copy from the top down, just mark the thick part of the change .

Now that the realization of ideas can be copied, then the code has become very easy.

Application.loadBeans

public class Application {
    /**
     * 加载相应的bean(Service)
     *
     * @param source
     */
    private static void LoadBeans(Class source) {
        //other code
        //上面是之前的逻辑
        //包括
        //1. 生成所有的bean并存储
        //2. 遍历已经生成的bean进行依赖注入
        /** 执行初始化方法 **/
        for (Object ob : applicationContext.getAllBeans()) {
            var classInfo = ob.getClass();
            if (classInfo.getDeclaredAnnotation(Service.class) != null) {
                for (var method : classInfo.getDeclaredMethods()) {
                    if (method.getDeclaredAnnotation(PostConstruct.class) != null) {
                        try {
                            method.invoke(ob);
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        } catch (InvocationTargetException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
    // other code
}

OK, now compile the project, execution, you will see the following output:

the controller is created!
the service :580fbd69-d82b-44b6-847a-b4c5cbc4d97b is created!
the service Id is :580fbd69-d82b-44b6-847a-b4c5cbc4d97b
The Mini-Boot Application Is Run! The Name is Hello

This output:

  • The first line is automatically created controller

  • When the second row of the table service is automatically created and the unique id is xxxx

  • The third line is the output of our controller in the init function, you can see where service has not empty, and that is automatically created between us!

In other words:

  1. We have implemented a simple automatic injection as expected

  2. We also expect to achieve in accordance with a simple automatic operation initialization function

Now, quietly Recall from our article, is there some doors have been opened to you 

other

Not to share the source code bullying!

So, our project address is: https: //github.com/yfge/mini-boot due, with the release of the article, the code will be constantly updated, so, tag this chapter is article-02 (forgive me a name of Level)

Abduction lean on old

  • Free-range program ape, wild lion architecture

  • Second-rate workers moving bricks, third-rate photographer

  • Prudish than funny, installed two forced literary reality

So, that being a public number, there will be code, scripts, there are beauties, have chicken soup, anyway, chaos seventy-eight was, chances are which just hit on its hands of it to your

What is not said, scan code to focus on it


Published 65 original articles · won praise 9 · views 20000 +

Guess you like

Origin blog.csdn.net/geyunfei_hit/article/details/104707799