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

环境搭建+抛出问题

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 {//自动装配@Autowiredprivate 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());//注意:这里如果返回的是空,则还是使用的原来的beanreturn 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.判断是否需要注入容器,查看有没有自定义的注解ComponentClass<?> aClass = classLoader.loadClass(fullPath);//如果该类使用了注解Component则说明是一个spring beanif (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.hqwc.cn/news/640010.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

leetcode每日一题第五十六天

今天心情不好&#xff0c;就做一道 class Solution { public:int countTarget(vector<int>& scores, int target) {return help(scores,target)-help(scores,target-1);}int help(vector<int>&scores,int target){int left 0;int right scores.size()-1;…

13-LINUX--消息队列

一.消息队列 1.消息队列&#xff1a;消息队列为一个进程向另一个进程发送一个数据块提供了条件&#xff0c;每个数据块会包含一个类型。 2.相关函数 1>.msgget(key_t key,int msgflg) : 创建消息队列 2>. msgsnd&#xff1a;把消息添加到消息队列 3>.msgrcv &#xf…

新手小白,在数学建模的过程中应该怎么分工?

大家知道&#xff0c;数学建模竞赛是需要一个团队的三个人在三天或四天的时间内&#xff0c;完成模型建立&#xff0c;编程实现和论文写作的任务&#xff0c;对许多第一次参加建模或者建模经验比较欠缺的团队来说&#xff0c;是时间紧任务重的&#xff0c;那么怎么办呢&#xf…

Vue 3 路由机制详解与实践

一、路由的理解 路由是指导用户界面导航的一种机制。它通过映射 URL 到应用程序的不同视图组件来实现页面间的切换和导航。 二、路由基本切换效果 路由基本切换效果指的是当用户在应用程序中进行页面导航时&#xff0c;通过路由可以实现页面的切换&#xff0c;从而展示不同的…

cookie与session区别和联系

在Web应用中&#xff0c;HTTP协议是无状态的&#xff0c;每次请求都是独立的&#xff0c;服务器无法直接识别一个用户的不同请求之间的关联。这就导致了如果我们希望在一个会话中保持一些数据的状态&#xff0c;比如用户的身份认证信息、购物车内容等&#xff0c;就需要借助Coo…

【昇腾产品应用】英码科技EA500I基于昇腾Mind SDK实现实时人体关键点检测

在教育、体育、安防、交通、医疗等领域中&#xff0c;实时人体关键点检测应用发挥着至关重要的作用&#xff0c;比如在体育训练时&#xff0c;实时人体关键点检测可以精确、实时地捕捉运动员的动作&#xff0c;从而进行动作分析和优化&#xff1b;在安防应用场景中&#xff0c;…

vector的底层与使用

前言&#xff1a;vector是顺序表&#xff08;本质也是数组&#xff09; 文档参考网站&#xff1a;https://legacy.cplusplus.com/reference/vector/vector/vector/ //底层代码 #include<assert.h> #include<iostream> #include<vector> #include<string&g…

Kafak详解(1)

简介 消息队列 为什么要有消息队列 图-1 消息队列的使用 消息队列 1)消息Message&#xff1a;网络中的两台计算机或者两个通讯设备之间传递的数据。例如说&#xff1a;文本、音乐、视频等内容。 2)队列Queue&#xff1a;一种特殊的线性表(数据元素首尾相接)&#xff0c;特…

echarts折线图默认不显示数据圆点,鼠标划上之后折线图才显示圆点

只需要设置showSymbol为false就可以了&#xff0c;表示只在 tooltip hover 的时候显示。 代码如下&#xff1a; option {tooltip: {trigger: axis},xAxis: {type: category,data: [Mon, Tue, Wed, Thu, Fri, Sat, Sun]},yAxis: {type: value},series: [{data: [150, 230, 224…

什么是防抖和节流?有什么区别? 如何实现?

防抖&#xff08;Debounce&#xff09;和节流&#xff08;Throttle&#xff09;是两种常用的技术手段&#xff0c;主要用于控制某个函数在一定时间内触发的次数&#xff0c;以减少触发频率&#xff0c;提高性能并避免资源浪费。 防抖&#xff08;Debounce&#xff09;的工作原…

图像哈希:GLCM+DCT

文章信息 作者&#xff1a;Ziqing Huang期刊&#xff1a;IEEE&#xff08;一区&#xff09;题目&#xff1a;Perceptual Image Hashing with Texture and Invariant Vector Distance for Copy Detection 目的、实验步骤及结论 目的&#xff1a;使用GLCM进行全局特征的提取&am…

Python 数据库简化操作:dataset 库介绍

文章目录 Python 数据库简化操作&#xff1a;dataset 库介绍第一部分&#xff1a;背景介绍第二部分&#xff1a;库是什么&#xff1f;第三部分&#xff1a;如何安装这个库&#xff1f;第四部分&#xff1a;库函数使用方法第五部分&#xff1a;场景应用第六部分&#xff1a;常见…