[java] Spring Framework -- Injection

Keywords: Java Spring xml

We have three purposes for this time:

1. Set up Bean Factory (context, container);

2. Ability to inject Bean members;

3. Bean s can be obtained;

Bean, also known as JavaBean, or POJO, is a trivial Java class.

Organizing these trivial Java class objects into a container forms a so-called "context" or "container". The formation of the container requires configuration tools, which can be in the form of annotations or Xml files. Here we use the form of annotations.

@Component//This annotation is used to indicate that this class is a class in a container.
public class Classone {
	@Autowired//This annotation is used to represent members to be injected
	private Complex complex;
	private String str;
	@Autowired
	private Point point;
	
	public Classone() {
		
	}

    }

Next we're going to finish writing the Bean Factory.

public class BeanFactory {
	private static final Map<String,BeanDefinition> beanpool;//container
	static {
		beanpool = new HashMap<String, BeanDefinition>();
	}
	
	public static void scanpackage(String pagename) {//Packet scan
		new PackageScanner() {
			
                        //Processing scanned classes, if not with Component annotations, skips directly
			@Override
			public void dealClass(Class<?> klass) {
				if(klass.isPrimitive() 
                                || klass.isAnnotation()
				||klass.isEnum()
                                ||klass.isArray()
			        ||!klass.isAnnotationPresent(Component.class)) {
					return;
				}
				
				try {
					Object obj = klass.newInstance();
                                   /*BeanDefinition Classes are further refinements of the classes we scan.
                                        Keep the value we want*/
					BeanDefinition bd = new BeanDefinition();
					bd.setKlass(klass);
					bd.setObj(obj);
					//Store it in map with the key as the class name of the class
					beanpool.put(klass.getName(), bd);
				} catch (Exception e) {
					
					e.printStackTrace();
				}
				
			}
		}.packageScanner(pagename);
		
	}

        BeanDefinition getBeanDefinition(String klassname) {//Get the value by the class name
		return beanpool.get(klassname);
	}
	
	BeanDefinition getBeanDefinition(Class<?> klassname) {//Getting values through classes
		return getBeanDefinition(klassname.getName());
		
	}

        public <T> T getBean(String klassname){//This method is used to get the object value directly in map, and the return value is generic instead of Object type because generic type does not need to be strong when invoked.
        BeanDefinition bd = getBeanDefinition(klassname);
        if(bd = null){
            return null;
            }
        return (T) bd.getObject();
        }

 

By passing Classone.class.getName() to test getBean, we find that the result is: Classone [complex=null, str=null,Point=null], which should be the case, because the object stored in map is obtained by newInstance(), so its member value is null. Next, we will inject the member of the object, at injection time. What we need to pay attention to (without discussing circular injection here, that is, B members in object A are to be injected, and A members in object B are to be injected to form a dead cycle):

1. We use the "Lazy Man" mode, that is, when the relevant beans are acquired, they will not be injected, and the beans in the bean factory are singletons, the same bean can only be injected once.

2. When injecting a member, the name of the member class should be searched in the map, that is, the value in the map should be injected.

3. Deep injection should be used when injecting, that is, when injecting a member of an object, we should first find out if there are any members in the member's class that need to be injected.

For example, there are B members in A and C members in B. When injecting B into A, it is reasonable to first judge whether there is or needs to be injected into B, that is, to inject C into B first.

4. If there is an injected member marked Autowire in the object, but there is no Bean in the map, the exception is reported and the operation is terminated.

After understanding the above information, see our implementation:

       //The Method of Obtaining Bean s
         @SuppressWarnings("unchecked")
	public <T> T getBean(String klassname) throws RuntimeException {
		BeanDefinition bd = getBeanDefinition(klassname);
		if(bd == null) {
			return null;
		}
		Object res = bd.getObj();
		if(!bd.isInject()) {//Determine whether the member of the object has been injected
		    injectPro(bd);//Input method is called without injection
		}
		
		return (T) res;
	}
        
        //Injecting members of the object to be injected
        private void injectPro(BeanDefinition bd) throws RuntimeException {
		Class<?> klass = bd.getKlass();
		Object obj = bd.getObj();
		
		Field[] field = klass.getDeclaredFields();//Reflection mechanism call to get all members of the object
		
		for(int i = 0; i < field.length;i++) {
			if(!field[i].isAnnotationPresent(Autowired.class)) {
				continue;    //Skip if there is no injection tag
			}
                        //If marked, first modify the permissions, and then find the corresponding values in the map
			field[i].setAccessible(true);
			
//			String klassname = field[i].getType().getName();
//			BeanDefinition bdm = getBeanDefinition(klassname); //Find in map

//			if(bdm == null) {// If not found in the map, it means that there is an error, then throw an exception.
//				throw new Exception
//                    ("class {"+klass.getName()+"} member {"+field[i]+"} has no corresponding value in map";
//			}

//			Field [i]. set (obj, bdm. getObj ();// inject into the member
		
			Object value = getBean(field[i].getType());//recursion
/*If you use the annotated code above, you can only find and inject the member (shallow level), but in case there are other members that need to be injected within the member, you will make an error, which will lead to the depth is not deep enough.
    For example, there are B members in A and C members in B. When injecting B in A, we should first judge whether there is or needs to be injected in B, that is, injecting C in B first, so there is no problem with recursive calls here. */
			if(value == null) {
//If value = null, it means that the member is injected but not found in the map, then it means that there is an error, then throw the exception, must throw the runtime exception, otherwise the run cannot be terminated.
				throw new StopException
            ("class{"+klass.getName()+"}Middle member{"+field[i].getName()+"}stay map There is no corresponding value in it.");
			}
			try {
				field[i].set(obj,value);//Inject the member
			} catch (IllegalAccessException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
			System.out.println("member["+field[i].getName()+"]To complete injection");
		}
		bd.setInject(true);//After all member injection is completed, the injection markup of the object must be changed so that only once injection can be guaranteed.
	}

	//The overload of this method directly uses class type as parameter
	public <T> T getBean(Class<?> klassname) throws RuntimeException {
		return getBean(klassname.getName());
		
	}

Below is BeanDefinition.java:

public class BeanDefinition {
	private Class<?> klass;
	private Object obj;
	private boolean inject;
	
	 BeanDefinition() {
		this.inject = false;
	}

	
	boolean isInject() {
		return inject;
	}


	void setInject(boolean inject) {
		this.inject = inject;
	}


	Class<?> getKlass() {
		return klass;
	}


	void setKlass(Class<?> klass) {
		this.klass = klass;
	}


	Object getObj() {
		return obj;
	}


	void setObj(Object obj) {
		this.obj = obj;
	}


	@Override
	public String toString() {
		return klass.getSimpleName() + " : " + obj;
	}
	
	
}

The following is the test of our injection and acquisition:

public static void main(String[] args) {
		BeanFactory bf = new BeanFactory();
		BeanFactory.scanpackage("com.mec.test");
		

		Classone co = bf.getBean(Classone.class);
		Classone cp = bf.getBean(Classone.class);
		System.out.println(co);
		System.out.println(cp);
	}
/*Output results:
    Members [complex] to complete injection
    Member [point] to complete injection
    Classone [complex=(0.0,0.0), str=null,Point [a=0, b=0]]
    Classone [complex=(0.0,0.0), str=null,Point [a=0, b=0]]*/

By the way, to test our exception handling, we remove the tags on the Point class so that there are no points in the map that are marked with Autowired to be injected:

 

public static void main(String[] args) {
		BeanFactory bf = new BeanFactory();
		BeanFactory.scanpackage("com.mec.test");
		

		Classone co = bf.getBean(Classone.class);
		System.out.println(co);

	}
/*Output results:
    Exception in thread "main" com.mec.spring.core.StopException: The member {point} in class {com.mec.test.Classone} has no corresponding value in map
	at com.mec.spring.core.BeanFactory.injectPro(BeanFactory.java:77)
	at com.mec.spring.core.BeanFactory.getBean(BeanFactory.java:102)
	at com.mec.spring.core.BeanFactory.getBean(BeanFactory.java:110)
	at com.mec.test.text.main(text.java:13)*/

So we have no problem with exception handling.

Posted by Fawkman on Thu, 26 Sep 2019 00:57:57 -0700