We're going to write simple in a few steps IOC, First design the components , Design the interface again , And then focus on Implementation .
1. Design components .
We remember Spring What are the most important components in ?BeanFactory
Containers ,BeanDefinition
Bean The basic data structure , Of course, you need to load Bean Of Resource loader
. Probably the most important components in the end .
The container is used to store initialized Bean,BeanDefinition Namely Bean The basic data structure , such as Bean The name of ,Bean Properties of PropertyValue
,Bean Methods , Whether to delay loading , Dependency, etc . The resource loader is simple , It's a read XML Class of configuration file , Read each tag and parse .
2. Design interfaces
First of all, there must be a BeanFactory, Namely Bean Containers , The container interface has at least 2 The easiest way to do it , One is to get Bean, A registration Bean.
/**
* Need one beanFactory Definition ioc Some behavior of the container For example, you can get bean, Such as registration bean, Parameter is bean The name of ,bean The definition of
*
* @author stateis0
* @version 1.0.0
* @Date 2017/11/30
*/public interface BeanFactory {
/**
* according to bean The name of is taken from the container bean object
*
* @param name bean name
* @return bean example
* @throws Exception abnormal
*/
Object getBean(String name) throws Exception;
/**
* take bean Register in container
*
* @param name bean name
* @param bean bean example
* @throws Exception abnormal
*/
void registerBeanDefinition(String name, BeanDefinition bean) throws Exception;}
according to Bean Get the name of Bean object , The registration parameters are 2 individual , One is Bean Name , One is BeanDefinition object .
It's over Bean The most basic container , And it needs to be the simplest BeanDefinition Interface , For our convenience , But because we don't have to think about expansion , Therefore, it can be directly designed as a class ,BeanDefinition What elements and methods are needed ?
Need one Bean object , One Class object , One ClassName character string , You also need a collection of elements PropertyValues. That makes up a basic BeanDefinition The class . So what methods are needed ? In fact, it's these attributes get set Method .
Let's look at the details of this class :
package cn.thinkinjava.myspring;
/**
* bean The definition of
*
* @author stateis0
*/public class BeanDefinition {
/**
* bean
*/
private Object bean;
/**
* bean Of CLass object
*/
private Class beanClass;
/**
* bean Class fully qualified name of
*/
private String ClassName;
/**
* The collection of properties of a class
*/
private PropertyValues propertyValues = new PropertyValues();
/**
* obtain bean object
*/
public Object getBean() {
return this.bean;
}
/**
* Set up bean The object of
*/
public void setBean(Object bean) {
this.bean = bean;
}
/**
* obtain bean Of Class object
*/
public Class getBeanclass() {
return this.beanClass;
}
/**
* By setting the class name, reflection generates Class object
*/
public void setClassname(String name) {
this.ClassName = name;
try {
this.beanClass = Class.forName(name);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* obtain bean Property collection for
*/
public PropertyValues getPropertyValues() {
return this.propertyValues;
}
/**
* Set up bean Properties of
*/
public void setPropertyValues(PropertyValues pv) {
this.propertyValues = pv;
}
}
With the basic BeanDefinition data structure , From another one XML Read and parse to BeanDefinition Operation class of , First we define a BeanDefinitionReader Interface , The interface is just an identifier , The abstract class implements a basic method and defines some basic properties , For example, a register container needs to be stored when reading , You also need a delegate, a resource loader ResourceLoader, Used for loading XML file , And we need to set the constructor to contain a resource loader , Of course, there are still some get set Method .
package cn.thinkinjava.myspring;
import cn.thinkinjava.myspring.io.ResourceLoader;
import java.util.HashMap;
import java.util.Map;
/**
* In the abstract bean Define the read class
*
* @author stateis0
*
/
public abstract class AbstractBeanDefinitionReader implements BeanDefinitionReader {
/**
* register bean Containers
*/
private Map<String, BeanDefinition> registry;
/**
* Resource loader
*/
private ResourceLoader resourceLoader;
/**
* The constructor must have a resource loader , The default plug-in creates a map Containers
*
* @param resourceLoader Resource loader
*/
protected AbstractBeanDefinitionReader(ResourceLoader resourceLoader) {
this.registry = new HashMap<>();
this.resourceLoader = resourceLoader;
}
/**
* Get the container
*/
public Map<String, BeanDefinition> getRegistry() {
return registry;
}
/**
* Get resource loader
*/
public ResourceLoader getResourceLoader() {
return resourceLoader;
}
}
With these abstract classes and interfaces , We can basically form a prototype ,BeanDefinitionReader For from XML Read configuration file from , Generate BeanDefinition example , Store in BeanFactory In the container , After initialization , You can call getBean Method to get the initialization successful Bean. Form a perfect closed loop .
3. How to achieve
We just talked about the specific process : from XML Read configuration file from , It can be interpreted as BeanDefinition, And finally into the container . To put it bluntly, just 3 Step . So let's design the first step first .
1. from XML Read configuration file from , It can be interpreted as BeanDefinition
We just designed a read BeanDefinition The interface of BeanDefinitionReader And an abstract class that implements it AbstractBeanDefinitionReader, Abstract defines some simple methods , There is a delegate class -----ResourceLoader, We haven't created , This class is a resource loader , Load resources according to the given path .
We can use Java The default class library java.net.URL To achieve , Define two classes , It's a package URL Class ResourceUrl, One is dependence ResourceUrl Resource loading class for .
ResourceUrl Code implementation
/**
* resources URL
*
/
public class ResourceUrl implements Resource {
/**
* Class library URL
*/
private final URL url;
/**
* Need a class library URL
*/
public ResourceUrl(URL url) {
this.url = url;
}
/**
* from URL Get the input stream from
*/
@Override
public InputStream getInputstream() throws Exception {
URLConnection urlConnection = url.openConnection();
urlConnection.connect();
return urlConnection.getInputStream();
}
}
ResourceLoader Realization
/**
* resources URL
*/
public class ResourceUrl implements Resource {
/**
* Class library URL
*/
private final URL url;
/**
* Need a class library URL
*/
public ResourceUrl(URL url) {
this.url = url;
}
/**
* from URL Get the input stream from
*/
@Override
public InputStream getInputstream() throws Exception {
URLConnection urlConnection = url.openConnection();
urlConnection.connect();
return urlConnection.getInputStream();
}
}
Of course, we need an interface , Only one abstract method is defined
package cn.thinkinjava.myspring.io;
import java.io.InputStream;
/**
* Resource definition
*
* @author stateis0
*/
public interface Resource {
/**
* Get input stream
*/
InputStream getInputstream() throws Exception;
}
Okay , AbstractBeanDefinitionReader The required elements already have , however , Obviously, this method can't implement reading BeanDefinition The task of . Then we need a class to inherit the abstract class , To implement specific methods , Since we are XML Profile read , So let's define a XmlBeanDefinitionReader Inherit AbstractBeanDefinitionReader , Implement some of the methods we need , For example, reading XML Of readrXML, For example, register the parsed elements to registry Of Map in , Some parsing details . Let's look at the code .
XmlBeanDefinitionReader Read the configuration file and parse it into Bean
package cn.thinkinjava.myspring.xml;
import cn.thinkinjava.myspring.AbstractBeanDefinitionReader;
import cn.thinkinjava.myspring.BeanDefinition;
import cn.thinkinjava.myspring.BeanReference;
import cn.thinkinjava.myspring.PropertyValue;
import cn.thinkinjava.myspring.io.ResourceLoader;
import java.io.InputStream;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* analysis XML file
*
* @author stateis0
*/
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
/**
* Constructors , Must contain a resource loader
*
* @param resourceLoader Resource loader
*/
public XmlBeanDefinitionReader(ResourceLoader resourceLoader) {
super(resourceLoader);
}
public void readerXML(String location) throws Exception {
// Create a resource loader
ResourceLoader resourceloader = new ResourceLoader();
// Get the input stream from the resource loader
InputStream inputstream = resourceloader.getResource(location).getInputstream();
// Get document builder factory instance
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
// Create the document builder
DocumentBuilder docBuilder = factory.newDocumentBuilder();
// Document builder parse flow Return the document object
Document doc = docBuilder.parse(inputstream);
// Parse according to the given document object , And register to the bean In the container
registerBeanDefinitions(doc);
// Closed flow
inputstream.close();
}
/**
* Parse according to the given document object , And register to the bean In the container
*
* @param doc Document object
*/
private void registerBeanDefinitions(Document doc) {
// Read the root element of the document
Element root = doc.getDocumentElement();
// Parse the root node of the element and all the child nodes under the root node and add it to the registration container
parseBeanDefinitions(root);
}
/**
* Parse the root node of the element and all the child nodes under the root node and add it to the registration container
*
* @param root XML File root node
*/
private void parseBeanDefinitions(Element root) {
// Read all child elements of the root element
NodeList nl = root.getChildNodes();
// Traversing child elements
for (int i = 0; i < nl.getLength(); i++) {
// Get the node of the given location of the root element
Node node = nl.item(i);
// Type judgment
if (node instanceof Element) {
// Strongly convert to a parent type element
Element ele = (Element) node;
// Resolve to a given node , Include name,class,property, name, value,ref
processBeanDefinition(ele);
}
}
}
/**
* Resolve to a given node , Include name,class,property, name, value,ref
*
* @param ele XML Parsing elements
*/
private void processBeanDefinition(Element ele) {
// Gets the... Of the given element name attribute
String name = ele.getAttribute("name");
// Gets the... Of the given element class attribute
String className = ele.getAttribute("class");
// Create a bean Define the object
BeanDefinition beanDefinition = new BeanDefinition();
// Set up bean Defining the Fully qualified class name
beanDefinition.setClassname(className);
// towards bean Inject the member variables in the configuration file
addPropertyValues(ele, beanDefinition);
// Register container with add to bean Name and bean Definition
getRegistry().put(name, beanDefinition);
}
/**
* Add attribute elements from configuration file to bean In the definition instance
*
* @param ele Elements
* @param beandefinition bean Definition object
*/
private void addPropertyValues(Element ele, BeanDefinition beandefinition) {
// Gets the... Of the given element property Attribute set
NodeList propertyNode = ele.getElementsByTagName("property");
// Cycle set
for (int i = 0; i < propertyNode.getLength(); i++) {
// Gets the node at a given location in the collection
Node node = propertyNode.item(i);
// Type judgment
if (node instanceof Element) {
// Force the node down to a child element
Element propertyEle = (Element) node;
// Element object acquisition name attribute
String name = propertyEle.getAttribute("name");
// Element object acquisition value Property value
String value = propertyEle.getAttribute("value");
// Judge value Not empty
if (value != null && value.length() > 0) {
// To the given “bean Definition ” Instance to add the member variable
beandefinition.getPropertyValues().addPropertyValue(new PropertyValue(name, value));
} else {
// If it is empty , Then get the property ref
String ref = propertyEle.getAttribute("ref");
if (ref == null || ref.length() == 0) {
// If the property ref It's empty , Throw an exception
throw new IllegalArgumentException(
"Configuration problem: <property> element for property '"
+ name + "' must specify a ref or value");
}
// If it's not empty , Test to create a “bean References to ” example , The construction parameter is the name , Instance is temporarily empty
BeanReference beanRef = new BeanReference(name);
// To the given “bean Definition ” Add a member variable to the
beandefinition.getPropertyValues().addPropertyValue(new PropertyValue(name, beanRef));
}
}
}
}
}
It can be said that the code comments are very detailed , The method is as follows :
-
public void readerXML(String location) Public parsing XML Methods , Given a string parameter of a position, you can .
-
private void registerBeanDefinitions(Document doc) Given a document object , And analyze .
-
private void parseBeanDefinitions(Element root) Given a root element , Loop through all the child elements under the root element .
-
private void processBeanDefinition(Element ele) Given a child element , And analyze the elements , Then take the parsed data and create a BeanDefinition object . And register to the BeanDefinitionReader Of Map Containers ( This container holds all of the Bean) in .
-
private void addPropertyValues(Element ele, BeanDefinition beandefinition) Given an element , One BeanDefinition object , Parse the element property Elements , And injected into the BeanDefinition In the example .
altogether 5 Step , Finished parsing XML All operations of the file . The ultimate goal is to put the parsed file into BeanDefinitionReader Of Map In the container .
Okay , Come here , We've finished from XML File reading and parsing steps , So when to put in BeanFactory What about containers? ? We just put in AbstractBeanDefinitionReader In the registration container of . So we have to base on BeanFactory Design to achieve how to build a really usable Bean Well ? Because of what just happened Bean Just some Bean Information about . There's no real business need for Bean.
2. Initialize what we need Bean( No Bean Definition ) And implement dependency injection
We know Bean Definition doesn't work , Just some Bean Information about , It's like a person ,BeanDefinition It's like your files in the Public Security Bureau , But you're not in the police station , But as long as the public security bureau takes your file, it can find you . It's such a relationship .
Then we'll base it on BeanFactory To design an abstract class AbstractBeanFactory.
package cn.thinkinjava.myspring.factory;
import cn.thinkinjava.myspring.BeanDefinition;
import java.util.HashMap;
/**
* An abstract class , Realized bean Methods , Contains a map, Used to store bean And bean The definition of
*
* @author stateis0
*/
public abstract class AbstractBeanFactory implements BeanFactory {
/**
* Containers
*/
private HashMap<String, BeanDefinition> map = new HashMap<>();
/**
* according to bean Get the name of bean, without , Throw an exception If there is , From bean Define object acquisition bean example
*/
@Override
public Object getBean(String name) throws Exception {
BeanDefinition beandefinition = map.get(name);
if (beandefinition == null) {
throw new IllegalArgumentException("No bean named " + name + " is defined");
}
Object bean = beandefinition.getBean();
if (bean == null) {
bean = doCreate(beandefinition);
}
return bean;
}
/**
* register bean Definition The abstract method implementation of , This is a template method , Call subclass method doCreate,
*/
@Override
public void registerBeanDefinition(String name, BeanDefinition beandefinition) throws Exception {
Object bean = doCreate(beandefinition);
beandefinition.setBean(bean);
map.put(name, beandefinition);
}
/**
* One less bean
*/
abstract Object doCreate(BeanDefinition beandefinition) throws Exception;
}package cn.thinkinjava.myspring.factory;
import cn.thinkinjava.myspring.BeanDefinition;
import cn.thinkinjava.myspring.PropertyValue;
import cn.thinkinjava.myspring.BeanReference;
import java.lang.reflect.Field;
/**
* Realize automatic injection and recursive injection (spring The standard implementation class of DefaultListableBeanFactory Yes 1810 That's ok )
*
* @author stateis0
*/
public class AutowireBeanFactory extends AbstractBeanFactory {
/**
* according to bean Define create instance , And take the example as key, bean Define as value Deposit , And call addPropertyValue Method For a given bean Properties for injection
*/
@Override
protected Object doCreate(BeanDefinition beandefinition) throws Exception {
Object bean = beandefinition.getBeanclass().newInstance();
addPropertyValue(bean, beandefinition);
return bean;
}
/**
* Given a bean Definition and a bean example , For a given bean Instance of property injection in .
*/
protected void addPropertyValue(Object bean, BeanDefinition beandefinition) throws Exception {
// Loop given bean Property collection for
for (PropertyValue pv : beandefinition.getPropertyValues().getPropertyValues()) {
// Get... According to the given attribute name A given bean Property object in
Field declaredField = bean.getClass().getDeclaredField(pv.getname());
// Set access to properties
declaredField.setAccessible(true);
// Get the object in the defined property
Object value = pv.getvalue();
// Judge whether the object is BeanReference object
if (value instanceof BeanReference) {
// Turn the attribute object to BeanReference object
BeanReference beanReference = (BeanReference) value;
// Calling the AbstractBeanFactory Of getBean Method , according to bean The name of the reference gets the instance , This is recursion
value = getBean(beanReference.getName());
}
// Reflection injection bean Properties of
declaredField.set(bean, value);
}
}
}
You can see doCreate Method uses reflection to create an object , And you also need to inject attributes into the object , If the attribute is ref type , So it's a dependency , You need to call getBean Method recursively to find that Bean( Because the last one Bean The properties of must be the basic type ). This completes a get instantiation Bean operation , It also implements class dependency injection .
4. summary
We have implemented a simple IOC The function of dependency injection , I know more about IOC, In the future Spring The initialization problem is no longer at a loss . Look directly at the source code can solve . ha-ha
good luck !!!
author : Mona Lu Road
link :https://www.jianshu.com/p/6e25dc62e3a1
Recent hot article recommends :
1.Java 15 Official release , 14 A new feature , Refresh your mind !!
2. Finally, I got it through open source projects IntelliJ IDEA Activation code , It's delicious !
3. I use Java 8 Wrote a piece of logic , I can't understand it , You try ..
4. To hang up Tomcat ,Undertow It's very powerful !!
5.《Java Development Manual ( Song Mountain version )》 The latest release , Download it quickly !
I think it's good , Don't forget to like it + Forward !