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

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

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

语法学习

  1. 类名首字母小写
    import org.apache.commons.lang.StringUtils;
    StringUtils.uncapitalize(className);
  2. 判断类对象(clazz)是否实现某个接口
    接口.class.isAssignableFrom(clazz)

🍝类加载器和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) zzw-myspring项目
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

1.在com.zzw.spring.annotation包下新建注解ComponentScan

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ComponentScan {
	//通过value可以指定要扫描的包
    String value() default "";
}

2.在com.zzw.spring.annotation包下新建注解 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

//MonsterService 是一个Service
//1.如果指定了value, 那么在注入spring容器时, 以我们指定的为准
//2.如果没有指定value, 则使用类名首字母小写的方式命名
@Component(value = "monsterService")// 把MonsterService注入我们自己写的spring容器中
public class MonsterService {

}

5.在com.zzw.spring.component包下新建MonsterDao

@Component(value = "monsterDao")
public class MonsterDao {
}

6.在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;
    }
}

7.在 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 "";
}

2.给MonsterService添加自定义注解@Scope

@Component(value = "monsterService")// 把MonsterService注入我们自己写的spring容器中
@Scope(value = "prototype")
public class MonsterService {

}

3.在com.zzw.spring.ioc包下新建BeanDefinition

//BeanDefinition 用于封装/记录Bean的信息[1. scope 2. Bean对应的class对象, 反射可以生成对应的对象]
public class BeanDefinition {
    
    private String scope;
    private Class clazz;
	//可以根据需求, 进行扩展
	
	//getter, setter方法, toString方法
}

4.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>

为了解决上述错误, 引入下面方案临时切换版本, 可以作为一个临时方案.
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5.将构造器中的全部代码挪到本类的beanDefinitionsByScan方法内

//该方法完成对指定包的扫描, 并将Bean信息封装到BeanDefinition对象, 再放入到Map
public void beanDefinitionsByScan(Class configClass) {

}

6.那么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.测试 src/main/java/com/zzw/spring/AppMain.java

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;
}

2.MonsterDao添加hi方法

@Component(value = "monsterDao")
public class MonsterDao {
    public void hi() {
        System.out.println("MonsterDao hi()...");
    }
}

3.MonsterService 增加代码

@Component(value = "monsterService")// 把MonsterService注入我们自己写的spring容器中
@Scope(value = "prototype")
public class MonsterService {

    //这里我们使用自己的注解@Autowired 来修饰属性
    //表示该属性, 是通过容器完成依赖注入
    //说明: 我们实现按照名字来进行组装即可
    @Autowired
    private MonsterDao monsterDao;

    public void m1() {
        monsterDao.hi();
    }
}

4.测试 src/main/java/com/zzw/spring/AppMain.java

public class AppMain {
    public static void main(String[] args) {
        ZzwSpringApplicationContext ioc =
                new ZzwSpringApplicationContext(ZzwSpringConfig.class);

        //测试一下依赖注入的功能
        MonsterService monsterService = (MonsterService) ioc.getBean("monsterService");
        monsterService.m1();
}

这时测试, 会报空指针异常. java.lang.NullPointerException
这是因为这是我们自己定义的@Autowired注解, 还没有完成依赖注入.

5.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;
}

6.测试com.zzw.spring.AppMain

public class AppMain {
    public static void main(String[] args) {
        ZzwSpringApplicationContext ioc =
                new ZzwSpringApplicationContext(ZzwSpringConfig.class);

        //测试一下依赖注入的功能
        MonsterService monsterService = (MonsterService) ioc.getBean("monsterService");
        monsterService.m1();
}

💗实现任务阶段5

🍚bean后置处理器实现

bean后置处理器实现

思路:先完成原生Spring 使用Bean后置处理器的案例, 然后实现自己的bean后置处理器

bean的生命周期~传送门

在idea中 shortcuts: shift+shift , 输入 InitializingBean, 搜索. 在zzw-spring项目里
在这里插入图片描述

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;
}

2.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 初始化方法被调用 程序员在这里加入初始化的业务...");
    }
}

说明
在创建好Bean实例后, 判断是否需要进行初始化. 【编程心得: 容器中常用的一个方法是, 根据该类是否实现了某个接口, 来判断是否要执行某个业务逻辑, 这里其实就是java基础的接口编程实际应用】

1.在ZzwSpringApplicationContext类的createBean方法遍历字段后加入如下代码

System.out.println("======创建好实例======" + instance);
//这里判断是否要执行Bean的初始化方法
//1.判断当前创建的Bean对象是否实现了InitializingBean接口
//2.instanceof 表示判断某个对象的运行类型是不是某个类型或其子类型
//3.这里使用到接口编程
if (instance instanceof InitializingBean) {
    //将instance转成接口InitializingBean
    ((InitializingBean) instance).afterPropertiesSet();
}
return instance;

2.注销ZzwSpringApplicationContext类的构造器中的下面两条代码

//System.out.println("singletonObjects 单例池=" + singletonObjects);
//System.out.println("beanDefinitionMap=" + beanDefinitionMap);

3.测试com.zzw.spring.AppMain

public class AppMain {
    public static void main(String[] args) {
        ZzwSpringApplicationContext ioc =
                new ZzwSpringApplicationContext(ZzwSpringConfig.class);

        MonsterService monsterService = (MonsterService)ioc.getBean("monsterService");
    }
}

4.运行效果

//上面输出结果忽略...
======创建好实例======com.zzw.spring.component.MonsterDao@5479e3f
======创建好实例======com.zzw.spring.component.MonsterService@33e5ccce
MonsterService 初始化方法被调用 程序员在这里加入初始化的业务...

5.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 初始化方法被调用...");
    }
}

6.测试com.zzw.spring.AppMain

public class AppMain {
    public static void main(String[] args) {
        ZzwSpringApplicationContext ioc =
                new ZzwSpringApplicationContext(ZzwSpringConfig.class);

        MonsterService monsterService = (MonsterService)ioc.getBean("monsterService");
    }
}

7.运行效果

上面输出结果忽略...
======创建好实例======com.zzw.spring.component.MonsterDao@27082746
MonsterDao 初始化方法被调用...
======创建好实例======com.zzw.spring.component.MonsterService@33e5ccce
MonsterService 初始化方法被调用 程序员在这里加入初始化的业务...

8.参考原生Spring容器的BeanPostProcessor接口, 定义com.zzw.spring.processor.BeanPostProcessor接口
注意: 在jdk8及jdk8以后, 接口中可以有默认实现方法, 需要是由default关键字修饰

//解读
//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;
    }
}

9.在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;
    }
}

这时后置处理器不会生效, 继续写代码支撑.

10.在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);
    }
}

11.在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;

12.测试com.zzw.spring.AppMain

public class AppMain {
    public static void main(String[] args) {
        ZzwSpringApplicationContext ioc =
                new ZzwSpringApplicationContext(ZzwSpringConfig.class);

        MonsterService monsterService = (MonsterService)ioc.getBean("monsterService");
    }
}

13.运行结果

//前面输出结果忽略...
======创建好实例======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的名字=...

这里有两份ZzwBeanPostProcessor, 去掉singletonObjects中的后置处理器对象, 保留beanPostProcessorList中的, 因为我们自定义的beanPostProcessorList操作起来方便.
在这里插入图片描述

1.在ZzwSpringApplicationContextbeanDefinitionsByScan方法内添加continue跳过循环

if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
    BeanPostProcessor beanPostProcessor
            = (BeanPostProcessor) clazz.newInstance();
    //将其放入到BeanPostProcessorList
    beanPostProcessorList.add(beanPostProcessor);
    👉continue;👈
}

2.测试com.zzw.spring.AppMain

public class AppMain {
    public static void main(String[] args) {
        ZzwSpringApplicationContext ioc =
                new ZzwSpringApplicationContext(ZzwSpringConfig.class);

        MonsterService monsterService = (MonsterService)ioc.getBean("monsterService");
    }
}

3.运行结果

//前面输出结果忽略...
======创建好实例======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的名字=...

在这里插入图片描述

1.新建com.zzw.spring.component.Car.让Car类实现InitializingBean初始化方法

@Component
public class Car implements InitializingBean {
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("Car的初始化方法...");
    }
}

2.测试com.zzw.spring.AppMain

public class AppMain {
    public static void main(String[] args) {
        ZzwSpringApplicationContext ioc =
                new ZzwSpringApplicationContext(ZzwSpringConfig.class);

        MonsterService monsterService = (MonsterService)ioc.getBean("monsterService");
    }
}

3.运行结果

//前面输出结果忽略...
======创建好实例======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的名字=...

4.修改com.zzw.spring.processor.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;
}

5.测试com.zzw.spring.AppMain

public class AppMain {
    public static void main(String[] args) {
        ZzwSpringApplicationContext ioc =
                new ZzwSpringApplicationContext(ZzwSpringConfig.class);

        MonsterService monsterService = (MonsterService)ioc.getBean("monsterService");
    }
}

6.运行结果

//前面输出结果忽略...
======创建好实例======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的名字=...

完善: 对下列代码的beanName进行完善.

instance = beanPostProcessor.postProcessBeforeInitialization(instance, "...");

1.ZzwSpringApplicationContext类的createBean方法增加一个形参String beanName

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👈);
    }
}

2.修改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");
    }
}

3.修改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);
        }
    }
}

4.测试com.zzw.spring.AppMain

public class AppMain {
    public static void main(String[] args) {
        ZzwSpringApplicationContext ioc =
                new ZzwSpringApplicationContext(ZzwSpringConfig.class);

        MonsterService monsterService = (MonsterService)ioc.getBean("monsterService");
    }
}

5.运行结果

//前面输出结果忽略...
======创建好实例======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

1.对于原生后置处理器的beforeafter方法是可以返回空的

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

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

3.但是我们自己写的后置处理器到目前为止如果返回null的话, 会报错. Exception in thread "main" java.lang.NullPointerException
修改src/main/java/com/zzw/spring/component/ZzwBeanPostProcessor.java

@Component
public class ZzwBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        //这里一定要体会到后置处理器是对容器创建的所有bean生效
        //相当于可以对多个对象编程, 切面编程
        //日志, 权限, 身份, 事务.
        if (bean instanceof Car) {
            System.out.println("这是一个Car对象, 我可以处理");
        }
        System.out.println("后置处理器ZzwBeanPostProcessor Before方法被调用 beand对象=" + bean
                + " bean名字=" + beanName);
        return null;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        System.out.println("后置处理器ZzwBeanPostProcessor After方法被调用 beand对象=" + bean
                + " bean名字=" + beanName);
        return null;
    }
}

4.改进 ZzwSpringApplicationContext类createBean方法
这里其实也可以不用赋值, 因为instance是引用类型.
以上说法是错误的, 因为后置处理器方法有可能返回代理类型, 所以必须给instance赋值

//我们在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;
    👆}
}

5.测试com.zzw.spring.AppMain

public class AppMain {
    public static void main(String[] args) {
        ZzwSpringApplicationContext ioc =
                new ZzwSpringApplicationContext(ZzwSpringConfig.class);

        MonsterService monsterService = (MonsterService)ioc.getBean("monsterService");
    }
}

💗实现任务阶段6

🍚AOP机制实现

AOP机制实现

Spring系列四:AOP切面编程 里, 我们有这么一段运行结果. 其中, 可以看到, smartDogpostProcessBeforeInitialization方法里是SmartDog类型, 到了postProcessAfterInitialization里变成了代理类型, 中间又没有执行初始化方法, 所以返回一个代理类型只能是在后置处理器的postProcessAfterInitialization方法内完成的.

因为只对SmartDog做了切入, 所以只有SmartDog返回了代理类型.
在这里插入图片描述


1.新建com.zzw.spring.component.SmartAnimalAble接口

public interface SmartAnimalAble {

    float getSum(float i, float j);

    float getSub(float i, float j);
}

2.新建com.zzw.spring.component.SmartDog

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

3.新建com.zzw.spring.component.SmartAnimalAspect切面类

//说明: SmartAnimalAspect当做一个切面类来使用
//,后面再分析如何做得更加灵活
public class SmartAnimalAspect {

    public static void showBeginLog() {
        System.out.println("前置通知..");
    }

    public static void showSuccessEndLog() {
        System.out.println("返回通知..");
    }
}

4.在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;
}

5.测试com.zzw.spring.AppMain

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");
    }
}

6.运行结果

//前面输出结果省略...
method=getSum
前置通知..
SmartDog getSum() 结果=12.0
返回通知..
method=getSub
SmartDog getSub() 结果=8.0
//后面输出结果省略...

思考扩展: 如何做的更加灵活

1).前面我们使用的硬编码, 不灵活, 但是已经把AOP的核心代码说清楚了.

2).思考一下如何把AOP做的更加灵活, 核心知识点(注解 + 数据结构/map/少许算法 + 业务处理), 和AOP机制关系不大了

示意代码
1.新建com.zzw.spring.annotation.Aspect注解

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface Aspect {
    String value() default "";
}

2.新建com.zzw.spring.annotation.Before注解

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Before {
    String value();

    String argNames() default "";
}

3.新建com.zzw.spring.annotation.AfterReturning注解

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface AfterReturning {
    String value() default "";

    String pointcut() default "";

    String returning() default "";

    String argNames() default "";
}

4.修改切面类com.zzw.spring.component.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("返回通知..");
    }
}

5.测试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);

            }
        }
    }
}

6.运行结果

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

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

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
Spring 的事务管理是建立在底层数据库事务之上的。在 Spring 中,事务的管理是通过 AOP 实现的,即通过动态代理技术,在运行时动态地将事务相关的代码织入到目标对象的方法中,从而实现事务的控制。 具体地说,Spring 的事务管理是通过 TransactionInterceptor 和 TransactionManager 两个组件来实现的。其中,TransactionInterceptor 是一个拦截器,负责拦截业务方法的调用,并在方法执行前后进行事务处理;而 TransactionManager 则负责管理底层数据库连接和事务的提交、回滚等操作。 当一个业务方法被调用时,TransactionInterceptor 会首先判断该方法是否需要进行事务管理。如果需要,则会获取一个事务连接,并在该连接上开启一个新的事务;如果不需要,则直接执行业务逻辑。当业务逻辑执行完成后,TransactionInterceptor 会根据业务方法的执行结果决定是否提交事务或回滚事务。 需要注意的是,Spring 的事务管理还支持声明式事务和编程式事务两种方式。声明式事务通过配置 AOP 切面和事务属性来实现,可以将事务管理与业务逻辑分离开来,提高代码的可读性和维护性;而编程式事务则需要手动在代码中控制事务的开启、提交和回滚等操作,相对来说更为灵活,但也更加复杂。 总之,Spring 的事务管理是建立在底层数据库事务之上的,通过 AOP 技术和 TransactionInterceptor、TransactionManager 等组件来实现事务的控制。同时,Spring 还支持声明式事务和编程式事务两种方式,可以根据具体需求来选择使用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

~ 小团子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值