Spring源码逻辑|Spring01 -- 启动和扫描逻辑模拟

准备:创建一个空的maven项目

//IOC的加载(启动容器) ApplicationContext applicationContext = new ApplicationContext(AppConfig.class);

1 容器加载时如何 判断所扫描到的类 上含有Component注解
1.根据传入的配置类拿到ComponentScan类信息 2.获取扫描路径包 com.tes.service--->需转为com/tes/service 3.根据获取到的扫描路径获取在路径包下所有类的信息 4.判断所扫描到的类上是否有Component注解,有的话表示当前类是一个Bean对象 ......

1.1 @Component
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface Component { String value() default ""; //默认值为空,可以不写 }

1.2 @ComponentScan
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface ComponentScan { //String value() default ""; //扫描路径默认值为空 String value(); }

1.3 AppConfig 配置类
@ComponentScan("com.tes.service") public class AppConfig {}

1.4 UserService
import com.spring.Component; import com.spring.Scope; @Component("userService") public class UserService {}

1.5 扫描逻辑代码
package com.spring; import java.io.File; import java.lang.annotation.Annotation; import java.net.URL; public class ApplicationContext { private Class configClass; public ApplicationContext(Class configClass) { this.configClass = configClass; /*1.根据传入的配置类拿到ComponentScan注解信息 2.获取扫描路径包 com.tes.service--->需转为com/tes/service 3.根据获取到的扫描路径获取在路径包下所有类的信息 4.判断所扫描到的类上是否有Component注解,有的话表示当前类是一个Bean对象 ...... * *///解析配置类//ComponentScan注解--->拿到扫描路径--->扫描///在类上是否有注解 //1. ComponentScan componentScanAnnotation = (ComponentScan) configClass.getDeclaredAnnotation(ComponentScan.class); //2. String path = componentScanAnnotation.value(); //获取注解得到的值是否为扫描路径 System.out.println("扫描获取的路径(包): "+path); //com.tes.service怎么根据包名得到报下面的所有类类加载器 path=path.replace(".", "/"); //--->转换为com/tes/service System.out.println("转换后的路径(包): "+path); //扫描 /*类加载器 *App--->classpath * */ ClassLoader classLoader = ApplicationContext.class.getClassLoader(); // //通过类加载器获取某一资源 //3. URL resource = classLoader.getResource(path); //相对路径 File file = new File(resource.getFile()); if (file.isDirectory()) {//是否为文件 File[] files = file.listFiles(); for (File f : files) { System.out.println("获取结果绝对路径: "+"\n"+f); String fileName = f.getAbsolutePath(); if (fileName.endsWith(".class")) {//是class文件才去下一步解析 String className = fileName.substring(fileName.indexOf("com"), fileName.indexOf(".class")); className = className.replace("\\", "."); // System.out.println("提取后: "+className); /*将得到的D:\project3\2109\spring_origin\target\classes\com\tes\service\UserService.class * 转化为com.tes.service.UserService * */ //Class aClass = classLoader.loadClass(".."); try { //Class clazz = classLoader.loadClass("com.tes.service.UserService"); //根据名字加载类,得到calss对象 Class clazz = classLoader.loadClass(className); //4.扫描这个类上是否有Component注解 if (clazz.isAnnotationPresent(Component.class)) { //判断所扫描到的类上是否有Component注解,有的话表示当前类是一个Bean //... } } catch (ClassNotFoundException e) { e.printStackTrace(); }} } }}public Object getBean(String beanName){ return null; } }

1.6 测试类
public static void main(String[] args) { ApplicationContext applicationContext = new ApplicationContext(AppConfig.class); System.out.println(applicationContext.getBean("userService")); //System.out.println(applicationContext.getBean("userService")); //System.out.println(applicationContext.getBean("userService")); }

Spring源码逻辑|Spring01 -- 启动和扫描逻辑模拟
文章图片

Spring源码逻辑|Spring01 -- 启动和扫描逻辑模拟
文章图片

2 根据Scope(单例Bean,多例)创建对象 如何拿到beanName
2.1 @Scope
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface Scope { String value(); }

2.2 BeanDefinition 【Spring源码逻辑|Spring01 -- 启动和扫描逻辑模拟】bean的定义,当前类型,作用域
package com.spring; public class BeanDefinition { private Class clazz; //当前bean的类型 private String scope; public Class getClazz() { return clazz; }public void setClazz(Class clazz) { this.clazz = clazz; }public String getScope() { return scope; }public void setScope(String scope) { this.scope = scope; } }

2.3 3 ApplicationContext
package com.spring; import java.io.File; import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import java.net.URL; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; public class ApplicationContext { private Class configClass; private ConcurrentHashMap,Object> singletonObjects=new ConcurrentHashMap<>(); //单例池 private ConcurrentHashMap,BeanDefinition> beanDefinitionMap=new ConcurrentHashMap<>(); public ApplicationContext(Class configClass) { this.configClass = configClass; /*1.根据传入的配置类拿到ComponentScan注解信息 2.获取扫描路径包 com.tes.service--->需转为com/tes/service 3.根据获取到的扫描路径获取在路径包下所有类的信息 4.判断所扫描到的类上是否有Component注解,有的话表示当前类是一个Bean对象 ...... * *///解析配置类//ComponentScan注解--->拿到扫描路径--->扫描--->BeanDefinition--->BeanDefinitionMap///在类上是否有注解 //1. scan(configClass); for (Map.Entry,BeanDefinition> entry : beanDefinitionMap.entrySet()) { String beanName = entry.getKey(); BeanDefinition beanDefinition = entry.getValue(); if (beanDefinition.getScope().equals("singleton")){ Object bean = createBean(beanDefinition); // singletonObjects.put(beanName,bean); } }} public Object createBean(BeanDefinition beanDefinition){ Class clazz = beanDefinition.getClazz(); try { Object instance = clazz.getDeclaredConstructor().newInstance(); return instance; } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); }return null; }private void scan(Class configClass) { ComponentScan componentScanAnnotation = (ComponentScan) configClass.getDeclaredAnnotation(ComponentScan.class); //2. String path = componentScanAnnotation.value(); //获取注解得到的值是否为扫描路径 System.out.println("扫描获取的路径(包): "+path); //com.tes.service怎么根据包名得到报下面的所有类类加载器 path=path.replace(".", "/"); //--->转换为com/tes/service System.out.println("转换后的路径(包): "+path); //扫描 /*类加载器 *App--->classpath * */ ClassLoader classLoader = ApplicationContext.class.getClassLoader(); // //通过类加载器获取某一资源 //3. URL resource = classLoader.getResource(path); //相对路径 File file = new File(resource.getFile()); if (file.isDirectory()) {//是否为文件 File[] files = file.listFiles(); for (File f : files) { System.out.println("获取结果绝对路径: "+"\n"+f); String fileName = f.getAbsolutePath(); if (fileName.endsWith(".class")) {//是class文件才去下一步解析 String className = fileName.substring(fileName.indexOf("com"), fileName.indexOf(".class")); className = className.replace("\\", "."); // System.out.println("提取后: "+className); /*将得到的D:\project3\2109\spring_origin\target\classes\com\tes\service\UserService.class * 转化为com.tes.service.UserService * */ //Class aClass = classLoader.loadClass(".."); try { //Class clazz = classLoader.loadClass("com.tes.service.UserService"); //根据名字加载类,得到calss对象 Class clazz = classLoader.loadClass(className); //4.扫描这个类上是否有Component注解 if (clazz.isAnnotationPresent(Component.class)) { //判断所扫描到的类上是否有Component注解,有的话表示当前类是一个Bean //... //解析类-->BeanDefinitionComponent componentAnnotation=clazz.getDeclaredAnnotation(Component.class); String beanName = componentAnnotation.value(); BeanDefinition beanDefinition = new BeanDefinition(); beanDefinition.setClazz(clazz); if (clazz.isAnnotationPresent(Scope.class)){ Scope scopeAnnotation=clazz.getDeclaredAnnotation(Scope.class); beanDefinition.setScope(scopeAnnotation.value()); }else{ beanDefinition.setScope("singleton"); } beanDefinitionMap.put(beanName, beanDefinition); } } catch (ClassNotFoundException e) { e.printStackTrace(); }} } } }public Object getBean(String beanName){ if (beanDefinitionMap.containsKey(beanName)){ BeanDefinition beanDefinition=beanDefinitionMap.get(beanName); if (beanDefinition.getScope().equals("singleton")){ Object o = singletonObjects.get(beanName); return o; }else { //创建Bean Object bean = createBean(beanDefinition); return bean; } }else { //不存在Bean throw new NullPointerException(); } // return null; } }

    推荐阅读