😀前言
手动实现 Spring 底层机制【初始化 IOC容器+依赖注入+BeanPostProcessor 机制+AOP】的第五篇具体实现了任务阶段 5- bean 后置处理器
🏠个人主页:尘觉主页
🧑个人简介:大家好,我是尘觉,希望我的文章可以帮助到大家,您的满意是我的动力😉😉
在csdn获奖荣誉: 🏆csdn城市之星2名
💓Java全栈群星计划top前5
🤗 端午大礼包获得者
💕欢迎大家:这里是CSDN,我总结知识的地方,欢迎来到我的博客,感谢大家的观看🥰
如果文章有什么需要改进的地方还请大佬不吝赐教 先在次感谢啦😊
文章目录
- 🤩实现任务阶段 5- bean 后置处理器
- ● 分析示意图
- 创建InitializingBean接口
- 修改MonsterService.java类
- 修改WyxSpringApplicationContext类
- 😋运行完成测试
- 创建 BeanPostProcessor接口
- 创建 WyxBeanPostProcessor类
- 修改WyxSpringApplicationContext类
- 😎运行完成测试
- 😄总结
🤩实现任务阶段 5- bean 后置处理器
● 分析示意图
● 代码实现, 说明,整个实现思路,就是参考 Spring 规范
创建InitializingBean接口
public interface InitializingBean {
void afterPropertiesSet() throws Exception;
}
修改MonsterService.java类
去实现 InitializingBean 接口
\1. afterPropertiesSet就是在bean的setter方法执行完毕后被spring容器调用
2 即就是初始化方法
@Component//(value = "monsterService") //把MonsterService注入我们自己的spring容器中
@Scope(value = "prototype")
public class MonsterService implements InitializingBean {
//这里我们使用自己的@Autowired来修饰属性
//表示该属性,是通过容器完成依赖注入
//说明: 我们实现按照名字来进行组装即可
@Autowired
private MonsterDao monsterDao;
public void m1() {
monsterDao.hi();
}
/**
* 1. afterPropertiesSet就是在bean的setter方法执行完毕后被spring容器调用
* 2 即就是初始化方法
* @throws Exception
*/
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("MonsterService 初始化方法被调用 程序员在这里加入初始化的业务..");
}
}
修改WyxSpringApplicationContext类
在创建好 Bean 实例后,判断是否需要进行初始化 【容器中常.否实现了某个接口,来判断是否要执行某个业务逻辑, 这里其实就是 java 基础的接口编程实际运用
//先简单实现实现,后面在完善.
private Object createBean(String beanName, BeanDefinition beanDefinition) {
//得到 bean 的类型
Class clazz = beanDefinition.getClazz();
try {
//使用反射得到实例
Object instance = clazz.getDeclaredConstructor().newInstance();
//完成依赖注入
for (Field declaredField : clazz.getDeclaredFields()) {
if (declaredField.isAnnotationPresent(Autowired.class)) {
// 处理@Autowired 注解的属性 required, 很简单,自己完成
// Autowired annotation =declaredField.getAnnotation(Autowired.class);
// System.out.println(annotation.required());
//如果该属性有@Autowired, 就进行组装
Object bean = getBean(declaredField.getName());
declaredField.setAccessible(true);//因为属性是 private,需要暴破
declaredField.set(instance, bean);
}
}
//这里还有其他,比如 Aware 回调. 不写了
//这里调用初始化,如果 bean 实现了 InitializingBean
System.out.println("======创建好了====" + instance);
if (instance instanceof InitializingBean) {
try {
((InitializingBean) instance).afterPropertiesSet();
} catch (Exception e) {
e.printStackTrace();
}
}
return instance;
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
//如果没有创建成功,返回 null
return null;
}
😋运行完成测试
创建 BeanPostProcessor接口
该接口可以参考原生 Spring 规范 , 注注意体会切面编程
-
参考原生Spring容器定义一个接口BeanPostProcessor
-
该接口有两个方法postProcessBeforeInitialization 和 postProcessAfterInitialization
-
这两个方法,会对Spring容器的所有Bean生效, 已经是切面编程的概念.
public interface BeanPostProcessor {
/**
* 1. postProcessBeforeInitialization在Bean的初始化方法前调用
* @param bean
* @param beanName
* @return
*/
default Object postProcessBeforeInitialization(Object bean, String beanName) {
return bean;
}
/**
* 1. postProcessAfterInitialization在Bean的初始化方法后调用
* @param bean
* @param beanName
* @return
*/
default Object postProcessAfterInitialization(Object bean, String beanName) {
return bean;
}
}
创建 WyxBeanPostProcessor类
@Component
public class WyxBeanPostProcessor implements BeanPostProcessor {
/**
* 该方法时在 bean 创建好后,进行初始化前调用
* @param bean : 创建好的 bean 对象
* @param beanName 创建好的 bean 的名字
* @return
*/
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
//这里程序员来决定业务逻辑,spring 只是提供处理机制
System.out.println("postProcessBeforeInitialization 被调用 " + beanName + " bean= " + bean.getClass());
return bean;
}
/**
* 该方法时在 bean 创建好后,初始化完成后调用
* @param bean : 创建好的 bean 对象
* @param beanName : 创建好的 bean 的名字
* @return
*/
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
//这里程序员来决定业务逻辑,spring 只是提供处理机制
System.out.println("postProcessAfterInitialization 被调用 " + beanName + " bean= " + bean.getClass());
return bean;
}
}
修改WyxSpringApplicationContext类
注意:这里 createBean(String beanName, BeanDefinition beanDefinition) 需要增加入 参 beanName, 就会导致好几个位置错误,需要根据错误提示,对应解决即可.
private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();
private void beanDefinitionsByscan(Class configClass) {
this.configClass = configClass;
ComponentScan componentScan = (ComponentScan)
this.configClass.getDeclaredAnnotation(ComponentScan.class);
String path = componentScan.value();
System.out.println("扫描路径 = " + path);
path = path.replace(".", "/");
URL resource = classLoader.getResource(path);
File file = new File(resource.getFile());
if (file.isDirectory()) {
File[] files = file.listFiles();
for (File f : files) {
String fileAbsolutePath = f.getAbsolutePath();
System.out.println("=======================================");
System.out.println("文件绝对路径 = " + fileAbsolutePath);
if (fileAbsolutePath.endsWith(".class")) {// 说明是类文件
// 通过类加载器获取来类文件的 Clazz 对象
// 先 得 到 类 的 完 整 类 路 径 形 式 为
com.wyxedu.spring.component.MonsterService
String className =
fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\") + 1,
fileAbsolutePath.indexOf(".class"));
String classFullPath = path.replace("/", ".") + "." + className;
System.out.println("类名 = " + className);
System.out.println("类的全路径 = " + classFullPath);
try {
// 获取到扫描包下的类的 clazz 对象
Class<?> clazz = classLoader.loadClass(classFullPath);
if (clazz.isAnnotationPresent(Component.class)) {
// 如果这个类有@Commponent, 说明是一个 spring bean
System.out.println("是一个 bean = " + clazz);
// 1. 增 加 一 个 逻 辑 , 如 果 这 个 clazz 类 型 是 实 现 了BeanPostProcessor 接口, 说明是一个 bean 处理器,特殊处理
// 2. 注意不能使用 clazz instanceof BeanPostProcessor 判断因为 clazz 并不是一个实例对象, 而是一个类对象
// 3. 这里实现是为了方便获取 bean 处理器对象,所以放在一个 beanPostProcessorList, spring 底层源码,
// 还 是 走 的 createBean(),getBean(), 只 是 需 要 在singletonObjects, 增加代码处理,
// 我这里主要讲的是 bean 处理器的工作机制,就不处理了,知道即可
if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
// 创建一个实例对象
BeanPostProcessor beanPostProcessor =
(BeanPostProcessor) clazz.newInstance();
// 放入到 beanPostProcessorList
beanPostProcessorList.add(beanPostProcessor);
continue;
// 1. 因为这里不能直接将 bean 实例放入 singletonObjects
// 2. 原因是如果 bean 是 prototype 是需要每次创建新的 bean 对象
// 3. 所 以 , Spring 底 层 是 这 样 设 计 的 : 将 bean 信 息 封 装 到
BeanDefinition 对象中, 便于 getBean 的操作
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setClazz(clazz);
// 获取 bean 的 name
Component componentAnnotation =
clazz.getDeclaredAnnotation(Component.class);
String beanName = componentAnnotation.value();
// 获取 bean 的 scope
if (clazz.isAnnotationPresent(Scope.class)) { // 如果有@Scope
Scope scopeAnnotation =
clazz.getDeclaredAnnotation(Scope.class);
beanDefinition.setScope(scopeAnnotation.value());
} else { // 如果没有@Scope, 默认是 singleton
beanDefinition.setScope("singleton");
}
// 放入到 beanDefinitionMap
beanDefinitionMap.put(beanName, beanDefinition);
} else {
// 如果这个类没有@Commponent, 说明不是一个 spring bean
System.out.println("不是一个 bean = " + clazz);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
System.out.println("=======================================");
}
}
}
}
// 先简单实现实现,后面在完善. private Object createBean(String beanName, BeanDefinition beanDefinition) {
// 得到 bean 的类型
Class clazz = beanDefinition.getClazz();
try
{
// 使用反射得到实例
Object instance = clazz.getDeclaredConstructor().newInstance();
// 完成依赖注入
for (Field declaredField : clazz.getDeclaredFields()) {
if (declaredField.isAnnotationPresent(Autowired.class)) {
// 处理@Autowired 注解的属性 required, 很简单,自己完成
// Autowired annotation = declaredField.getAnnotation(Autowired.class);
// System.out.println(annotation.required());
// 如果该属性有@Autowired, 就进行组装
Object bean = getBean(declaredField.getName());
declaredField.setAccessible(true);// 因为属性是 private,需要暴破
declaredField.set(instance, bean);
}
}
// 这里还有其他,比如 Aware 回调. 不写了
// 说明
// 1. 在 bean 初始化前调用所有 bean 处理器的 postProcessBeforeInitialization
// 2. 调用时,不能保证顺序
// 3. 可以通过加入@Order("值"), 来指定 bean 处理器调用顺序,同学们可以自行完成, 不难
// 4. 如果希望指定对哪些 bean 进行初始化前处理 , 可以在处理器的postProcessBeforeInitialization()
// 加入相关业务判断即可.比如:
/**
* @Override
* public Object postProcessBeforeInitialization(Object bean, String
beanName) {
* if("monsterService".equalsIgnoreCase(beanName)) {
* //这里程序员来决定业务逻辑,spring 只是提供处理机制
* System.out.println("postProcessBeforeInitialization 被调用 " * + beanName + " bean= " + bean.getClass());
* return bean;
* }else {
* return bean;
* }
* }
*/
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
// 4. 也会返回一个对象,这个返回的对象是什么,由程序员在编写 bean 处理器决定,可能是原来的 bean, 也可能被改变了
instance = beanPostProcessor.postProcessBeforeInitialization(instance, beanName);
}
// 这里调用初始化,如果 bean 实现了 InitializingBean
if (instance instanceof InitializingBean) {
try {
((InitializingBean) instance).afterPropertiesSet();
} catch (Exception e) {
e.printStackTrace();
}
}
// 说明
// 1. 在 bean 初始化后调用所有 bean 处理器的 postProcessAfterInitialization
// 2. 调用时,不能保证顺序
// 3. 可以通过加入@Order("值"), 来指定 bean 处理器调用顺序,同学们可以自行完成, 不难
// 4. 如 果 希 望 指 定 对 哪 些 bean 进 行 初 始 化 后 处 理 , 可 以 在 处 理 器 的postProcessAfterInitialization()
// 加入相关业务判断即可
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
// 4. 也会返回一个对象,这个返回的对象是什么,由程序员在编写 bean 处理器决定,可能是原来的 bean, 也可能被改变了
instance = beanPostProcessor.postProcessAfterInitialization(instance, beanName);
}
return instance;
} catch(InstantiationException e){
e.printStackTrace();
} catch(IllegalAccessException e){
e.printStackTrace();
} catch(InvocationTargetException e){
e.printStackTrace();
} catch(NoSuchMethodException e){
e.printStackTrace();
}
// 如果没有创建成功,返回 null
return null;
😎运行完成测试
😄总结
本篇我们完成了bean 后置处理器是我们的手写进一步完善了手动实现 Spring 底层机制
😉手动实现 Spring 底层机制【初始化 IOC容器+依赖注入+BeanPostProcessor 机制+AOP】系列
手动实现 Spring 底层机制 实现任务阶段一编写自己 Spring 容器-准备篇【1】
手动实现 Spring 底层机制 实现任务阶段一编写自己 Spring 容器-准备篇【2】
手动实现 Spring 底层机制 实现任务阶段一编写自己 Spring 容器,实现任务阶段 2- 扫描将 bean 信息封装到 BeanDefinition 对象
手动实现 Spring 底层机制【初始化 IOC容器+依赖注入+BeanPostProcessor 机制+AOP】实现任务阶段 3- 初始化 bean 单例池 和 实现任务阶段 4- 完成依赖注入
😁热门专栏推荐
想学习vue的可以看看这个
java基础合集
数据库合集
redis合集
nginx合集
linux合集
等等等还有许多优秀的合集在主页等着大家的光顾感谢大家的支持
🤔欢迎大家加入我的社区 尘觉社区
文章到这里就结束了,如果有什么疑问的地方请指出,诸佬们一起来评论区一起讨论😁
希望能和诸佬们一起努力,今后我们一起观看感谢您的阅读🍻
如果帮助到您不妨3连支持一下,创造不易您们的支持是我的动力🤞