IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 源码系列(Spring/SpringBoot/HashMap) -> 正文阅读

[Java知识库]源码系列(Spring/SpringBoot/HashMap)

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)) {
//获取BeanName
    Component 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();
        //依赖注入:找所有属性-》哪些有@Autowired
        for (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 {
    @Autowired
    private OrderService orderService;
    
    String BeanName;
    public void test(){
        System.out.println(orderService);
    }

    @Override
    public void setBeanName(String name) {
        //该方法中会传入name, 就可以用传入的name
        this.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 {
    @Autowired
    private OrderService orderService;

    public String BeanName;
    public void test(){
        System.out.println(orderService);
    }

    @Override
    public void setBeanName(String name) {
        //该方法中会传入name, 就可以用传入的name
        this.BeanName = name;

    }

    @Override
    public 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 {
    @Override
    public Object postProcessorBeforeInitializing(Object bean, String beanName) {
        System.out.println("初始化前");
        //只针对某个Bean处理需要判断名字
        if (beanName.equals("userService")) {
            ((UserService)bean).setName("小狗");
        }
        return bean;
    }

    @Override
    public Object postProcessorAfterInitializing(Object bean, String beanName) {
        System.out.println("初始化后");
        return bean;
    }
}

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

  //判断当前clazz是否实现了BeanPostProcessor接口,是就存入List
                        if (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 {
        @Resource
        helloProperties properties;
    
        Logger logger = Logger.getLogger(this.getClass().getName());
    
        @Bean
        @ConditionalOnMissingBean//项目中没有该Bean,就返回这个Bean
        public 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;
        }
        // 方法必须重新,节点初始化后可能为空
        @Override
        public K getKey() {
            return this.key;
        }

        @Override
        public V getValue() {
            return this.value;
        }

        @Override
        public 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;
        }

        // 方法必须重新,节点初始化后可能为空
        @Override
        public K getKey() {
            return this.key;
        }

        @Override
        public V getValue() {
            return this.value;
        }

        @Override
        public 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;
    }

    @Override
    public Object getKey() {
        return null;
    }

    @Override
    public Object getValue() {
        return null;
    }

    @Override
    public 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; //判断链表长度是否大于8
            ListNode 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 {
        //compare
        if(arrayList.get(index) ==null)
        //swap
        arrayList.set(index, newNode);
        break;
    }while (arrayList.get(index) ==null);
}

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

else synchronized (arrayList.get(index)){...}
  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-10-08 20:27:04  更:2022-10-08 20:31:20 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年3日历 -2025/3/10 17:16:20-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码