实现Spring底层机制(阶段1—编写自己的Spring容器,扫描包,得到bean的Class对象)

news2024/12/23 9:12:55

环境搭建+抛出问题

1.环境搭建

1.创建maven项目

image-20240221100240215

2.导入依赖
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>org.example</groupId>
  <artifactId>sun-spring</artifactId>
  <packaging>war</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>sun-spring Maven Webapp</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  <!--配置spring的基本包-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.3.8</version>
    </dependency>
  <!--加入spring开发切面编程需要的包-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>5.3.8</version>
    </dependency>
  </dependencies>
  <build>
    <finalName>sun-spring</finalName>
  </build>
</project>

3.maven的资源路径问题
类路径

image-20240221102805149

image-20240221103233329

4.IOC演示
1.文件目录

image-20240221103716087

2.UserAction.java
package com.sxs.spring.component;

import org.springframework.stereotype.Component;

/**
 * 这是一个Controller
 *
 * @author 孙显圣
 * @version 1.0
 */
@Component
public class UserAction {
}

3.UserDao.java
package com.sxs.spring.component;

import org.springframework.stereotype.Component;

/**
 * 这是一个dao
 *
 * @author 孙显圣
 * @version 1.0
 */
@Component
public class UserDao {
    public void hi() {
        System.out.println("UserDao-hi()");
    }
}

4.UserService.java
package com.sxs.spring.component;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * 这是一个Service
 *
 * @author 孙显圣
 * @version 1.0
 */
@Component
public class UserService {
    //自动装配
    @Autowired
    private UserDao userDao;
    public void m1() {
        userDao.hi();
    }
}

5.AppMain.java
package com.sxs.spring;

import com.sxs.spring.component.UserAction;
import com.sxs.spring.component.UserDao;
import com.sxs.spring.component.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author 孙显圣
 * @version 1.0
 */
public class AppMain {
    public static void main(String[] args) {
        ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
        UserAction action1 = ioc.getBean(UserAction.class);
        UserAction action2 = ioc.getBean(UserAction.class);
        System.out.println("action1=" + action1);
        System.out.println("action2=" + action2);
        UserService service = ioc.getBean(UserService.class);
        System.out.println("service=" + service);
        service.m1();
        UserDao userDao = ioc.getBean(UserDao.class);
        System.out.println("userDao=" + userDao);
    }
}

6.beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
    <!--配置自动扫描-->
    <context:component-scan base-package="com.sxs.spring.component"/>
</beans>
7.结果

image-20240221104039882

2.抛出问题

1.prototype怎么实现的?

image-20240221105154393

image-20240221105208460

2.Autowired怎么实现的?

image-20240221110223106

3.后置处理器是怎么实现的?
1.使用xml配置后置处理器
<!--配置后置处理器,就是反射创建了一个bean对象,也可以使用component注解创建-->
<bean class="com.sxs.spring.process.MyBeanPostProcessor" id="beanPostProcessor"/>
2.使用注解配置后置处理器
1.配置自动扫描
<!--如果使用注解来配置后置处理器,别忘了在这里配置自动扫描!!!-->
<context:component-scan base-package="com.sxs.spring.process"/>
2.MyBeanPostProcessor.java
package com.sxs.spring.process;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

/**
 * @author 孙显圣
 * @version 1.0
 */
//反射创建后置处理器的bean对象
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
    /**
     * 在bean的init初始化方法之前执行
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessBeforeInitialization被调用 " + beanName + "bean=" + bean.getClass());
        return bean;
    }

    /**
     * 在bean的init初始化方法之后执行
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("postProcessAfterInitialization被调用 " + beanName + "bean=" + bean.getClass());
        //注意:这里如果返回的是空,则还是使用的原来的bean
        return bean;
    }
}

4.Spring AOP是怎么实现的?
1.文件目录

image-20240221144148700

2.SmartAnimalable.java
package com.sxs.spring.aop;

/**
 * @author 孙显圣
 * @version 1.0
 */
public interface SmartAnimalable {
    float getSum(float i, float j);
    float getSub(float i, float j);
}

3.SmartDog.java
package com.sxs.spring.aop;

import org.springframework.stereotype.Component;

/**
 * @author 孙显圣
 * @version 1.0
 */
@Component
public class SmartDog implements SmartAnimalable{
    public float getSum(float i, float j) {
        float res = i + j;
        System.out.println("SmartDog-getSum=" + res);
        return res;
    }

    public float getSub(float i, float j) {
        float res = i - j;
        System.out.println("SmartDog-getSub=" + res);
        return res;
    }
}

4.SmartAnimalAspect.java
package com.sxs.spring.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

/**
 * @author 孙显圣
 * @version 1.0
 */
@Component
@Aspect
public class SmartAnimalAspect {
    @Pointcut(value = "execution(public float SmartDog.*(float, float)))")
    public void myPointCut() {
    }


    @Before(value = "myPointCut()")
    public void showBeginLog(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        System.out.println("SmartAnimalAspect-切面类showBeginLog()[使用的myPointCut()]-方法执行前-日志-方法名-" + signature.getName() + "-参数 "
                + Arrays.asList(joinPoint.getArgs()));
    }


    @AfterReturning(value = "myPointCut()", returning = "res")
    public void showSuccessEndLog(JoinPoint joinPoint, Object res) {
        Signature signature = joinPoint.getSignature();
        System.out.println("SmartAnimalAspect-切面类showSuccessEndLog()-方法执行正常结束-日志-方法名-" + signature.getName() + " 返回的结果是=" + res);
    }


    @AfterThrowing(value = "myPointCut()", throwing = "throwable")
    public void showExceptionLog(JoinPoint joinPoint, Throwable throwable) {
        Signature signature = joinPoint.getSignature();
        System.out.println("SmartAnimalAspect-切面类showExceptionLog()-方法执行异常-日志-方法名-" + signature.getName() + " 异常信息=" + throwable);
    }


    @After(value = "myPointCut()")
    public void showFinallyEndLog(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        System.out.println("SmartAnimalAspect-切面类showFinallyEndLog()-方法最终执行完毕-日志-方法名-" + signature.getName());
    }

}

5.beans.xml
    <!--扫描aop-->
    <context:component-scan base-package="com.sxs.spring.aop"/>
    <!--启动aop注解-->
    <aop:aspectj-autoproxy/>

image-20240221144422676

3.AOP与后置处理器的关系

1.介绍

image-20240221145441554

2.补充说明

这个SmartDog在init之后变成了代理对象,原因是这个类的方法被切面切入了,所以会通过后置处理器返回代理对象

image-20240221150632758

1.Spring整体架构分析 image-20240221153118515

2. 阶段1框架图

image-20240221153328319

3.二说类加载器

image-20240221153659576

image-20240221153636966

4.代码实现

1.环境搭建
1.创建新模块

image-20240221154656270

2.将新模块放在与当前模块并行的位置(location里自己看)

image-20240221160746860

image-20240221160828962

3.成功创建!

image-20240221160903342

4.修改语言级别

image-20240221160949169

5.解决启动错误

这个错误是版本问题

image-20240221172629107

image-20240221172803248

image-20240221173016328

2.文件目录

image-20240221173656748

3.自定义扫描注解,指定要扫描的包ComponentScan.java
package com.sun.spring.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 容器扫描的注解:通过value可以指定要扫描的包
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ComponentScan {
    String value() default "";
}

4.自定义Component注解,Component.java
package com.sun.spring.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 组件注解:指定要自动创建bean对象的类,还可以自定义value作为创建bean对象的id
 *
 * @author 孙显圣
 * @version 1.0
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
    String value() default "";
}

5.三个测试组件
1.Car.java
package com.sun.spring.component;

/**
 * @author 孙显圣
 * @version 1.0
 */
//没有加Component注解
public class Car {
}

2.MonsterDao.java
package com.sun.spring.component;

import com.sun.spring.annotation.Component;

/**
 * @author 孙显圣
 * @version 1.0
 */
@Component(value = "monsterDao")
public class MonsterDao {
}

3.MonsterService.java
package com.sun.spring.component;

import com.sun.spring.annotation.Component;

/**
 * @author 孙显圣
 * @version 1.0
 */

//自定义注解,自动反射创建bean对象,如果指定了value则id为value否则为首字母小写
@Component(value = "monsterService")
public class MonsterService {
}

6.自定义spring容器SunSpringApplicationContext.java
package com.sun.spring.ioc;

import com.sun.spring.annotation.Component;
import com.sun.spring.annotation.ComponentScan;


import java.io.File;
import java.net.URL;

/**
 * 类似于Spring原生的ioc容器
 *
 * @author 孙显圣
 * @version 1.0
 */
public class SunSpringApplicationContext {
    //传进来一个配置类的Class对象
    private Class configClass;

    //构造器,接收配置类的class对象
    public SunSpringApplicationContext(Class configClass) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        this.configClass = configClass;
        //一、获取要扫描的包
        //1.首先反射获取类的注解信息
        ComponentScan componentScan = (ComponentScan) this.configClass.getDeclaredAnnotation(ComponentScan.class);
        //2.通过注解来获取要扫描的包的路径
        String path = componentScan.value();
        System.out.println("要扫描的包=" + path);

        //二、得到要扫描包的.class文件对象,从而得到全路径进行反射
        //1.获取类加载器
        ClassLoader classLoader = SunSpringApplicationContext.class.getClassLoader();
        //2.获取要扫描包的真实路径,默认刚开始在根目录下
        path = path.replace(".", "/");
        URL resource = classLoader.getResource(path);
        //3.由该路径创建一个文件对象,可使用resource.getFile()将URL类型转化为String类型
        File file = new File(resource.getFile());
        //4.遍历该文件夹下的所有.class文件对象
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File f : files) {
                //反射注入容器
                //1.获取所有文件的绝对路径
                String absolutePath = f.getAbsolutePath();
                //只处理class文件
                if (absolutePath.endsWith(".class")) {
                    //2.分割出类名
                    String className = absolutePath.substring(absolutePath.lastIndexOf("\\") + 1, absolutePath.indexOf("."));
                    //3.得到全路径
                    String fullPath = path.replace("/", ".") + "." + className;
                    //4.判断是否需要注入容器,查看有没有自定义的注解Component
                    Class<?> aClass = classLoader.loadClass(fullPath);

                    //如果该类使用了注解Component则说明是一个spring bean
                    if (aClass.isAnnotationPresent(Component.class)) {
                        System.out.println("这是一个Spring bean=" + aClass);
                    } else {
                        System.out.println("这不是一个Spring bean=" + aClass);
                    }

                }
            }
        }
    }

    //返回容器中的对象
    public Object getBean(String name) {
        return null;
    }

}

7.自定义配置类(跟自定义扫描注解一起使用,相当于原来的配置文件)SunSpringConfig.java
package com.sun.spring.ioc;

import com.sun.spring.annotation.ComponentScan;

/**
 * 相当于原来的xml配置文件
 * @author 孙显圣
 * @version 1.0
 */
@ComponentScan(value = "com.sun.spring.component") //自定义注解:指定要扫描的包
public class SunSpringConfig {

}
8.启动类AppMain.java
package com.sun.spring;

import com.sun.spring.ioc.SunSpringApplicationContext;
import com.sun.spring.ioc.SunSpringConfig;

/**
 * @author 孙显圣
 * @version 1.0
 */
public class AppMain {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        //
        SunSpringApplicationContext ioc = new SunSpringApplicationContext(SunSpringConfig.class);

    }
}

9.执行结果

image-20240221174318016

5.当前阶段完成的任务

  • 自定义扫描注解和自定义配置类充当配置文件,value存储需要扫描的包
  • 自定义Component注解,用于指定需要自动创建bean对象的类
  • 自定义Spring容器,当容器启动时,创建容器对象传入容器的配置类的Class对象,容器通过构造方法,获取配置类的注解信息,得到要扫描的包的全路径,然后进行一些操作得到这个包下面的所有类的全路径

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

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

相关文章

RK3568 学习笔记 : u-boot 通过 tftp 网络更新 u-boot自身

前言 开发板型号&#xff1a; 【正点原子】 的 RK3568 开发板 AtomPi-CA1 使用 虚拟机 ubuntu 20.04 收到单独 编译 RK3568 u-boot 使用 rockchip Linux 内核的设备树 【替换】 u-boot 下的 rk3568 开发板设备树文件&#xff0c;解决 u-boot 下千兆网卡设备能识别但是无法 Pi…

MQTT服务器EMQX的安装和使用(Windows)

一、下载地址&#xff1a; 下载 EMQX 二、安装环境&#xff1a; Windows Server2016 16G 500G 三、启动服务&#xff1a; 下载文件解压后放入以下目录&#xff08;注意&#xff0c;目录名一定是英文&#xff0c;否则会造成启动不成功&#xff01;&#xff09;&#xff1a…

Linux部署Coturn以及关于打洞的思考

目录 Coturn介绍部署架构图 2.1 局域网——无NAT映射 2.2 NAT网Corturn安装步骤验证 4.1 局域网——无NAT映射 4.2 NAT网 4.2.1 Cywin安装步骤 4.2.2 Coturn安装步骤 4.2.3 验证引言 下文部署架构图为Corturn为解决互联网NAT环境下“找朋友”的部署架构,也是Coturn发挥其价值…

C# 给图片添加文字水印

目录 应用场景 开发运行环境 方法说明 方法代码 调用示例 小结 应用场景 在某些应用项目&#xff08;如电子档案信息管理&#xff09;中&#xff0c;查看电子图片信息是经常使用到的功能&#xff0c;此时我们就需要给显示在浏览器中的图片添加文字水印版权或提示信息。…

Oracle EBS Interface/API(54)- GL日记账审批

背景: 客户化创建薪酬凭证或者银企付款入账日记账以后,用户希望自动提交审批流程,无需到系统标准功能点击审批,减少用户操作。 快速参考 参考点内容功能导航N: GL->日记账->输入并发请求None基表GL.GL_JE_BATCHESAPI参考下面介绍错误信息表None接口FormNone接口Reque…

PLSQL数据库

目录 什么是PLSQL数据库 PL数据库的实现方法 PL数据库的基本语法 1.作用 2.语法 3.赋值输出 4.引用 5.异常处理 6.if 判断 7.loop循环 8.while循环 9.for循环 10.游标 11.参数游标 12.索引 13.分区表 什么是PLSQL数据库 PL/SQL&#xff08;Procedure Language/…

mysql虚拟列问题

1&#xff0c;虚拟列的用途 1.1&#xff0c;虚拟列可以在创建表时定义&#xff0c;并在查询时像普通列一样使用&#xff1b;方便查询数据&#xff1b;比如从复杂的列数据中&#xff0c;解析taskID&#xff0c;使用虚拟列就很方便&#xff1b; 1.2&#xff0c;mapvalue是txt类…

查理·芒格的 100 个思维模型是什么?一文弄懂多元思维模型!

查理芒格&#xff0c;全名查尔斯托马斯芒格&#xff0c;是一位美国投资者、商业人士以及慈善家。他最为人所知的身份是作为伯克希尔哈撒韦公司的副主席&#xff0c;与投资大师沃伦巴菲特共同塑造了该公司的投资风格和成功。芒格以其深厚的智慧、跨学科的思考方式和独到的价值投…

vivado 使用波形 ILA 触发器和导出功能

使用波形 ILA 触发器和导出功能 • 启用自动重新触发 &#xff1a; 选中“ Waveform ”窗口工具栏上的“启用自动重新触发 (Enable Auto Re-Trigger) ”按钮即可在 成功完成触发 上传 显示操作后 &#xff0c; 启用 Vivado IDE 以自动重新装备与“ Waveform ”窗口触发器…

自动化软件测试用例,自动化测试用例设计

&#x1f525; 交流讨论&#xff1a;欢迎加入我们一起学习&#xff01; &#x1f525; 资源分享&#xff1a;耗时200小时精选的「软件测试」资料包 &#x1f525; 教程推荐&#xff1a;火遍全网的《软件测试》教程 &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1…

书生·浦语大模型第二期实战营(5)笔记

大模型部署简介 难点 大模型部署的方法 LMDeploy 实践 安装 studio-conda -t lmdeploy -o pytorch-2.1.2conda activate lmdeploypip install lmdeploy[all]0.3.0模型 ls /root/share/new_models/Shanghai_AI_Laboratory/ln -s /root/share/new_models/Shanghai_AI_Laborato…

浏览器不兼容的问题和通用解决方案

大家好&#xff0c;我是咕噜铁蛋&#xff0c;今天我想和大家聊聊一个在我们日常上网过程中经常遇到的问题——浏览器不兼容。这个问题看似微小&#xff0c;但却常常让我们在浏览网页、使用在线应用时感到困扰。接下来&#xff0c;我将详细分析浏览器不兼容的原因&#xff0c;并…

【雪天鱼】Vitis HLS 学习笔记(1) Pipelining 流水线

文章目录 1 自媒体账号2 Pipelining 流水线2.1 只对最内层的 LOOP_J 循环进行 pipeline2.2 对最外层的 LOOP_I 循环进行 pipeline2.3 对函数 loop_pipeline 进行 pipeline&#xff0c;并对数组A进行分组2.4 思考 1 自媒体账号 目前运营的自媒体账号如下&#xff1a; 哔哩哔哩…

【uniapp】【uview2.0】【u-sticky】Sticky 吸顶

把pages.json文件中的 “navigationStyle"设置为"custom”, 出现的问题是&#xff0c;莫名奇妙多了个 一个高度 解决方法 /* 使用CSS的sticky定位 */ .sticky {/* #ifdef H5 */ position: -webkit-sticky;position: sticky;top: 0; /* 设置距顶部的距离 */z-ind…

《王者荣耀》Hello Kitty 小兵皮肤完整设置指南

王者荣耀与三丽鸥的联动活动上线了 Hello Kitty 小兵皮肤&#xff0c;让我们的峡谷小兵们也能穿上漂亮的衣服啦&#xff01;这款皮肤极具卡哇伊风格&#xff0c;引起了许多玩家的关注。许多小伙伴都想知道如何使用这款 Hello Kitty 小兵皮肤&#xff0c;今天小编将为大家整理出…

1081. 度的数量(@数位dp)

活动 - AcWing 求给定区间 [X,Y] 中满足下列条件的整数个数&#xff1a;这个数恰好等于 K 个互不相等的 B 的整数次幂之和。 例如&#xff0c;设 X15,Y20,K2,B2&#xff0c;则有且仅有下列三个数满足题意&#xff1a; 172420 182421 202422 输入格式 第一行包含两个整数 X…

恶意软件逆向工程:Ghidra 入门 -第三部分-分析 WannaCry 勒索软件

逆向工程是最受欢迎和最有价值的网络安全/信息安全技能之一。但很少有人能将自己的技能水平发展到精通这一备受追捧的技能。Ghidra 是美国间谍机构 NSA 提供的一种相对较新且免费的逆向工程工具。 在本教程中&#xff0c;我们将研究历史上最臭名昭著的一个恶意勒索病毒&#x…

使用docker部署数据可视化平台Metabase

目前公司没有人力开发数据可视化看板&#xff0c;因此考虑自己搭建开源可视化平台MetaBase。在此记录下部署过程~ 一、镜像下载 docker pull metabase/metabase:latest 运行结果如下&#xff1a; 二、创建容器 docker run -dit --name matebase -p 3000:3000\ -v /home/loc…

vulfocus靶场名称: apache-cve_2021_41773/apache-cve_2021_42013

Apache HTTP Server 2.4.49、2.4.50版本对路径规范化所做的更改中存在一个路径穿越漏洞&#xff0c;攻击者可利用该漏洞读取到Web目录外的其他文件&#xff0c;如系统配置文件、网站源码等&#xff0c;甚至在特定情况下&#xff0c;攻击者可构造恶意请求执行命令&#xff0c;控…

程序员缓解工作压力的小窍门

目录 1.概述 2.工作与休息的平衡 3.心理健康与自我关怀 4.社交与网络建设 1.概述 作为程序员&#xff0c;缓解工作压力是非常重要的。压力太大有可能会写很多bug。我们可以采取以下方法来保持高效和创新&#xff0c;同时维护个人健康和工作热情。 定时休息&#xff1a;保持…