IOC of handwritten spin framework core source code
1. Use Spring framework
2. Use reflection mechanism
IOC controls the permission of reverse of controller to create objects. The objects needed by Java programmers are no longer created by programmers themselves, but are created by IOC containers
Simulate the use of IOC containers
1. Use POM XML file information
<dependencies> <!-- introduce Servlet rely on --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> </dependency> <!-- Import Spring rely on --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.3</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.16</version> </dependency> </dependencies> <!-- set up Maven of JDK The default version is 5. It needs to be manually changed to 8 --> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> </configuration> </plugin> </plugins> </build>
2. Create a servlet file
/** * Demonstration of the return value of the specification of testing mvc pattern * @author liugang * @version 1.0 * @date 2021/6/21 14:54 */ @WebServlet("/hello") public class HelloServlet extends HttpServlet { private HelloService helloService = new HelloServiceImpl(); @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.getWriter().write("hello spring:"+helloService.findAll()); } }
3. Deploy to tomcat
4.servlet,service,dao when our requirements change, we may need to modify the Java code frequently, which is very inefficient
Static factory mode
public class BeanFactory { public static HelloDao getDao(){ return new HelloDaoImpl(); } } private HelloDao helloDao = BeanFactory.getDao();
The above methods can not solve our problems. When the requirements change, we still need to modify the code. How can we achieve class switching without changing the Java code?
In the way of external configuration files, Java programs only need to read configuration files, such as XML, yaml, properties and JSON
1. Define external profile
helloDao=com.southwind.dao.impl.HelloDaoImpl
The Java program reads this configuration file
/** * Create a static factory class * For our programs, we often put some things that change a lot, and we often hope to put them in * In some configuration files, so that we can better reduce the possibility of coupling * @author liugang * @version 1.0 * @date 2021/6/21 15:22 */ public class BeanFactory { //Configure the contents of its corresponding mapping file private static Properties properties; //Define a cache to store our instantiated objects private static Map<String,Object> cache = new HashMap<>(); static { properties = new Properties(); try { properties.load(BeanFactory.class.getClassLoader().getResourceAsStream("factory.properties")); } catch (IOException e) { e.printStackTrace(); } } /*Produce objects through factory mode*/ public static Object getDao(String beanName){ //First, determine whether there are bean s in the cache if (!cache.containsKey(beanName)){ //At this time, we use the lock mechanism to control the related containers synchronized (BeanFactory.class){ //First, judge whether there are beans in the cache. If there are no beans, we will store their corresponding beans in the corresponding cache if (!cache.containsKey(beanName)){ try { String value = properties.getProperty("helloDao"); //Create objects using reflection mechanism Class clazz = Class.forName(value); Object object = clazz.getConstructor().newInstance(null); cache.put(beanName,object); } catch (Exception e) { e.printStackTrace(); } } } } return cache.get(beanName); } }
3. Modify service
private HelloDao helloDao = (HelloDao) BeanFactory.getDao();
In this way, the program changes from tight coupling / strong dependence to loose coupling / weak dependence. It can still be modified after compilation, which makes the program have good expansibility
You give up the permission to create objects and give the permission to create objects to BeanFactory. The idea of giving control to others is control inversion
Use of Spring Ioc
xml and annotation. xml has been eliminated. At present, the mainstream is annotation based, and spring boot is also annotation based
/** * Injecting entity classes into containers * @author liugang * @version 1.0 * @date 2021/6/22 17:04 */ @Data @Component public class Account { @Value("1") private Integer id; @Value("liugang") private String name; @Value("20") private int age; } /** * @author liugang * @version 1.0 * @date 2021/6/22 17:27 */ public class SpringTest { public static void main(String[] args) { //Get the ioc container. Here is the form of control over the annotation method. Get the container under the relevant package ApplicationContext applicationContext = new AnnotationConfigApplicationContext("com.zzuli.spring.entity"); //The names of all JavaBean s in the Spring container, and the return type is a string array. String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames(); //Gets the number of bean s in the Spring container System.out.println(applicationContext.getBeanDefinitionCount()); for (String beanDefinitionName : beanDefinitionNames) { System.out.println(beanDefinitionName); //Get such an instantiated bean object System.out.println(applicationContext.getBean(beanDefinitionName)); } } }
IOC annotation based execution principle
Idea of handwritten code:
1. Customize a MyAnnotationConfigApplicationContext and pass in a package to be scanned in the constructor
2. Get all classes under this package
3. Traverse these classes, find the class with @ Component annotation, obtain its corresponding class and corresponding beanName, package it into a beanDefinition and store it in the Set set. This opportunity is the raw materials automatically loaded by IOC
4. Traverse the Set, create objects through the reflection mechanism, and detect whether the @ Value object is added to the attribute. If so, it is also necessary to assign a Value to the attribute. Then, these dynamically created objects are stored in the cache in the form of k-v
5. Provide getBean and other methods to get the corresponding bean through beanName
code implementation
An object collection of entity classes encapsulated by each annotation class and container
/** * The collection of objects for the container's entity classes * @author liugang * @version 1.0 * @date 2021/6/23 9:36 */ @Data @AllArgsConstructor public class BeanDefinition { private String beanName; private Class beanClass; } /** * Here are the auto loading annotations * @author liugang * @version 1.0 * @date 2021/6/24 14:36 */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Autowired { } /** * Custom container annotations * @author liugang * @version 1.0 * @date 2021/6/23 20:41 */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Component { String value() default ""; //"Default" means that the corresponding default value is empty. At this time, the annotation of component can not add a value } /** * Annotation loaded by name * @author liugang * @version 1.0 * @date 2021/6/24 14:41 */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Qualifier { String value(); } /** * @author liugang * @version 1.0 * @date 2021/6/23 22:38 */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Value { String value(); //The default value is not added here, so that we can assign this value. When we use this annotation, we are required to assign it manually }
Core writing
/** * @author liugang * @version 1.0 * @date 2021/6/23 20:10 */ public class MyAnnotationConfigApplicationContext { //Define a cache container private Map<String,Object> ioc = new HashMap<>(); /*Construction method to encapsulate the data we want*/ public MyAnnotationConfigApplicationContext(String pack) { //Traverse the package to find the target class (raw material) Set<BeanDefinition> beanDefinitions = findBeanDefinitions(pack); /*Iterator<BeanDefinition> iterator = beanDefinitions.iterator(); while (iterator.hasNext()){ BeanDefinition beanDefinition = iterator.next(); System.out.println(beanDefinition); }*/ //Create bean s from raw materials createObject(beanDefinitions); //Automatic loading function autowireObject(beanDefinitions); } /*Automatic loading function*/ public void autowireObject(Set<BeanDefinition> beanDefinitions){ Iterator<BeanDefinition> iterator = beanDefinitions.iterator(); while (iterator.hasNext()){ BeanDefinition beanDefinition = iterator.next(); //Get its corresponding bytecode file Class aClass = beanDefinition.getBeanClass(); Field[] declaredFields = aClass.getDeclaredFields(); for (Field declaredField : declaredFields) { //Traverse each element information corresponding to it Autowired annotation = declaredField.getAnnotation(Autowired.class); if (annotation!=null){ //Get the qualifier annotation information Qualifier qualifier = declaredField.getAnnotation(Qualifier.class); if (qualifier!=null){ //byName try { String beanName = qualifier.value(); Object bean = getBean(beanName); //Order object corresponding to account String fieldName = declaredField.getName(); String methodName = "set"+fieldName.substring(0, 1).toUpperCase()+fieldName.substring(1); Method method = aClass.getMethod(methodName, declaredField.getType()); Object object = getBean(beanDefinition.getBeanName()); //account object method.invoke(object, bean); } catch (Exception e) { e.printStackTrace(); } } else { //byType } } } } } /*Method to get ioc container name*/ public Object getBean(String beanName){ return ioc.get(beanName); } /*Create bean s from raw materials*/ public void createObject(Set<BeanDefinition> beanDefinitions){ Iterator<BeanDefinition> iterator = beanDefinitions.iterator(); while (iterator.hasNext()){ BeanDefinition beanDefinition = iterator.next(); //Get the class attribute of the corresponding raw material and related objects Class aClass = beanDefinition.getBeanClass(); String beanName = beanDefinition.getBeanName(); try { Object object = aClass.getConstructor().newInstance(); //Before putting the raw material into the container, we must complete the corresponding assignment operation //Get each variable element Field[] fields = aClass.getDeclaredFields(); for (Field field : fields) { Value valueAnnotation = field.getAnnotation(Value.class); if (valueAnnotation!=null){ //Get the corresponding value String value = valueAnnotation.value(); String fieldName = field.getName(); String methodName = "set"+fieldName.substring(0,1).toUpperCase()+fieldName.substring(1); Method method = aClass.getMethod(methodName, field.getType()); //Because the types of data formats are different, there will be many errors. Here you can complete the relevant type conversion Object val = null; switch (field.getType().getName()){ case "java.lang.Integer": val = Integer.parseInt(value); break; case "java.lang.String": val = value; break; case "java.lang.Float": val = Float.parseFloat(value); break; } method.invoke(object,val); } } ioc.put(beanName,object); //Here, the corresponding raw material object is stored in the ioc container } catch (Exception e) { e.printStackTrace(); } } } /*Obtain raw materials*/ private Set<BeanDefinition> findBeanDefinitions(String pack) { //1. Traverse all classes under the package Set<Class<?>> classes = MyTools.getClasses(pack); //Define a collection containing BeanDefinition Set<BeanDefinition> beanDefinitions =new HashSet<>(); Iterator<Class<?>> iterator = classes.iterator(); while (iterator.hasNext()){ //2. Traverse these classes to find the annotated classes Class<?> aClass = iterator.next(); Component componentAnno = aClass.getAnnotation(Component.class); if (componentAnno!=null){ //Gets the value of the Component annotation String beanName = componentAnno.value(); if ("".equals(beanName)){ //If the value of the annotation is empty, the lowercase value of the class name is obtained String className = aClass.getName().replaceAll(aClass.getPackage().getName() + ".", ""); beanName = className.substring(0,1).toLowerCase()+className.substring(1); } //3. Encapsulate these into bean definitions and store them in the collection beanDefinitions.add(new BeanDefinition(beanName,aClass)); } } return beanDefinitions; } }
Test class writing and demonstration effect
public class MyspringTest { public static void main(String[] args) { //As soon as you start, you can see the corresponding constructor to access MyAnnotationConfigApplicationContext myAnnotationConfigApplicationContext = new MyAnnotationConfigApplicationContext("com.zzuli.myspring.entity"); //Get the corresponding bean System.out.println(myAnnotationConfigApplicationContext.getBean("account")); System.out.println(myAnnotationConfigApplicationContext.getBean("myOrder")); } }