Handwritten IOC

IOC inversion of control hosts all of our classes for us through reflection. What I want to implement myself is to use XML injection beans and inject beans with annotations (@Service, etc.)

IOC principles for the Xml version of Spring

The XML version of SpringIOC uses Dom4j and reflection to parse the XML and inject all the beans into the class when the ApplicationContext is created

XML version injection

Parsing XML yourself

A small Demo that parses XML itself, using Dom4j to parse XML, is shown below

public class XmlUtils { public static void main(String[] args) throws DocumentException { XmlUtils xmlUtils = new XmlUtils(); xmlUtils.readXml("student.xml"); } public void readXml(String xmlPath) throws DocumentException { SAXReader saxReader = new SAXReader(); Document document = saxReader.read(getResourceAsSteam(xmlPath)); Element rootElement = document.getRootElement(); getNodes(rootElement); } private static void getNodes(Element rootElement) {system.out. print(" node name: " + rootElement.getName()+"\t\t"); List<Attribute> attributes = rootElement. Attributes (); For (Attribute Attribute: attributes) {system.out.print (" Attribute: "+attribute.getName()+"-- "+ attribute.gettext ()+"\t\t"); } // get the attribute value String Value = rootElement. GetTextTrim (); if (! Stringutils.isempty (value) {system.out.print (" value: "+ value+"\t\t"); } System.out.println(); / / traverse the child nodes Iterator < Element > elementIterator = rootElement. ElementIterator (); while (elementIterator.hasNext()) { Element next = elementIterator.next(); getNodes(next); } } private InputStream getResourceAsSteam(String xmlPath) { return this.getClass().getClassLoader().getResourceAsStream(xmlPath); }}Copy the code

Implement the ApplicationContext of the XML fetch Bean yourself

  • Implementation steps
    • Reading configuration XML
    • Check whether the BeanId passed in matches the BeanId in the Xml
    • Use reflection to create objects and return them
  • Implement your ApplicationContext core method by following the steps above (the getBean method is implemented by following the steps above, which I have extracted)
Public Object GetBean (String beanId) throws DocumentException, IllegalAccessException, InstantiationException, ClassNotFoundException {if (stringutils.isempty (beanId)) {throw new RuntimeException(" beanId isEmpty "); List<Element> elements = readXml(); If (elements = = null | | elements. The isEmpty ()) {throw new RuntimeException (" no Bean information "); String ClassName = getClassName(beanId, elements); If (stringutils.isempty (className)) {throw new RuntimeException(" no class information is configured "); } // Use reflection to create newInstance(className); }Copy the code

The full text is as follows

public class ExtClassPathXmlApplicationContext { private String xmlPath; public ExtClassPathXmlApplicationContext(String xmlPath) { this.xmlPath = xmlPath; } /** Public Object GetBean (String beanId) throws DocumentException, IllegalAccessException, InstantiationException, ClassNotFoundException {if (stringutils.isempty (beanId)) {throw new RuntimeException(" beanId isEmpty "); List<Element> elements = readXml(); If (elements = = null | | elements. The isEmpty ()) {throw new RuntimeException (" no Bean information "); String ClassName = getClassName(beanId, elements); If (stringutils.isempty (className)) {throw new RuntimeException(" no class information is configured "); } // Use reflection to create newInstance(className); Private List<Element> readXml() throws DocumentException {SAXReader SAXReader = new SAXReader(); Document document = saxReader.read(getResourceAsSteam()); Element rootElement = document.getRootElement(); List<Element> elements = rootElement.elements(); return elements; }}Copy the code

Then in the main method to create the above Context, using the getBean method, can get want Bean (like Spring ClassPathApplicationContext)

Inject beans with annotations

Some properties to be aware of

  • You need to put known annotated classes into a collection that can be easily accessed
  • The above collection is initialized only at load time, and you need to be aware of thread-safety issues
  • Load beans in lazy load mode
  • Only singletons are implemented, and all getBeans create an instance
  • Only the default beanId is implemented for injection (the first letter of the class name is lowercase), and no custom ids are available

Implement the annotation assembly Bean and get the Bean through the getBean method

  • Implementation steps
    • Use the reflection mechanism, sweep, get all the classes (using an open source sweep utility class. No self-implementation)
    • Determines whether there are annotations for the injected bean on each class
    • Class initialization using reflection mechanism
  • Follow the steps above to implement your own ApplicationContext core code
Private void initBeans() throws IllegalAccessException, InstantiationException { beans = new ConcurrentHashMap<String, Object>(); List<Class<? >> classes = ClassUtils.getClasses(packageName); FindClassExistAnnotation (classes); // Check whether all classes have annotations on them. If so, add them to the Bean container. If (beans = = null | | beans. IsEmpty ()) {throw new RuntimeException (" no class plus the annotation "); }}Copy the code

The full text is as follows

public class ExtAnnotationApplicationContext { private String packageName; Private ConcurrentHashMap<String, Object> beans = null; private ConcurrentHashMap<String, Object> beans = null; public ExtAnnotationApplicationContext(String packageName) throws InstantiationException, IllegalAccessException { this.packageName = packageName; initBeans(); } /** Initialize the Bean container */ private void initBeans() throws IllegalAccessException InstantiationException { beans = new ConcurrentHashMap<String, Object>(); List<Class<? >> classes = ClassUtils.getClasses(packageName); FindClassExistAnnotation (classes); // Check whether all classes have annotations on them. If so, add them to the Bean container. If (beans = = null | | beans. IsEmpty ()) {throw new RuntimeException (" no class plus the annotation "); Private void findClassExistAnnotation(List<Class<? >> classes) throws InstantiationException, IllegalAccessException { for (Class classInfo : Annotation Annotation = classinfo.getannotation (extservice.class); if (annotation ! String className = classinfo.getName (); // The default Id is lowercase beans.put(toLowerCaseFirestOne(classinfo.getSimplename ()), newInstance(classInfo)); }} /** className lowercase */ private String toLowerCaseFirestOne(String className) {return new StringBuilder().append(Character.toLowerCase(className.charAt(0))).append(className.substring(1)).toString(); Public Object getBean(String beanId) throws IllegalAccessException, InstantiationException {if (stringutils.isempty (beanId)) {throw new RuntimeException(" beanId isEmpty "); } return beans.get(beanId); } /** Create a Bean using reflection */ private Object newInstance(Class classInfo) throws IllegalAccessException, InstantiationException {if (classInfo == null) {throw new RuntimeException(" bean without this ID "); InstantiationException {if (classInfo == null) {throw new RuntimeException(" bean without this ID "); } return classInfo.newInstance(); } /** private void attrAssign(Class<? > classInfo) {Field[] fields = classinfo.getFields (); For (Field Field: fields) {ExtService ExtService = field.getannotation (extservice.class); if (extService ! String fieldName = field.getName(); }}}}Copy the code

To get a Bean, you simply add your own Service annotation to the class and use the getBean method to pass the first letter of the class name in lower case

Implement auto-assembly (dependency injection)

  • Autoloassembly/Dependency Injection principles (implementation steps)
    • Use reflection to get all attributes of the current class
    • Determines whether the current class has an annotation
    • Find the object in the Bean container using the default name, and assign the value
  • The core code
Private void attrAssign(Object Object) throws IllegalAccessException {// Get all the attributes of this class Field[] fields = object.getClass().getDeclaredFields(); For (Field Field: fields) {ExtService ExtService = field.getannotation (extservice.class); if (extService ! String fieldName = field.getName(); Object target = beans.get(fieldName); If (target == null) {throw new RuntimeException(" injection \"" + fieldName + "\" property failed, bean container does not have this object "); } // Allow access to the private property field.setaccessible (true); Field. Set (object,target); }}}Copy the code

This method is used to inject beans into the Object of the Bean container after the Bean container is initialized, so as to achieve the dependency injection effect.

public ExtAnnotationApplicationContext(String packageName) throws InstantiationException, IllegalAccessException { this.packageName = packageName; initBeans(); // All beans are automatically injected into all beans in all Bean containersfor (Map.Entry<String, Object> entry : beans.entrySet()) {
            System.out.println("beanId:"+entry.getKey()); Object bean = entry.getValue(); attrAssign(bean); }}Copy the code

Context with dependency injection is as follows

public class ExtAnnotationApplicationContext { private String packageName; Private ConcurrentHashMap<String, Object> beans = null; private ConcurrentHashMap<String, Object> beans = null; public ExtAnnotationApplicationContext(String packageName) throws InstantiationException, IllegalAccessException { this.packageName = packageName; initBeans(); // All beans are automatically injected into all beans in all Bean containersfor (Map.Entry<String, Object> entry : beans.entrySet()) {
            System.out.println("beanId:"+entry.getKey()); Object bean = entry.getValue(); attrAssign(bean); Private void initBeans() throws IllegalAccessException, InstantiationException { beans = new ConcurrentHashMap<String, Object>(); List<Class<? >> classes = ClassUtils.getClasses(packageName); FindClassExistAnnotation (classes); // Check whether all classes have annotations on them. If so, add them to the Bean container.if (beans == null || beans.isEmpty()) {
            throw new RuntimeException("No class is annotated."); Private void findClassExistAnnotation(List<Class<? >> classes) throws InstantiationException, IllegalAccessException {forAnnotation Annotation = classinfo.getannotation (extservice.class);if(annotation ! String className = classinfo.getName (); // The default Id is lowercase beans.put(toLowerCaseFirestOne(classinfo.getSimplename ()), newInstance(classInfo)); }} /** Private String toLowerCaseFirestOne(String className) {returnnew StringBuilder().append(Character.toLowerCase(className.charAt(0))).append(className.substring(1)).toString(); } public Object getBean(String beanId) throws IllegalAccessException, InstantiationException {if (StringUtils.isEmpty(beanId)) {
            throw new RuntimeException("BeanID is empty");
        }
        returnbeans.get(beanId); } private Object newInstance(Class classInfo) throws IllegalAccessException, InstantiationException {if (classInfo == null) {
            throw new RuntimeException("Bean without this ID");
        }
        returnclassInfo.newInstance(); } /** Automatically inject attributes of this Object */ private void attrAssign(Object Object) throws IllegalAccessException {// Get all attributes of this class Field[] fields = object.getClass().getDeclaredFields(); // Determine if the current attribute has annotationsfor (Field field : fields) {
            ExtResource extResource = field.getAnnotation(ExtResource.class);
            if(extResource ! = null) {// Allow access to the private property field.setaccessible (true); FieldName = field.getName(); fieldName = field.getName(); Object target = beans.get(fieldName);if (target == null) {
                    throw new RuntimeException("Injection \" " + fieldName + "\" property failed, bean container does not have this object"); } // The first argument is the object in which this attribute is located; field.set(object,target); }}}}Copy the code