项目导入IDEA
环境:Windows,JDK8,Gradle 6.5.1,IDEA 2020.1.3
进入
https://github.com/spring-projects/spring-framework
,选择版本5.2.X
,下载zip或clone到本地;编辑项目目录下的build.gradle,全局搜索
allprojects
,编辑其repositories
属性,配置阿里云镜像,如下:repositories {
maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }
maven{ url 'http://maven.aliyun.com/nexus/content/repositories/jcenter'}
mavenCentral()
maven { url "https://repo.spring.io/libs-spring-framework-build" }
}进入项目根目录下,预编译
spring-oxm
,在命令行中执行:gradlew :spring-oxm:compileTestJava
(需耐心等待3-5分钟左右…) 成功后打印
BUILD SUCCESSFUL
导入项目至idea;
移除
spring-aspects
模块;
阅读reference文档
GA:General Availability,官方正式发布的稳定版本(RELEASE,Stable,Final)
RC:Release Candidate,发行候选版本,基本不再加入新的功能
Alpha:内部测试版本,bug较多,功能不全
Beta:公开测试版本,比Alpha晚些,还会加功能,修bug
M:Milestone,开发期发行版本,边开发边发行
单一职责原则
门面模式 facade pattern
子系统的外部与其内部的通信必须通过统一的对象进行
- 提供一个高层次的接口,使得子系统更易于使用
适配器模式
泛型
让数据类型变得参数化
- 定义泛型时,对应的数据类型是不确定的;
- 泛型方法被调用时,会指定具体类型;
核心目标:解决容器类型在编译时安全检查的问题
泛型类
泛型的参数不支持基本类型
泛型相关的信息不会进入到运行阶段
能否在泛型里面使用具备继承关系的类?不可以
解决办法:
- 使用通配符
?
,但是会使得泛型的类型检查失去意义; - 给泛型加入上边界
? extends E
; - 给泛型加入下边界
? super E
;
- 使用通配符
泛型接口
泛型方法
public class GenericClassExample<T> {
/**
* member这个成员变量的类型为T,T的类型由外部指定
*/
private T member;
public GenericClassExample(T member) {
this.member = member;
}
public static <E> void printArray(E[] inputArray) {
for (E element : inputArray) {
System.out.printf("%s", element);
System.out.printf(" ");
}
System.out.println();
}
public T handleSomething(T target) {
return target;
}
}泛型方法中的泛型标识符可独立于泛型类存在的,而泛型类中其他方法受制于泛型标识符的
泛型字母的含义
- E - Element 在集合中使用,因为集合中存放的是元素;
- T - Type Java类;
- K - Key 键;
- V - Value 值;
- N - Number 数值类型;
Servlet原理总结
减少Servlet的数量
参照SpringMVC,仅通过DispatcherServlet进行请求派发;
- 拦截所有请求
- 解析请求
- 派发给对应的Controller里面的方法进行处理
简单工厂模式
定义一个工厂类,根据传入的参数值不同返回不同的实例
特点:被创建的实例具有共同的父类或接口
适用场景:
- 需要创建的对象较少;
- 客户端不关心对象的创建过程;
优点:可以对创建的对象进行“加工”,对客户端隐藏相关细节;
缺点:因创建逻辑复杂或创建对象过多而造成代码臃肿;新增、删除子类均会违反开闭原则;
开闭原则(待补全)
工厂方法模式
定义一个用于创建对象的接口,让子类决定实例化哪一个类
对类的实例化延迟到子类
优点:
- 遵循开闭原则
- 对客户端隐藏对象的创建细节
- 遵循单一职责
缺点:
- 增加子类的时候“拖家带口”
- 只支持同一类产品的创建
抽象工厂
提供一个创建一系列相关或相互依赖对象的接口
- 抽象工厂模式侧重的是同一产品族
- 工厂方法模式更加侧重于同一产品等级
解决了工厂模式只支持生产一种产品的弊端
- 新增一个产品族,只需要增加一个新的具体工厂,不需要修改代码;
IOC
Spring IOC容器使用了工厂模式+反射机制
反射:允许程序在运行时来进行自我检查并且对内部的成员进行操作
1)作用:
- 在运行时判断任意一个对象所属的类;
- 在运行时获取类的对象;
- 在运行时访问Java对象的属性、方法、构造方法等;
2)java.lang.reflect 类库里主要的类
- Field:表示类中的成员变量
- Method:表示类中的方法
- Constructor:表示类的构造方法
- Array:该类提供了动态创建数组和访问数组元素的静态方法
3)反射依赖的Class
JVM中只有唯一一个和类相对应的Class对象来描述其类型信息。
4)获取Class对象的三种方式:
- Object -> getClass()
- 任何数据类型(包括基本数据类型)都有一个“静态”的class属性
- 通过Class类的静态方法:forName(String className) (常用)
public class ReflectTarget {
public static void main(String[] args) throws ClassNotFoundException {
ReflectTarget reflectTarget = new ReflectTarget();
// 第一种方式获取class对象
Class reflectTargetClass1 = reflectTarget.getClass();
// 第二种
Class reflectTargetClass2 = ReflectTarget.class;
System.out.println(reflectTargetClass1 == reflectTargetClass2);
// 第三种
Class reflectTargetClass3 = Class.forName("demo.reflect.ReflectTarget");
System.out.println(reflectTargetClass2 == reflectTargetClass3);
}
}5)获取并操作构造函数
// 获取所有"公有的"的构造方法
public Constructor<?>[] getConstructors() throws SecurityException {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
return copyConstructors(privateGetDeclaredConstructors(true));
}
// 获取所有的构造方法(包括private public default protected)
public Constructor<?>[] getDeclaredConstructors() throws SecurityException {
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
return copyConstructors(privateGetDeclaredConstructors(false));
}
// 获取单个的"公有的"构造方法
public Constructor<T> getConstructor(Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
return getConstructor0(parameterTypes, Member.PUBLIC);
}
// 获取某个构造方法(包括private public default protected)
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException {
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
return getConstructor0(parameterTypes, Member.DECLARED);
}
// 调用构造方法
Constructor -> newInstance(Object... initargs)
// 暴力访问(忽略掉访问修饰符)
Constructor -> setAccessible(true)6)获取并操作成员变量
// 获取所有的"公有字段" 包含继承字段
public Field[] getFields() throws SecurityException {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
return copyFields(privateGetPublicFields(null));
}
// 获取所有字段(包括private修饰的) 不包含继承的字段
public Field[] getDeclaredFields() throws SecurityException {
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
return copyFields(privateGetDeclaredFields(false));
}
// 获取某个"公有的"字段 包含继承字段
public Field getField(String name)
throws NoSuchFieldException, SecurityException {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
Field field = getField0(name);
if (field == null) {
throw new NoSuchFieldException(name);
}
return field;
}
// 获取某个字段(可以是私有的) 不包括继承字段
public Field getDeclaredField(String name)
throws NoSuchFieldException, SecurityException {
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
Field field = searchFields(privateGetDeclaredFields(false), name);
if (field == null) {
throw new NoSuchFieldException(name);
}
return field;
}
// 设置字段的值 obj:要设置的字段所在的对象 value:要为字段设置的值
Field -> public void set(Object obj, Object value);7)获取并操作成员方法
// 获取所有的"公有方法" 包含了父类的方法和Object类
public Method[] getMethods() throws SecurityException {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
return copyMethods(privateGetPublicMethods());
}
// 获取所有的成员方法,包括私有的,不包含继承的
public Method[] getDeclaredMethods() throws SecurityException {
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
return copyMethods(privateGetDeclaredMethods(false));
}
// name:方法名 Class...:形参的Class类型对象
public Method getMethod(String name, Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException {
checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
Method method = getMethod0(name, parameterTypes, true);
if (method == null) {
throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
}
return method;
}
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
throws NoSuchMethodException, SecurityException {
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);
Method method = searchMethods(privateGetDeclaredMethods(false), name, parameterTypes);
if (method == null) {
throw new NoSuchMethodException(getName() + "." + name + argumentTypesToString(parameterTypes));
}
return method;
}
// 调用方法 obj:要调用方法的对象 args:调用方法时传递的实参
Method -> public Object invoke(Object obj, Object... args);8)反射的获取源
- 用XML来保存类相关的信息以供反射调用;
- 用注解来保存类相关的信息以供反射调用;
注解:提供一种为程序元素设置元数据的方法
元数据是添加到程序元素如方法、字段、类和包上的额外信息。
1)功能
- 作为特定的标记,用于告诉编译器一些信息;
- 编译时动态处理,如动态生成代码;
- 运行时动态处理,作为额外信息的载体,如获取注解信息;
2)注解分类
- 标准注解:Override、Deprecated、SuppressWarnings
- 元注解:@Retention、@Target、@Inherited、@Documented
- 自定义注解
3)元注解
@Target:注解的作用目标
@Retention:注解的生命周期
@Documented:注解是否应当被包含在JavaDoc文档中
@Inherited:是否允许子类继承该注解
单例模式(待总结)
使用反射时恶汉和懒汉模式都不能保证单例,所以采取枚举解决(枚举原理待补全)
需要实现的点
创建注解
提取标记对象 extractPackageClass
指定范围,获取范围内的所有类;
遍历所有类,获取被注解标记的类并加载进容器里;
实现容器
保存Class对象及其实例的载体
容器的加载(配置的管理与获取,获取指定范围内的Class对象,依据配置提取Class对象,连同实例一并存入容器)
容器的操作方式(涉及到容器的增删改查:增加、删除操作,根据Class获取对应实例,获取所有的Class和实例,通过注解来获取被注解标注的Class,通过超类获取对应的子类Class,获取容器载体保存Class的数量)
依赖注入
- 定义相关的注解标签
- 实现创建被注解标记的成员变量实例,并将其注入到成员变量里
- 依赖注入的使用
Spring框架有多种作用域
singleton
prototype
request
session
globalsession
IOC源码学习
配置文件:
1)根据配置,生成用来描述bean的BeanDefinition,常用属性:
作用范围scope(@Scope)
懒加载lazy-init(@Lazy):决定Bean实例是否延迟加载
首选primary(@Primary):设置为true的bean会是优先的实现类
factory-bean和factory-method(@Configuration和@Bean)
GenericBeanDefinition
2)术语补充
组件扫描:自动发现应用容器中需要创建的Bean
自动装配:自动满足Bean之间的依赖
3)ApplicationContext常用容器
传统的基于XML配置的经典容器
- FileSystemXmlApplicationContext:从文件系统加载配置
- ClassPathXmlApplicationContext:从classpath加载配置
- XmlWebApplicationContext:用于Web应用程序的容器
4)容器的初始化:refresh()
在已启动的情况下调用refresh()可以清除缓存,重新装载配置信息。
主要功能:
- 容器初始化、配置解析
- BeanFactoryPostProcessor和BeanPostProcessor的注册和激活
- 国际化配置
- web内置容器的构造
模板方法模式:围绕抽象类,实现通用逻辑,定义模板结构,部分逻辑由子类实现
作用:复用代码,反向控制
模式涉及的方法种类:模板方法,具体方法,钩子方法,抽象方法
重要类:AbstractApplicationContext
5)Resource ResourceLoader 容器之间的关系
根据资源地址自动选择正确的Resource
- 自动识别classpath,file等资源地址前缀;
- 支持自动解析Ant风格带通配符的资源地址;
Ant:路径匹配表达式,用来对URI进行匹配
- ? 匹配任何单字符;
*
匹配0或者任意数量的字符;**
匹配0或者更多的目录;
ResourceLoader:实现不同的Resource加载策略,按需返回特定类型的Resource。
6)BeanDefinitionReader
- 读取BeanDefinition
- BeanDefinitionRegistry
总结
1)容器初始化主要做的事情
场景问题
BeanFactory和factoryBean有什么联系和区别
后置处理器PostProcessor
本身也是一种需要注册到容器里的bean
其里面的方法会在特定的时机被容器调用;
实现不改变容器或者bean核心逻辑的情况下对bean进行扩展;
对bean进行包装,影响其行为、修改bean的内容等;
PostProcessor种类
大类分为容器级别的后置处理器以及Bean级别的后置处理器
BeanDefinitionRegistryPostProcessor 容器级别
BeanFactoryPostProcessor 容器级别
BeanPostProcessor bean级别
defaultResourceLoader