Build a Spring from Zero(1)

Create class from XML file

Posted by Haiming on March 28, 2020

1. IoC and AOP

IoC: Inversion of Control, or Dependency Injection(DI). In my understanding, it is we put business class and configuration file into Spring container, and then it can produce a system that all dependencies of class already done, which already can use.

1585405647747

The dependency information can be done by XML file, by Spring Annotations like @Autowired, or some Java Annotations like @Configuration.

AOP(Aspect Oriented Programming): Also comes from reality problems. Such as we have some functional modules, like User Management Module and Order Management Module, they not only have relationships with each other, also have relationships with some non-functional modules, such as log system and security system.

If we only in functional systems call non-functional systems, even only very small change of non-functional modules we need to change methods in functional modules. This can not be endured.

1585406318025

So we can isolate the functional codes and non-functional codes like this way.

2. Steps for unit test

One unit test has 4 steps:

  1. Think and write test cases: which always included corner cases and normal cases.
  2. Run the test case and find error.
  3. Write just enough code to make test cases.
  4. Refactor code to make it clean.

Then loop until all functions are developed.

3. Step1: How to build a class from XML file?

This is a class I call it DefaultBeanFactory, which I used to generate bean from XML file.

First step, I used a package called dom4j which maven format is this:

		<dependency>
			<groupId>dom4j</groupId>
			<artifactId>dom4j</artifactId>
			<version>1.6.1</version>
		</dependency>

This package produce abilities for reading XML files. Use this, I can change XML file into inputStream and then read XML elements.

The XML file I want to analysis is this one:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd">
  
  <bean id="petStore"
        class="org.litespring.service.v1.PetStoreService" >   
  </bean> 

</beans>

So we can see the bean has elements id and class in pair. After analysis, I will put them into a map, so that we don;’t need to analysis it once and once again.

I use another class GenericBeanDefination which implements the interface BeanDefination, produces constructor like this:

package org.litespring.beans.factory.support;

import org.litespring.beans.BeanDefinition;

public class GenericBeanDefinition implements BeanDefinition {
	private String id;
	private String beanClassName;
	public GenericBeanDefinition(String id, String beanClassName) {
		
		this.id = id;
		this.beanClassName = beanClassName;
	}
	public String getBeanClassName() {
		
		return this.beanClassName;
	}

}

Then I need one thing that can convert BeanDefination into Class, which is a classloader. Here I use ClassUtil which jdk provided.

Simply saying, ClassUtil has 3 steps to get a classLoader, the first step is to use Thread.currentThread().getContextClassLoader() to get the currentThread’s context classloader. After that, it uses ClassUtils.class.getClassLoader() to get itself’s classLoader, then if still cannot get, it will use getSystemClassLoader(), which is also the AppClassLoader

ClassLoader.getSystemClassLoader will often return ApplicationClassLoader, it only load class file under classpath,which is often the ` bin/ in Java SE. And in Java EE, Thread.currentThread().getContextClassLoader() also returns ApplicationClassLoader`

But in Java EE. class of our project is from WebAppClassLoader, which can be get from ` Thread.currentThread().getContextClassLoader()`.

Code is just from JDK, so I paste it here:

package org.litespring.util;



public abstract class ClassUtils {
	public static ClassLoader getDefaultClassLoader() {
		ClassLoader cl = null;
		try {
			cl = Thread.currentThread().getContextClassLoader();
		}
		catch (Throwable ex) {
			// Cannot access thread context ClassLoader - falling back...
		}
		if (cl == null) {
			// No thread context class loader -> use class loader of this class.
			cl = ClassUtils.class.getClassLoader();
			if (cl == null) {
				// getClassLoader() returning null indicates the bootstrap ClassLoader
				try {
					cl = ClassLoader.getSystemClassLoader();
				}
				catch (Throwable ex) {
					// Cannot access system ClassLoader - oh well, maybe the caller can live with null...
				}
			}
		}
		return cl;
	}
}

After I get the classLoader, I can pass the name of class to create the class use reflection, then return an instance of the class, here in the first edition I just use the default constructor, which has no parameters.

		try {
			Class<?> clz = cl.loadClass(beanClassName);
			return clz.newInstance();
		} catch (ClassNotFoundException e) {			
			e.printStackTrace();
		} catch (InstantiationException e) {
			
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			
			e.printStackTrace();
		}

Refactor code:

Here the way that try catch is not very easy to read, so I get some Exceptions to make the code cleaner. The relationship is like this:

1585454213803

BeanDefinationStoreException is the Exception when I read XML file wrongly, and BeanCreationException is the Exception when create the Bean wrongly.