相关系列
java中spring底层核心原理解析(1)-CSDN博客
java中spring底层核心原理解析(2)-CSDN博客
在以上详细讲解了spring底层核心原理,今天我们就来手写一个spring框架。主流程是:
1、编写启动类
2、依据ComponentScan设置的value属性进行扫描路径下的文件
3、根据扫描得到的文件,判断是否加了Component注解,如果加了,则添加到beanDefinitionMap
4、编写DzendApplicationContext中的getBean方法,判断是单例bean还是原型bean,单例bean则到singletonObjects中去获取,如果为null,则创建;如果是原型bean,则创建;把刚获取的bean返回,就拿到了bean
第一步编写启动类Test.java
这个类是应用程序的主入口,用于程序的启动
package com.dzend;import com.dzend.service.UserService;
import com.spring.DzendApplicationContext;public class Test {public static void main(String[] args) {//扫描 ->创建对象DzendApplicationContext applicationContext = new DzendApplicationContext(AppConfig.class);UserService userService = (UserService) applicationContext.getBean("userService");userService.test();}
}
第二步编写配置类AppConfig.java
是一个配置类,用以配置要扫描的文件路径。
package com.dzend;import com.spring.ComponentScan;@ComponentScan("com.dzend.service")
public class AppConfig {
}
第三步,根据第二步用了@ComponentScan注解,创建ComponentScan注解
用来配置扫描路径的注解。
package com.spring;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)
public @interface ComponentScan {String value() default "";
}
Component.java
用于配置要加入到BeanDefinitionMap 中,也就是管理这个service类的,比如缓存。
package com.spring;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)
public @interface Component {String value() default "";
}
Scope.java
用于标注是单例bean,还是原型bean。在创建bean的时候,如果是单例bean,则从singletonObjects对象获取,如果值为null,则创建这个对象,并放入到 singletonObjects对象中。如果为原型bean,则每次获取的时候都动态的创建新类并返回。
package com.spring;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)
public @interface Scope {String value();
}
第四步编写DzendApplicationContext.java文件
这是一个核心文件,用来工作的,核心就是扫描路径,创建 singletonObjects。
package com.spring;import java.beans.Introspector;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;public class DzendApplicationContext {private Class<?> configClass;private Map<String,BeanDefinition> beanDefinitionMap= new HashMap<>();private Map<String, Object> singletonObjects=new HashMap<>();private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();public DzendApplicationContext(Class configClass) {this.configClass=configClass;scan(configClass);}private void scan(Class configClass) {if(configClass.isAnnotationPresent(ComponentScan.class)){ComponentScan componentScan = (ComponentScan) configClass.getAnnotation(ComponentScan.class);String path = componentScan.value();path = path.replace(".","/");ClassLoader classLoader = DzendApplicationContext.class.getClassLoader();URL resource = classLoader.getResource(path);try {path = URLDecoder.decode(path, "UTF-8");} catch (UnsupportedEncodingException e) {throw new RuntimeException(e);}File file = null;try {file = new File( URLDecoder.decode(resource.getFile(), "UTF-8"));} catch (UnsupportedEncodingException e) {throw new RuntimeException(e);}if(file.isDirectory()){for (File f : file.listFiles()) {String absolutePath = f.getAbsolutePath();absolutePath = absolutePath.substring(absolutePath.indexOf("com"),absolutePath.indexOf(".class"));absolutePath=absolutePath.replace("\\",".");try {Class<?> clazz = classLoader.loadClass(absolutePath);if (clazz.isAnnotationPresent(Component.class)) {if (BeanPostProcessor.class.isAssignableFrom(clazz)) {BeanPostProcessor instance = (BeanPostProcessor) clazz.getConstructor().newInstance();beanPostProcessorList.add(instance);}Component componentAnnotaion = clazz.getAnnotation(Component.class);String beanName= componentAnnotaion.value();if("".equals(beanName)){beanName = Introspector.decapitalize(clazz.getSimpleName());}BeanDefinition beanDefinition = new BeanDefinition();beanDefinition.setType(clazz);if (clazz.isAnnotationPresent(Scope.class)) {Scope scopeAnnotation = clazz.getAnnotation(Scope.class);String value = scopeAnnotation.value();beanDefinition.setScope(value);}else{beanDefinition.setScope("singleton");}beanDefinitionMap.put(beanName,beanDefinition);}} catch (ClassNotFoundException e) {e.printStackTrace();} catch (InvocationTargetException e) {throw new RuntimeException(e);} catch (InstantiationException e) {throw new RuntimeException(e);} catch (IllegalAccessException e) {throw new RuntimeException(e);} catch (NoSuchMethodException e) {throw new RuntimeException(e);}}}}}public Object getBean(String beanName){if(!beanDefinitionMap.containsKey(beanName)){throw new NullPointerException();}BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);if(beanDefinition.getScope().equals("singleton")){Object singletonObject = singletonObjects.get(beanName);if(singletonObject == null){singletonObject = createBean(beanName,beanDefinition);singletonObjects.put(beanName,singletonObject);}return singletonObject;}else{//原型Object prototypeBean = createBean(beanName, beanDefinition);return prototypeBean;}}private Object createBean(String beanName, BeanDefinition beanDefinition) {Class clazz = beanDefinition.getType();Object instance = null;try {instance = clazz.getConstructor().newInstance();} catch (InstantiationException e) {throw new RuntimeException(e);} catch (IllegalAccessException e) {throw new RuntimeException(e);} catch (InvocationTargetException e) {throw new RuntimeException(e);} catch (NoSuchMethodException e) {throw new RuntimeException(e);}return instance;}
}
编写相关service类
定义相关的业务类,并加上Component注解用来标注是不是要放入spring容器中缓存。
OrderService.java
package com.dzend.service;import com.spring.Autowired;
import com.spring.Component;@Component(value = "orderService")
public class OrderService {public void test(){System.out.println("Hello world!!!");}}
UserService.java
package com.dzend.service;import com.spring.Autowired;
import com.spring.BeanNameAware;
import com.spring.Component;@Component(value = "userService")
public class UserService {@Autowiredprivate OrderService orderService;public OrderService getOrderService() {return orderService;}}