Implementation of a simple IOC container __spring

Source: Internet
Author: User
Tags aop

Looking at spring's source code recently, the implementation part of the IOC container that relies on XML configuration files is harder to read. The main reason is that the IOC implementation mechanism is not very well understood, so the initiation of the principle of a simple IOC container, so that they can have a more in-depth understanding of spring's IOC implementation. What is the IOC

IOC is the abbreviation of English control reversal, is a kind of design idea. For the spring framework, the IOC is the spring that is responsible for the relationship between the object's lifecycle and the object. Before you need an object, you have to have a new one to hold the object's reference, the object coupling is serious. The Spring IOC is apt to dynamically provide the other objects it needs to an object by unifying the relationship of the responsible object. implementation Steps

What steps are needed to achieve an IOC, I'll summarize it for the following points. As spring is known, Spring uses an XML configuration file to configure the Bean. So the first step is to read and parse the XML configuration file to generate the bean instance.
1. Read the XML configuration file. The Spring framework uses the DOM4J framework to read XML files. It generates the document object with the XML file, and the XML configuration attributes are found in the Document object, and the XML configuration properties and the Document object's properties are one by one corresponding.
2. Loads the Element in the Document object. What is element, so to say, XML file <bean> </bean> is a element,<bean> the following <constructor-arg> is also an element, Each configuration attribute in the XML is represented by an element in the Document object.
3. Resolve element. Resolves the loaded Element property. For example, to parse <bean> class attribute values, you can find the location of the Bean class and generate his object instance.
4. Generate instances and cache. The bean instance is generated and cached according to the data parsed in the third step.
5. Injection properties. Property injection of the bean instance is performed according to the third step parsing to the property.
Finally, the implementation of a total container class integrates the previous steps and provides an interface to get the bean instance. Now follow this step to implement the functions of each part. read the XML configuration file.

Configure an interface class that has a getdocument (String path) method. Generates a document object by passing in the location path of the XML file.

XML file read interface class public
interface Documentholder {

    document GetDocument (String filePath);

}

Take a look at the concrete implementation of the interface.

public class Xmldocumentholder implements Documentholder {//Create a hashmap for storing strings and documents private map<string, documen

    t> docs = new hashmap<string, document> (); @Override public Document getdocument (String filePath) {document doc=this.docs.get (FilePath);//Use HashMap first according to the path

        Get the document if (Doc==null) {this.docs.put (FilePath, Readdocument (FilePath));//If empty, put the path and document in}
    Return This.docs.get (FilePath); /** * Read Document * @param FilePath * @return/Private document Readdocument by Path (String fil

        Epath) {Document doc =null; try {saxreader reader = new Saxreader (true);//Borrow DOM4J parser reader.setentityresolver (new Iocentit

            Yresolver ()); File XmlFile = new file (FilePath);

            Create file based on path doc = Reader.read (xmlFile)///read from reader read with DOM4J to return a document} catch (Exception e) {

        E.printstacktrace (); } return DOc }


}

The iocentityresolver inside this is the class that you implement. What is this class for? Let's first introduce the XML configuration file namespace, understand the XML file namespace, you know what this class is doing. The following are two configuration headers for the spring XML file.

//xsd validation xml file <?xml version= "1.0" encoding= "UTF-8"?> <beans xmlns= "http://" Www.springframework.org/schema/beans "xmlns:xsi=" http://www.w3.org/2001/XMLSchema-instance "xmlns:context=" http: Www.springframework.org/schema/context "xmlns:aop=" HTTP://WWW.SPRINGFRAMEWORK.ORG/SCHEMA/AOP "xmlns:tx=" http:/ /www.springframework.org/schema/tx "xsi:schemalocation=" Http://www.springframework.org/schema/beans http:// Www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http:// Www.springframework.org/schema/context/spring-context-3.0.xsd HTTP://WWW.SPRINGFRAMEWORK.ORG/SCHEMA/AOP http:// Www.springframework.org/schema/aop/spring-aop-3.0.xsd Http://www.springframework.org/schema/tx Http://www.sprin Gframework.org/schema/tx/spring-tx-3.0.xsd "> 

Namespaces are equivalent to packages in Java, where xmlns is the equivalent of a reserved word to declare a namespace. It is stated that the alias is xsi, context, AOP, and TX namespaces, and the URL following the alias contains information about the namespace. By declaring these namespaces, you can use the elements of that namespace. For example, the AOP namespace is stated above, and the slice can be configured with <aop:config>. Spring uses XML to configure bean information, and when parsing an XML file, it first validates the correctness of the XML file. XML validation complains when an XML file uses an element that does not exist in the namespace or if there is a problem with it. So how to verify it. Usually a namespace URL corresponds to an XSD address, as shown in the above xsi:schemalocation, so the URLs behind the xsi:schemalocatio are all pairs. This XSD allows you to obtain an XSD file that contains all the usage rules for that corresponding namespace. So how XML validation is validated is through these XSD files to verify that the equivalent namespace element is used correctly.

DTD way validation xml file
<?xml version= "1.0" encoding= "UTF-8"?>  
<! DOCTYPE beans Public "-//crazyit//dtd bean//en" "Http://www.crazyit.org/beans.dtd" >  
<beans>  

</ Beans>  

The above is a DTD way to validate an XML file. Declares the beans namespace, which is followed by two parts, publicID and Systeid respectively. As shown above, publicID is-//crazyit//dtd Bean//en,systemid is http://www.crazyit.org/beans.dtd. The SystemID URL defines the location of the DTD file, as in the schemalocation URL in the XSD. This DTD file feature is the same as XSD, and contains all the usage rules for that corresponding namespace, except that there are differences in how the XML file is validated. In the case of XSD or DTD, to ensure that the XSD and DTD files are available when the local network is not networked, sping preserves versions of the XSD and DTD files in the local package and gets it via the URL network when it is not available locally. So what iocentityresolver this class, is to specify the location of the local XSD or DTD file, so that when validating the XML, it is read directly from the Local. The implementation of the Iocentityresolver class is as follows:

public class Iocentityresolver implements Entityresolver {
    @Override public
    inputsource resolveentity (String publicID, String SystemID) throws Saxexception, IOException {
        if ("Http://www.crazyit.org/beans.dtd". Equals ( SystemID)) {
            InputStream stream = IoCEntityResolver.class.getResourceAsStream ("Spring-beans-2.0.dtd");
            return new InputSource (stream);
        } else {return
            null;
        }
    }
}

This uses the method of validating DTDs. So my XML configuration file is as follows.

<?xml version= "1.0" encoding= "UTF-8"?> <!
DOCTYPE beans Public "-//crazyit//dtd bean//en" "Http://www.crazyit.org/beans.dtd" >
<beans>

    < Bean id= "category" Class= "Com.kdk.action.domain.Category" >
        <constructor-arg>
        <value type= " Java.lang.Integer ">552</value>
        </constructor-arg>
        <constructor-arg>
           <value Type= "java.lang.String" >code</value>
        </constructor-arg>
    </bean>
    <bean id= " Book "class=" Com.kdk.action.domain.Book "autowire=" byname ">
        <constructor-arg>
            <value type=" Java.lang.String ">kdk</value>
        </constructor-arg>
        <constructor-arg>
            < Value type= "java.lang.String" >c study</value>
        </constructor-arg>
    </bean>

</beans>

When SystemID is HTTP://WWW.CRAZYIT.ORG/BEANS.DTD, the DTD file is fetched locally, and my local DTD file is provided directly with spring and is not customized. From here, you can generate my XML file as a Document object. load Element element

Once you get to the Document object, you can then load the element element in the document. Defines the interface that loads an element.

Public interface Elementloader {
    //load all of the Element in Doucument and cache in local
    void Addelements (Document doc);

    Gets the element
    element getelements (String ID) based on the ID of the element;

    Returns all Element
    collection<element> getelements () in Document;

The method of implementing the interface sequentially, as defined by the interface.

public class Elementloaderimpl implements Elementloader {
    private map<string,element> elements = new hashmap& Lt;> ();

    @Override public
    void Addelements (Document doc) {
        list<element> eles = doc.getrootelement (). elements (); For
        (Element e:eles) {
            String id = e.attributevalue ("id");
            Elements.put (id,e);
        }

    @Override public
    Element getelements (String ID) {return
        elements.get (ID);
    }

    @Override public
    collection<element> getelements () {return
        elements.values ();
    }
}

Loading element elements is relatively simple, using the method provided by document to get all the root element and cache it in the Map. parsing Element

In this simple IOC container, I only implemented a partial attribute of the bean Element parsing, but understanding this part of the parsing, naturally also understand the other attributes of the analytic principle. The Element resolution interface is as follows:

The public interface Elementreader {
    //is lazy to load a
    Boolean islazy (element element);

    Get the Constructor-arg
    list<element> getconstructorelements (element Element) below the bean element;

    Gets the property value
    String getattribute (Element element,string name) for the property name;

    Boolean Issingleton (element element);

    Gets all the property elements list<element> getpropertyelements (element Element) below a bean element
    ;

    Gets the Autowire object Autowire Getautowire (element Element) corresponding to the bean element
    ;

    Gets the value (including type and value)
    list<dataelement> getconstructorvalue (element Element) of all Constructor-arg under the bean element;

    List<propertyelement> GetPropertyValue (element Element);
}

The next step is to implement the analysis of the properties of each part. Lazy Load Attribute parsing

@Override Public
    Boolean Islazy (element Element) {
        String lazy = getattribute (element, "Lazy-init");
        Element parent = Element.getparent ();
        Boolean parentlazy = new Boolean (GetAttribute (parent, "dafault-lazy-init"));
        if (parentlazy) {
            if ("false". Equals (lazy)) return false;
            return true;
        } else {
            if (' true '. Equals (lazy)) return true;
            return false;
        }
    }

To an XML bean lazy load case, the case in the look at the lazy load analysis at a glance.

<beans Default-lazy-init = "true" > 
      <bean id= "Service1" type= "Bean Path" lazy-init= "true"/> 
      < Bean id= "Service2" type= "Bean Path" lazy-init= "false" > 
             <property name= "Service1" ref= "Service1"/> 
      </bean> 
</beans>

The default lazy load value and itself Lazy-init value are determined based on the setting of the parent element to determine whether lazy loading. Parsing Constructor-arg Properties

@Override public
    list<element> getconstructorelements (element Element) {
        list<element> Children = Element.elements ();
        list<element> result = new arraylist<element> ();
        for (Element e:children) {
            if ("Constructor-arg". Equals (E.getname ())) {
                result.add (e);
            }
        }
        return result;
    }

Similarly, a case of an XML configuration Constructor-arg attribute is given.

<bean id= "book" class= "Com.kdk.action.domain.Book" autowire= "byname" >
        <constructor-arg>
            <value type= "java.lang.String" >kdk</value>
        </constructor-arg>
        <constructor-arg >
            <value type= "java.lang.String" >c study</value>
        </constructor-arg>
</ Bean>

As mentioned earlier, the attribute configuration of XML is that element,<constructor-arg> is a child element of the parent <bean> element. By this logic, you get all the child Element of <bean> and then determine whether it is a <constructor-arg> Element, so it is saved. Get the value of a property

@Override public
    String getattribute (element element, String name) {
        String value = Element.attributevalue (name );
        return value;
    }

such as <bean id= "book" class= "Com.kdk.action.domain.Book" > id attribute value is book, the class attribute value is Com.kdk.action.domain.Book. Whether it is P state

If a bean is set to a P state, the IOC container will generate an instance of the bean and cache it when it is started. If it is not set to P state, it is not cached, and each time the bean is requested, it is recreated.

@Override Public
    Boolean Issingleton (element Element) {
        Boolean singleton = new Boolean (GetAttribute (element, " Singeleton "));
        return singleton;
    }
Resolve Property Properties

Parsing property Properties and parsing Constructor-arg attribute principle exactly the same, know how to parse Constructor-arg properties, Nature also know to parse property properties.

@Override public
    list<element> getpropertyelements (element Element) {
        list<element> children = Element.elements ();
        list<element> result = new arraylist<element> ();
        for (Element e:children) {
            if (' Property '. Equals (E.getname ())) {
                result.add (e);
            }
        } return result;
    }

Its implementation is almost the same as parsing constructor-arg. Parsing Automatic Assembly Autowire properties

The Spring framework Autowire can be configured with a variety of values, including No, byname, Bytype, constructor, and default. Here, I have only implemented the configuration of No and byname. Default to
No, that is, automatic assembly is not used. When configured as ByName, the setter method inside the bean is parsed, if the setter method parameter is a different bean, and the bean is already configured in XML and loaded into the container,
It is automatically injected into the bean set autowire= "ByName".

@Override public
    Autowire Getautowire (element Element) {
        String value = This.getattribute (element, "Autowire") ;
        String Parentvalue = This.getattribute (Element.getparent (), "Default-autowire");
        if ("No". Equals (Parentvalue)) {
            if ("ByName". Equals (value)) Return to new Bynameautowire (value)
            ; return new Noautowire (value);
        } else if ("ByName". Equals (Parentvalue)) {
            if ("No". Equals (value)) Return to new Noautowire (value)
            ; return new Bynameautowire (value);
        }
        return new Noautowire (value);
    }
    Public interface Autowire {
        String getValue ();
    }
    public class Bynameautowire implements autowire{
    private String value;

    Public Bynameautowire (String value) {
        this.value = value;
    }

    @Override public
    String GetValue () {return
        value;
    }
}

public class Noautowire implements autowire{
    private String value;

    Public Noautowire (String value) {
        this.value = value;
    }

    @Override public
    String GetValue () {return
        value;
    }
}

Its implementation is similar to parsing lazy load familiar. Here the autowire of the value byname is encapsulated into Bynameautowire, and the autowire of No is encapsulated as Noautowire. Parsing Constructor-arg values

All of the Constructor-arg element has been parsed before, and the value of the Constructor-arg is now needed to be taken out.

@Override public
    list<dataelement> Getconstructorvalue (element Element) {
        list<element> cons = Getconstructorelements (element);
        list<dataelement> result = new arraylist<> ();
        for (Element e:cons) {
            list<element> elements = e.elements ();
            Dataelement dataelement = getdataelement (elements.get (0));
            Result.add (dataelement);
        }
        return result;
    }

The getconstructorelements (Element e) is already implemented earlier. The value of the Constructor-arg is encapsulated with dataelement, encapsulating the type and specific data of the Constructor-arg value.

public interface Dataelement {//Return data of type String getType ();
Returns the value of the data Object getValue (); //<value></value> Configure Constructor-arg value values for encapsulation class public class Valueelement implements Dataelement {private

    Object value;
    Public valueelement (Object value) {this.value = value;
    @Override public String GetType () {return ' value ';
    @Override public Object GetValue () {return this.value; The encapsulation class public class Refelement implements Dataelement {private objec//<ref></ref> Configure Constructor-arg value values

    T value;
    Public refelement (Object value) {this.value = value;
    @Override public String GetType () {return "ref";
    @Override public Object GetValue () {return value; }
}

Constructor-arg through <value> configuration value, the value is encapsulated into valueelement, its type is "value", and the value is encapsulated Refelement,type "ref" by <ref> configuration value. Again, give a constructor-arg case.

<bean id= "category" Class= "Com.kdk.action.domain.Category" ></bean>
<bean "book" Id= " Com.kdk.action.domain.Book "<

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.