源码系列(Spring/SpringBoot/HashMap)
SpringBoot源码
效果:加入springBoot注解后,在run方法中传入application.class就能实现自动启动并匹配到对象的controller返回结果。
导入Spring依赖
定义注解(在自己的应用Apllication加入该注解,作为SpringBoot项目启动),注解上加入@ComponentScan。当应用添加后,相当于添加@ComponentScan注解在自己的类上,就会默认扫描自己包下的所有文件,就可以扫描到controller文件夹下的文件。
自定义Run方法,并在其中启动tomcat
添加DispatchServlet(SpringMVC),需要接受一个spring容器,因为DispathServlet是在该容器中找对应的Controller.
添加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
导入辅助依赖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>
配置属性编写,导入该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;} }
配置类编写,可以返回创建好的对象
@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();} }
创建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)相关推荐
- Spring源码系列- Spring Beans - 核心类的基本介绍
Spring源码系列- Spring Beans - 核心类的基本介绍 读过上一篇文章的读者应该都能对Spring的体系结构有一个大致的了解,在结尾处,我也说过会从spring-beans包开始分析, ...
- 死磕源码系列【springboot项目打印is not eligible for getting processed by all BeanPostProcessors (for example: n
标题上打印的日志是info级别的,不影响程序正常使用,但是有代码洁癖的我还是看着不爽,总想找出问题所在,查了很久知道了是IOC容器注册Bean的时候使用到了还未注册到IOC容器中的Bean,也就是某一 ...
- 死磕源码系列【springboot之ConditionEvaluationReport记录报告和日志条件评估详情源码解析】
ConditionEvaluationReport用来记录自动化配置过程中条件匹配的详细信息及日志信息: 1.ConditionOutcome类输出条件匹配及日志信息 public class Con ...
- 【spring源码系列-05】refresh中prepareRefresh方法的执行流程
Spring源码系列整体栏目 内容 链接地址 [一]spring源码整体概述 https://blog.csdn.net/zhenghuishengq/article/details/13094088 ...
- Spring源码系列:依赖注入(二)createBean
在Spring源码系列:依赖注入(一)(AbstractBeanFactory-getBean)最后说道getBean是依赖注入的起点,bean的创建都是通过createBean来完成具体的创建的.c ...
- Spring源码系列:BeanDefinition载入(下)
在Spring源码系列:BeanDefinition载入(上)中已经大概捋了一下解析过程,本篇将记录一下bean的注册过程. bean的注册就是DefaultListableBeanFactory中r ...
- Ioc容器beanDefinition-Spring 源码系列(1)
Ioc容器beanDefinition-Spring 源码系列(1) 目录: Ioc容器beanDefinition-Spring 源码(1) Ioc容器依赖注入-Spring 源码(2) Ioc容器 ...
- Spring源码系列(十二)Spring创建Bean的过程(二)
1.写在前面 上篇博客主要Spring在创建Bean的时候,第一次调用的Bean的后置处理器的过程,同时笔者也打算将整个Spring创建的Bean的过程,通过这个系列,将Bean的创建过程给讲清楚,废 ...
- Spring读源码系列之AOP--03---aop底层基础类学习
Spring读源码系列之AOP--03---aop底层基础类学习 引子 Spring AOP常用类解释 AopInfrastructureBean---免被AOP代理的标记接口 ProxyConfig ...
最新文章
- gcore java_获取一直FullGC下的java进程HeapDump的小技巧
- 脚踏实地,自强不息——清华大学大数据软件团队2021年度先进个人风采展示
- 在Linux上自动调整屏幕亮度保护眼睛
- 前端学习(2441):删除处理完成
- 二叉树 | 根据前序、后序生成中序
- python无人机路径规划算法_快速拓展随机树(RRT)路径规划,python
- AngularJS 监控对象属性:$watch和$digest
- 绝对路径中“./”、“../”、“../../”代表的含义,包你一看就会
- 人工智能与深度学习实战(4)——口罩佩戴识别(CNN)
- 智能实验室-全能优化(Guardio) 4.6.0.760
- 【转】多态与 new [C#]
- 算法学习(二)——Alpha-Beta剪枝算法
- java工作流引擎:jbpm和activiti对比分析
- 物联网解决方案:智慧物流方案
- STM32F401CCU6 核心板的功能描述(针对采集数据)
- CSS 中 a 标签为什么不能继承父类的颜色?
- Android10 系统应用wifi连接和静态ip代理设置
- 利用turtle库绘制五角星(以及填充)
- ThnBoV1.3.0一款针对WordPress开发的缩略图美化插件
- 常见面试算题题中的滑动窗口问题
热门文章
- Warning C206
- 【Acowdemia】 牛的学术圈I II III
- Tic-Tac-Toe人机对弈程序(python实现)
- 3GPP R16的Conditional handover 功能有啥优势?
- HTML布局之左右布局
- sip网络电话代码含义
- (02)Cartographer源码无死角解析-(50) 2D点云扫描匹配→相关性暴力匹配2:RealTimeCorrelativeScanMatcher2D
- Python爬虫 - 00.实现什么值得买签到
- Java java.lang.IllegalArgumentException: Illegal pattern character ‘‘问题解决
- (附源码)SSM驾校考试车预约管理系统 毕业设计