SpringBoot源码

效果:加入springBoot注解后,在run方法中传入application.class就能实现自动启动并匹配到对象的controller返回结果。

  1. 导入Spring依赖

  2. 定义注解(在自己的应用Apllication加入该注解,作为SpringBoot项目启动),注解上加入@ComponentScan。当应用添加后,相当于添加@ComponentScan注解在自己的类上,就会默认扫描自己包下的所有文件,就可以扫描到controller文件夹下的文件。

  3. 自定义Run方法,并在其中启动tomcat

  4. 添加DispatchServlet(SpringMVC),需要接受一个spring容器,因为DispathServlet是在该容器中找对应的Controller.

  5. 添加Spring容器,需要传入一个配置类

Spring源码

基础准备

使用Spring框架时,用到的创建语句

public class MiaoboApplicationContext {Class configClass;public MiaoboApplicationContext(Class configClass){this.configClass = configClass;}public Object getBean(){return null;}
}

定义ApplicationContext类,定义需要填入的配置文件属性以及getBean方法

public class MiaoboApplicationContext {Class configClass;public MiaoboApplicationContext(Class configClass){this.configClass = configClass;}public Object getBean(){return null;}
}

用户的配置类添加@ComponentScan,自定义@ComponentScan,定义属性指定扫描路径

@ComponentScan("com.miaobo.service")
public class AppConfig {}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ComponentScan {String value(); //传入扫描路径的值
}

Service类添加@Component注解表示定义的Bean,自定义@Component

@Component("userService")
public class UserService {}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Component {String value(); //传入BeanName
}

基础完成后,Application需要在初始化后解析配置类,通过@ComponentScan扫描带有@Component

扫描

获取扫描路径,并通过类加载器得到所有该路径下的文件

Class configClass;public MiaoboApplicationContext(Class configClass) throws ClassNotFoundException {this.configClass = configClass;//配置类注解获取扫描路径ComponentScan componentScanAnnotation = (ComponentScan) configClass.getDeclaredAnnotation(ComponentScan.class);String path = componentScanAnnotation.value();path = path.replace(".","/");//获取应用类加载器ClassLoader configClassLoader = MiaoboApplicationContext.class.getClassLoader();//加载扫描路径,获取目录。特别注意所有目录不允许空格和中文URL resource = configClassLoader.getResource(path);//类加载器位于的目录为\Spring-miaobo\target\classes"File directory = new File(resource.getFile());//遍历文件,查看是否带有@Component注解if(directory.isDirectory()){File[] files = directory.listFiles();for(File file: files){System.out.println(file);//获取到扫描路径下的文件地址}}}

在获取到的文件中寻找带有@Component注解的类。即先根据包名得到所有的类,再判断是否有@Component注解。

//遍历文件,查看是否带有@Component注解
if(directory.isDirectory()){File[] files = directory.listFiles();for(File file: files){//绝对路径转为类加载器使用的路径格式String fileName = file.getAbsolutePath();if (fileName.endsWith(".class")) {fileName = fileName.substring(fileName.indexOf("com"), fileName.indexOf(".class"));fileName = fileName.replace("\\", ".");//使用类加载器加载类Class<?> clazz = configClassLoader.loadClass(fileName);//判断是否有@Component注解if (clazz.isAnnotationPresent(Component.class)) {System.out.println("yes");}}}
}

实例化Bean(单例/原型)

getBean方法中需要通过传入的BeanName去寻找类的信息(困难),因此在ApplicationContex初始化时就创建每个Bean的BeanDefinition对象(包含类信息、是否单例等)。

HashMap singlePool = new HashMap<String, Object>();//单例池
ConcurrentHashMap beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();//放入BeanDefinition

发现@Component后,需要判断是否为原型Bean或单例Bean。添加@Scope(“Prototype”)表示原型Bean。单例就直接创建Bean放入单例池。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Scope {String value();
}
//判断是否有@Component注解
if (clazz.isAnnotationPresent(Component.class)) {//获取BeanNameComponent componentAnnotation = clazz.getDeclaredAnnotation(Component.class);String beanName = componentAnnotation.value();BeanDefinition beanDefinition = new BeanDefinition();beanDefinition.setClazz(clazz);//判断是否为单例是则创建并返回单例池Scope scopeAnnotation = clazz.getDeclaredAnnotation(Scope.class);if (scopeAnnotation.value().equals("prototype")) {beanDefinition.setScope("prototype");}else {beanDefinition.setScope("singleton");}beanDefinitionMap.put(beanName, beanDefinition);
}

将放入扫描包—》所有类均创建对应BeanDefinition——》放入BeanDefinitionMap,提取成一个Scan方法。并对BeanDefinition中原型Bean进行创建并放入单例池。

//扫描路径->获取注解@Component->创建BeanDefinition并放入BeanDefinitionMap
scan(configClass);
//根据BeanDefinitionMap中的BeanDefinition创建单例对象
for(Map.Entry<String, BeanDefinition> entry: beanDefinitionMap.entrySet()){String beanName = entry.getKey();BeanDefinition beanDefinition = entry.getValue();if (beanDefinition.getScope().equals("singleton")) {Object o = createBean(beanName);singlePool.put(beanName, o);}
}

getBean从BeanDefinitionMap取BeanDefinition,如果为单例就去查找单例池。

public Object getBean(String beanName) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {if (beanDefinitionMap.containsKey(beanName)) {BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);if (beanDefinition.getScope().equals("singleton")) {Object o = singlePool.get(beanName);return o;}else {return createBean(beanName);}}else {throw new  NullPointerException();}}

初始化以及getBean中均用到createBean方法

private Object createBean(String BeanName) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {BeanDefinition beanDefinition = beanDefinitionMap.get(BeanName);Class clazz = beanDefinition.getClazz();Object o = clazz.getConstructor().newInstance();return o;
}

依赖注入

createBean中:找到所有属性-》查看具有@Autowored注解的

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Autowired {}
    private Object createBean(String BeanName) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {BeanDefinition beanDefinition = beanDefinitionMap.get(BeanName);Class clazz = beanDefinition.getClazz();Object o = clazz.getConstructor().newInstance();//依赖注入:找所有属性-》哪些有@Autowiredfor (Field declaredField : clazz.getDeclaredFields()) {if (declaredField.isAnnotationPresent(Autowired.class)) {declaredField.setAccessible(true);Object bean = getBean(declaredField.getName());declaredField.set(o, bean);}}return o;}
}

Aware回调

需要实现注入String BeanName属性值,模仿BeanNameAware接口

接口创建

public interface BeanNameAware {public void setBeanName(String name);
}

需要自动获得BeanName的类实现该接口

@Component("userService")
@Scope("prototype")
public class UserService implements BeanNameAware {@Autowiredprivate OrderService orderService;String BeanName;public void test(){System.out.println(orderService);}@Overridepublic void setBeanName(String name) {//该方法中会传入name, 就可以用传入的namethis.BeanName = name;}
}

createBean方法中判断是否实现接口,是则将BeanName赋予

//处理Aware接口
if (o instanceof BeanNameAware) {BeanNameAware beanNameAware = (BeanNameAware) o;beanNameAware.setBeanName(BeanName);
}

下列代码,测试结果为:com.miaobo.service.OrderService@533ddba userService

public class Test {public static void main(String[] args) throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {MiaoboApplicationContext applicationContext = new MiaoboApplicationContext(AppConfig.class);UserService userService = (UserService) applicationContext.getBean("userService");userService.test();System.out.println(userService.BeanName);}
}

初始化(类似Aware)

实现接口,内部由程序员决定功能

public interface InitializingBean {public void  afterPropertiesSet();
}
@Component("userService")
@Scope("prototype")
public class UserService implements BeanNameAware, InitializingBean {@Autowiredprivate OrderService orderService;public String BeanName;public void test(){System.out.println(orderService);}@Overridepublic void setBeanName(String name) {//该方法中会传入name, 就可以用传入的namethis.BeanName = name;}@Overridepublic void afterPropertiesSet() {//由程序员决定初始化后功能System.out.println("初始化");}
}

createBean中判断接口实现,并执行afterPropertiesSet()

//初始化
if (o instanceof InitializingBean) {InitializingBean initializingBean = (InitializingBean) o;initializingBean.afterPropertiesSet();
}

BeanPostProcessor

针对所有Bean(如果针对某个Bean可以在方法中判断),在Bean的创建过程添砖加瓦。不同于判断接口的方式,而是直接将实现类加入容器中,在容器中判断是否Bean需要BeanPostProcessor方法。

定义接口(此处实现初始化前后的BeanPostProcessor)

public interface BeanPostProcessor {Object postProcessorBeforeInitializing(Object bean, String beanName);Object postProcessorAfterInitializing(Object bean, String beanName);
}

程序员实现接口,并自定义方法。将该实现类注册为Bean

@Component("miaoboBeanPostProcessor")
@Scope("prototype")
public class MiaoboBeanPostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessorBeforeInitializing(Object bean, String beanName) {System.out.println("初始化前");//只针对某个Bean处理需要判断名字if (beanName.equals("userService")) {((UserService)bean).setName("小狗");}return bean;}@Overridepublic Object postProcessorAfterInitializing(Object bean, String beanName) {System.out.println("初始化后");return bean;}
}

ApplicationContext初始化过程中,判断容器中是否有类实现了接口,并将其存入列表(存入列表示因为可能有多个BeanPostProcess的实现类,这些类中的方法都是需要在初始化前后进行添加的操作,创建一个Bean就需要将所有BeanPostProcess所有实现类执行完)。

  //判断当前clazz是否实现了BeanPostProcessor接口,是就存入Listif (BeanPostProcessor.class.isAssignableFrom(clazz)) {BeanPostProcessor beanPostProcessor = (BeanPostProcessor) clazz.getDeclaredConstructor().newInstance();beanPostProcessorArrayList.add(beanPostProcessor);}

在类的创建过程中运行BeanPostProcessor实现的方法(所有Bean都会有)

}
//初始化前操作
for (BeanPostProcessor beanPostProcessor : beanPostProcessorArrayList) {beanPostProcessor.postProcessorBeforeInitializing(o, beanName);
}
//初始化
if (o instanceof InitializingBean) {InitializingBean initializingBean = (InitializingBean) o;initializingBean.afterPropertiesSet();
}
//初始化后操作
for (BeanPostProcessor beanPostProcessor : beanPostProcessorArrayList) {beanPostProcessor.postProcessorAfterInitializing(o, beanName);
}

Aop

初始化后的方法中进行aop,即postProcessorAfterInitializing()方法中。不同于BeanPostProcess针对Bean的生产过程前后的添砖加瓦,aop针对某个方法执行前后进行。

由于jdk的动态代理基于接口,因此创建需要实现aop类的接口对象

public interface UserServiceInterface {public void test();
}
@Component("userService")
@Scope("prototype")
public class UserService implements BeanNameAware, InitializingBean, UserServiceInterface{...}

DisapthServlet源码

通过url直接能返回页面

SpringStater源码

管理相关依赖,读取配置

模块1:业务项目:spring-boot-hello

package com.hello.service;public class TestService {}

模块2:自动配置依赖:spring-boot-autoconfiguration-hello

  1. 导入辅助依赖spring-boot-autoconfiguration, spring-boot-autoconfiguration-processor

    <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-autoconfigure</artifactId><version>2.7.3</version>
    </dependency>
    <!--将属性名转换为json,才能在导入stater后使用默认属性-->
    <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-autoconfigure-processor</artifactId><optional>true</optional><!--依赖不会传递--><version>2.7.3</version>
    </dependency>
    <!--导入自己写的service,方便自动创建对象-->
    <dependency><groupId>com.example</groupId><artifactId>spring-boot-hello</artifactId><version>0.0.1-SNAPSHOT</version>
    </dependency>
    
  2. 配置属性编写,导入该starter后,可以读取编写的默认的属性:hello.xxxx=10

    package com.hello;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    @ConfigurationProperties("hello.word")//配置属性的前缀
    public class helloProperties {private String value;public String getValue() {return value;}public void setValue(String value) {this.value = value;}
    }
    
  3. 配置类编写,可以返回创建好的对象

    @Configuration(proxyBeanMethods = false) //关闭代理,启动的时候就不是显示的代理
    @ConditionalOnClass(TestService.class) //只有导入某个Service配置才生效
    @EnableConfigurationProperties(helloProperties.class)//指定属性文件
    @ConditionalOnWebApplication //只在web应用下才自动配置
    public class helloAutoConfiguration {@ResourcehelloProperties properties;Logger logger = Logger.getLogger(this.getClass().getName());@Bean@ConditionalOnMissingBean//项目中没有该Bean,就返回这个Beanpublic TestService testService(){logger.info("自定义stater启动");logger.info("或取的默认属性值"+properties.getValue());return new TestService();}
    }
    
  4. 创建resourse/META-INF/spring.factories,与自己编写的autoConfiguration挂钩,spring会扫描spring.factories文件并自动配置

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\com.hello.helloAutoConfiguration
    

模块3:启动器(删除src,只进行包管理):spring-boot-starter-hello

    <!--导入自己写的配置--><dependency><groupId>com.example</groupId><artifactId>spring-boot-autoconfiguration-hello</artifactId><version>0.0.1-SNAPSHOT</version></dependency>

删除root项目pom.xml中的打包依赖,lifecycle/install安装到本地后,在另外的项目导入自编写starter

HashMap源码

定义HashMap,以及put/get方法

public class TestHashMap<K, V> {//数组容量final static int arrayCapacity = 8;//维护一个数组ArrayList<Map.Entry> arrayList = new ArrayList(arrayCapacity);public Map.Entry get(){return null;}public void put(K k, V v){return;}public void transform(){return;}public static void main(String[] args) {HashMap<Integer, String> integerStringHashMap = new HashMap<>();integerStringHashMap.put(1, "小明");integerStringHashMap.put(2, "小芳");System.out.println(integerStringHashMap.get(1));}
}

定义链表节点

    public class ListNode<K, V> implements Map.Entry<K, V>{K key;V value;ListNode<K, V> next;public ListNode(K key, V value, ListNode<K, V> next) {this.key = key;this.value = value;this.next = next;}// 方法必须重新,节点初始化后可能为空@Overridepublic K getKey() {return this.key;}@Overridepublic V getValue() {return this.value;}@Overridepublic V setValue(V value) {this.value = value;return this.value;}}

定义二叉树节点

//二叉树节点
class TreeNode<K, V> implements Map.Entry<K, V>{TreeNode<K, V> left;TreeNode<K,V> right;K key;V value;TreeNode(K key, V value, TreeNode<K, V> left, TreeNode<K, V> right){this.key = key;this.value = value;this.left = left;this.right = right;}// 方法必须重新,节点初始化后可能为空@Overridepublic K getKey() {return this.key;}@Overridepublic V getValue() {return this.value;}@Overridepublic V setValue(V value) {this.value = value;return this.value;}}

定义二叉树

//二叉树
class AVL<K, V> implements Map.Entry {int size=0;TreeNode head;public AVL(int size) {this.size = size;}public void insert(K k, V v){TreeNode newTreeNode = new TreeNode<>(k, v, null, null);if(this.size==0){head = newTreeNode;}else {TreeNode cur = head;TreeNode parent = head;while (cur!=null){if(cur.key.hashCode()<=newTreeNode.key.hashCode()){parent=cur;cur = cur.left;}else {parent = cur;cur = cur.right;}}if(parent.key.hashCode()>newTreeNode.key.hashCode()){parent.left=newTreeNode;}else {parent.right = newTreeNode;}}size++;return;}public V get(K k){if(size==0){return null;}TreeNode ptr = this.head;while (ptr!=null){if(ptr.key.hashCode()==k.hashCode()){return (V) ptr.value;} else if (ptr.key.hashCode()<k.hashCode()) {ptr=ptr.left;}else {ptr=ptr.right;}}return null;}@Overridepublic Object getKey() {return null;}@Overridepublic Object getValue() {return null;}@Overridepublic Object setValue(Object value) {return null;}
}

put方法,java源码中的put方法包含数组扩容(负载因子默认0.75)。当数组大小大于64,链表大于8时,将链表转换为红黑树。此处省略扩容,当链表长度大于8是就转换链表为红黑树。

public void put(K k, V v){ListNode newNode = new ListNode(k, v, null);//放入数组,如果该数组位置存在元素,使用链地址法int index = k.hashCode() % arrayCapacity; //放入的下标,java源码中是位运算if(arrayList.get(index) ==null){arrayList.set(index, newNode);}else {int flag = 0; //判断链表长度是否大于8ListNode head = (ListNode)arrayList.get(index);ListNode cur=head;while (cur.next!=null){cur = cur.next;flag++;// 大于8链表转换为红黑树(实际的java中HashMap是当数组大于64(负载因子参与扩容),链表大于8转换)if (flag>=8) {//如果是链表节点就,创建新二叉树if(arrayList.get(index).getClass()==AVL.class){AVL<K, V> avl = new AVL<>(0);cur = head;while (cur!=null){avl.insert((K)cur.key, (V)cur.value);}avl.insert(k, v);arrayList.set(index, avl);}//大于8,就已经转换为二叉树,只需要将元素插入元素到二叉树else {AVL<K, V> avl = (AVL<K, V>) arrayList.get(index);avl.insert(k, v);}return;}}cur.next = newNode;}return;}

get方法

public V get(K k){int index = k.hashCode()%arrayCapacity;Map.Entry head = arrayList.get(index);// 面向接口编程//链表查询if(head.getClass()==ListNode.class){ListNode listHead  = (ListNode)head;while (listHead!=null){if(listHead.key==k){return (V)listHead.value;}listHead = listHead.next;}return null;}//红黑树查询else {AVL avl = (AVL)head;return (V) avl.get(k);}}

ConcurrentHashMap源码

不再像1.7给segment上锁,而是给每个节点上锁,当添加元素时:

节点处为空:cas+valite创建节点

if(arrayList.get(index) ==null){//如果节点不为空,cas+volatile创建新节点ListNode newNode = new ListNode(k, v, null);do {//compareif(arrayList.get(index) ==null)//swaparrayList.set(index, newNode);break;}while (arrayList.get(index) ==null);
}

节点处不为空:锁住,添加元素

else synchronized (arrayList.get(index)){...}

源码系列(Spring/SpringBoot/HashMap)相关推荐

  1. Spring源码系列- Spring Beans - 核心类的基本介绍

    Spring源码系列- Spring Beans - 核心类的基本介绍 读过上一篇文章的读者应该都能对Spring的体系结构有一个大致的了解,在结尾处,我也说过会从spring-beans包开始分析, ...

  2. 死磕源码系列【springboot项目打印is not eligible for getting processed by all BeanPostProcessors (for example: n

    标题上打印的日志是info级别的,不影响程序正常使用,但是有代码洁癖的我还是看着不爽,总想找出问题所在,查了很久知道了是IOC容器注册Bean的时候使用到了还未注册到IOC容器中的Bean,也就是某一 ...

  3. 死磕源码系列【springboot之ConditionEvaluationReport记录报告和日志条件评估详情源码解析】

    ConditionEvaluationReport用来记录自动化配置过程中条件匹配的详细信息及日志信息: 1.ConditionOutcome类输出条件匹配及日志信息 public class Con ...

  4. 【spring源码系列-05】refresh中prepareRefresh方法的执行流程

    Spring源码系列整体栏目 内容 链接地址 [一]spring源码整体概述 https://blog.csdn.net/zhenghuishengq/article/details/13094088 ...

  5. Spring源码系列:依赖注入(二)createBean

    在Spring源码系列:依赖注入(一)(AbstractBeanFactory-getBean)最后说道getBean是依赖注入的起点,bean的创建都是通过createBean来完成具体的创建的.c ...

  6. Spring源码系列:BeanDefinition载入(下)

    在Spring源码系列:BeanDefinition载入(上)中已经大概捋了一下解析过程,本篇将记录一下bean的注册过程. bean的注册就是DefaultListableBeanFactory中r ...

  7. Ioc容器beanDefinition-Spring 源码系列(1)

    Ioc容器beanDefinition-Spring 源码系列(1) 目录: Ioc容器beanDefinition-Spring 源码(1) Ioc容器依赖注入-Spring 源码(2) Ioc容器 ...

  8. Spring源码系列(十二)Spring创建Bean的过程(二)

    1.写在前面 上篇博客主要Spring在创建Bean的时候,第一次调用的Bean的后置处理器的过程,同时笔者也打算将整个Spring创建的Bean的过程,通过这个系列,将Bean的创建过程给讲清楚,废 ...

  9. Spring读源码系列之AOP--03---aop底层基础类学习

    Spring读源码系列之AOP--03---aop底层基础类学习 引子 Spring AOP常用类解释 AopInfrastructureBean---免被AOP代理的标记接口 ProxyConfig ...

最新文章

  1. gcore java_获取一直FullGC下的java进程HeapDump的小技巧
  2. 脚踏实地,自强不息——清华大学大数据软件团队2021年度先进个人风采展示
  3. 在Linux上自动调整屏幕亮度保护眼睛
  4. 前端学习(2441):删除处理完成
  5. 二叉树 | 根据前序、后序生成中序
  6. python无人机路径规划算法_快速拓展随机树(RRT)路径规划,python
  7. AngularJS 监控对象属性:$watch和$digest
  8. 绝对路径中“./”、“../”、“../../”代表的含义,包你一看就会
  9. 人工智能与深度学习实战(4)——口罩佩戴识别(CNN)
  10. 智能实验室-全能优化(Guardio) 4.6.0.760
  11. 【转】多态与 new [C#]
  12. 算法学习(二)——Alpha-Beta剪枝算法
  13. java工作流引擎:jbpm和activiti对比分析
  14. 物联网解决方案:智慧物流方案
  15. STM32F401CCU6 核心板的功能描述(针对采集数据)
  16. CSS 中 a 标签为什么不能继承父类的颜色?
  17. Android10 系统应用wifi连接和静态ip代理设置
  18. 利用turtle库绘制五角星(以及填充)
  19. ThnBoV1.3.0一款针对WordPress开发的缩略图美化插件
  20. 常见面试算题题中的滑动窗口问题

热门文章

  1. Warning C206
  2. 【Acowdemia】 牛的学术圈I II III
  3. Tic-Tac-Toe人机对弈程序(python实现)
  4. 3GPP R16的Conditional handover 功能有啥优势?
  5. HTML布局之左右布局
  6. sip网络电话代码含义
  7. (02)Cartographer源码无死角解析-(50) 2D点云扫描匹配→相关性暴力匹配2:RealTimeCorrelativeScanMatcher2D
  8. Python爬虫 - 00.实现什么值得买签到
  9. Java java.lang.IllegalArgumentException: Illegal pattern character ‘‘问题解决
  10. (附源码)SSM驾校考试车预约管理系统 毕业设计