Spring源码搭建及相关知识点记录


Gradle安装

安装示例

allprojects {
    repositories {
        maven { url 'file:///D:/develop/IDE-Repository'}
        mavenLocal()
        maven { name "Alibaba" ; url "https://maven.aliyun.com/repository/public" }
        maven { name "Bstek" ; url "https://nexus.bsdn.org/content/groups/public/" }
        mavenCentral()
    }

    buildscript { 
        repositories { 
		maven { url 'https://maven.aliyun.com/repository/google'}
        maven { url 'https://maven.aliyun.com/repository/jcenter'}
        maven { url 'https://maven.aliyun.com/nexus/content/groups/public'}
        }
    }
}

repositories中配置获取依赖jar包的顺序:
先是从本地maven仓库中获取
然后mavenLocal()是获取maven本地仓库的路径,和第一条一样,但是不冲突
第三条第四条分别为国内alibaba镜像仓库和国外bstek镜像仓库
最后mavenCentral()是从apache提供的中央仓库中获取依赖jar包

Spring源码下载

官方下载地址

Spring对应DK版本:

  • Spring Framework 6.0.x:JDK 17-21(预期)
  • Spring Framework 5.3.x:JDK 8-19(预期)
  • Spring 框架 5.2.x:JDK 8-15
  • Spring 框架 5.1.x:JDK 8-12
  • Spring 框架 5.0.x:JDK 8-10
  • Spring Framework 4.3.x:JDK 6-8

修改Spring源码中Gradle配置

阿里镜像配置

在下载的Spring源码下修改gradle/wrapper/gradle-wrapper.properties文件如下:

distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
## distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
## 配置本地gradle, 配置之后需要按下例配置gradle的中央仓库(阿里云maven中央仓库)
distributionUrl=file:///D:/develop/IDE-Gradle/gradle-7.3.1-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

或者distributionUrl配置为distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-7.3-bin.zip

修改Spring源码根目录下build.gradle文件中的repositories:

repositories {
         // 配置本地maven仓库
         mavenLocal()
         // 配置阿里云maven仓库
         maven { url "https://maven.aliyun.com/nexus/content/groups/public/" }
         maven { url "https://maven.aliyun.com/nexus/content/repositories/jcenter/" }
         // maven中央仓库
         mavenCentral()
         maven { url "https://repo.spring.io/libs-spring-framework-build" }
     }

修改Spring源码根目录下settings.gradle文件中pluginManagement下的repositories:

pluginManagement {
	repositories {
		// 配置阿里云 maven 中央仓库
		maven { url 'https://maven.aliyun.com/repository/public/' }
		gradlePluginPortal()
		maven { url 'https://repo.spring.io/plugins-release/' }
	}
}

如果仓库地址不为HTTPS,则需要在仓库地址后加 ‘allowInsecureProtocol=true’:

maven { 
	url 'http://maven.aliyun.com/repository/public/'
	allowInsecureProtocol=true 
}

构建Spring源码

Spring源码所在的文件目录下使用cmd执行 gradlew :spring-oxm:compileTestJava

如某个jar包没下载成功等,只需要重新执行gradlew :spring-oxm:compileTestJava再次进行预编译就行了。

构建完成之后修改根目录下setting.gradle文件,注释掉spring-aspects:

....
include "spring-aop"
// 移除aspects
// include "spring-aspects"
include "spring-beans"
include "spring-context"
....

Spring源码目录下有 import-into-eclipse.mdimport-into-idea.md两个文档,分别对应eclipse导入和Idea导入步骤,可参考。

导入IDEA

使用 IDEA 选择源码路径下build.gradle文件导入。

  • 点击File | Settings | Build, Execution, Deployment | Build Tools | Gradle,配置IDEA的Gradle配置:配置Gradle user home为自己Gradle本地仓库目录

  • Ctrl+Alt+Shift+S快捷键打开Project Structure。修改工程的SDK,分别将Project、Modules、SDKs中的JDK设置为本地安装的JDK(版本为1.8及以上)。

  • Ctrl+Alt+Shift+S快捷键打开Project Structure。在Modules中排除spring-aspects:

  • Alt+F12快捷键打开Terminal,使用gradlew.bat命令进行编译

编译成功后会出现BUILD SUCCESSFUL的提示。如果有报错根据报错信息进行处理,多编译几次即可。

使用shift+shift快捷键输入ApplicationContext类,使用Ctrl+Shift+Alt+U如果能够成功打开类图,也证明Spring源码构建成功

创建Spring源码debug调试模块

项目右键新建Gradle模块,名称随意,这里取spring-gabriel

修改新建模块build.gradle文件,导入依赖和配置仓库地址 :

init.d配置 :

repositories {

    // 配置本地 maven 仓库
    mavenLocal()
    // 配置阿里云 maven 仓库
    maven { url 'https://maven.aliyun.com/nexus/content/groups/public/' }
    maven { url 'https://maven.aliyun.com/nexus/content/repositories/jcenter' }

    mavenCentral()
}

dependencies {
   
    // 测试需要依赖
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0'
    //导入 spring-context模块, 包含bean工厂
    implementation(project(':spring-context'))
    //导入spring-instrument 模块, 此模块为 spring-context 模块编译所必须的
    implementation(project('::spring-instrument'))
    //compile和testCompile已经被gradle 7.x 弃用
    //testCompile group: 'junit', name: 'junit', version: '4.12'
    //compile(project(":spring-context"))

}

test {
    useJUnitPlatform()
}

IDEA默认新建模块后默认Gradle配置名称为build.gradle,需要手动修改为spring-gabriel.gradle

搭建博客及AspectJ搭建

spring模块

spring-core
spring-beans
spring-context:以core和beans模块为基础构建,提供上下文的构建
spring-context-support:整合第三方库
spring-expression:提供表达式支持
spring-aop:切面模块
spring-aspects:提供AspetJ的集成
spring-instrument:类加载器的实现
spring-instrument-tomcat:模块包含了支持Tomcat的植入代理
spring-messaging:用于消息转递,类似于基于Spring MVC注释的编程模型
spring-jdbc:模块提供了一个JDBC –抽象层
spring-tx:模块支持用于实现特殊接口和所有POJO(普通Java对象)的类的编程和声明式事务 管理
spring-orm:模块为流行的对象关系映射(object-relational mapping )API提供集成层,包括JPA和Hibernate。使用spring-orm模块,您可以将这些O / R映射框架与Spring提供的所有其他功能结合使用,例如前面提到的简单声明性事务管理功能
spring-oxm:模块提供了一个支持对象/ XML映射实现的抽象层
spring-jms:模块(Java Messaging Service) 包含用于生产和消费消息的功能。
spring-web:模块提供基本的面向Web的集成功能,例如多部分文件上传功能,以及初始化一个使用了Servlet侦听器和面向Web的应用程序上下文的IoC容器。它还包含一个HTTP客户端和Spring的远程支持的Web相关部分。
spring-webmvc:模块(也称为Web-Servlet模块)包含用于Web应用程序的Spring的模型-视图-控制器(MVC)和REST Web Services实现。

相关错误解决

错误集合
启动相关错误


 class jdk.internal.loader.ClassLoaders$PlatformClassLoader cannot be cast to class java.net.URLClassLoader (jdk.internal.loader.ClassLoaders$PlatformClassLoader and java.net.URLClassLoader are in module java.base of loader 'bootstrap') xxx

解决 : PlatformClassLoader 是JDK9+才有的类,降低Gradle对应的jdk版本至1.8即可(不是项目代码JDK)

在这里插入图片描述


错误: 程序包jdk.jfr.Xxx不存在
	......
Task :spring-core:compileJava FAILED

解决

Error:Kotlin: Incompatible classes were found in dependencies. Remove them from the classpath or use '-Xskip-metadata-version-check' to suppress errors

在这里插入图片描述

将Idea的Kotlin插件禁用

切面源码

Spring中有两种切面,一种是被@Aspect标注的类,另一种是实现Advisor的类:

@Aspect创建示例略。

实现Advisor示例,这里以实现Advisor的子接口PointcutAdvisor为例 :

创建切面实现类MethodInterceptor

public class CustomMethodInterceptor implements MethodInterceptor {

	Random random = new Random();

	@Override
	public Object invoke(MethodInvocation methodInvocation) throws Throwable {

		System.out.println("-----CustomMethodInterceptor--------");

		String name = methodInvocation.getMethod().getName();
		Class<? extends Method> aClass = methodInvocation.getMethod().getClass();
		Object returnValue = methodInvocation.proceed();//执行目标真实方法
		if (returnValue == null){
			return returnValue;
		}

		String valueStr = String.valueOf(returnValue);
		String[] split = valueStr.split(";");
		int nextInt = random.nextInt(split.length);
		String finalStr = split[nextInt];

		return finalStr;
	}
}

创建MethodMatcher

public class CustomMethodMatcher implements MethodMatcher {

	@Override
	public boolean matches(Method method, Class<?> aClass) {
		//Method specificMethod = AopUtils.getMostSpecificMethod(method, aClass);

		Class<?>[] interfaces = aClass.getInterfaces();
		for (Class<?> anInterface : interfaces) {
			boolean assignableFrom = ConfigurableEnvironment.class.isAssignableFrom(anInterface);
			if(assignableFrom){
				return true;
			}
		}
		//boolean res = specificMethod.isAnnotationPresent(Hero.class);

		return false;
	}

	@Override
	public boolean isRuntime() {
		return false;
	}

	@Override
	public boolean matches(Method method, Class<?> aClass, Object... objects) {
		return false;
	}
}

创建Pointcut

public class CustomPointCut implements Pointcut {
	@Override
	public ClassFilter getClassFilter() {
		return ClassFilter.TRUE;
	}

	@Override
	public MethodMatcher getMethodMatcher() {
		return new CustomMethodMatcher();
	}
}

创建PointcutAdvisor 并注册进容器:

@Component
public class CustomAdvisor implements PointcutAdvisor {

	@Override
	public Pointcut getPointcut() {
		return new CustomPointCut();
	}

	@Override
	public Advice getAdvice() {
		return new CustomMethodInterceptor();
	}

	@Override
	public boolean isPerInstance() {
		return false;
	}
}

到此完成一个低级的切面.

这里的高级和低级并不代表其功能强弱,而是低级切面比较适合框架内部使用,而高级切面比较适合编码开发使用。因为低级切面的功能比较基本。

@Aspect切面里面可以包含一组或多组通知与切面;而Advisor仅支持一组通知和切面。

@Aspect虽然是一种高级切面,但是Spring处理这种高级切面的时候,依然会把高级切面转化成低级切面。因为只有转化为低级切面才能被Spring内部所使用。


@Aspect等相关注解解析是由AnnotationAwareAspectJAutoProxyCreator代理创建器来进行解析的,将高级切面转换成低级切面,用来处理被@AspectJ注解标注的切面类和Spring Advisors的。

这里面有两个比较重要的方法:

  • findEligibleAdvisors:这个方法是用来找有资格的Advisors,这里说的有资格的Advisor一部分是低级切面,一部分是高级切面
  • wrapIfNecessary:其内部调用的findEligibleAdvisors, 只要返回集合不空, 则表示需要创建代理

高级切面就转为低级切面流程 :

以@Before为例

  • 首先遍历Aspect类中所有方法

  • 接着判断方法上是否有@Before注解
    如果有,则通过method.getAnnotation(Before.class).value()获取@Before的值,新建一个切点AspectJExpressionPointcut,给这个切点设置表达式pointcut.setExpression(expression);

  • 最后需要一个通知类AspectJMethodBeforeAdvice.
    新建这个通知类需要三个参数,第一个是方法对象,第二个是切点,第三个是切面实例工厂。

    • 切面实例工厂也就是指new SingletonAspectInstanceFactory(new Aspect())

    • 最后new一个低级切面new DefaultPointcutAdvisor(pointcut, advice)

这样高级切面就转为低级切面了;

代码示例 :

public class Example {

	public static void main(String[] args) throws Throwable {

		AspectInstanceFactory factory = new SingletonAspectInstanceFactory(new MyAspect());

		// 高级切面转低级切面类
		List<Advisor> list = new ArrayList<>();
		for (Method method : Aspect.class.getDeclaredMethods()) {
			if (method.isAnnotationPresent(Before.class)) {
				// 解析切点
				String expression = method.getAnnotation(Before.class).value();
				AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
				pointcut.setExpression(expression);
				// 通知类
				AspectJMethodBeforeAdvice advice = new AspectJMethodBeforeAdvice(method, pointcut, factory);
				// 切面
				Advisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
				list.add(advisor);
			}
		}
		for (Advisor advisor : list) {
			System.out.println(advisor);
		}

	}

	static class MyAspect {
		@Before("execution(* foo())")
		public void before1() {
			System.out.println("before1");
		}

		@Before("execution(* foo())")
		public void before2() {
			System.out.println("before2");
		}

		@After("execution(* foo())")
		public void after() {
			System.out.println("after");
		}

		@AfterReturning("execution(* foo())")
		public void afterReturning() {
			System.out.println("afterReturning");
		}

		@AfterThrowing("execution(* foo())")
		public void afterThrowing() {
			System.out.println("afterThrowing");
		}

		@Around("execution(* foo())")
		public Object around(ProceedingJoinPoint pjp) throws Throwable {
			try {
				System.out.println("around...before");
				return pjp.proceed();
			} finally {
				System.out.println("around...after");
			}
		}
	}
	
	// 目标代理类
	static class Target {
		public void foo() {
			System.out.println("target foo");
		}
	}


}

Spring的启动流程

  1. 准备环境对象,事件监听者。
  2. 加载Spring配置文件。即读入并解析xml配置文件,构建初始Spring IOC容器。
  3. 对创建的Spring IOC容器进行一些初始化配置,设置忽略接口,注册环境上下文。
  4. 调用Spring IOC容器的postProcessBeanFactory,留给子类实现。
  5. 实例化并执行BeanFactoryPostProcessor相关接口,包括解析配置类。并将BeanFactoryPostProcessor相关类放入特定容器,@Configuration的扫描及配置在此。
  6. 实例化并注册所有BeanPostProcessor进容器类。
  7. 初始化MessageSource,用于支持国际化。
  8. 初始化事件广播器。
  9. 注册监听者。
  10. 实例化和初始化IOC容器中剩下的所有Bean。在初始化Bean时,Spring 根据Bean的定义以及配置信息,实现对Bean的实例化、属性赋值、以及初始化等操作。

完成IoC容器的准备工作。所有单例的Bean都已经被实例化、初始化并装配到容器中后,容器的准备工作就完成了,此时Spring框架已经可以对外提供服务。

  1. 执行定制化的后置处理器。Spring容器中可能会存在一些实现了BeanPostProcessor接口的定制化组件。这些组件会参与到IoC容器中Bean的生命周期过程,比如AOP、事务处理等。

  2. 执行自定义的初始化方法和销毁方法。容器中某些Bean可能需要在容器启动时执行自定义的初始化方法。这些方法在容器启动时就会被调用;同理,某些Bean在容器关闭时需要调用自定义的销毁方法,以清理资源。

  3. 容器启动后,整个应用将进入正常的工作状态。

Spring启动流程

学习博客

知乎

cdsn1

csdn2

Bean扫描及注册解析

@Configuration注解解析步骤

@Configuration注解较细解析

处理配置类上的注解

详细的配置解析

解决依赖循环的三级缓存分析

spring代理创建时机及@Aspect原理

aop代理创建时机

aop寻找匹配的切面及代理创建源码

aop链式调用逻辑

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值