实现Spring底层机制-03

7.实现任务阶段5

7.1分析

阶段5目标:bean后置处理器的实现

7.2代码实现

新增:

1.创建 InitializingBean 接口,实现该接口的 Bean 需要实现 Bean 的初始化方法

bean后置处理器的两个方法,调用时机分别在 Bean 初始化方法的前后。因此要实现bean后置处理器,首先要实现 Bean 的初始化方法。

可以参考原生 Spring 规范来定义这个接口

package com.li.spring.processor;

/**
* @author 李
* @version 1.0
* 说明:
* 1.根据 spring 原生机制定义了一个接口
* 2.该接口有一个方法 afterPropertiesSet(),
* 3.afterPropertiesSet() 在 bean的 setter方法后执行,相当于原生的spring的 bean初始化方法
* 4.当一个Bean实现了这个接口后,就要实现afterPropertiesSet(),即 bean的初始化方法
*/
public interface InitializingBean {
void afterPropertiesSet() throws Exception;
}

2.为了测试,在MonsterService中实现该接口,并实现该方法,作为MonsterService的初始化方法

注:其他注解及自动装配的实现详见上一篇

package com.li.spring.component;

import com.li.spring.annotation.AutoWired;
import com.li.spring.annotation.Component;
import com.li.spring.annotation.Scope;
import com.li.spring.processor.InitializingBean; /**
* @author 李
* @version 1.0
* MonsterService 是一个 Service
* 1.如果指定了value,那么在注入spring容器时,以你指定的为准
* 2.如果没有指定value,则使用类名(首字母小写)作为默认名
*/
@Component//(value = "monsterService") //将 MonsterService注入到自己的spring容器中
@Scope(value = "prototype")
public class MonsterService implements InitializingBean {
//使用自定义注解修饰属性,表示该属性通过容器完成依赖注入
// (说明:按照名字进行组装)
@AutoWired(required = true)
private MonsterDao monsterDao; public void m1() {
monsterDao.hi();
} /**
* 1.afterPropertiesSet()就是在bean的setter方法执行完毕之后,被spring容器调用
* 2.即 bean的初始化方法
*
* @throws Exception
*/
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("MonsterService 初始化方法被调用");
}
}

3.修改MySpringApplicationContext.java,添加逻辑:在创建后bean实例后,判断是否需要进行初始化。

容器中常用的一个方法是,根据该类是否实现了某个接口,来判断该类是否要执行某个业务逻辑。这个接口被称为标记接口。

部分代码:

//完成createBean(BeanDefinition)方法
public Object createBean(BeanDefinition beanDefinition) {
//得到Bean的class对象
Class clazz = beanDefinition.getClazz();
try {
//反射创建bean实例
Object instance = clazz.getDeclaredConstructor().newInstance();
//todo 这里要加入依赖注入的业务逻辑
//1.遍历当前要创建对象的所有属性字段
for (Field declaredField : clazz.getDeclaredFields()) {
//2.判断字段是否有AutoWired注解
if (declaredField.isAnnotationPresent(AutoWired.class)) {
//判断是否需要自动装配
AutoWired autoWiredAnnotation =
declaredField.getAnnotation(AutoWired.class);
if (autoWiredAnnotation.required()) {
//3.得到字段的名称
String name = declaredField.getName();
//4.通过getBean()方法获取要组装的对象
//如果name对应的对象时单例的,就到单例池去获取,如果是多例的,就反射创建并返回
Object bean = getBean(name);
//5.进行组装
//暴破
declaredField.setAccessible(true);
declaredField.set(instance, bean);
}
}
}
System.out.println("======已创建好实例=====" + instance); //todo 判断是否要执行bean的初始化方法
// 1.判断当前创建的 bean对象是否实现了 InitializingBean
// 2.instanceof 表示判断对象的运行类型 是不是 某个类型或某个类型的子类型
if (instance instanceof InitializingBean) {
//3.将instance转成接口类型,调用初始化方法
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();
}
//如果反射对象失败
return null;
}

4.测试类

//...
public class AppMain {
public static void main(String[] args) {
MySpringApplicationContext ioc =
new MySpringApplicationContext(MySpringConfig.class);
MonsterService monsterService = (MonsterService) ioc.getBean("monsterService");
}
}

测试结果:MonsterService的初始化方法成功被调用。

5.创建 BeanPostProcessor 接口

package com.li.spring.processor;

/**
* @author 李
* @version 1.0
* 说明:
* 1.此接口参考原生Spring容器定义
* 2.该接口有两个方法 postProcessBeforeInitialization和 postProcessAfterInitialization
* 3.这两个方法会对spring容器的所有bean生效(切面编程)
*/
public interface BeanPostProcessor { /**
* postProcessBeforeInitialization方法在bean的初始化方法前 调用
*
* @param bean
* @param beanName
* @return
*/
default Object postProcessBeforeInitialization(Object bean, String beanName) {
return bean;
} /**
* postProcessAfterInitialization方法在bean的初始化方法后 调用
*
* @param bean
* @param beanName
* @return
*/
default Object postProcessAfterInitialization(Object bean, String beanName) {
return bean;
}
}

6.为了测试,自定义MyBeanPostProcessor实现上述接口。

package com.li.spring.component;

import com.li.spring.annotation.Component;
import com.li.spring.processor.BeanPostProcessor; /**
* @author 李
* @version 1.0
* 1.这是我们自己的一个后置处理器,它实现了我们自定义的 BeanPostProcessor
* 2.通过重写 BeanPostProcessor的方法实现相应业务
* 3.仍然将 MyBeanPostProcessor 看做一个Bean对象,使用Component注解,注入到spring容器中。
*/
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
System.out.println("后置处理器MyBeanPostProcessor的 Before()方法被调用,bean类型="
+ bean.getClass() + ",bean的名字=" + beanName);
return bean;
} @Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
System.out.println("后置处理器MyBeanPostProcessor的 After()方法被调用,bean类型="
+ bean.getClass() + ",bean的名字=" + beanName);
return bean;
}
}

7.修改 MySpringApplicationContext.java(模拟原生的ioc)

后置处理器和普通bean的创建顺序问题:

因为后置处理器要对容器中的所有bean对象都生效。因此后置处理器的bean对象的创建,要比普通的bean对象创建的时机靠前。

为了解决这个问题,我们将后置处理器对象的反射创建,提前到扫描包的时候完成(因为ioc容器初始化时,扫描工作要比反射对象的工作靠前)。

在原生的spring容器中,对后置处理器还是走的getBean,createBean方法。但如果这样做,需要在singletonObjects中加入相应业务逻辑,这里为了方便,我们简化处理了

然后将创建的后置处理器对象放到一个单独的集合来调用。

部分代码:

//该方法完成对指定包的扫描,并将Bean信息封装到BeanDefinition对象,再放入map中
public void beanDefinitionByScan(Class configClass) {
//步骤一:获取要扫描的包
//... //步骤二:得到要扫描的包下的所有资源(类.class)
//... //步骤三:获取全类名反射对象,并放入容器中
//...
//判断该类是否有特定注解
if (clazz.isAnnotationPresent(Component.class)) {
//如果该类使用了 @Component ,说明是spring bean
System.out.println("是一个spring bean=" + clazz + " 类名=" + className);
//--------------------新增代码------------------------
/*
为了方便,如果发现是一个后置处理器,将其放入到ArrayList集合中
1.在原生的spring容器中,对后置处理器还是走的getBean,createBean方法
2.如果这样做,需要在singletonObjects中加入相应业务逻辑
3.这里为了方便,我们简化处理
*/
//判断当前class有没有实现接口
//注意这里我们不能通过 instanceof 判断 class是否实现了 BeanPostProcessor
//因为clazz不是一个实例对象,而是一个Class对象,需要如下判断:
if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
BeanPostProcessor beanPostProcessor =
(BeanPostProcessor)clazz.newInstance();
//放入ArrayList集合中
beanPostProcessorList.add(beanPostProcessor);
continue;//简化处理,不再将后置处理器的bean信息放到beanDefinition对象中
}
//--------------------新增代码------------------------ //得到 BeanName-key
//... //将 Bean信息封装到 BeanDefinition对象-value
//... //将beanDefinition对象放入Map中
//...
} else {
//如果没有使用,则说明不是spring bean
System.out.println("不是一个 spring bean=" + clazz + " 类名=" + className);
}
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println("===========================");
}}} //完成createBean(BeanDefinition)方法
public Object createBean(String beanName, BeanDefinition beanDefinition) {
//得到Bean的class对象
Class clazz = beanDefinition.getClazz();
try {
//反射创建bean实例
Object instance = clazz.getDeclaredConstructor().newInstance();
// 这里要加入依赖注入的业务逻辑
//...
System.out.println("======已创建好实例=====" + instance); //--------------------新增代码start------------------------
//在bean的初始化方法前调用后置处理器的before方法
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
//在后置处理器的before方法,可以对容器的bean进行处理
// 然后返回处理后的bean实例。相当于做了前置处理
Object current = beanPostProcessor.
postProcessBeforeInitialization(instance, beanName);
//如果current为null,就不对instance做处理
if (current != null) {
instance = current;
}
}
//--------------------新增代码end------------------------ //判断是否要执行bean的初始化方法
// 1.判断当前创建的 bean对象是否实现了 InitializingBean
// 2.instanceof 表示判断对象的运行类型 是不是 某个类型或某个类型的子类型
if (instance instanceof InitializingBean) {
//3.将instance转成接口类型,调用初始化方法
try {
((InitializingBean) instance).afterPropertiesSet();
} catch (Exception e) {
e.printStackTrace();
}
} //--------------------新增代码start------------------------
// 在bean的初始化方法后调用后置处理器的 after方法
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
//在后置处理器的 after方法,可以对容器的 bean进行处理
// 然后返回处理后的bean实例。相当于做了后置处理
Object current = beanPostProcessor.
postProcessAfterInitialization(instance, beanName);
if (current != null) {
instance = current;
}
}
//--------------------新增代码end------------------------ return instance;
} catch (InstantiationException e) {
//...
}
//如果反射对象失败
return null;
}

8.测试类

//...
public class AppMain {
public static void main(String[] args) {
MySpringApplicationContext ioc =
new MySpringApplicationContext(MySpringConfig.class);
MonsterService monsterService = (MonsterService) ioc.getBean("monsterService");
monsterService.m1();
}
}

测试结果:成功在所有bean对象的初始化方法前后,调用后置处理器的方法(无论bean对象有无初始化方法)

后置处理器是对容器的所有bean生效,因此相当于可以对多个对象编程,即切面编程。日志,身份,权限,事务……都可以在后置处理器进行处理。

8.实现任务阶段6

8.1分析

阶段6目标:实现自己的AOP机制

在原生的Spring中,如果我们同时配置了后置处理器和切面类,并在后置处理器的两个方法中输出bean的运行类型。你会发现,在后置处理器的before...()方法输出bean的类型时,bean对象还是原生类型;但在后置处理器的after...()方法输出时,bean对象已经被封装成了代理对象。

当然,变化的只是aop的目标方法对应的bean对象

因此,我们需要编写方法,在后置处理器的after方法中,将原生的bean对象封装成代理对象并返回。在代理对象中切入要执行的前置/返回/异常/最终通知等。

8.2代码实现

1.为了测试,创建接口和实现类,模拟aop的需求

(1)SmartAnimal 接口

package com.li.spring.component;

/**
* @author 李
* @version 1.0
*/
public interface SmartAnimal {
public float getSum(float i, float j); public float getSub(float i, float j);
}

(2)SmartDog 实现类

package com.li.spring.component;

import com.li.spring.annotation.Component;

/**
* @author 李
* @version 1.0
*/
@Component(value = "smartDog")
public class SmartDog implements SmartAnimal {
@Override
public float getSum(float i, float j) {
float res = i + j;
System.out.println("SmartDog-getSum()-res=" + res);
return res;
} @Override
public float getSub(float i, float j) {
float res = i - j;
System.out.println("SmartDog-getSub()-res=" + res);
return res;
}
}

2.模拟切面类

原生的切面类有@Aspect,@Before,@AfterReturning,@AfterThrowing,@After等注解。

这里为了简化,先将SmartAnimalAspect 当做一个“切面类”来使用,后面再分析如何做得更加灵活

package com.li.spring.component;

/**
* @author 李
* @version 1.0
* 先将SmartAnimalAspect 当做一个切面类来使用
* 后面再分析如何做得更加灵活
*/
public class SmartAnimalAspect {
public static void showBeginLog() {
System.out.println("前置通知...");
} public static void showSuccessLog() {
System.out.println("返回通知...");
}
}

3.修改配置的后置处理器 MyBeanPostProcessor

在实现后置处理器时,调用createBean()方法反射bean对象后,会调用后置处理器的两个方法。因此,我们可以在postProcessAfterInitialization()方法中判断当前bean是否要返回代理对象,并进行处理。

部分代码:

package com.li.spring.component;

import com.li.spring.annotation.Component;
import com.li.spring.processor.BeanPostProcessor; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy; /**
* @author 李
* @version 1.0
* 这是我们自己的一个后置处理器,它实现了我们自定义的 BeanPostProcessor
*/
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
//...
//...
} @Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
System.out.println("后置处理器MyBeanPostProcessor的 After()方法被调用,bean类型="
+ bean.getClass() + ",bean的名字=" + beanName);
//实现aop,返回代理对象,即对bean进行包装
//先写死,后面我们可以通过注解的方式更加灵活运用
if ("smartDog".equals(beanName)) {
//使用jdk的动态代理,返回bean的代理对象
Object proxyInstance = Proxy.newProxyInstance(MyBeanPostProcessor.class.getClassLoader(),
bean.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("method=" + method.getName());
Object result = null;
//假设我们要进行前置,返回通知处理的方法是getSum()
//(原生的spring的通知方法是通过注解来获取的)
//这里我们后面再通过注解来做得更加灵活
if ("getSum".equals(method.getName())) {
//前置通知
SmartAnimalAspect.showBeginLog();
//目标方法
result = method.invoke(bean, args);
//返回通知
SmartAnimalAspect.showSuccessLog();
} else {
result = method.invoke(bean, args);
}
return result;
}
});
//如果bean需要返回代理对象,这里就直接return ProxyInstance
return proxyInstance;
}
//如果不需要AOP,直接返回 bean
return bean;
}
}

4.测试类

//...
public class AppMain {
public static void main(String[] args) {
MySpringApplicationContext ioc =
new MySpringApplicationContext(MySpringConfig.class);
SmartAnimal smartDog = (SmartAnimal) ioc.getBean("smartDog");
System.out.println("smartDog类型=" + smartDog.getClass());
smartDog.getSum(100, 20);
}
}

测试结果:成功通过后置处理器的 postProcessAfterInitialization 方法,对bean对象进行包装返回,同时通过invoke方法,插入前置通知和后置通知的方法。

拓展:如何做得更加灵活?

  1. 前面我们使用的硬编码,不灵活,但是aop的核心机制差不多如此。
  2. 如果要把aop做得更加灵活,需要实现一点其他的逻辑(注解+数据结构map+切入表达式,其实和aop机制关系不大了)

day13-实现Spring底层机制-03的更多相关文章

  1. Hibernate工作原理及为什么要用?. Struts工作机制?为什么要使用Struts? spring工作机制及为什么要用?

    三大框架是用来开发web应用程序中使用的.Struts:基于MVC的充当了其中的试图层和控制器Hibernate:做持久化的,对JDBC轻量级的封装,使得我们能过面向对象的操作数据库Spring: 采 ...

  2. spring工作机制及为什么要用?

    spring工作机制及为什么要用?1.spring mvc请所有的请求都提交给DispatcherServlet,它会委托应用系统的其他模块负责对请求进行真正的处理工作.2.DispatcherSer ...

  3. 如何妙用Spring 数据绑定机制?

    前言 在剖析完 「Spring Boot 统一数据格式是怎么实现的? 」文章之后,一直觉得有必要说明一下 Spring's Data Binding Mechanism 「Spring 数据绑定机制」 ...

  4. Spring 事务机制详解

    原文出处: 陶邦仁 Spring事务机制主要包括声明式事务和编程式事务,此处侧重讲解声明式事务,编程式事务在实际开发中得不到广泛使用,仅供学习参考. Spring声明式事务让我们从复杂的事务处理中得到 ...

  5. spring工作机制

    Hibernate.struts,还差一个spring 就一起发出去.. spring工作机制及为什么要用? 1.springmvc请所有的请求都提交给DispatcherServlet,它会委托应用 ...

  6. [转]STL 容器一些底层机制

    1.vector 容器 vector 的数据安排以及操作方式,与 array 非常相似.两者的唯一区别在于空间的运用的灵活性.array 是静态空间,一旦配置了就不能改变,vector 是动态数组.在 ...

  7. Spring 事务机制详解(事务的隔离性和传播性)

    原文出处: 陶邦仁 Spring事务机制主要包括声明式事务和编程式事务,此处侧重讲解声明式事务,编程式事务在实际开发中得不到广泛使用,仅供学习参考. Spring声明式事务让我们从复杂的事务处理中得到 ...

  8. Spring事件机制详解

    一.前言 说来惭愧,对应Spring事件机制之前只知道实现 ApplicationListener 接口,就可以基于Spring自带的事件做一些事情(如ContextRefreshedEvent),但 ...

  9. C++ STL容器底层机制

    1.vector容器 vector的数据安排以及操作方式,与array非常相似.两者的唯一区别在于空间的运用的灵活性.array是静态空间,一旦配置了就不能改变.vector是动态空间,随着元素的加入 ...

  10. 探索C++的底层机制

    探索C++的底层机制 在看这篇文章之前,请你先要明白一点:那就是c++为我们所提供的各种存取控制仅仅是在编译阶段给我们的限制,也就是说是编译器确保了你在完成任务之前的正确行为,如果你的行为不正确,那么 ...

随机推荐

  1. 在ubuntu 上安装golang

    https://golang.google.cn/dl/ 方式一 下载安装包 wget https://golang.google.cn/dl/go1.19.linux-amd64.tar.gz 解压 ...

  2. pycharm系列---django

    manage debug Python Console基本配置 DJANGO_SETTINGS_MODULE=mini_project.settings import sys import djang ...

  3. 基于socket开发网络调试助手

    1.什么是Socket? 在计算机领域socket被翻译为套接字,它是计算机之间进行通信的一种方式,通过socket这种约定,一台计算机可以向另外一台计算机发送数据和接收数据. 2.Socket的本质 ...

  4. [ PHP 内核与扩展开发系列] 内存管理 —— 引用计数

    对于 PHP 这种需要同时处理多个请求的程序来说,申请和释放内存的时候应该慎之又慎,一不小心便会酿成大错.另一方面,除了要安全申请和释放内存外,还应该做到内存的最小化使用,因为它可能要处理每秒钟数以千 ...

  5. 关于phalcon框架中DI的理解

    DI(依赖注入) https://www.imooc.com/learn/867 https://www.imooc.com/learn/912

  6. PHY驱动调试之 ---PHY设备驱动(三)

    1. 前言 内核版本:linux 4.9.225,以freescale为例.(部分内容待修改和补充,不一定准确) 2. 概述 上一篇文章讲了控制器的驱动使用的是platform总线的连接方式,本节要讲 ...

  7. 自学 TypeScript 第四天,手把手项目搭建

    前言: 学了三天,我们学习了 TS 的基本类型声明,TS 的编译,webpack 打包,其实也就差不多了,剩下的也就一些 类,继承,构造函数,抽象类,泛型一些的,如果都细致的讲可能写好久,感兴趣的可以 ...

  8. C++初阶(list容器+模拟实现)

    list介绍 list的本质是一个带头的双向循环链表. 链表是一种物理存储单元上非连续.非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的.链表由一系列结点(链表中每一个元素称为结点 ...

  9. jquery 简单分页插件jQuerypage

    昨天项目手机端要用到table的分页,考虑到手机端界面小,系统数据不多,在没考虑大批量数据处理的前提前就下载了这个插件,简单. 展示数据datas为json格式. <!DOCTYPE html& ...

  10. .net 温故知新:【10】.NET ORM框架EFCore使用入门之CodeFirs、DBFirst

    前言:本系列是我自己学习.net相关知识,以便跟上.net跨平台的步伐,目前工作原因基本在.net Framework4.7以下,所以才有了这一系列的学习总结,但是并不是从基本的C#语法和基础知识开始 ...