文章目录
- Gradle安装
- Spring源码下载
- 修改Spring源码中Gradle配置
- 构建Spring源码
- 导入IDEA
- 创建Spring源码debug调试模块
- spring模块
- 相关错误解决
- 切面源码
- 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.md
和import-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
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的启动流程
- 准备环境对象,事件监听者。
- 加载Spring配置文件。即读入并解析xml配置文件,构建初始Spring IOC容器。
- 对创建的Spring IOC容器进行一些初始化配置,设置忽略接口,注册环境上下文。
- 调用Spring IOC容器的postProcessBeanFactory,留给子类实现。
- 实例化并执行BeanFactoryPostProcessor相关接口,包括解析配置类。并将BeanFactoryPostProcessor相关类放入特定容器,@Configuration的扫描及配置在此。
- 实例化并注册所有BeanPostProcessor进容器类。
- 初始化MessageSource,用于支持国际化。
- 初始化事件广播器。
- 注册监听者。
- 实例化和初始化IOC容器中剩下的所有Bean。在初始化Bean时,Spring 根据Bean的定义以及配置信息,实现对Bean的实例化、属性赋值、以及初始化等操作。
完成IoC容器的准备工作。所有单例的Bean都已经被实例化、初始化并装配到容器中后,容器的准备工作就完成了,此时Spring框架已经可以对外提供服务。
- 执行定制化的后置处理器。Spring容器中可能会存在一些实现了BeanPostProcessor接口的定制化组件。这些组件会参与到IoC容器中Bean的生命周期过程,比如AOP、事务处理等。
- 执行自定义的初始化方法和销毁方法。容器中某些Bean可能需要在容器启动时执行自定义的初始化方法。这些方法在容器启动时就会被调用;同理,某些Bean在容器关闭时需要调用自定义的销毁方法,以清理资源。
- 容器启动后,整个应用将进入正常的工作状态。