【2】Spring手写模拟-依赖注入、初始化、AOP

news2024/11/17 16:32:00

在这里插入图片描述

首先回顾一下我们之前的流程ApplicationContext 构造方法

  • 获取Spring配置类@ComponetScan 注解,扫描包,获取其路径及其对应的字节码文件
  • 逐个扫描字节码文件
    • 利用字节码文件获取类,查看是否包含@Componet 注解,并获取或者生成BeanName
    • 获取BeanDefinition ,包含其类和类型(单例,多例)
    • 将其信息保存在beanDefinitionMap <String beanName, Class clazz>
  • 创建单例对象保存在singletonObjects <BeanName, Object>

getBean(String beanName) 方法

  • BeanDefinition 获取类信息
  • 如果是单例,尝试在singletonObjects 获取对象
    • 如果获取到的bean为空,在creatBean() // 后续说为什么为空
    • 返回获取或者创建的对象
  • 如果是多例,直接creatBean() ,返回创建的对象

creatBean(Class clazz)

返回创建的对象

完善Component

如果Component 不包含BeanName

在扫描扫描包的时候,发现含有Component 注解的时候,查看其value值,如果为空,我们用方法将其类名首字母小写为BeanName


    try {
        Class<?> clazz = classLoader.loadClass(className);
        if(clazz.isAnnotationPresent(Component.class)){
            // 获得注解value值 beanName
            Component component = clazz.getAnnotation(Component.class);
            String beanName = component.value();

            // component未赋值
            if("".equals(beanName)){
                beanName  = Introspector.decapitalize(clazz.getSimpleName());
            }




            BeanDefinition beanDefinition=new BeanDefinition();
            beanDefinition.setType(clazz);

            if(clazz.isAnnotationPresent(Scope.class)){
                Scope scope = clazz.getAnnotation(Scope.class);
                beanDefinition.setScope(scope.value());
            } else {
                beanDefinition.setScope("singleton");
            }
            beanDefinitionMap.put(beanName, beanDefinition);




        }
    } catch (ClassNotFoundException e) {
        throw new RuntimeException(e);
    }


Autoware依赖注入

我们需要实现依赖注入, @Autoware注解,在什么时候注入依赖呢

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)  // 用在变量声明上
public @interface Autoware {
}
@Scope(value = "prototype")
@Component
public class UserService {
    @Autoware
    OrderService orderService;
    public void  test(){
        System.out.println("UserService - test");
    }

}


我们应该在创建对象的时候,扫描其FIELD是否有@Autoware注解,如果包含其注解

    private Object creatBean(String beanName, BeanDefinition beanDefinition) {
        Class clazz=beanDefinition.getType();
        try {
            // 反射获取对象
            Object instance = clazz.getConstructor().newInstance();

            // 依赖注入
            for (Field f : clazz.getDeclaredFields()) {
                if(f.isAnnotationPresent(Autoware.class)){
                    f.setAccessible(true);
                    f.set(instance, getBean(f.getName())); // 通过获取其名字获取其对象 装载上去
                }
            }

            return instance;
        } 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);
        }

    }/

这里就解答了为什么当时

如果获取到的bean为空,在creatBean() // 后续说为什么为空

我们是逐个创建对象,有可能当前创建的对象依赖后面的还未创建的对象

Aware回调

对于UserService 想知道自己的BeanName

创建BeanNameAware 接口,包含setBeanName(String beanName) 方法

package com.yqyang.spring;

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

对于UserService 实现该接口

我们在creatBean() 时就能查看类是否instanceof 该接口,如果是可以回调UserService 实现的接口方法

    private Object creatBean(String beanName, BeanDefinition beanDefinition) {
        Class clazz=beanDefinition.getType();
        try {
            // 反射获取对象
            Object instance = clazz.getConstructor().newInstance();

            // 依赖注入
            for (Field f : clazz.getDeclaredFields()) {
                if(f.isAnnotationPresent(Autoware.class)){
                    f.setAccessible(true);
                    f.set(instance, getBean(f.getName()));
                }
            }

            // Aware回调
            if(instance instanceof BeanNameAware){
                ((BeanNameAware) instance).setBeanName(beanName);
            }



            return instance;
        } 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);
        }

    }

模拟Spring初始化机制

和上面差不多,定义InitializingBean 接口

package com.yqyang.spring;

public interface InitializingBean {
    public void afterPropertiesSet();
}

对于UserService 实现该接口

我们在creatBean() 时就能查看类是否instanceof 该接口,如果是可以回调UserService 实现的接口方法

    private Object creatBean(String beanName, BeanDefinition beanDefinition) {
        Class clazz=beanDefinition.getType();
        try {
            // 反射获取对象
            Object instance = clazz.getConstructor().newInstance();

            // 依赖注入
            for (Field f : clazz.getDeclaredFields()) {
                if(f.isAnnotationPresent(Autoware.class)){
                    f.setAccessible(true);
                    f.set(instance, getBean(f.getName()));
                }
            }

            // Aware回调
            if(instance instanceof BeanNameAware){
                ((BeanNameAware) instance).setBeanName(beanName);
            }

            // 初始化
            if (instance instanceof InitializingBean) {
                ((InitializingBean)instance).afterPropertiesSet();
            }



            return instance;
        } 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);
        }

    }

BeanPostProcessor

可能在初始化前后执行一些操作

定义BeanPostProcessor接口,返回值为Object为了后面的动态代理AOP

public interface BeanPostProcessor {
    public Object postProcessBeforeInitialization(String beanName, Object bean);
    public Object postProcessAfterInitialization(String beanName, Object bean);
}

我们需要在ApplicationContext添加变量

private ArrayList<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();

其获取是在扫描包时,获取实现该接口的对象,添加到beanPostProcessorList

try {
  Class<?> clazz = classLoader.loadClass(className);

  if (clazz.isAnnotationPresent(Component.class)) {

      if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
          BeanPostProcessor instance = (BeanPostProcessor) clazz.newInstance();
          beanPostProcessorList.add(instance);
      }

      Component component = clazz.getAnnotation(Component.class);
      String beanName = component.value();

      if (beanName.equals("")) {
          beanName = Introspector.decapitalize(clazz.getSimpleName());
      }


      BeanDefinition beanDefinition = new BeanDefinition();
      beanDefinition.setType(clazz);

      if (clazz.isAnnotationPresent(Scope.class)) {
          Scope scopeAnnotation = clazz.getAnnotation(Scope.class);
          beanDefinition.setScope(scopeAnnotation.value());
      } else {
          beanDefinition.setScope("singleton");
      }

      beanDefinitionMap.put(beanName, beanDefinition);

  }

creatBean() 初始化前后,遍历列表,获取各个对象执行方法

  • 这样如果有多个对象实现BeanPostProcessor ,也就是beanPostProcessorList 中有多个方法,创建一个对象的时候会导致各个实现该对象的方法重复执行,在实现接口的时候可以利用传入创建Bean的参数启到限制的作用,只让我们创建的对象执行。
    • beanName
    • instance
private Object createBean(String beanName, BeanDefinition beanDefinition){

        Class clazz = beanDefinition.getType();

        try {
            Object instance = clazz.getConstructor().newInstance();

            // 依赖注入
            for (Field f : clazz.getDeclaredFields()) {
                if (f.isAnnotationPresent(Autowired.class)) {
                    f.setAccessible(true);
                    f.set(instance, getBean(f.getName()));
                }
            }

            // Aware
            if (instance instanceof BeanNameAware) {
                ((BeanNameAware)instance).setBeanName(beanName);
            }

            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                instance = beanPostProcessor.postProcessBeforeInitialization(beanName, instance);
            }

            // 初始化
            if (instance instanceof InitializingBean) {
                ((InitializingBean)instance).afterPropertiesSet();
            }

            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                instance = beanPostProcessor.postProcessAfterInitialization(beanName, instance);
            }

            return instance;

        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

        return null;
    }

测试类-记得放在扫描包下

@Component
public class YqyangBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(String beanName, Object bean) {
       if (beanName.equals("userService")) {
           System.out.println("1111");
       }

       return bean;
    }

    @Override
    public Object postProcessAfterInitialization(String beanName, Object bean) {

        if (beanName.equals("userService")) {
            System.out.println("2222");
        }

        return bean;
    }
}

在这里插入图片描述

AOP

jdk实现动态代理需要接口,创建接口UserInterface

public interface UserInterface {

    public void test();
}

UserService 实现该接口

@Scope
@Component
public class UserService implements UserInterface {
    @Autoware
    OrderService orderService;
    public void  test(){
        System.out.println("UserService - test");
    }
}

在实现postProcessAfterInitialization 接口时,获取其动态代理对象,并且调用原来的方法。

@Component
public class ZhouyuBeanPostProcessor implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(String beanName, Object bean) {
       if (beanName.equals("userService")) {
           System.out.println("1111");
       }

       return bean;
    }

    @Override
    public Object postProcessAfterInitialization(String beanName, Object bean) {

        if (beanName.equals("userService")) {
            Object proxyInstance = Proxy.newProxyInstance(ZhouyuBeanPostProcessor.class.getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    System.out.println("代理逻辑");
                    return method.invoke(bean, args);
                }
            });
            return proxyInstance;
        }

        return bean;
    }
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/756918.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vv、vt 埋点上报自动化文档

vv、vt 埋点自动化文档 文章目录 vv、vt 埋点自动化文档一、项目简介二、环境搭建Airtest官方参考文档AirtestIDE下载Python环境安装Airtest安装安卓环境IOS 环境iOS-Tagent 编译 设置代理 三、框架介绍四、实现原理4.1 框架流程图4.2 初始化配置4.2.1 读取mock server配置&…

sqli-labs

目录 Less1 首先来爆字段 联合注入 判断注入点 爆数据库名 爆破表名 information_schema information_schmea.tables group_concat() 爆破列名 information_schema.columns 爆值 SQLMAP 主要对sqli-labs 的深入学习 Less1 我们先看看源代码 <?php //includ…

山西电力市场日前价格预测【2023-07-16】

日前价格预测 预测明日&#xff08;2023-07-16&#xff09;山西电力市场全天平均日前电价为395.36元/MWh。其中&#xff0c;最高日前电价为510.19元/MWh&#xff0c;预计出现在20: 00。最低日前电价为309.52元/MWh&#xff0c;预计出现在13: 15。 价差方向预测 1&#xff1a;实…

如何从零开始搭建公司自动化测试框架?

搭建的自动化测试框架要包括API测试&#xff0c;UI测试&#xff0c;APP测试三类。以上三类其实可以简化为两类&#xff0c;那就是&#xff1a; 1&#xff09;接口自动化测试框架搭建 2&#xff09;UI自动化测试框架搭建。 没问题&#xff0c;安排&#xff0c;且是手把手教你如何…

Next.js框架入门笔记

内置组件 ‘pages/_document.js’ 文件&#xff0c;自定义document DOC&#xff1a; https://www.nextjs.cn/docs/advanced-features/custom-document <Head>组件 <Head>是一个内置在 Next.js 中的 React 组件。它允许您修改页面的<head>。 Docs: https:/…

JVM理论(四)运行时数据区--堆/方法区

堆(heap) 堆内存逻辑上分为三部分 一个JVM实例只存在一个堆内存,JVM启动时创建堆区&#xff0c;通常情况下也是最大的内存空间&#xff0c;几乎所有的对象实例都要在堆中分配内存&#xff0c;所以堆也是垃圾回收的重点区域堆是被所有线程共享的,在堆里面也可以划分线程私有的缓…

肝!熬夜到天明,阿里顶配级 Spring Security 笔记

Spring Security Spring Security 是一个能够为基于 Spring 的企业应用系统提供声明式的安全访问控制解决方案的安全框架。由于它是 Spring 生态系统中的一员&#xff0c;因此它伴随着整个 Spring 生态系统不断修正、升级&#xff0c;在 spring boot 项目中加入 springsecurit…

Ubuntu下安装Miniconda

下载 到根据自己本地python版本到官网下载 https://docs.conda.io/en/latest/miniconda.html#linux-installers 我本地是python3.8 然后上传到Ubuntu服务器上&#xff0c;或者直接使用wget下载&#xff1a; wget https://repo.anaconda.com/miniconda/Miniconda3-py38_23.5…

WebSocket理论和实战

一 WebSocket理论 1.1 什么是http请求 http链接分为短链接、长链接&#xff0c;短链接是每次请求都要三次握手才能发送自己的信息。即每一个request对应一个response。长链接是在一定的期限内保持链接&#xff08;但是是单向的&#xff0c;只能从客户端向服务端发消息&#x…

单例模式、指令重排序、锁、有序性

今天在回顾单例模式时&#xff0c;我们都知道懒汉式单例中有一种叫做双重检查锁的单例模式。 我们来看下下面的代码有没有问题&#xff1a; 这段代码我们可以看到&#xff0c;即优化了性能&#xff0c;在多线程情况下&#xff0c;如果实例不为空了&#xff0c;则直接返回了。…

1766_perl实现readlines功能

全部学习汇总&#xff1a; GreyZhang/perl_basic: some perl basic learning notes. (github.com) 近段时间写一个Perl程序&#xff0c;中间反反复复用到了文件的读写。虽说是用Perl的基本功能实现读写非常简单&#xff0c;但是写的过程中我不止一次在想Python以及MATLAB的功能…

华为OD机试真题 Java 实现【评论转换输出】【2023 B卷 100分】,附详细解题思路

目录 专栏导读一、题目描述在这里插入图片描述二、输入描述三、输出描述四、解题思路五、Java算法源码六、效果展示1、输入2、输出 华为OD机试 2023B卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A…

什么是元学习?外循环和内循环?支持集和查询集?

一、前言 元学习近几年也算是一个比较热门的研究方向&#xff0c;大部分被用来解决低资源少样本零样本学习的任务场景中。 那么为什么元学习可以提升低资源少样本的学习效果呢&#xff1f;活着说元学习到底是一个什么阳的算法呢&#xff1f; 这里做一个简单的概念阐述。元学…

echart之map地图图表使用教程

本文以echarts展示成都地图为例子。 echarts map &#xff08;echarts地图&#xff09;使用教程 效果展示准备阶段获取地图geojson数据安装echarts 开始绘制容器准备js代码 补充事项vue3.0 用ref定义echarts报错toRaw、markRaw 扩展 地图隐藏南海诸岛地图显示提示框地图实现下钻…

Vue项目的启动

前言&#xff1a; 由于最近开始实习&#xff0c;负责人上来就给我丢一个前端vue项目和后端文件&#xff0c;让我在本机完成部署&#xff0c;由于之前学的基本上都是后端相关知识&#xff0c;很少有了解到前端的东西&#xff0c;因此在这里将自己部署Vue项目时遇到的问题和解决过…

编译libtiff库给IOS平台用

打开libtiff官方网 : libtiff / libtiff GitLab 克隆: git clone --recursive https://gitlab.com/libtiff/libtiff.git 克隆成功并打开libtiff目录,发现有autogen.sh 与CMakeLists.txt所以可生成Configure程序来配置并编译,也可直接使用CMake-GUI来配置编译,选择其中一种 …

远程会诊如何实现?

比如&#xff1a;医生遇到复杂病情需要求助院外专家远程会诊过程中&#xff0c;需要将电脑中的病人资料给院外专家看&#xff0c;同时确保医院电脑和网络系统绝对安全&#xff0c;电脑不允许安装任何外部软件&#xff0c;不能被外人控制和操作&#xff0c;外部设备不能接入医院…

【Java技术专题】「攻破技术盲区」带你攻破你很可能存在的Java技术盲点之技术功底指南(鲜为人知的技术)

带你攻破你很可能存在的Java技术盲点之技术功底指南 基本类型的包装类技术盲点&#xff1a;基本类型的比较技术盲点&#xff1a;字符串内部化&#xff08;string interning&#xff09;字符串内部化的示例 技术盲点&#xff1a;类型缓存机制&#xff08;空间换时间&#xff09;…

微信小程序border-radius不圆滑

border-radius可以设置&#xff1a;百分比或者像素值 1.使用像素值比较圆滑 2.使用百分比不够圆滑

习题1.25

对吗?实践出真知,运行看看。代码如下。 (defn square [x] (* x x))(defn fast-expt[b n](println "call iter" n)(cond (= 1 n) b(= 2 n) (square b)(even? n) (square (fast-expt b (/ n 2))):else (* b (fast-expt b (- n 1)))))(defn expmod [base exp m](mod…