In the previous article, we discussed how to write the Spring IOC container by hand. Now that we have IOC, we are ready to write DI by hand


Instantiate the singleton Bean ahead of time

For singleton beans, you can pre-instantiate them using the following method

/** * Build the singleton bean project ahead of time */
public class PreBuildBeanFactory extends DefaultBeanFactory {

	private final Logger logger = LoggerFactory.getLogger(getClass());

	private List<String> beanNames = new ArrayList<>();

	@Override
	public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionRegisterException {
		super.registerBeanDefinition(beanName, beanDefinition);
		synchronized(beanNames) { beanNames.add(beanName); }}public void preInstantiateSingletons(a) throws Exception {
		synchronized (beanNames) {
			for (String name : beanNames) {
				BeanDefinition bd = this.getBeanDefinition(name);
				if (bd.isSingleton()) {
					this.doGetBean(name);
					if (logger.isDebugEnabled()) {
						logger.debug("preInstantiate: name=" + name + "" + bd);
					}
				}
			}
		}
	}
}

Copy the code

DI analysis

DI dependency injection has setter injection, constructor injection, etc., so there are two kinds of dependencies:

  1. Structural parameter dependence
  2. Attribute dependence

The essence of dependency injection is to give values, to give values to construct parameters, to give values to properties

Parameter and attribute values can be basic or bean dependent, such as basic data type values, Strings, arrays, collections, maps, properties, and so on

Both parameter and attribute values are values that the bean factory feeds into when it does dependency injection


The realization of the DI

Structural parameter dependence

One: Definition analysis

public class Girl {
    public Girl(String name, int age, Boy boy) {
        / /...}}Copy the code

The above code is a simple class with some parameters in the constructor, so normally create the Girl through the new method, i.e.

 Boy boy = new Boy("Xiao Ming");
 Girl girl = new Girl("Small fang".18, boy);
Copy the code

You can create a Girl by giving the value directly to Girl, and it’s as simple as that

To define a construct parameter dependency, you can do the following:

  • The first parameter value is “xiaofang”.
  • The value of the second parameter is 18
  • The third argument has the value Boy, which is a bean dependency

You can have multiple constructor parameter values, and they’re in order, and if they’re in the wrong order, then the types of the parameters might not match, and you’ll get an error, so the list in Java can be used to store constructor parameters, and it’s ordered, right? Because parameter values can be direct values such as primitive types, Strings, maps, and so on, or they can be bean dependencies, Object is the only way to represent them

With Object, there is another problem: how do bean dependencies distinguish them

You can define a datatype to represent bean dependencies. When constructing bean instances, the bean factory iterates over the parameters to determine if they are of its own datatype and, if so, replaces them with the dependent bean instance

If the parameter values are collections, arrays, and also have bean dependencies, again, they need to be iterated over and replaced

Define a class BeanReference

The BeanReference class is used to specify which Bean depends on it

/ * * *@className: BeanReference
 * @description: the * that describes bean dependencies in dependency injection@date: 2021/4/6 *, nay@author: jinpeng.sun
 */
public class BeanReference {

    /** beanName */
    private String beanName;

    public BeanReference(String beanName) {
        super(a);this.beanName = beanName;
    }

    /** Get beanName */
    public String getBeanName(a) {
        return this.beanName; }}Copy the code

The BeanDefinition interface and its implementation classes

Once the construct parameters are defined, you need to inject them in the Bean factory, first adding the interface to get the construct parameters in the BeanDefinition interface, and then implementing it in the implementation class

BeanDefinition adds the following two interfaces:

   /** Gets the constructor argument */List<? > getConstructorArgumentValues();/** Sets the constructor argument */
    void setConstructorArgumentValues(List
        constructorArgumentValues);
Copy the code

Add the following implementation code to the GeneralBeanDefinition implementation class:

    // Construct a set of parameters
    privateList<? > constructorArgumentValues;@Override
    publicList<? > getConstructorArgumentValues() {return constructorArgumentValues;
    }

    @Override
    public void setConstructorArgumentValues(List
        constructorArgumentValues) {
        this.constructorArgumentValues = constructorArgumentValues;
    }
Copy the code

At this point, we can get the construct parameter dependency, and here is the injection to implement the construct parameter dependency

DefaultBeanFactory class increment method

As you can see from the above, the first thing you need to do is convert the constructor parameter references in the bean definition to real values. Add a method to the DefaultBeanFactory class to do this

    /** Gets the construction parameter value */
    private Object[] getConstructorArgumentValues(BeanDefinition bd) throws Exception {
        return this.getRealValues(bd.getConstructorArgumentValues());
    }

    /** Get the actual parameter value */
    privateObject[] getRealValues(List<? > args)throws Exception {
        // If the argument is null, return null
        if (CollectionUtils.isEmpty(args)) {
            return null;
        }
        // Define an array with the same length as the set of values passed
        Object[] values = new Object[args.size()];
        int i = 0;
        Object v = null;
        // Iterate over the parameter values to replace the instance on which the bean depends
        for (Object rv : args) {
            if (rv == null) {
                v = null;
            } else if (rv instanceof BeanReference) {
                //TODO gets the referenced bean dependent instance
                v = doGetBean(((BeanReference) rv).getBeanName());
            } else  if (rv instanceof Object[]) {
               //TODO handles bean references in arrays
            } else  if (rv instanceof Collection) {
               //TODO handles bean references in the collection
            } else  if (rv instanceof Properties) {
               //TODO handles bean references in Properties
            } else  if (rv instanceof Map) {
               //TODO handles bean references in the Map
            } else {
                //TODO basic type
                v = rv;
            }
            values[i++] = v;
        }
        return values;
    }
Copy the code

Five: construct parameter injection implementation

Once you have parameters, how do you know which is a constructor and which is a factory method?

Methods can be overloaded; Parameters may define interfaces and superclasses, while arguments are implemented by concrete subclasses

Reflection provides the following API to get constructors and base methods:

Judgment logic:

  • The first method is used to do an exact match lookup based on the type of the argument, and the next step is used if none is found
  • Get all the constructors and iterate through them, filtering through the number of arguments and comparing parameter types with argument types

Once the constructor or factory method is identified, the constructor or factory method can be cached for a prototype bean, that is, a multi-instance bean, and fetched directly from the cache the next time it is fetched

Add the following interface to the BeanDefinition interface:

   /** get the constructor */Constructor<? > getConstructor();/** Sets the constructor */
    void setConstructor(Constructor
        constructor);

    /** Get factory method */
    Method getFactoryMethod(a);

    /** Set the factory method */
    void setFactoryMethod(Method factoryMethod);
Copy the code

The GeneralBeanDefinition implementation class implements:

    // constructor
    privateConstructor<? > constructor;// Factory method
    private Method factoryMethod;
    
    @Override
    publicConstructor<? > getConstructor() {return constructor;
    }

    @Override
    public void setConstructor(Constructor
        constructor) {
        this.constructor = constructor;
    }

    @Override
    public Method getFactoryMethod(a) {
        return factoryMethod;
    }

    @Override
    public void setFactoryMethod(Method factoryMethod) {
        this.factoryMethod = factoryMethod;
    }
Copy the code

The next step is to implement the find constructor or factory method

    /** find constructor */
    private Constructor determineConstructor(BeanDefinition bd, Object[] args) throws Exception {
        Constructor ct = null;
        // The parameter is null
        if (args == null) {
            return bd.getBeanClass().getConstructor(null);
        }
        // Define an array of parameters of the size of the construction parameter set passedClass<? >[] paramTypes =new Class[args.length];

        // For the prototype bean, the bean instance can be fetched from the cache from the second start
        ct = bd.getConstructor();
        if(ct ! =null) {
            return ct;
        }

        //1. Get the constructor according to the constructor parameter type
        int i = 0;
        for (Object p : args) {
            paramTypes[i++] = p.getClass();
        }
        ct = bd.getBeanClass().getConstructor(paramTypes);
        //2. Get all the constructors
        if (ct == null) { Constructor<? >[] cts = bd.getBeanClass().getConstructors();// Determine the number of arguments, and then the parameters and arguments
            outer: for (Constructor c : cts) {
                // Get the arguments in the constructorClass<? >[] parameterTypes = c.getParameterTypes();if (parameterTypes.length == args.length) {
                    for (int j =0; i< parameterTypes.length; j++) {
                        if(! parameterTypes[i].isAssignableFrom(args[j].getClass())) {continue outer;
                        }
                    }
                    ct = c;
                    breakouter; }}}if(ct ! =null) {
            // For prototype beans, cache them
            if(bd.isProtoType()) { bd.setConstructor(ct); }}else {
            throw new Exception("No constructor found:" + bd);
        }
        return ct;
    }
Copy the code

Modify the method of building bean instances through constructors:

    /** Build bean */ with constructor
    private Object createBeanByConstructor(BeanDefinition bd) throws Exception {
        Object object = null;
        if (CollectionUtils.isEmpty(bd.getConstructorArgumentValues())) {
            // If the constructor parameter value is empty, no arguments are passed
            object = bd.getBeanClass().newInstance();
        } else {
            // If the obtained parameter value is empty, no parameter is passed
            Object[] args = getConstructorArgumentValues(bd);
            if (args == null) {
                object = bd.getBeanClass().newInstance();
            } else {
                // call the method to implement dependency injection for the construction parameters
                returndetermineConstructor(bd, args).newInstance(args); }}return object;
    }
Copy the code

After the constructor method is written, follow the logic above to implement the static factory and the method that the factory method gets the bean instance

private Method determineFactoryMethod(BeanDefinition bd, Object[] realArgs, Class
        type) throws Exception {
        if (type == null) {
            type = bd.getBeanClass();
        }
        // Get the factory method name
        String factoryMethodName = bd.getFactoryMethodName();

        if (realArgs == null) {
            return type.getMethod(factoryMethodName, null);
        }
        Method m = null;

        // For the prototype bean, the bean instance can be fetched from the cache from the second start
        m = bd.getFactoryMethod();
        if(m ! =null) {
            return m;
        }

        //1. Exact matching method according to parameter type
        Class[] paramTypes = new Class[realArgs.length];
        int i = 0;
        for (Object p : realArgs) {
            paramTypes[i++] = p.getClass();
        }

        try {
            m = type.getMethod(factoryMethodName, paramTypes);
        } catch (Exception e) {
            // Do nothing
            m = null;
        }
        //2. Get all the methods and iterate over them
        if (m == null) {
            // Determine the number of arguments, and then the parameters and arguments
            outer: for (Method m0 : type.getMethods()) {
                // If the method name is different, continue to iterate directly
                if(! m0.getName().equals(factoryMethodName)) {continue ;
                }
                // Get all the arguments to find the methodClass<? >[] parameterTypes = m0.getParameterTypes();if (parameterTypes.length == realArgs.length) {
                    for (int j =0; j< parameterTypes.length; j++) {
                        if(! parameterTypes[j].isAssignableFrom(realArgs[j].getClass())) {continue outer;
                        }
                    }
                    m = m0;
                    breakouter; }}}if(m ! =null) {
            // For the prototype bean, cache the method below
            if(bd.isProtoType()) { bd.setFactoryMethod(m); }}else {
            throw new Exception("No corresponding method can be found:" + bd);
        }
        return m;
    }
Copy the code

Next, modify the methods of getting beans from static factories and member factories

    /** Build beans from static factories */
    private Object createBeanByStaticFactoryMethod(BeanDefinition bd) throws Exception { Class<? > type = bd.getBeanClass(); Object[] realArgs = getRealValues(bd.getConstructorArgumentValues()); Method method = determineFactoryMethod(bd, realArgs, type); Object object = method.invoke(type, realArgs);return object;
    }
Copy the code
    /** Build beans from member factories */
    private Object createBeanByFactoryBean(BeanDefinition bd) throws Exception {
        String factoryBeanName = bd.getFactoryBeanName();
        Object factoryBean = getBean(factoryBeanName);
        Object[] realArgs = getRealValues(bd.getConstructorArgumentValues());
        Method method = determineFactoryMethod(bd, realArgs, factoryBean.getClass());
        Object object = method.invoke(factoryBean, realArgs);
        return object;
    }
Copy the code

Six: construct parameter dependency tests

Several classes used for testing:

public interface Boy {
    void sayLove(a);
    void play(a);
}
Copy the code
public class Lad implements Boy {

    private String name;

    private Girl girl;

    private Money money;

    public Lad(String name) {
        this.name = name;
    }
    public Lad(String name, Girl gf) {
        this.name = name;
        this.girl = gf;
        System.out.println("Constructor with Girl argument called");
    }
    public Lad(String name, MagicGirl gf) {
        this.name = name;
        this.girl = gf;
        System.out.println("Constructor with MMagicGirll argument called");
    }
    public Lad(String name, Money m) {
        this.name = name;
        this.money = m;
        System.out.println("Constructor with Money argument called");
    }

    public Girl getFriend(a) {
        return girl;
    }

    public void setFriend(Girl girl) {
        this.girl = girl;
    }

    @Override
    public void sayLove(a) {
        if (girl == null) {
            System.out.println("It's so sad to have no one!" + hashCode());
        } else {
            System.out.println("I love you, honey!+ girl); }}@Override
    public void play(a) {
        if (money == null) {
            System.out.println("How to play without money!" + hashCode());
        } else {
            System.out.println("Have fun with money!"+ money); }}public void init(a) {
        System.out.println("God give me a date!");
    }

    public void destroy(a) {
        System.out.println("Since ancient times love spare hate, this hate continuous no unique period!"); }}Copy the code
public interface Girl {}Copy the code
public class MagicGirl implements Girl {
    private String name;
    private Boy friend;
    public MagicGirl(a){}
    public MagicGirl(String name) {
        this.name = name;
    }

    public Boy getFriend(a) {
        return friend;
    }
    public void setFriend(Boy friend) {
        this.friend = friend;
    }

    public String getName(a) {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString(a) {
        return "MagicGril{" +
                "name='" + name + '\' ' +
                '} '; }}Copy the code
public interface Money {
    void pay(a);
}
Copy the code
public class Renminbi implements Money {

    @Override
    public void pay(a) {
        System.out.println("Payment was successfully made using RMB."); }}Copy the code
public class BoyFactory {

    public static Boy getBean(String name, Money money) {
        return newLad(name, money); }}Copy the code
public class BoyFactoryBean {

    public Boy buildBoy(String name, Girl girl) {
        return newLad(name, girl); }}Copy the code

Constructor injection test:

   static PreBuildBeanFactory bf = new PreBuildBeanFactory();

    /** Constructor injection test */
    @Test
    public void testConstructorDI(a) throws Exception {
        GenericBeanDefinition definition = new GenericBeanDefinition();
        / / set beanClass
        definition.setBeanClass(Lad.class);
        // Set the constructor parameters
        List<Object> args = new ArrayList<>();
        args.add("Sun Wukong");
        args.add(new BeanReference("magicGirl"));
        definition.setConstructorArgumentValues(args);
        bf.registerBeanDefinition("swk", definition);

        definition = new GenericBeanDefinition();
        / / set beanClass
        definition.setBeanClass(MagicGirl.class);
        // Set the constructor parameters
        args = new ArrayList<>();
        args.add(Bone fairy);
        definition.setConstructorArgumentValues(args);
        bf.registerBeanDefinition("magicGirl", definition);

        bf.preInstantiateSingletons();

        Lad lad = (Lad) bf.getBean("swk");
        lad.sayLove();
    }
Copy the code

Execution Result:

Static factory injection test:

    /** Static factory injection test */
    @Test
    public void testStaticFactoryDI(a) throws Exception {
        GenericBeanDefinition definition = new GenericBeanDefinition();
        / / set beanClass
        definition.setBeanClass(BoyFactory.class);
        // Set the method name
        definition.setFactoryMethodName("getBean");
        // Set the constructor parameters
        List<Object> args = new ArrayList<>();
        args.add("Cowboy");
        args.add(new BeanReference("rmb"));
        definition.setConstructorArgumentValues(args);
        bf.registerBeanDefinition("nl", definition);

        definition = new GenericBeanDefinition();
        / / set beanClass
        definition.setBeanClass(Renminbi.class);
        bf.registerBeanDefinition("rmb", definition);

        bf.preInstantiateSingletons();

        Boy boy = (Boy) bf.getBean("nl");
        boy.play();
    }
Copy the code

Execution Result:

Member factory injection test:

/** Member factory injection test */
    @Test
    public void testFactoryMethodDI(a) throws Exception {
        GenericBeanDefinition definition = new GenericBeanDefinition();

        String fBeanName = "boyFactoryBean";
        // Set up the factory bean
        definition.setFactoryBeanName(fBeanName);
        // Set the method name
        definition.setFactoryMethodName("buildBoy");
        // Set the constructor parameters
        List<Object> args = new ArrayList<>();
        args.add("Pig Eight Quit");
        args.add(new BeanReference("xlv"));
        definition.setConstructorArgumentValues(args);
        bf.registerBeanDefinition("zbj", definition);

        definition = new GenericBeanDefinition();
        definition.setBeanClass(BoyFactoryBean.class);
        bf.registerBeanDefinition("boyFactoryBean", definition);

        definition = new GenericBeanDefinition();
        / / set beanClass
        definition.setBeanClass(MagicGirl.class);
        bf.registerBeanDefinition("xlv", definition);

        bf.preInstantiateSingletons();

        Boy boy = (Boy) bf.getBean("zbj");
        boy.sayLove();
    }
Copy the code

Execution Result:

Loop dependent processing

Can we loop dependencies when we build objects?

Write an example to test it:

public class NiuLang {

    private ZhiNv zhiNv;

    public NiuLang(ZhiNv zhiNv) {
        this.zhiNv = zhiNv; }}Copy the code
public class ZhiNv {

    private NiuLang niuLang;

    public ZhiNv(NiuLang niuLang) {
        this.niuLang = niuLang; }}Copy the code

The test class:

    @Test
    public void testCycleDI(a) throws Exception {
        GenericBeanDefinition definition = new GenericBeanDefinition();
        / / set beanClass
        definition.setBeanClass(NiuLang.class);
        // Set the constructor parameters
        List<Object> args = new ArrayList<>();
        args.add(new BeanReference("zv"));
        definition.setConstructorArgumentValues(args);
        bf.registerBeanDefinition("nl", definition);


        definition = new GenericBeanDefinition();
        / / set beanClass
        definition.setBeanClass(Renminbi.class);
        // Set the constructor parameters
        args = new ArrayList<>();
        args.add(new BeanReference("nl"));
        definition.setConstructorArgumentValues(args);
        bf.registerBeanDefinition("zv", definition);

        bf.preInstantiateSingletons();
    }
Copy the code

Running results:

As you can see, there is an error

If you build instance objects with cyclic dependencies, you get stuck in a zombie situation, so this is not allowed

You can add a record of the Bean being built to the factory implementation class, add it to the record while the Bean is being built, and delete the record when the build is complete. If there is a dependency, it depends on whether the dependency is under construction. If so, it forms a circular dependency and throws an exception

   /** The bean being created */
    private Set<String> buildingBeans = Collections.newSetFromMap(new ConcurrentHashMap());
Copy the code

Add the following code to the doGetBean method:

       Set<String> beans = buildingBeans;

        // Check for loop dependencies
        if (beans.contains(beanName)) {
            throw new Exception(beanName + "Cyclic dependency" + beans);
        }

        beans.add(beanName);
Copy the code
        // Delete the bean instance after it is created
        beans.remove(beanName);

        // Processing of singleton beans
        if (bd.isSingleton()) {
            beanMap.put(beanName, bean);
        }

Copy the code

Run the result again:

Attribute dependence

Attribute dependency is when an attribute is dependent on a value

If you need to describe a property dependency, that is, the property name + value, you can define a class to represent the property dependency

If multiple attributes are stored using a List, attribute dependencies are handled in much the same way as constructing parameter values

public class Girl {
    
    private String name;

    private Integer age;

    private Boy boy;
}
Copy the code

One: the definition of attribute dependencies

Define the PropertyValue class to represent property dependencies

* * *@className: PropertyValue
 * @description: Property value type, used for property dependency injection *@date: 2021/4/7 09:11
 * @author: jinpeng.sun
 */
public class PropertyValue {

    private String name;

    private Object value;

    public PropertyValue(String name, Object value) {
        super(a);this.name = name;
        this.value = value;
    }

    public String getName(a) {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Object getValue(a) {
        return value;
    }

    public void setValue(Object value) {
        this.value = value; }}Copy the code

The BeanDefinition interface and its implementation classes

The BeanDefinition class adds the following two interfaces:

    /** Gets the attribute value */
    List<PropertyValue> getPropertyValues(a);

    /** Sets the property value */
    void setPropertyValues(List<PropertyValue> propertyValues);
Copy the code

Add the corresponding implementation code to the GeneralBeanDefinition implementation class:

    / / property values
    private List<PropertyValue> propertyValues;

    @Override
    public List<PropertyValue> getPropertyValues(a) {
        return propertyValues;
    }

    @Override
    public void setPropertyValues(List<PropertyValue> propertyValues) {
        this.propertyValues = propertyValues;
    }
Copy the code

Three: DefaultBeanFactory class implementation attribute dependency

    /** Attributes depend on */
    private void setPropertyDIValues(BeanDefinition bd, Object bean) throws Exception {
        // Get the value of the attribute
        if (CollectionUtils.isEmpty(bd.getPropertyValues())) {
            return;
        }

        // Iterate over all attribute values
        for (PropertyValue pv : bd.getPropertyValues()) {
            String name = pv.getName();
            if (StringUtils.isBlank(name)) {
                continue; } Class<? > classz = bean.getClass();// Get attributes
            Field p = classz.getDeclaredField(name);

            p.setAccessible(true);

            Object rv = pv.getValue();
            Object v = null;
            if (rv == null) {
                v = null;
            } else if (rv instanceof BeanReference) {
                v = doGetBean(((BeanReference) rv).getBeanName());
            } else  if (rv instanceof Object[]) {
                //TODO handles bean references in arrays
            } else  if (rv instanceof Collection) {
                //TODO handles bean references in the collection
            } else  if (rv instanceof Properties) {
                //TODO handles bean references in Properties
            } else  if (rv instanceof Map) {
                //TODO handles bean references in the Map
            } else {
                // Basic typev = rv; } p.set(bean, v); }}Copy the code

Property dependencies are called after the bean instance is created but before the bean is initialized, so you need to change the doGetBean method

    @Override
    public Object getBean(String beanName) throws Exception {
        // Get the bean definition
        BeanDefinition bd = beanDefinitionMap.get(beanName);
        Object bean = doGetBean(beanName);

        // Attribute injection
        setPropertyDIValues(bd, bean);

        // The bean's life cycle
        if (StringUtils.isNotBlank(bd.getInitMethodName())) {
            doInitMethod(bean,bd);
        }
        return doGetBean(beanName);
    }

    public Object doGetBean(String beanName) throws Exception {

        Object bean = beanMap.get(beanName);
        if(bean ! =null) {
            return bean;
        }

        // Get the bean definition
        BeanDefinition bd = beanDefinitionMap.get(beanName);
        Objects.requireNonNull(bd, "Can't find ["+beanName+"] bean definition information"); Class<? > type = bd.getBeanClass();// Check for loop dependencies
        Set<String> beans = this.buildingBeans;
        if (beans.contains(beanName)) {
            throw new Exception(beanName + "Cyclic dependency" + beans);
        }
        beans.add(beanName);

        if(type ! =null) {
            if (StringUtils.isBlank(bd.getFactoryMethodName())) {
                // Build the bean through the constructor
                bean = createBeanByConstructor(bd);
            } else {
                // Build beans from static factoriesbean = createBeanByStaticFactoryMethod(bd); }}else {
            // Build beans from member factories
            bean = createBeanByFactoryBean(bd);
        }

        // Delete the instance after it is created
        beans.remove(beanName);

        // Handle singleton beans
        if (bd.isSingleton()) {
            beanMap.put(beanName, bean);
        }
        return bean;
    }
Copy the code

Four: attribute dependence test

    /** Attribute injection test */
    @Test
    public void testPropertyDI(a) throws Exception {
        GenericBeanDefinition definition = new GenericBeanDefinition();
        / / set beanClass
        definition.setBeanClass(Lad.class);
        // Set the constructor parameters
        List<Object> args = new ArrayList<>();
        args.add("Sun Wukong");
        args.add(new BeanReference("bgj"));
        definition.setConstructorArgumentValues(args);
        bf.registerBeanDefinition("swk", definition);

        definition = new GenericBeanDefinition();
        / / set beanClass
        definition.setBeanClass(MagicGirl.class);
        // Set the parameter values for property injection
        List<PropertyValue> propertyValues = new ArrayList<>();
        propertyValues.add(new PropertyValue("name".Bone fairy));
        propertyValues.add(new PropertyValue("friend".new BeanReference("swk")));
        definition.setPropertyValues(propertyValues);
        bf.registerBeanDefinition("bgj", definition);

        bf.preInstantiateSingletons();

        MagicGirl magicGirl = (MagicGirl) bf.getBean("bgj");
        System.out.println(magicGirl.getName() + ":" + magicGirl.getFriend());
        magicGirl.getFriend().sayLove();
    }
Copy the code

Execution Result:

That’s the end of writing Spring DI, and I hope this article has been helpful to you!