Overview
IOC (inversion of control) "controlled inversion", but the more popular term is "dependency injection" (di-dependency injection).
What is "inversion of control"? The fact is that control (the right to create a dependency between objects and objects) is given to the spring container. We used to use new Xxximpl () when we were writing code that needed an object, and with the spring IOC container, it was responsible for object creation and dependency injection, which was just as good as the spring IOC container when we needed an object.
The IOC sounds very tall, but it's not complicated to realize. This article focuses on the implementation of an IOC container based on an XML configuration, followed by a separate chapter on how to implement an IOC container through annotations. usage
The specific usage is similar to the spring IOC, as follows:
1. Beans.xml configuration 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"
xsi:schemalocation= "http// Www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd ">
<bean id= "Userdao" class= "Com.ricky.ioc.sample.dao.UserDaoImpl" scope= "singleton" init-method= "Init" > </bean>
<bean id= "UserService" class= "Com.ricky.ioc.sample.service.UserServiceImpl" >
< Property Name= "Userdao" ref= "Userdao" ></property>
</bean>
<bean id= "Usercontroller" class= "Com.ricky.ioc.sample.controller.UserController" >
<property name= "UserService" ref= "UserService" ></property>
</bean>
</beans>
2. Add Maven Dependency
<dependency>
<groupId>com.ricky.framework</groupId>
<artifactid>ioc</ artifactid>
<version>1.0.0</version>
</dependency>
3. Load Bean configuration file
ApplicationContext CTX = new Classpathxmlapplicationcontext ("Beans.xml");
Get bean Usercontroller by id
usercontroller = (usercontroller) ctx.getbean ("Usercontroller");
Usercontroller.login ("Ricky", "123");
Get Bean
UserService userservice = Ctx.getbean (Userservice.class) through class;
System.out.println (userservice);
Userservice.login ("Ricky", "abc");
Ctx.close ();
The results of the operation are as follows:
Usercontroller Login name->ricky,password->123
Userserviceimpl Login name->ricky,password->123
Userdaoimpl Find Name->ricky
com.ricky.ioc.sample.service.userserviceimpl@214c265e
Userserviceimpl Login Name->ricky,password->abc
Userdaoimpl Find Name->ricky
Container close ... Specific Implementation
Ideas:
Parsing Beans.xml Gets the list of beans and the dependencies between them, and then constructs the bean instance through reflection technology and makes the Bean assembly based on the dependency between the beans.
First, look at the ApplicationContext class with the following code:
Package COM.RICKY.FRAMEWORK.IOC;
Import java.beans.IntrospectionException;
Import Java.beans.Introspector;
Import Java.beans.PropertyDescriptor;
Import java.lang.reflect.InvocationTargetException;
Import Java.lang.reflect.Method;
Import Org.apache.commons.lang3.StringUtils;
Import com.ricky.framework.ioc.model.BeanDefinition;
Import com.ricky.framework.ioc.model.PropertyDefinition;
Import Com.ricky.framework.ioc.util.ReflectionUtils;
Public abstract class ApplicationContext {public abstract Object Getbean (String ID);
Public abstract <T> T Getbean (class<t> clazz);
public abstract void Close ();
Protected abstract beandefinition getbeandefinition (String ID); Protected Object Createbean (beandefinition BD) {try {Object bean = reflectionutils.newinstance (bd.g
Etclassname ()); if (Stringutils.isnotempty (Bd.getinitmethodname ())) {Reflectionutils.invokemethod (bean, Bd.getinitmethodnam
E ());
} return bean; } catch (ClassNotFoundException | instantiationexception | illegalaccessexception | IllegalArgumentException | InvocationTargetException |
Nosuchmethodexception e) {throw new RuntimeException ("Create bean error, class->" +bd.getclassname (), E);
}} protected void Injectbeanproperties (Object bean, beandefinition beandefinition) {try {
Propertydescriptor[] PS = Introspector.getbeaninfo (Bean.getclass ()). Getpropertydescriptors (); For (PropertyDefinition propertyDefinition:beanDefinition.getProperties ()) {for (PropertyDescriptor Pro
PERTYDESCRIPTOR:PS) {if (Propertydescriptor.getname (). Equals (Propertydefinition.getname ())) {
Method setter = Propertydescriptor.getwritemethod ();
Setter.setaccessible (TRUE); Setter.invoke (Bean, Getbean (propertydefinitiOn.getref ())); }}}} catch (SecurityException | illegalaccessexception | IllegalArgumentException | InvocationTargetException |
Introspectionexception e) {throw new RuntimeException ("Inject bean properties Error", E);
}
}
}
There are two key classes involved: Beandefinition and propertydefinition, which are used to describe the definition of javabean and the definition of JavaBean attribute, a beandefinition There can be 1 or more propertydefinition, and they are 1:n relationships. The code is as follows:
Beandefinition.java
Package Com.ricky.framework.ioc.model;
Import java.util.List;
public class Beandefinition {private String ID;
Private String ClassName; Private String scope;
Singleton|prototype private String Initmethodname;
Private list<propertydefinition> properties;
Public beandefinition (string ID, string className) {this.id = ID;
This.classname = ClassName;
} public String GetId () {return id;
} public void SetId (String id) {this.id = ID;
} public String GetClassName () {return className;
} public void Setclassname (String className) {this.classname = ClassName;
} public String Getscope () {return scope;
The public void Setscope (String scope) {this.scope = scope;
} public String Getinitmethodname () {return initmethodname; } public void Setinitmethodname (String initmethodname) {this.initmethodname = InitmethodName;
} public list<propertydefinition> GetProperties () {return properties;
} public void SetProperties (List<propertydefinition> properties) {this.properties = properties;
} @Override Public String toString () {return "beandefinition [id=" + ID + ", classname=" + className + ", scope=" + Scope + ", initmethodname=" + Initmethodname + ", properties=" + Properties +
"]";
}
}
Propertydefinition.java
Package Com.ricky.framework.ioc.model;
public class PropertyDefinition {
private String name;
Private String ref;
Public propertydefinition (string name, string ref) {
this.name = name;
This.ref = ref;
}
Public String GetName () {
return name;
}
public void SetName (String name) {
this.name = name;
}
Public String GetRef () {
return ref;
}
public void SetRef (String ref) {
this.ref = ref;
}
@Override public
String toString () {
return "propertydefinition [name=" + name + ", ref=" + ref + "]";
}
}
2, Next is the Classpathxmlapplicationcontext class, the code is as follows:
Package COM.RICKY.FRAMEWORK.IOC;
Import java.io.FileNotFoundException;
Import Java.util.HashMap;
Import java.util.List;
Import Java.util.Map;
Import Org.apache.commons.lang3.StringUtils;
Import org.dom4j.DocumentException;
Import com.ricky.framework.ioc.model.BeanDefinition;
Import Com.ricky.framework.ioc.parser.BeanXmlConfigParser;
Import Com.ricky.framework.ioc.util.BeanScope;
Import Com.ricky.framework.ioc.util.ReflectionUtils; public class Classpathxmlapplicationcontext extends ApplicationContext {private map<string, beandefinition> be
Andefinitionmap = new hashmap<string, beandefinition> ();
Protected Map<string, object> beaninstancemap = new hashmap<string, object> (); Public Classpathxmlapplicationcontext (String xmlfilepath) {System.out.println ("****************container init is
gin**************** ");
READXML (Xmlfilepath);
Initbeans ();
Injectbeans (); System.out.println ("****************container INIT end**************** "); private void ReadXml (String xmlfilepath) {beanxmlconfigparser beanxmlconfigparser = new Beanxmlconfigpars
ER ();
List<beandefinition> bean_def_list = null;
try {bean_def_list = Beanxmlconfigparser.parse (Xmlfilepath); } catch (FileNotFoundException e) {throw new RuntimeException ("Not Found bean XML, file->" +xmlfilepath, E)
;
} catch (Documentexception e) {throw new RuntimeException ("Bean XML format error, file->" +xmlfilepath, E); } for (Beandefinition beandefinition:bean_def_list) {if (Stringutils.isempty (Beandefiniti On.getid ()) | | Stringutils.isempty (Beandefinition.getclassname ())) {throw new illegalargumentexception ("Bean definition I
S empty! ");}
if (Beandefinitionmap.containskey (Beandefinition.getid ())) {throw new IllegalArgumentException ( "DUPlicated Bean ID, id-> "+ Beandefinition.getid ());
} beandefinitionmap.put (Beandefinition.getid (), beandefinition); }} private void Initbeans () {for (map.entry<string, beandefinition> me:beanDefinitionMap.entry
Set ()) {beandefinition BD = Me.getvalue ();
if (Stringutils.isempty (Bd.getscope ()) | | Bd.getscope (). Equals (Beanscope.singleton)) {try {
Object Bean = Createbean (BD);
Beaninstancemap.put (Bd.getid (), Bean); } catch (Exception e) {throw new IllegalArgumentException ("Create Bean error,class->" +bd.getclassn
Ame (), E); }}}} private void Injectbeans () {for (map.entry<string, beandefinition> me
: Beandefinitionmap.entryset ()) {Beandefinition beandefinition = Me.getvalue ();
Determine if there is an injected attribute if (beandefinition.getproperties () = null && beandefinition.getproperties (). Size () >0) {
Object bean = Beaninstancemap.get (Beandefinition.getid ());
try {injectbeanproperties (bean, beandefinition);
} catch (Exception e) {e.printstacktrace (); }}}} @Override public Object Getbean (String ID) {//System.out.println ("Get
Bean by ID: "+id);
if (Stringutils.isempty (ID)) {return null;
} if (Beandefinitionmap.containskey (ID)) {beandefinition BD = beandefinitionmap.get (ID); if (Stringutils.isempty (Bd.getscope ()) | | Bd.getscope (). Equals (Beanscope.singleton)) {return Beaninsta
Ncemap.get (ID);
} Object bean = null;
try {bean = Createbean (BD);
Injectbeanproperties (Bean, BD); Beaninstancemap.put (Bd.getid (), Bean);
} catch (Exception e) {e.printstacktrace ();
} return bean;
} throw new IllegalArgumentException ("Unknown bean, id->" + ID); } @SuppressWarnings ("Unchecked") @Override public <T> T Getbean (class<t> clazz) {//System
. OUT.PRINTLN ("Get Bean by Type:" +clazz.getname ()); For (map.entry<string, beandefinition> me:beanDefinitionMap.entrySet ()) {beandefinition BD = Me.getval
UE ();
Class<?> beanclass = null;
try {beanclass = Reflectionutils.loadclass (Bd.getclassname ());
} catch (ClassNotFoundException e) {e.printstacktrace (); } if (Beanclass!=null && clazz.isassignablefrom (beanclass)) {//System.out.println ("Find
Bean by Type, class-> "+clazz.getname ()); Return (T) Getbean(Bd.getid ());
}} return null;
} @Override protected beandefinition getbeandefinition (String ID) {return beandefinitionmap.get (ID);
} @Override public void Close () {System.out.println ("container close ...");
Release resource Beandefinitionmap.clear ();
Beandefinitionmap = null;
Beaninstancemap.clear ();
Beaninstancemap = null;
}
}
In the Classpathxmlapplicationcontext class, there are three main functions: parsing an XML configuration file, building a bean instance from reflection, and assembling the bean.
Summary
All of the above code has been uploaded to GitHub and you are welcome to fork. In addition, because of the time is more hasty, the code design has unreasonable place also please forgive, later will take time to refactor the code.