上文中, 我们学习到了 Spring系列四:AOP切面编程
接下来我们学习, 手动实现Spring底层机制
🍝类加载器和classpath详解
● java的类加载器 3 种
- Bootstrap类加载器---------------------对应路径jre\lib
- Ext类加载器------------------------------对应路径jre\lib\ext
- App类加载器-----------------------------对应路径classpath
●classpath 类路径, 就是 java.exe 执行时, 指定的路径, 比如
复制, 粘贴到txt文档中,,如下
"D:\Program Files\Java\jdk1.8.0_361\bin\java.exe
" -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:55339,suspend=y,server=n -javaagent:C:\Users\97896\AppData\Local\JetBrains\IntelliJIdea2022.3\captureAgent\debugger-agent.jar -Dfile.encoding=GBK -classpath
“D:\Program Files\Java\jdk1.8.0_361\jre\lib\charsets.jar;D:\Program Files\Java\jdk1.8.0_361\jre\lib\deploy.jar;D:\Program Files\Java\jdk1.8.0_361\jre\lib\ext\access-bridge-64.jar;D:\Program Files\Java\jdk1.8.0_361\jre\lib\ext\cldrdata.jar;D:\Program Files\Java\jdk1.8.0_361\jre\lib\ext\dnsns.jar;D:\Program Files\Java\jdk1.8.0_361\jre\lib\ext\jaccess.jar;D:\Program Files\Java\jdk1.8.0_361\jre\lib\ext\jfxrt.jar;D:\Program Files\Java\jdk1.8.0_361\jre\lib\ext\localedata.jar;D:\Program Files\Java\jdk1.8.0_361\jre\lib\ext\nashorn.jar;D:\Program Files\Java\jdk1.8.0_361\jre\lib\ext\sunec.jar;D:\Program Files\Java\jdk1.8.0_361\jre\lib\ext\sunjce_provider.jar;D:\Program Files\Java\jdk1.8.0_361\jre\lib\ext\sunmscapi.jar;D:\Program Files\Java\jdk1.8.0_361\jre\lib\ext\sunpkcs11.jar;D:\Program Files\Java\jdk1.8.0_361\jre\lib\ext\zipfs.jar;D:\Program Files\Java\jdk1.8.0_361\jre\lib\javaws.jar;D:\Program Files\Java\jdk1.8.0_361\jre\lib\jce.jar;D:\Program Files\Java\jdk1.8.0_361\jre\lib\jfr.jar;D:\Program Files\Java\jdk1.8.0_361\jre\lib\jfxswt.jar;D:\Program Files\Java\jdk1.8.0_361\jre\lib\jsse.jar;D:\Program Files\Java\jdk1.8.0_361\jre\lib\management-agent.jar;D:\Program Files\Java\jdk1.8.0_361\jre\lib\plugin.jar;D:\Program Files\Java\jdk1.8.0_361\jre\lib\resources.jar;D:\Program Files\Java\jdk1.8.0_361\jre\lib\rt.jar;D:\idea_project\zzw_spring\zzw-spring\target\classes;
D:\maven\repository\org\springframework\spring-context\5.3.8\spring-context-5.3.8.jar;D:\maven\repository\org\springframework\spring-aop\5.3.8\spring-aop-5.3.8.jar;D:\maven\repository\org\springframework\spring-beans\5.3.8\spring-beans-5.3.8.jar;D:\maven\repository\org\springframework\spring-core\5.3.8\spring-core-5.3.8.jar;D:\maven\repository\org\springframework\spring-jcl\5.3.8\spring-jcl-5.3.8.jar;D:\maven\repository\org\springframework\spring-expression\5.3.8\spring-expression-5.3.8.jar;D:\maven\repository\org\springframework\spring-aspects\5.3.8\spring-aspects-5.3.8.jar;D:\maven\repository\org\aspectj\aspectjweaver\1.9.6\aspectjweaver-1.9.6.jar;D:\Program Files\IntelliJ IDEA 2022.3.2\lib\idea_rt.jar” com.zzw.spring.AppMain
💗实现任务阶段1
🍚编写自己Spring容器, 扫描包得到bean的class对象
编写自己Spring容器, 扫描包得到bean的class对象
第一阶段的代码参考👉Spring系列三:基于注解配置bean
1.在zzw-spring项目上创建子模块(java maven module)
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.zzw</groupId>
<artifactId>zzw-myspring</artifactId>
<version>1.0-SNAPSHOT</version>
</project>
2.在 子模块 com.zzw.spring.annotation
包下新建注解ComponentScan
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ComponentScan {
//通过value可以指定要扫描的包
String value() default "";
}
同包下, 自定义 Component
注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
//通过value可以给注入的bean/对象指定名字
String value() default "";
}
3.在com.zzw.spring.ioc
包下新建 ZzwSpringConfig
//这是一个配置类, 作用类似于我们原生spring的 beans.xml 容器配置文件
@ComponentScan(value = "com.zzw.spring.component")
public class ZzwSpringConfig {
}
4.在com.zzw.spring.component
包下新建 MonsterService,
MonsterDao
//MonsterService 是一个Service
//1.如果指定了value, 那么在注入spring容器时, 以我们指定的为准
//2.如果没有指定value, 则使用类名首字母小写的方式命名
@Component(value = "monsterService")// 把MonsterService注入我们自己写的spring容器中
public class MonsterService {
}
@Component(value = "monsterDao")
public class MonsterDao {
}
5.在com.zzw.spring.ioc
包下新建 ZzwSpringApplicationContext
//模拟spring-ioc容器
//ZzwSpringApplicationContext 类的作用类似于Spring原生ioc容器
public class ZzwSpringApplicationContext {
private Class configClass;
//构造器
public ZzwSpringApplicationContext(Class configClass) {
this.configClass = configClass;
System.out.println("this.configClass=" + this.configClass);
//获取要扫描的包
//1.先得到ZzwSpringConfig配置类的 @ComponentScan(value = "com.zzw.spring.component")
ComponentScan componentScan =
(ComponentScan) this.configClass.getDeclaredAnnotation(ComponentScan.class);
//2.通过componentScan的value => 即要扫描的包
String path = componentScan.value();
System.out.println("要扫描的包=" + path);//com.zzw.spring.component
//得到要扫描的包下的所有资源(.class 类)
//1.得到类的加载器->APP 类加载器
ClassLoader classLoader = ZzwSpringApplicationContext.class.getClassLoader();
//2.通过类的加载器获取到要扫描包的资源url =>类似一个路径
path = path.replace(".", "/");//一定要把 .替换成 / com/zzw/spring/component
URL resource = classLoader.getResource(path);
System.out.println("resource=" + resource);//resource=file:/D:/idea_project/zzw_spring/zzw-myspring/target/classes/com/zzw/spring/component
//3.将要加载的资源(.class) 路径下的文件进行遍历
File file = new File(resource.getFile());//在io中, 目录也是文件
if (file.isDirectory()) {
File[] files = file.listFiles();
for (File f : files) {
//System.out.println(f.getAbsolutePath());//D:\idea_project\zzw_spring\zzw-myspring\target\classes\com\zzw\spring\component\MonsterDao.class
String fileAbsolutePath = f.getAbsolutePath();
//这里我们只处理.class文件
if (fileAbsolutePath.endsWith(".class")) {
//1.获取类名
String className =
fileAbsolutePath.substring(fileAbsolutePath.lastIndexOf("\\") + 1, fileAbsolutePath.lastIndexOf(".class"));
//2.获取类的完整的路径(全类名)
// path.replace("/", ".") => com.zzw.spring.component
String classFullName = path.replace("/", ".") + "." + className;//比如 com.zzw.spring.component.UserDao
//3.判断该类是不是需要注入到容器, 就看该类是不是有注解 @Component @Controller...
try {
Class<?> clazz = classLoader.loadClass(classFullName);
//这里就是演示了一下机制
if (clazz.isAnnotationPresent(Component.class)) {
//如果该类使用了@Component注解, 说明是Spring bean
System.out.println("是一个Spring bean=" + clazz + " 类名=" + className);
} else {
//如果该类没有使用@Component注解, 说明不是Spring bean
System.out.println("不是一个Spring bean=" + clazz + " 类名=" + className);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
System.out.println("=====================================================================================");
}
}
//编写方法返回容器对象
public Object getBean(String name) {
return null;
}
}
6.在 com.zzw.spring
包下新建 AppMain.java
public class AppMain {
public static void main(String[] args) {
ZzwSpringApplicationContext ioc =
new ZzwSpringApplicationContext(ZzwSpringConfig.class);
}
}
运行, 发现报错 java: Compilation failed: internal java compiler error
. 这个错误一般是版本造成的.
改正之后, 运行结果 (这个Car类是component
包下新建的, 没有被@Component修饰)
this.configClass=class com.zzw.spring.ioc.ZzwSpringConfig
要扫描的包=com.zzw.spring.component
resource=file:/D:/idea_project/zzw_spring/zzw-myspring/target/classes/com/zzw/spring/component
不是一个Spring bean=class com.zzw.spring.component.Car 类名=Car
是一个Spring bean=class com.zzw.spring.component.MonsterDao 类名=MonsterDao
是一个Spring bean=class com.zzw.spring.component.MonsterService 类名=MonsterService
=============================
💗实现任务阶段2
🍚扫描将bean信息封装到BeanDefinition对象, 并放入到Map
扫描将bean信息封装到BeanDefinition对象, 并放入到Map
1.在 com.zzw.spring.annotation
包下新建注解 Scope
//Scope 可以指定Bean的作用范围[singleton, prototype]
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Scope {
//通过value可以指定singleton, prototype
String value() default "";
}
给MonsterService
添加自定义的@Scope
. MonsterDao
不添加, 默认是单例的.
@Component(value = "monsterService")// 把MonsterService注入我们自己写的spring容器中
@Scope(value = "prototype")
public class MonsterService {
}
@Component(value = "monsterDao")
public class MonsterDao {
}
2.在com.zzw.spring.ioc
包下新建BeanDefinition
类
//BeanDefinition 用于封装/记录Bean的信息[1. scope 2. Bean对应的class对象, 反射可以生成对应的对象]
public class BeanDefinition {
private String scope;
private Class clazz;
//可以根据需求, 进行扩展
//getter, setter方法, toString方法
}
3.ZzwSpringApplicationContext
增添代码 -> 封装BeanDefinition 放入到Map
//定义属性BeanDefinitionMap -> 存放BeanDefinition对象
private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap
= new ConcurrentHashMap<>();
//定义singletonObjects -> 存放单例对象
private ConcurrentHashMap<String, Object> singletonObjects
= new ConcurrentHashMap<>();
//这里就是演示了一下机制
if (clazz.isAnnotationPresent(Component.class)) {
//如果该类使用了@Component注解, 说明是Spring bean
System.out.println("是一个Spring bean=" + clazz + " 类名=" + className);
//先得到beanName
//1.得到Component注解
Component componentAnnotation = clazz.getDeclaredAnnotation(Component.class);
//2.得到配置的value值 如果程序员没有配置value[后续处理...]
String beanName = componentAnnotation.value();
if ("".equals(beanName)) {//如果没有写value
//将该类的类名首字母小写作为beanName
//StringUtils - import org.apache.commons.lang.StringUtils;
beanName = StringUtils.uncapitalize(className);
}
//3.将Bean的信息封装到BeanDefinition对象中 -> 放入到beanDifinitionMap
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setClazz(clazz);
//4.获取Scope值
if (clazz.isAnnotationPresent(Scope.class)) {
//如果配置了Scope, 获取它指定的值
Scope scopeAnnotation = clazz.getDeclaredAnnotation(Scope.class);
beanDefinition.setScope(scopeAnnotation.value());
} else {
//如果没有配置Scope, 就配置默认值-singleton
beanDefinition.setScope("singleton");
}
//将beanDefinition, 对象放入到Map
beanDefinitionMap.put(beanName, beanDefinition);
} else {
//如果该类没有使用@Component注解, 说明不是Spring bean
System.out.println("不是一个Spring bean=" + clazz + " 类名=" + className);
}
Spring原生框架可以使用StringUtils工具类, 即import org.springframework.util.StringUtils;
但我们这里是手写的spring容器, 所以StringUtils无法使用.
StringUtils在commons-lang
包下也有, 所以为解决StringUtils问题, 我们在pom.xml
引入依赖. 但是ZzwSpringApplicationContext
类的代码会报错
<dependencies>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
</dependencies>
为了解决上述错误, 引入下面方案临时切换版本
, 可以作为一个临时方案.
将构造器中的全部代码挪到本类的beanDefinitionsByScan
方法内
//该方法完成对指定包的扫描, 并将Bean信息封装到BeanDefinition对象, 再放入到Map
public void beanDefinitionsByScan(Class configClass) {
}
那么ZzwSpringApplicationContext
类大体面貌如下
public class ZzwSpringApplicationContext {
private Class configClass;
//定义属性BeanDefinitionMap -> 存放BeanDefinition对象
private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap
= new ConcurrentHashMap<>();
//定义singletonObjects -> 存放单例对象
private ConcurrentHashMap<String, Object> singletonObjects
= new ConcurrentHashMap<>();
//构造器
public ZzwSpringApplicationContext(Class configClass) {
beanDefinitionsByScan(configClass);
System.out.println("beanDefinitionMap=" + beanDefinitionMap);
}
//该方法完成对指定包的扫描, 并将Bean信息封装到BeanDefinition对象, 再放入到Map
public void beanDefinitionsByScan(Class configClass) {
//代码省略...
}
//编写方法返回容器对象
public Object getBean(String name) {
return null;
}
}
💗实现任务阶段3
🍚初始化bean单例池, 并完成getBean方法, createBean方法
初始化bean单例池, 并完成getBean方法, createBean方法
1.ZzwSpringApplicationContext
类中添加createBean
方法
//完成createBean(BeanDefinition beanDefinition) 方法
//说明: 目前我们简单实现
private Object createBean(BeanDefinition beanDefinition) {
//得到Bean的class对象
Class clazz = beanDefinition.getClazz();
//使用反射得到实例
try {
Object instance = clazz.getDeclaredConstructor().newInstance();
System.out.println("======创建好实例======" + instance);
return instance;
} catch (Exception e) {
}
//如果反射创建对象失败
return null;
}
2.ZzwSpringApplicationContext
类的构造器添加如下代码
补充: 遍历枚举
//构造器
public ZzwSpringApplicationContext(Class configClass) {
//完成扫描指定的包
beanDefinitionsByScan(configClass);
//通过beanDefinitionMap, 初始化singletonObjects 单例池
//封装成方法
//遍历所有的beanDefinition对象
Enumeration<String> keys = beanDefinitionMap.keys();
while (keys.hasMoreElements()) {
//得到beanName, 得到对应的beanDefinition
String beanName = keys.nextElement();
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
//判断该bean是singleton, 还是prototype
if ("singleton".equals(beanDefinition.getScope())) {
//将该bean实例放入到singletonObjects 集合
Object bean = createBean(beanDefinition);
singletonObjects.put(beanName, bean);
}
}
System.out.println("singletonObjects 单例池=" + singletonObjects);
System.out.println("beanDefinitionMap=" + beanDefinitionMap);
}
3.ZzwSpringApplicationContext
类的getBean()方法, 添加如下代码
//编写方法getBean(String name), 编写方法返回容器对象
public Object getBean(String name) {
//加一个判断条件: 传入的beanName是否在beanDefinitionMap中存在
if (beanDefinitionMap.containsKey(name)) {//如果存在
BeanDefinition beanDefinition = beanDefinitionMap.get(name);
//得到beanDefinition的scope, 分别进行处理
if ("singleton".equalsIgnoreCase(beanDefinition.getScope())) {
//说明是单例配置, 就直接从单例池获取
return singletonObjects.get(name);
} else {//如果不是单例的, 我们就调用createBean, 反射一个对象返回
return createBean(beanDefinition);
}
} else {//如果不存在
//抛出一个空指针异常, 也可以自定义
throw new NullPointerException("没有该bean");
}
}
4.测试
public class AppMain {
public static void main(String[] args) {
ZzwSpringApplicationContext ioc =
new ZzwSpringApplicationContext(ZzwSpringConfig.class);
MonsterService monsterService = (MonsterService) ioc.getBean("monsterService");
MonsterService monsterService2 = (MonsterService) ioc.getBean("monsterService");
System.out.println("monsterService=" + monsterService);
System.out.println("monsterService2=" + monsterService2);
MonsterDao monsterDao = (MonsterDao) ioc.getBean("monsterDao");
MonsterDao monsterDao2 = (MonsterDao) ioc.getBean("monsterDao");
System.out.println("monsterDao=" + monsterDao);
System.out.println("monsterDao2=" + monsterDao2);
System.out.println("ok");
}
}
运行结果
this.configClass=class com.zzw.spring.ioc.ZzwSpringConfig
要扫描的包=com.zzw.spring.component
resource=file:/D:/idea_project/zzw_spring/zzw-myspring/target/classes/com/zzw/spring/component
不是一个Spring bean=class com.zzw.spring.component.Car 类名=Car
是一个Spring bean=class com.zzw.spring.component.MonsterDao 类名=MonsterDao
是一个Spring bean=class com.zzw.spring.component.MonsterService 类名=MonsterService
=====================================================================================
singletonObjects 单例池={monsterDao=com.zzw.spring.component.MonsterDao@5479e3f}
beanDefinitionMap={monsterService=BeanDefinition{scope='prototype', clazz=class com.zzw.spring.component.MonsterService}, monsterDao=BeanDefinition{scope='singleton', clazz=class com.zzw.spring.component.MonsterDao}}
monsterService=com.zzw.spring.component.MonsterService@66133adc
monsterService2=com.zzw.spring.component.MonsterService@7bfcd12c
monsterDao=com.zzw.spring.component.MonsterDao@5479e3f
monsterDao2=com.zzw.spring.component.MonsterDao@5479e3f
ok
💗实现任务阶段4
🍚完成依赖注入
完成依赖注入
1.自定义@Autowired
注解并配置
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
//这个属性的功能, 可以自己完成.
//boolean required() default true;
}
MonsterDao
添加hi
方法
@Component(value = "monsterDao")
public class MonsterDao {
public void hi() {
System.out.println("MonsterDao hi()...");
}
}
MonsterService
增加代码
@Component(value = "monsterService")// 把MonsterService注入我们自己写的spring容器中
@Scope(value = "prototype")
public class MonsterService {
//这里我们使用自己的注解@Autowired 来修饰属性
//表示该属性, 是通过容器完成依赖注入
//说明: 我们实现按照名字来进行组装即可
@Autowired
private MonsterDao monsterDao;
public void m1() {
monsterDao.hi();
}
}
这时测试, 会报空指针异常. Exception in thread "main" java.lang.NullPointerException
这是因为这是我们自己定义的@Autowired注解, 还没有完成依赖注入.
public class AppMain {
public static void main(String[] args) {
ZzwSpringApplicationContext ioc =
new ZzwSpringApplicationContext(ZzwSpringConfig.class);
//测试一下依赖注入的功能
MonsterService monsterService = (MonsterService) ioc.getBean("monsterService");
monsterService.m1();
}
2.ZzwSpringApplicationContext
类的 createBean()
方法 增加以下代码 实现依赖注入
private Object createBean(BeanDefinition beanDefinition) {
//得到Bean的class对象
Class clazz = beanDefinition.getClazz();
//使用反射得到实例
try {
Object instance = clazz.getDeclaredConstructor().newInstance();
//分析: 这里会加入依赖注入的业务逻辑
//1.遍历当前要创建的对象的所有字段
for (Field declaredField : clazz.getDeclaredFields()) {
//2.判断这个字段是否有@autowired修饰
if (declaredField.isAnnotationPresent(Autowired.class)) {
Autowired autowiredAnnotation =
(Autowired) clazz.getDeclaredAnnotation(Autowired.class);
//处理@Autowired 的required.
//如果 required 为 true, 进行组装; 如果 required 为 false, 不进行组装
//3.得到这个字段的名字
String name = declaredField.getName();
//4.通过getBean方法来获取要组装的对象
Object bean = getBean(name);
//5.进行组装
//因为属性是私有的, 需要暴破
declaredField.setAccessible(true);
declaredField.set(instance, bean);
}
}
System.out.println("======创建好实例======" + instance);
return instance;
} catch (Exception e) {
}
//如果反射创建对象失败
return null;
}
💗实现任务阶段5
🍚bean后置处理器实现
bean后置处理器实现
思路:先完成原生Spring 使用Bean后置处理器的案例, 然后实现自己的bean后置处理器
bean的生命周期~传送门
在idea中 shortcuts: ctrl+n
, 输入 InitializingBean, 搜索
1.在com.zzw.spring.processor
包下定义接口 InitializingBean
. 实现该接口的Bean, 需要实现Bean初始化方法, 可以参考 原生Spring规范来定义这个接口
👆
//解读
//1.我们根据原生Spring 定义了一个InitializingBean
//2.这个接口有一个方法 void afterPropertiesSet() throws Exception;
//3.afterPropertiesSet() 在Bean的 setter后执行, 即就是我们原来的初始化方法
//4.当一个Bean实现这个接口后, 就实现这个afterPropertiesSet(), 这个方法就是初始化方法
public interface InitializingBean {
void afterPropertiesSet() throws Exception;
}
MonsterService
实现这个接口
@Component(value = "monsterService")// 把MonsterService注入我们自己写的spring容器中
@Scope(value = "prototype")
public class MonsterService implements InitializingBean {
@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 初始化方法被调用 程序员在这里加入初始化的业务...");
}
}
2.在创建好Bean实例后, 判断是否需要进行初始化. 【编程心得: 容器中常用的一个方法是, 根据该类是否实现了某个接口, 来判断是否要执行某个业务逻辑, 这里其实就是java基础的接口编程实际应用】
在ZzwSpringApplicationContext
类的createBean
方法遍历字段后
加入如下代码
System.out.println("======创建好实例======" + instance);
//这里判断是否要执行Bean的初始化方法
//1.判断当前创建的Bean对象是否实现了InitializingBean接口
//2.instanceof 表示判断某个对象的运行类型是不是某个类型或者某个类型的子类型
//3.这里使用到接口编程
if (instance instanceof InitializingBean) {
//将instance转成接口InitializingBean
((InitializingBean) instance).afterPropertiesSet();
}
return instance;
注销ZzwSpringApplicationContext
类的构造器中的下面两条代码
//System.out.println("singletonObjects 单例池=" + singletonObjects);
//System.out.println("beanDefinitionMap=" + beanDefinitionMap);
运行效果
//上面输出结果忽略...
======创建好实例======com.zzw.spring.component.MonsterDao@5479e3f
======创建好实例======com.zzw.spring.component.MonsterService@33e5ccce
MonsterService 初始化方法被调用 程序员在这里加入初始化的业务...
//MonsterDao hi()...
//ok
MonsterDao
实现InitializingBean
的方法
@Component(value = "monsterDao")
//@Scope(value = "prototype")
public class MonsterDao implements InitializingBean {
public void hi() {
System.out.println("MonsterDao hi()...");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("MonsterDao 初始化方法被调用...");
}
}
运行效果
上面输出结果忽略...
======创建好实例======com.zzw.spring.component.MonsterDao@27082746
MonsterDao 初始化方法被调用...
======创建好实例======com.zzw.spring.component.MonsterService@33e5ccce
MonsterService 初始化方法被调用 程序员在这里加入初始化的业务...
//MonsterDao hi()...
//ok
3.在com.zzw.spring.processor
包下, 参考原生Spring容器
, 定义BeanPostProcessor
接口
//解读
//1.参考原生Spring容器定义一个接口BeanPostProcessor
//2.该接口有两个方法postProcessBeforeInitialization 和 postProcessAfterInitialization
//3.这两个方法, 会对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;
}
}
4.在com.zzw.spring.component
包下新建ZzwBeanPostProcessor
/**
* 说明
* 1.这是我们自己的一个后置处理器
* 2.实现了BeanPostProcessor
* 3.我们可以重写before和after方法
* 4.在Spring容器中, 仍然把ZzwBeanPostProcessor当做一个Bean对象, 要注入到容器
* 5.@Component 标识
* 6.我们要让ZzwBeanPostProcessor成为真正的后置处理器, 需要在容器中加入业务代码
* 7.还要考虑多个后置处理器对象注入到容器的问题
*/
@Component
public class ZzwBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
System.out.println("后置处理器ZzwBeanPostProcessor Before方法 被调用... bean类型="
+ bean.getClass() + " bean的名字=" + beanName);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
System.out.println("后置处理器ZzwBeanPostProcessor After方法 被调用... bean类型="
+ bean.getClass() + " bean的名字=" + beanName);
return bean;
}
}
这时后置处理器不会生效, 继续写代码支撑.
5.在ZzwSpringApplicationContext
类中定义一个属性, 在beanDefinitionsByScan
方法中添加代码
//代码省略...
//定义一个属性beanPostProcessorList, => 存放后置处理器
private List<BeanPostProcessor> beanPostProcessorList
= new ArrayList<>();
//代码省略...
public void beanDefinitionsByScan(Class configClass) {
//代码省略...
try {
Class<?> clazz = classLoader.loadClass(classFullName);
//这里就是演示了一下机制
if (clazz.isAnnotationPresent(Component.class)) {
//如果该类使用了@Component注解, 说明是Spring bean
System.out.println("是一个Spring bean=" + clazz + " 类名=" + className);
//说明👇
//1.为了方便, 这里将后置处理器放入到一个ArrayList
//2.如果发现是一个后置处理器, 就放入到beanPostProcessorList
//3.在原生的Spring容器中, 对后置处理器还是走的getBean(),createBean()
// ,但是需要我们在singletonObjects中加入相应的业务逻辑
//4.因为这里我们只是为了学习后置处理器的机制, 就简化处理.
//5.如果仍然走以前的逻辑, 也可以, 就是要麻烦一点.
//判断当前这个clazz有没有实现我们定义的BeanPostProcessor接口
//说明, 这里我们不能用 instanceof 来判断clazz是否实现了BeanPostProcessor接口
//原因: clazz不是一个实例对象, 而是一个类对象/clazz, 使用isAssignableFrom
// 将其当作一个语法理解
if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
BeanPostProcessor beanPostProcessor
= (BeanPostProcessor) clazz.newInstance();
//将其放入到BeanPostProcessorList
beanPostProcessorList.add(beanPostProcessor);
}👆
//代码省略...
} else {
//如果该类没有使用@Component注解, 说明不是Spring bean
System.out.println("不是一个Spring bean=" + clazz + " 类名=" + className);
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
6.在ZzwSpringApplicationContext
类的createBean
方法中添加如下代码
System.out.println("======创建好实例======" + instance);
👇//我们在Bean的初始化方法前, 调用后置处理器的before方法
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
//在后置处理器的before方法, 可以对容器bean进行处理
//然后返回处理后的bean实例, 相当于做了一个前置处理
instance =
beanPostProcessor.postProcessBeforeInitialization(instance, "...");
👆}
//这里判断是否要执行Bean的初始化方法
//1.判断当前创建的Bean对象是否实现了InitializingBean接口
//2.instanceof 表示判断某个对象的运行类型是不是某个类型或者某个类型的子类型
//3.这里使用到接口编程
if (instance instanceof InitializingBean) {
//3.将instance转成接口InitializingBean
((InitializingBean) instance).afterPropertiesSet();
}
👇//我们在Bean的初始化方法后, 调用后置处理器的after方法
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
//在后置处理器的after方法, 可以对容器bean进行处理
//然后返回处理后的bean实例, 相当于做了一个后置处理
instance =
beanPostProcessor.postProcessAfterInitialization(instance, "...");
👆}
return instance;
运行结果
//前面输出结果忽略...
======创建好实例======com.zzw.spring.component.ZzwBeanPostProcessor@4c75cab9
后置处理器ZzwBeanPostProcessor Before方法 被调用... bean类型=class com.zzw.spring.component.ZzwBeanPostProcessor bean的名字=...
后置处理器ZzwBeanPostProcessor After方法 被调用... bean类型=class com.zzw.spring.component.ZzwBeanPostProcessor bean的名字=...
======创建好实例======com.zzw.spring.component.MonsterDao@67117f44
后置处理器ZzwBeanPostProcessor Before方法 被调用... bean类型=class com.zzw.spring.component.MonsterDao bean的名字=...
MonsterDao 初始化方法被调用...
后置处理器ZzwBeanPostProcessor After方法 被调用... bean类型=class com.zzw.spring.component.MonsterDao bean的名字=...
======创建好实例======com.zzw.spring.component.MonsterService@3930015a
后置处理器ZzwBeanPostProcessor Before方法 被调用... bean类型=class com.zzw.spring.component.MonsterService bean的名字=...
MonsterService 初始化方法被调用 程序员在这里加入初始化的业务...
后置处理器ZzwBeanPostProcessor After方法 被调用... bean类型=class com.zzw.spring.component.MonsterService bean的名字=...
//MonsterDao hi()...
//ok
这里有两份ZzwBeanPostProcessor, 去掉singletonObjects中的后置处理器对象, 保留beanPostProcessorList中的, 因为我们自定义的beanPostProcessorList操作起来方便.
在ZzwSpringApplicationContext
的beanDefinitionsByScan
方法内添加continue
跳过循环
if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
BeanPostProcessor beanPostProcessor
= (BeanPostProcessor) clazz.newInstance();
//将其放入到BeanPostProcessorList
beanPostProcessorList.add(beanPostProcessor);
👉continue;👈
}
运行结果
//前面输出结果忽略...
======创建好实例======com.zzw.spring.component.MonsterDao@7bfcd12c
后置处理器ZzwBeanPostProcessor Before方法 被调用... bean类型=class com.zzw.spring.component.MonsterDao bean的名字=...
MonsterDao 初始化方法被调用...
后置处理器ZzwBeanPostProcessor After方法 被调用... bean类型=class com.zzw.spring.component.MonsterDao bean的名字=...
======创建好实例======com.zzw.spring.component.MonsterService@1ef7fe8e
后置处理器ZzwBeanPostProcessor Before方法 被调用... bean类型=class com.zzw.spring.component.MonsterService bean的名字=...
MonsterService 初始化方法被调用 程序员在这里加入初始化的业务...
后置处理器ZzwBeanPostProcessor After方法 被调用... bean类型=class com.zzw.spring.component.MonsterService bean的名字=...
//MonsterDao hi()...
//ok
7.测试
让Car类实现InitializingBean
初始化方法
@Component
public class Car implements InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("Car的初始化方法...");
}
}
运行结果
//前面输出结果忽略...
======创建好实例======com.zzw.spring.component.Car@6f79caec
后置处理器ZzwBeanPostProcessor Before方法 被调用... bean类型=class com.zzw.spring.component.Car bean的名字=...
Car的初始化方法...
后置处理器ZzwBeanPostProcessor After方法 被调用... bean类型=class com.zzw.spring.component.Car bean的名字=...
======创建好实例======com.zzw.spring.component.MonsterDao@5d3411d
后置处理器ZzwBeanPostProcessor Before方法 被调用... bean类型=class com.zzw.spring.component.MonsterDao bean的名字=...
MonsterDao 初始化方法被调用...
后置处理器ZzwBeanPostProcessor After方法 被调用... bean类型=class com.zzw.spring.component.MonsterDao bean的名字=...
======创建好实例======com.zzw.spring.component.MonsterService@50675690
后置处理器ZzwBeanPostProcessor Before方法 被调用... bean类型=class com.zzw.spring.component.MonsterService bean的名字=...
MonsterService 初始化方法被调用 程序员在这里加入初始化的业务...
后置处理器ZzwBeanPostProcessor After方法 被调用... bean类型=class com.zzw.spring.component.MonsterService bean的名字=...
//后面输出结果忽略...
ZzwBeanPostProcessor
的before方法
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
//这里一定要体会到后置处理器是对容器创建的所有bean生效
//, 相当于是可以对多个对象编程, 切面编程
//日志, 权限, 身份, 事务...
if (bean instanceof Car) {
System.out.println("这是一个Car对象, 我可以处理");
//((Car) bean)
}
System.out.println("后置处理器ZzwBeanPostProcessor Before方法 被调用... bean类型="
+ bean.getClass() + " bean的名字=" + beanName);
return bean;
}
运行结果
//前面输出结果忽略...
======创建好实例======com.zzw.spring.component.Car@6f79caec
这是一个Car对象, 我可以处理
后置处理器ZzwBeanPostProcessor Before方法 被调用... bean类型=class com.zzw.spring.component.Car bean的名字=...
Car的初始化方法...
后置处理器ZzwBeanPostProcessor After方法 被调用... bean类型=class com.zzw.spring.component.Car bean的名字=...
======创建好实例======com.zzw.spring.component.MonsterDao@5d3411d
后置处理器ZzwBeanPostProcessor Before方法 被调用... bean类型=class com.zzw.spring.component.MonsterDao bean的名字=...
MonsterDao 初始化方法被调用...
后置处理器ZzwBeanPostProcessor After方法 被调用... bean类型=class com.zzw.spring.component.MonsterDao bean的名字=...
======创建好实例======com.zzw.spring.component.MonsterService@50675690
后置处理器ZzwBeanPostProcessor Before方法 被调用... bean类型=class com.zzw.spring.component.MonsterService bean的名字=...
MonsterService 初始化方法被调用 程序员在这里加入初始化的业务...
后置处理器ZzwBeanPostProcessor After方法 被调用... bean类型=class com.zzw.spring.component.MonsterService bean的名字=...
//后面输出结果忽略...
8.完善. 对下列代码的beanName
进行完善.
instance = beanPostProcessor.postProcessBeforeInitialization(instance, "...");
ZzwSpringApplicationContext
类的createBean
方法增加一个形参String name
private Object createBean(BeanDefinition beanDefinition, 👉String beanName👈) {
//内容忽略...
//我们在Bean的初始化方法前, 调用后置处理器的before方法
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
//在后置处理器的before方法, 可以对容器bean进行处理
//然后返回处理后的bean实例, 相当于做了一个前置处理
instance =
beanPostProcessor.postProcessBeforeInitialization(instance, 👉beanName👈);
}
//我们在Bean的初始化方法后, 调用后置处理器的after方法
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
//在后置处理器的after方法, 可以对容器bean进行处理
//然后返回处理后的bean实例, 相当于做了一个后置处理
instance =
beanPostProcessor.postProcessAfterInitialization(instance, 👉beanName👈);
}
}
ZzwSpringApplicationContext
类的getBean
方法
//编写方法getBean(String name), 编写方法返回容器对象
public Object getBean(String name) {
//加一个判断条件: 传入的beanName是否在beanDefinitionMap中存在
if (beanDefinitionMap.containsKey(name)) {//如果存在
BeanDefinition beanDefinition = beanDefinitionMap.get(name);
//得到beanDefinition的scope, 分别进行处理
if ("singleton".equalsIgnoreCase(beanDefinition.getScope())) {
//说明是单例配置, 就直接从单例池获取
return singletonObjects.get(name);
} else {//如果不是单例的, 我们就调用createBean, 反射一个对象返回
return createBean(beanDefinition, 👉name👈);
}
} else {//如果不存在
//抛出一个空指针异常, 也可以自定义
throw new NullPointerException("没有该bean");
}
}
ZzwSpringApplicationContext
类的构造器
方法
//构造器
public ZzwSpringApplicationContext(Class configClass) {
//完成扫描指定的包
beanDefinitionsByScan(configClass);
//通过beanDefinitionMap, 初始化singletonObjects 单例池
//封装成方法
//遍历所有的beanDefinition对象
Enumeration<String> keys = beanDefinitionMap.keys();
while (keys.hasMoreElements()) {
//得到beanName, 得到对应的beanDefinition
String beanName = keys.nextElement();
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
//判断该bean是singleton, 还是prototype
if ("singleton".equals(beanDefinition.getScope())) {
//将该bean实例放入到singletonObjects 集合
Object bean = createBean(beanDefinition, 👉beanName👈);
singletonObjects.put(beanName, bean);
}
}
}
在ZzwSpringApplicationContext
类的createBean
方法的return instance;
前加一个分隔符
System.out.println("---------------------------------------------------------------");
运行结果
//前面输出结果忽略...
=====================================================================================
======创建好实例======com.zzw.spring.component.Car@6f79caec
这是一个Car对象, 我可以处理
后置处理器ZzwBeanPostProcessor Before方法 被调用... bean类型=class com.zzw.spring.component.Car bean的名字=car
Car的初始化方法...
后置处理器ZzwBeanPostProcessor After方法 被调用... bean类型=class com.zzw.spring.component.Car bean的名字=car
---------------------------------------------------------------
======创建好实例======com.zzw.spring.component.MonsterDao@5d3411d
后置处理器ZzwBeanPostProcessor Before方法 被调用... bean类型=class com.zzw.spring.component.MonsterDao bean的名字=monsterDao
MonsterDao 初始化方法被调用...
后置处理器ZzwBeanPostProcessor After方法 被调用... bean类型=class com.zzw.spring.component.MonsterDao bean的名字=monsterDao
---------------------------------------------------------------
======创建好实例======com.zzw.spring.component.MonsterService@50675690
后置处理器ZzwBeanPostProcessor Before方法 被调用... bean类型=class com.zzw.spring.component.MonsterService bean的名字=monsterService
MonsterService 初始化方法被调用 程序员在这里加入初始化的业务...
后置处理器ZzwBeanPostProcessor After方法 被调用... bean类型=class com.zzw.spring.component.MonsterService bean的名字=monsterService
---------------------------------------------------------------
//后面输出结果忽略...
9.对于原生后置处理器的before
和after
方法是可以返回空的
证明: 对前面我们写的spring项目, 进行Debug. 传送门
但是我们自己写的后置处理器到目前为止如果返回null的话, 会报错. Exception in thread "main" java.lang.NullPointerException
改进
ZzwSpringApplicationContext类
的createBean
方法
//我们在Bean的初始化方法前, 调用后置处理器的before方法
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
//在后置处理器的before方法, 可以对容器bean进行处理
//然后返回处理后的bean实例, 相当于做了一个前置处理
👇Object current =
beanPostProcessor.postProcessBeforeInitialization(instance, beanName);
if (current != null) {
instance = current;
👆}
}
//这里判断是否要执行Bean的初始化方法
//1.判断当前创建的Bean对象是否实现了InitializingBean接口
//2.instanceof 表示判断某个对象的运行类型是不是某个类型或者某个类型的子类型
//3.这里使用到接口编程
if (instance instanceof InitializingBean) {
//3.将instance转成接口InitializingBean
((InitializingBean) instance).afterPropertiesSet();
}
//我们在Bean的初始化方法后, 调用后置处理器的after方法
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
//在后置处理器的after方法, 可以对容器bean进行处理
//然后返回处理后的bean实例, 相当于做了一个后置处理
// 原生Spring容器, 比我们这个还要复杂
👇Object current =
beanPostProcessor.postProcessAfterInitialization(instance, beanName);
if (current != null) {
instance = current;
👆}
}
💗实现任务阶段6
🍚AOP机制实现
AOP机制实现
在 Spring系列四:AOP切面编程 里, 我们有这么一段运行结果. 其中, 可以看到, smartDog
在postProcessBeforeInitialization
方法里是SmartDog
类型, 到了postProcessAfterInitialization
里变成了代理类型
, 中间又没有执行初始化方法, 所以返回一个代理类型只能是在后置处理器的postProcessafterInitialization
方法内完成的.
1.在com.zzw.spring.component
包下新建SmartAnimalAble接口, SmartDog类, SmartAnimalAspect切面类
public interface SmartAnimalAble {
float getSum(float i, float j);
float getSub(float i, float j);
}
@Component(value = "smartDog")
public class SmartDog implements SmartAnimalAble{
public float getSum(float i, float j) {
float res = i + j;
System.out.println("SmartDog getSum() 结果=" + res);
return res;
}
public float getSub(float i, float j) {
float res = i - j;
System.out.println("SmartDog getSub() 结果=" + res);
return res;
}
}
//说明: SmartAnimalAspect当做一个切面类来使用
// ,后面再分析如何做得更加灵活
public class SmartAnimalAspect {
public static void showBeginLog() {
System.out.println("前置通知..");
}
public static void showSuccessEndLog() {
System.out.println("返回通知..");
}
}
2.在ZzwBeanPostProcessor
的after方法
内写入代码
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
System.out.println("后置处理器ZzwBeanPostProcessor After方法 被调用... bean类型="
+ bean.getClass() + " bean的名字=" + beanName);
//实现AOP, 返回代理对象, 即对Bean进行包装
//1.先死后活 -> 后面我们可以通过注解就可以更加灵活
if ("smartDog".equals(beanName)) {
//使用Jdk的动态代理, 返回该bean的代理对象
Object proxyInstance = Proxy.newProxyInstance(ZzwBeanPostProcessor.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
// 后面可以通过注解来做的更加灵活@Before @AfterReturning
if ("getSum".equals(method.getName())) {
SmartAnimalAspect.showBeginLog();
result = method.invoke(bean, args);//执行目标方法
//进行返回通知的处理
SmartAnimalAspect.showSuccessEndLog();
} else {
result = method.invoke(bean, args);//执行目标方法
}
return result;
}
});
//如果bean是需要返回代理对象的, 这里就直接return proxyInstance
return proxyInstance;
}
//如果不需要AOP, 返回 bean
return bean;
}
测试
public class AppMain {
public static void main(String[] args) {
//创建自己的容器
ZzwSpringApplicationContext ioc =
new ZzwSpringApplicationContext(ZzwSpringConfig.class);
SmartAnimalAble smartDog = (SmartAnimalAble) ioc.getBean("smartDog");
smartDog.getSum(10, 2);
smartDog.getSub(10, 2);
System.out.println("ok");
}
}
运行结果
//前面输出结果省略...
method=getSum
前置通知..
SmartDog getSum() 结果=12.0
返回通知..
method=getSub
SmartDog getSub() 结果=8.0
//后面输出结果省略...
思考扩展: 如何做的更加灵活
1).前面我们使用的硬编码, 不灵活, 但是已经把AOP的核心代码说清楚了.
2).思考一下如何把AOP做的更加灵活, 核心知识点(注解 + 数据结构/map/少许算法 + 业务处理), 和AOP机制关系不大了
示意代码
1.在com.zzw.spring.annotation
包下自定义@Aspect, @Before, @AfterReturning
注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface Aspect {
String value() default "";
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Before {
String value();
String argNames() default "";
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface AfterReturning {
String value() default "";
String pointcut() default "";
String returning() default "";
String argNames() default "";
}
2.自定义切面类SmartAnimalAspect
用我们自己的注解修饰
@Aspect //我们自己的注解
@Component //我们自己的注解
public class SmartAnimalAspect {
@Before(value = "execution com.zzw.spring.component.SmartDog getSum")
public static void showBeginLog() {
System.out.println("前置通知..");
}
@AfterReturning(value = "execution com.zzw.spring.component.SmartDog getSum")
public static void showSuccessEndLog() {
System.out.println("返回通知..");
}
}
3.在com.zzw.spring
包下新建测试类ZzwTest
public class ZzwTest {
public static void main(String[] args) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
//1.获取SmartAnimalAspect的class对象
Class<SmartAnimalAspect> clazz = SmartAnimalAspect.class;
//2.遍历该类的所有方法
for (Method declaredMethod : clazz.getDeclaredMethods()) {
//如果切面类的方法有Before注解
if (declaredMethod.isAnnotationPresent(Before.class)) {
//得到切面类的切入方法
System.out.println("method名字=" + declaredMethod.getName());
//得到Before注解
Before beforeAnnotation = declaredMethod.getDeclaredAnnotation(Before.class);
//得到Before注解 的value
String value = beforeAnnotation.value();
System.out.println("value=" + value);
//得到要切入的方法[通过反射调用]
Method declaredMethod1 = clazz.getDeclaredMethod(declaredMethod.getName());
//调用切入方法
declaredMethod1.invoke(clazz.newInstance(), null);
} else if (declaredMethod.isAnnotationPresent(AfterReturning.class)) {
//如果切面类的方法有AfterReturning注解, 同样可以进行类似处理
//得到切面类的切入方法
System.out.println("method名字=" + declaredMethod.getName());
//得到AfterReturning注解
AfterReturning afterReturningAnnotation = declaredMethod.getAnnotation(AfterReturning.class);
//得到AfterReturning注解 的value
String value = afterReturningAnnotation.value();
System.out.println("value=" + value);
//得到要切入的方法
Method declaredMethod1 = clazz.getDeclaredMethod(declaredMethod.getName());
//调用切入方法[通过反射调用]
declaredMethod1.invoke(clazz.newInstance(), null);
}
}
}
}
运行结果文章来源:https://www.toymoban.com/news/detail-653497.html
method名字=showBeginLog
value=execution com.zzw.spring.component.SmartDog getSum
前置通知..
method名字=showSuccessEndLog
value=execution com.zzw.spring.component.SmartDog getSum
返回通知..
完结散花…文章来源地址https://www.toymoban.com/news/detail-653497.html
到了这里,关于Spring系列五:手动实现Spring底层机制的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!