The previous article describes how to implement a simple IOC container based on an XML file. But spring not only supports the way XML files are configured, but also supports configuration via annotations, this article focuses on how to implement a simple IOC container with annotations. Use
First, let's look at how to do dependency injection based on annotations, with the following code:
Import Com.ricky.framework.ioc.bind.annotation.Inject;
Import com.ricky.ioc.sample.model.Student;
Import Com.ricky.ioc.sample.service.StudentService;
@Bean (id= "Studentcontroller") public
class Studentcontroller {
@Inject
// @Inject (name= " Studentservice ")
private studentservice studentservice;
Public String find (long id) {
Student stu = studentservice.find (ID);
System.out.println ("Stu:" +stu);
Return "Fin
}
}
Studentservice interface
Import com.ricky.ioc.sample.model.Student;
Public interface Studentservice {public
Student find (long ID);
}
Studentservice interface Implementation Class
Import Com.ricky.framework.ioc.bind.annotation.Bean;
Import Com.ricky.framework.ioc.bind.annotation.Inject;
Import Com.ricky.ioc.sample.dao.StudentDao;
Import com.ricky.ioc.sample.model.Student;
Import Com.ricky.ioc.sample.service.StudentService;
@Bean (id= "Studentservice") Public
class Studentserviceimpl implements Studentservice {
@Inject
private Studentdao Studentdao;
@Override Public
Student find (Long id) {
return studentdao.find (ID);
}
}
The above uses two custom annotations: @Bean and @Inject. @Bean annotations are annotated on the Studentserviceimpl class, indicating that the class is given to "container" processing, and the ID of the Bean generated by the IOC container is the ID value of the @bean annotation. @Inject annotations are annotated on the Studentservice field, indicating that the field will be injected without new Studentserviceimpl (), @Inject annotations By default find the bean based on the name of the field, while @inject Annotations also support specifying beans through the Name property.
Then we need to configure the automatic annotation properties in the Beans.xml configuration file:
<?xml version= "1.0" encoding= "UTF-8"?>
<beans>
<!--the package to be scanned--
<context: Component-scan base-package= "Com.ricky.ioc.sample"/>
<bean id= "Userdao" class= " Com.ricky.framework.ioc.dao.UserDaoImpl "></bean>
<bean id=" UserService "class=" Com.ricky.framework.ioc.service.UserServiceImpl ">
<property name=" Userdao "ref=" Userdao "></ property>
</bean>
<bean id= "Usercontroller" class= " Com.ricky.framework.ioc.controller.UserController ">
<property name=" UserService "ref=" UserService "> </property>
</bean>
</beans>
In this case, the annotated IOC container is already configured, exactly as the spring IOC is used, and the next step is to see how this function is implemented.
Implement
@Bean annotations
Import java.lang.annotation.Documented;
Import Java.lang.annotation.ElementType;
Import java.lang.annotation.Retention;
Import Java.lang.annotation.RetentionPolicy;
Import Java.lang.annotation.Target;
@Retention (retentionpolicy.runtime)
@Target ({elementtype.type})
@Documented public
@interface Bean {/ /similar to bean String ID in spring config file
();
}
@Inject annotations
Import java.lang.annotation.Documented;
Import Java.lang.annotation.ElementType;
Import java.lang.annotation.Retention;
Import Java.lang.annotation.RetentionPolicy;
Import Java.lang.annotation.Target;
@Retention (retentionpolicy.runtime)
@Target ({elementtype.field, elementtype.method})
@Documented Public
@interface Inject {public
String name () default "";
}
Finally, we scan all classes with @bean annotations under the current classpath, then construct the bean instance object with reflection and complete the dependency injection. The entire code is as follows:
Import Java.beans.Introspector;
Import Java.beans.PropertyDescriptor;
Import java.io.IOException;
Import Java.lang.reflect.Field;
Import Java.lang.reflect.Method;
Import Java.util.HashMap;
Import java.util.List;
Import Java.util.Map;
Import Com.ricky.framework.ioc.bind.annotation.Bean;
Import Com.ricky.framework.ioc.bind.annotation.Inject;
Import Com.ricky.framework.ioc.util.ClassDetector; public class Annotationioccontainer {protected Map<string, object> beaninstancemap = new hashmap<string, Ob
Ject> ();
public void bind (String packagename) {Initializebean (PackageName);
Inject (); }/** * Instantiate bean */private void Initializebean (String packagename) {try {list< ;
class<?>> list = new Classdetector (packagename). Detect (Bean.class);
for (class<?> clazz:list) {Bean beanannotation = clazz.getannotation (Bean.class); System.out.println ("class=" +Clazz.getname () + ", bean_id=" +beanannotation.id ()); Object bean = clazz.newinstance ();
If you need to inject through the constructor, you need to deal with Beaninstancemap.put (Beanannotation.id (), Bean) here;
}} catch (IOException e) {e.printstacktrace ();
} catch (Instantiationexception e) {e.printstacktrace ();
} catch (Illegalaccessexception e) {e.printstacktrace (); }} private void Inject () {for (String BeanName:beanInstanceMap.keySet ()) {Object Bean =
Beaninstancemap.get (Beanname);
if (bean!=null) {processsetterannotation (bean);
Processfieldannotation (Bean); }}}/** * processing annotations on field * @param bean */protected void processfieldannotation (Obje
CT Bean) {try {field[] fields = Bean.getclass (). Getdeclaredfields (); for (Field field:fields) {if (Field!=null && field.isannotationpresent (inject.class)) {Inject resource = Field.getannotati
On (Inject.class);
String name = Resource.name ();
Object Injectbean = null; if (name!=null&&! "".
Equals (name)) {Injectbean = Beaninstancemap.get (name); }else{for (String Key:beanInstanceMap.keySet ()) {//Determines whether the type to which the current property belongs
An if (Field.gettype (). IsAssignableFrom (Beaninstancemap.get (key). GetClass ()) is present in the configuration file {
Gets the instance object of type match Injectbean = Beaninstancemap.get (key);
Break }}} if (Injectbean!=null) {//Allow visit Ask the Private field field.setaccessible (trUE);
Inject the reference object into the attribute Field.set (bean, Injectbean);
}else{System.out.println ("Field inject failed,name=" +name);
}}}} catch (Exception e) {e.printstacktrace (); }}/** * Handling annotations on set methods * @param bean */protected void processsetterannotation (Object bean) {try {//Get Bean's property descriptor propertydescriptor[] PS = Introspector.getbean
Info (Bean.getclass ()). Getpropertydescriptors ();
for (PropertyDescriptor pd:ps) {Method setter = Pd.getwritemethod ();
if (Setter!=null && setter.isannotationpresent (inject.class)) {//Gets the current annotation and determines if the Name property is empty
Inject resource = setter.getannotation (Inject.class);
String name = Resource.name (); Object Injectbean = null; if (name!=null&&! "".
Equals (name)) {Injectbean = Beaninstancemap.get (name);
}else{//If the current annotation does not specify a Name property, the match is based on the type for (String Key:beanInstanceMap.keySet ()) {
if (Pd.getpropertytype (). IsAssignableFrom (Beaninstancemap.get (key). GetClass ())) {
Injectbean = Beaninstancemap.get (key);
Break }}} if (Injectbean!=null) {//Allow visit
Ask Private Method Setter.setaccessible (True);
Inject the reference object into the attribute Setter.invoke (bean, Injectbean);
}else{System.out.println ("Setter inject failed,name=" +name); }}}} catch (ExcEption e) {e.printstacktrace (); }
}
}