Spring系列五:手动实现Spring底层机制

这篇具有很好参考价值的文章主要介绍了Spring系列五:手动实现Spring底层机制。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

上文中, 我们学习到了 Spring系列四:AOP切面编程

接下来我们学习, 手动实现Spring底层机制
Spring系列五:手动实现Spring底层机制,Spring5,spring,python,java

🍝类加载器和classpath详解

● java的类加载器 3 种

  1. Bootstrap类加载器---------------------对应路径jre\lib
  2. Ext类加载器------------------------------对应路径jre\lib\ext
  3. App类加载器-----------------------------对应路径classpath

●classpath 类路径, 就是 java.exe 执行时, 指定的路径, 比如

Spring系列五:手动实现Spring底层机制,Spring5,spring,python,java

复制, 粘贴到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系列五:手动实现Spring底层机制,Spring5,spring,python,java


第一阶段的代码参考👉Spring系列三:基于注解配置bean

1.在zzw-spring项目上创建子模块(java maven module)
Spring系列五:手动实现Spring底层机制,Spring5,spring,python,java
Spring系列五:手动实现Spring底层机制,Spring5,spring,python,java
Spring系列五:手动实现Spring底层机制,Spring5,spring,python,java
Spring系列五:手动实现Spring底层机制,Spring5,spring,python,java
Spring系列五:手动实现Spring底层机制,Spring5,spring,python,java

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. 这个错误一般是版本造成的.
Spring系列五:手动实现Spring底层机制,Spring5,spring,python,java
改正之后, 运行结果 (这个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
Spring系列五:手动实现Spring底层机制,Spring5,spring,python,java

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>

为了解决上述错误, 引入下面方案临时切换版本, 可以作为一个临时方案.
Spring系列五:手动实现Spring底层机制,Spring5,spring,python,java
Spring系列五:手动实现Spring底层机制,Spring5,spring,python,java
Spring系列五:手动实现Spring底层机制,Spring5,spring,python,java

将构造器中的全部代码挪到本类的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方法

Spring系列五:手动实现Spring底层机制,Spring5,spring,python,java

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, 搜索
Spring系列五:手动实现Spring底层机制,Spring5,spring,python,java

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操作起来方便.
Spring系列五:手动实现Spring底层机制,Spring5,spring,python,java
ZzwSpringApplicationContextbeanDefinitionsByScan方法内添加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

Spring系列五:手动实现Spring底层机制,Spring5,spring,python,java

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的名字=...
//后面输出结果忽略...

ZzwBeanPostProcessorbefore方法

@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.对于原生后置处理器的beforeafter方法是可以返回空的

证明: 对前面我们写的spring项目, 进行Debug. 传送门

Spring系列五:手动实现Spring底层机制,Spring5,spring,python,java
Spring系列五:手动实现Spring底层机制,Spring5,spring,python,java

但是我们自己写的后置处理器到目前为止如果返回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切面编程 里, 我们有这么一段运行结果. 其中, 可以看到, smartDogpostProcessBeforeInitialization方法里是SmartDog类型, 到了postProcessAfterInitialization里变成了代理类型, 中间又没有执行初始化方法, 所以返回一个代理类型只能是在后置处理器的postProcessafterInitialization方法内完成的.
Spring系列五:手动实现Spring底层机制,Spring5,spring,python,java


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.在ZzwBeanPostProcessorafter方法内写入代码

@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);

            }
        }
    }
}

运行结果

method名字=showBeginLog
value=execution com.zzw.spring.component.SmartDog getSum
前置通知..
method名字=showSuccessEndLog
value=execution com.zzw.spring.component.SmartDog getSum
返回通知..

Spring系列五:手动实现Spring底层机制,Spring5,spring,python,java
完结散花…文章来源地址https://www.toymoban.com/news/detail-653497.html

到了这里,关于Spring系列五:手动实现Spring底层机制的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 手动开发-实现SpringMVC底层机制--小试牛刀

    在这里说的底层机制的实现主要是指:前端控制器、Controller、Service注入容器、对象自动装配、控制器方法获取参数、视图解析、返回json数据。 前端控制器就是核心控制器。在这里我们可以设计一个Servlet来充当核心控制器: LingDispatcherServlet.java .这个控制器的作用主要是接收

    2024年02月08日
    浏览(48)
  • 手写Spring底层机制

    加入到createBean()中 加入到createBean()中 AOP需要在后置处理器的before方法中实现 1.单例/多例怎么实现的?@scope为什么可以实现? 回答:@scope 的value属性可以设置为singleton /prototype 通过getBean()方法 如果bean中的属性scope为singleton 就从单例池直接拿,如果是prototype 就调用createB

    2024年04月09日
    浏览(38)
  • Spring5系列学习文章分享---第三篇(AOP概念+原理+动态代理+术语+Aspect+操作案例(注解与配置方式))

    开篇: 欢迎再次来到 Spring 5 学习系列!在这个博客中,我们将深入研究 Spring 框架的AOP概念+原理+动态代理+术语+Aspect+操作案例(注解与配置方式)。 概念 什么是AOP (1)面向切面编程(方面),利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得 业务逻辑各部分之间的

    2024年01月24日
    浏览(45)
  • Spring源码系列:初探底层,手写Spring

    在学习Spring框架源码时,记住一句话:源码并不难,只需要给你各种业务场景或者项目经理,你也能实现自己的Spring。虽然你的实现可能无法与开源团队相媲美,但是你肯定可以实现一个0.0.1版本。因此,初次阅读源码时,不要陷入太深的细节中。先了解大体逻辑,再仔细研

    2023年04月12日
    浏览(43)
  • 【spring源码系列-01】spring底层源码整体概述

    Spring源码系列整体栏目 内容 链接地址 【一】spring源码整体概述 https://blog.csdn.net/zhenghuishengq/article/details/130940885 【二】通过refresh方法剖析IOC的整体流程 https://blog.csdn.net/zhenghuishengq/article/details/131003428 【三】xml配置文件启动spring时refresh的前置工作 https://blog.csdn.net/zhenghuishen

    2024年02月07日
    浏览(40)
  • Spring5系列学习文章分享---第一篇(概述+特点+IOC原理+IOC并操作之bean的XML管理操作)

    开篇: 欢迎来到 Spring 5 学习系列!在这个系列中,我们将深入研究 Spring 框架的最新版本,揭示其在现代Java开发中的精髓。无论你是初学者还是有经验的开发者,本系列都旨在为你提供深刻的理解和实用的知识,让你在构建强大、可维护和高效的应用程序方面更上一层楼。

    2024年01月22日
    浏览(69)
  • SpringBoot 底层机制分析【Tomcat 启动+Spring 容器初始化+Tomcat 如何关联Spring 容器】【下】

    😀前言 本篇博文是关于SpringBoot 底层机制分析实现,希望能够帮助你更好的了解SpringBoot 😊 🏠个人主页:晨犀主页 🧑个人简介:大家好,我是晨犀,希望我的文章可以帮助到大家,您的满意是我的动力😉😉 💕欢迎大家:这里是CSDN,我总结知识的地方,欢迎来到我的博客

    2024年02月13日
    浏览(42)
  • Spring5框架——AOP操作:通过Aspectj注解方式和配置文件方式来实现

    o((⊙﹏⊙))o. ** 之前的博客介绍了什么是AOP,以及AOP的底层原理,AOP主要是在原本的基础上添加一些之外的功能但是添加的功能是不会修改原定的代码,接下来为你介绍的是Aspectj注解,Spring 框架一般都是基于 AspectJ 实现 AOP 操作。AspectJ 不是 Spring 组成部分,独立 AOP 框架,一

    2024年02月16日
    浏览(49)
  • 【Spring】Spring的手动实现

    🎄欢迎来到@边境矢梦°的csdn博文🎄 🎄本文主要梳理手动实现Spring底层机制🎄 🌈我是边境矢梦°,一个正在为秋招和算法竞赛做准备的学生🌈 🎆喜欢的朋友可以关注一下 🫰🫰🫰 ,下次更新不迷路🎆 Ps: 月亮越亮说明知识点越重要 (重要性或者难度越大)🌑🌒🌓🌔🌕

    2024年02月07日
    浏览(29)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包