知识点:
1.抛出问题 Spring AOP
知识点 |
核心内容 |
重点 |
Spring AOP实现原理 |
通过@Aspect注解定义切面类,结合@Before/@After等通知类型实现方法拦截 |
注解配置遗漏(需@EnableAspectJAutoProxy启用自动代理) |
切面类配置 |
使用@Component+@Aspect声明切面,通过表达式或直接指定目标方法 |
前置通知(@Before)与最终通知(@After)执行顺序差异 |
动态代理机制 |
原生Spring通过后置处理器解析AOP注解并生成代理对象 |
代理对象与原始Bean的调用链路区别 |
注解驱动开发 |
@EnableAspectJAutoProxy启用基于注解的AOP功能 |
未添加该注解导致切面失效的高频错误 |
通知类型实践 |
演示前置、返回、异常、最终通知的代码编写与输出验证 |
异常通知(@AfterThrowing)仅触发条件 |
2.简析Spring AOP 和 BeanPostProcessor关系
知识点 |
核心内容 |
重点 |
AOP与BeanPostProcessor关系 |
代理对象生成机制:Bean创建后根据AOP处理决定返回代理对象或原生Bean |
为什么SmartDog对象在after后置处理器调用时变成代理对象 |
EnableAspectJAutoProxy注解 |
启用切面自动代理功能,引入AspectJAutoProxyRegistrar类 |
注解底层与BeanPostProcessor的关联机制 |
AnnotationAwareAspectJAutoProxyCreator |
核心代理创建器,继承自BeanPostProcessor接口 |
类图继承关系与动态代理技术结合点 |
Spring底层实现机制 |
分六阶段实现: 1.获取BeanClass 2.封装BeanDefinition 3.单例/多例处理 4.依赖注入 5.后置处理器 6.AOP机制 |
单例池预先创建 vs 多例动态生成的区别 |
动态代理技术应用 |
AOP通过代理对象实现方法拦截,底层使用反射调用机制 |
原生类型与代理类型的转换时机 |
学习深度建议 |
掌握Bean生命周期管理、依赖注入、AOP代理生成即可满足面试需求 |
框架复杂度与学习深度的平衡点 |
3.补充说明
知识点 |
核心内容 |
重点 |
AOP代理机制 |
解释为何smart dog对象在后置处理器中变成代理对象,而user action和user dao没有 |
区分原生类型和代理对象的转换条件 |
切面编程原理 |
当类方法被切面切入时,Spring会通过动态代理生成代理对象 |
理解切面配置与代理对象生成的因果关系 |
后置处理器作用 |
after initialization阶段决定是否将Bean包装为代理对象 |
后置处理器对所有Bean的处理差异 |
接口代理与类代理 |
演示即使不按接口注入,被切入的类仍会生成代理对象 |
CGLIB与JDK动态代理的选择条件 |
切面配置验证 |
通过注销切面配置证明代理对象生成与切面存在直接关联 |
切面注解的生效范围实验 |
懒加载机制 |
单例Bean的初始化时机与代理对象生成的关系说明 |
懒加载对AOP代理的影响 |
4.Spring整体架构分析(1)
知识点 |
核心内容 |
重点 |
Spring容器创建初始化流程 |
1. 扫描包获取Bean的Class对象并排除非Bean类; 2. 将Bean信息封装到BeanDefinition对象并存入BeanDefinitionMap; 3. 根据单例/多例判断实例化并存储到单例池(SingletonObjects) |
BeanDefinition的作用; 单例池与原型模式的区别 |
Spring容器getBean实现机制 |
1. 初始化完成后通过getBean方法获取对象; 2. 底层依赖反射和BeanDefinition信息动态生成实例 |
反射调用与注解解析的关联; 懒加载与即时初始化的差异 |
IOC与后置处理器机制 |
1. 依赖注入通过扫描@Component等注解实现; 2. 后置处理器(如BeanPostProcessor)干预Bean生命周期 |
注解扫描的包路径过滤逻辑; 后置处理器的执行时机 |
Spring整体架构分析 |
1. 先整体后局部的讲解逻辑; 2. 结合流程图解析核心模块(容器、Bean工厂、AOP等) |
架构图中各模块的交互关系 |
5.Spring整体架构分析(2)
知识点 |
核心内容 |
重点 |
Spring容器架构 |
IOC容器结构分析,包含bin definition map和单例池设计 |
单例与原型模式的区别,集合存储结构差异 |
Bean定义机制 |
扫描包后生成BeanDefinition对象并存入Map集合 |
Bean名称生成规则(类名小写 vs 注解指定) |
单例模式实现 |
非懒加载单例对象预创建并存入单例池 |
单例池(singletonObjects)与原型模式创建时机差异 |
GetBean实现逻辑 |
三阶段处理:不存在检查→单例获取→原型实例化 |
原型模式动态创建与单例缓存机制对比 |
依赖注入机制 |
@Autowired注解解析与Bean组装实现 |
循环依赖问题的处理方案 |
扩展机制 |
BeanPostProcessor与AOP动态代理的衔接 |
后置处理器调用时机与代理对象生成流程 |
设计模式应用 |
工厂模式(BeanFactory)与策略模式(Scope处理) |
单例模式与原型模式的选择依据 |
6.二说类加载器和classpath
知识点 |
核心内容 |
重点 |
Spring容器实现 |
编写自定义Spring容器,扫描包获取Bean的Class对象 |
扫描包路径配置 vs XML配置文件解析 |
类加载器机制 |
Bootstrap/Ext/App三类加载器的默认加载路径差异 |
ClassPath动态性(多路径集合) |
类路径解析 |
ClassPath包含target/classes等编译输出目录 |
开发目录(src)与运行目录(target)的区分 |
配置方式对比 |
使用Java类(HspSpringConfig)替代beans.xml配置 |
DOM4J解析传统XML方式的兼容性 |
包扫描过滤 |
扫描com.hsbedu.spring.component包并排除非Bean类 |
注解识别与类过滤逻辑实现 |
7.编写自己Spring容器 扫描包得到bean(1)
知识点 |
核心内容 |
重点 |
Spring容器开发 |
实现扫描包获取Bean的Class对象 |
注解扫描机制与IOC容器关系 |
自定义注解 |
@ComponentScan和@Component注解开发 |
Value属性指定扫描包/Bean命名 |
项目结构搭建 |
创建并行子模块hsp-my-spring |
Maven模块与语言级别(JDK8)配置 |
代码复用原则 |
复用已有注解实现代码 |
新旧版本功能对比验证 |
组件注册流程 |
通过注解自动注册Bean到IOC容器 |
@Controller/@Service等衍生注解处理 |
8.编写自己Spring容器 扫描包得到bean(2)
知识点 |
核心内容 |
重点 |
IOC容器配置类 |
使用hspSpringConfigure类替代beans.xml配置文件,通过@ComponentScan指定扫描包路径 |
配置类与XML的等价性; (需理解注解驱动与XML配置的映射关系) |
组件扫描机制 |
通过@Component注解标记需注入容器的类,支持自定义value命名(未指定时默认类名首字母小写) |
命名优先级规则; (显式value > 默认类名转换) |
依赖注入准备 |
定义MonsterService和MonsterDAO组件,演示后续装配逻辑 |
@Component作用域与角色区分; (需区分@Service/@Repository等衍生注解) |
自定义容器实现 |
动态扫描包路径、过滤非@Component类,暂不实例化对象(仅收集Class信息) |
类加载器路径处理; (APPClassLoader获取target/classes路径) |
单例与多例设计 |
预告后续容器将扩展单例池与多例模式处理逻辑 |
getBean()方法复杂度升级; (需同时支持两种作用域) |
9.编写自己Spring容器 扫描包得到bean(3)
知识点 |
核心内容 |
重点 |
Spring组件扫描机制 |
通过@Component注解识别Spring Bean,使用类加载器获取target/classes目录下的类文件 |
类路径定位和注解识别逻辑 |
版本兼容性问题处理 |
解决Java Compiler版本不匹配问题(1.5→1.8) |
编译器版本配置与注解支持关系 |
类过滤逻辑实现 |
双重注解检查优化为单次判断,排除非@Component类 |
代码逻辑错位导致的冗余判断 |
调试信息优化 |
简化日志输出格式(类名+Bean状态) |
有效信息提取与输出格式化 |
运行时异常处理 |
预期错误JAVA_COMPILER_ERROR的版本诊断方法 |
错误预判与快速定位技巧 |
类加载路径验证 |
确认扫描范围限定在target/classes目录 |
物理路径与包路径映射关系 |
10.封装BeanDefinition 放入Map(1)
知识点 |
核心内容 |
重点 |
bin信息封装 |
将bin信息封装到bin definition对象中,并存入map管理 |
bin名称的确定规则(指定名称或类首字母小写) |
bin作用域处理 |
区分singleton和prototype两种作用域 |
prototype类型需反射创建新实例 vs singleton直接从单例池获取 |
bin definition对象属性 |
需包含class对象、作用域标识 |
单例模式需要额外维护单例池集合 |
map结构设计 |
key为bin名称,value为bin definition对象 |
未指定名称时自动生成命名规则 |
11.封装BeanDefinition 放入Map(2)
知识点 |
核心内容 |
重点 |
注解scope |
用于指定bean是单例(singleton)还是多实例(prototype) |
单例与多实例的区别:单例全局共享一个实例,多实例每次getBean返回新对象 |
bean定义封装 |
通过BeanDefinition类记录bean的scope和对应class对象 |
关键属性:scope值、class对象(用于反射生成实例) |
注解实现细节 |
@Scope注解需设置@Target(TYPE)和@Retention(RUNTIME) |
value属性:用于接收singleton/prototype参数值 |
多实例验证 |
在MonsterService类添加@Scope(prototype)进行测试 |
默认情况:未加@Scope注解时默认为单例模式 |
反射机制应用 |
通过存储的Class对象动态生成多实例bean |
核心方法:Class.newInstance()或构造器调用 |
12.封装BeanDefinition 放入Map(3)
知识点 |
核心内容 |
重点 |
Spring容器初始化 |
定义并初始化beanDefinitionMap和singletonObjects集合 |
ConcurrentHashMap的使用场景选择 |
Bean定义封装 |
将扫描到的@Component类封装为BeanDefinition对象 |
scope默认值(singleton)处理逻辑 |
Bean名称解析 |
通过@Component注解value值或类名首字母小写确定beanName |
未配置value值时的异常处理 |
注解属性提取 |
使用反射获取@Scope注解配置值 |
自定义注解与原生的区分 |
集合类型选择 |
ConcurrentHashMap保证线程安全的容器操作 |
key-value类型设计(String-Object) |
调试验证 |
通过断点检查map中beanDefinition的正确性 |
prototype与singleton的存储验证 |
异常处理 |
未配置scope时的默认值处理 |
空值情况的防御性编程 |
13.封装BeanDefinition 放入Map(4)
知识点 |
核心内容 |
重点 |
Bean命名规则 |
未配置value时默认返回空串,需通过类名首字母小写作为替代方案 |
空串处理逻辑 vs 默认命名规则 |
StringUtils工具类应用 |
使用org.apache.commons.lang包实现首字母小写转换 |
框架依赖冲突(Spring vs Commons) |
JDK版本兼容性问题 |
Maven默认JDK1.5导致getDeclaredAnnotation报错 |
临时解决方案(IDE设置) vs 永久方案(POM配置) |
代码重构优化 |
将扫描逻辑抽离为beanDefinitionScan方法 |
构造器臃肿 vs 单一职责原则 |
调试技巧 |
通过断点验证beanDefinitionMap动态数据 |
空值注入风险 vs 命名规范化 |