您现在的位置是:首页 > 正文

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

2024-01-30 20:15:11阅读 2

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

接下来我们学习, 手动实现Spring底层机制
在这里插入图片描述

🍝类加载器和classpath详解

● java的类加载器 3 种

  1. Bootstrap类加载器---------------------对应路径jre\lib
  2. Ext类加载器------------------------------对应路径jre\lib\ext
  3. 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操作起来方便.
在这里插入图片描述
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

在这里插入图片描述

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. 传送门

在这里插入图片描述
在这里插入图片描述

但是我们自己写的后置处理器到目前为止如果返回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方法内完成的.
在这里插入图片描述


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
返回通知..

在这里插入图片描述
完结散花…

网站文章

  • 每日五问(java)

    每日五问

    2024-01-30 20:15:04
  • 操作系统 进程的描述与控制

    本系列是计算机操作系统的笔记 采用的书是《计算机操作系统》汤子瀛第二章 进程的描述与控制2.1前驱图和程序执行2.1.1前驱图1. 所谓前驱图,是指一个有向无循环图,可记为DAG(Directed Acyclic Graph),它用于描述进程之间执行的先后顺序。2.1.2程序顺序执行 程序的顺序执行 程序顺序执行时的特征 顺序性 封闭性 可再现性 2.1.3 程序并发执行 程序的并发执行 程序并发

    2024-01-30 20:14:57
  • Laravel - 队列应用及踩过的坑

    Laravel - 队列应用及踩过的坑

    Laravel 自己实现了队列的机制,如果要看源码解析,这两篇文章写的很好。 https://laravel-china.org/articles/4169/analysis-of-laravel-m...

    2024-01-30 20:14:29
  • Centos7安装Redis单节点

    Centos7安装Redis单节点

    Centos7安装Redis单节点下载Redis安装包服务器中使用wget直接下载解压安装gcc依赖编译&安装修改默认配置文件启动Redis连接客户端停止Redis下载Redis安装包Redis官网地址:https://redis.io/这里下载最新的5.0.5版本。下载地址:服务器中使用wget直接下载服务器需要能够访问外网,可以参考VirtualBox配置Centos7固定...

    2024-01-30 20:14:23
  • 计算机中丢失mplugin.dll,安装MathType时提示缺少Mplugin.dll文件

    计算机中丢失mplugin.dll,安装MathType时提示缺少Mplugin.dll文件

    MathType在编辑数学公式确实很方便,因为它的功能很强大,能够进行多种数学符号编辑。但是MathType在使用安装时也有很多朋友遇到了很多问题,比如Mplugin.dll文件缺失,这样就导致了Ma...

    2024-01-30 20:14:16
  • Python基础学习:逻辑回归理论(第一天)

    Python基础学习:逻辑回归理论(第一天)

    Python基础学习:逻辑回归理论(第一天)1文章内容Python基础学习:逻辑回归理论(第一天)[^1]1.逻辑回归理论简介2.逻辑回归应用2.1 简单示例2.1.1 导入库函数2.1.2 创建实例...

    2024-01-30 20:13:46
  • Python基础专栏11-python基础篇-复合数据类型-集合

    Python基础专栏11-python基础篇-复合数据类型-集合

    今天要讲解的内容是另外一种数据结构类型-集合,集合这个概念对于大家来说,应该不陌生;在数学当中,我们已经学过这个概念,也大概应该知道集合的作用;集合是一个存放数据的容器,并且集合当中也是可以存放任何数据类型的数据,集合的特点是无序性和唯一性,集合当中的元素是没有顺序的,不能按照索引位置进行元素访问,唯一性是指集合当中的元素是不重复的。这一点也是经常被用作数据去重。...

    2024-01-30 20:13:39
  • HTML实体编码、URL编码、正则表达式、状态码总结

    编码是指将数字和字符通过一定的转换后使其能够在计算机中进行展示的行为,因为计算机只能识别0,1的信号,所以输入计算机中的信息都要转换成0,1串的形式才能被计算机识别。在计算机中,编码的本质是用来保存或...

    2024-01-30 20:13:32
  • 超燃!高效 MacBook 工作环境配置,超实用!

    超燃!高效 MacBook 工作环境配置,超实用!

    关注“Java后端技术全栈”回复“面试”获取最新资料回复“加群”邀您进技术交流群工欲善其事,必先利其器,工具永远都是用来解决问题的,没必要为了工具而工具,一切工具都是为了能快速准确的完成...

    2024-01-30 20:13:04
  • 做了3年Java的朋友跳槽天猫,拿下offer(面经总结)

    做了3年Java的朋友跳槽天猫,拿下offer(面经总结)

    很多人去面试之前都很做足准备,下面就分享一次,从面试中,其实可以看得出来现在的公司对于面试者的知识体系要求,具有一定的参考价值,感兴趣的小伙伴们可以参考一下。 天猫一面: 第一步先自我介绍? 介绍自己...

    2024-01-30 20:12:56