Spring框架学习笔记(四):手动实现 Spring 底层机制(初始化 IOC容器+依赖注入+BeanPostProcessor 机制+AOP)

news2024/11/15 9:27:18

Spring 整体架构示意图

阶段 -- 编写自己的 Spring 容器,实现多层扫描包

编写自己的 Spring 容器,实现多层扫描包,排除包下不是bean的, 得到 bean 对象,放入到临时ioc容器中

代码实现:

(1)准备代码 创建两个注解ComponentScan.java、Component.java,ComponentScan.java注解用于指定需要扫描的包,Component.java用于标识bean,即加了该注解的类就是bean,没加就不是

package com.spring.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ComponentScan {
    //表示ComponentScan注解可以传入一个value属性,指定要扫描的包
    String value() default "";
}
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
    //表示Component注解可以传入一个value属性,指定id值
    String value() default "";
}

(2)创建配置类 SpringConfigXml 用来替代xml文件,创建 MonsterService.java、MonsterDao.java用来作为需要放入容器中的bean

import com.spring.annotation.ComponentScan;

/**
 * 这是一个配置类,用来替代xml文件
 */
@ComponentScan(value = "com.spring")
public class SpringConfigXml {
}
import com.spring.annotation.Component;

@Component(value = "monsterDao")
public class MonsterDao {
}
import com.spring.annotation.Component;

/**
 * MonsterService 是一个 Service
 * 1.如果指定value,注入容器时,以指定的value值为准
 * 2.如果没用指定value,则默认为类名首字母小写
 */
@Component(value = "monsterService")//把MonsterService注入到我们自己的spring容器中
public class MonsterService {
}

(3)创建 SpringApplicationContext.java 充当容器类

import com.spring.annotation.Component;
import com.spring.annotation.ComponentScan;
import java.io.File;
import java.net.URL;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 该类作用类似于Spring原生ioc容器
 */
@SuppressWarnings("all")
public class SpringApplicationContext {
    //拿到配置类.class文件
    private Class configClass;
    //ioc存放的就是通过反射创建的对象
    private final ConcurrentHashMap<String, Object> ioc = new ConcurrentHashMap<>();

    //构造器
    public SpringApplicationContext(Class configClass) {
        this.configClass = configClass;
//        System.out.println("this.configClass=" + this.configClass);
        //获取要扫描的包
        //1.先获取到ComponentScan注解
        ComponentScan componentScan = (ComponentScan) this.configClass.getDeclaredAnnotation(ComponentScan.class);
        //2.获取到注解的value值
        String path = componentScan.value();
//        System.out.println("要扫描的包= " + path);

        //得到要扫描的包下的所有class文件(在target目录下)
        //1.得到类的加载器
        ClassLoader classLoader = SpringApplicationContext.class.getClassLoader();
        //2.通过类的加载器获取到要扫描的包的资源路径
        path = path.replace(".", "/");//一定要把.替换成/
        URL resource = classLoader.getResource(path);
//        System.out.println(resource);
        //3.将要加载的资源(.class) 路径下的文件进行遍历
        File file = new File(resource.getFile());
        readAllFiles(file, path, classLoader);
    }

    //递归遍历所有包,实现扫描多层包结构
    public void readAllFiles(File file, String path, ClassLoader classLoader) {
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File f : files) {
                if (f.isFile()) {
                    dealFile(f,path,classLoader);
                } else if (f.isDirectory()) {
                    readAllFiles(f, path, classLoader);
                }
            }

        } else if (file.isFile()) {
            dealFile(file,path,classLoader);
        }
    }

    public void dealFile(File f, String path, ClassLoader classLoader){
        //获取到.class文件的绝对路径
        String fileAbsolutePath = f.getAbsolutePath();
        //这里只处理.class文件
        if (fileAbsolutePath.endsWith(".class")) {
            //获取到类全类名
            //1.获取到类名
            String className = fileAbsolutePath.substring
                    (fileAbsolutePath.lastIndexOf("\\") + 1, fileAbsolutePath.indexOf(".class"));
//                System.out.println(className);
            //2.拼接成全类名
            String temp = fileAbsolutePath.replace("\\","/");
            String classFullName = temp.substring(temp.indexOf(path),temp.indexOf(".class"));
            classFullName = classFullName.replace("/",".");
//                    System.out.println(classFullName);
            //3.判断该类是不是需要注入容器,看该类是不是有注解 @Component @Service @Repository @Controller
            try {
                //这时,就得到了该类的Class对象
                //1. Class clazz = Class.forName(classFullName) 可以反射加载类
                //2. classLoader.loadClass(classFullName); 也可以反射类的Class
                //3. 区别是 : 上面方式会调用该类的静态方法, 下面方法不会
                //4. aClass.isAnnotationPresent(Component.class) 判断该类是否有 @Component
                Class<?> aClass = classLoader.loadClass(classFullName);
                //判断该类是否有 @Component @Service @Repository @Controller
                if (aClass.isAnnotationPresent(Component.class)) {
                    //org.springframework.util.StringUtils包中的静态方法uncapitalize可以将字符串首字母小写
//                            String keyVal = StringUtils.uncapitalize(className);
                    String keyVal = className.substring(0, 1).toLowerCase() + className.substring(1);
                    //获取到value值,替换指定id
                    if (aClass.isAnnotationPresent(Component.class)) {
                        Component annotation = aClass.getDeclaredAnnotation(Component.class);
                        String value = annotation.value();
                        if (!value.equals("")) {
                            keyVal = value;
                        }
                    }
                    //这时候就可以创建该类的对象,并放入到ioc容器中
                    Class<?> clazz = Class.forName(classFullName);
                    Object o = clazz.newInstance();
                    ioc.put(keyVal, o);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    //通过id获取到bean对象
    public Object getBean(String id){
        return ioc.get(id);
    }
}

阶段 2 -- 将bean信息封装到BeanDefinition对象中,并放入到 Map,初始化单例池

将bean信息封装到BeanDefinition对象中,并放入到 Map,初始化单例池,修改getbean方法,如果是单例返回单例池中的对象,如果是多例则临时创建一个对象

 代码实现:

(1)创建注解 Scope.java 用来标识bean为单例还是多例 ,prototype为多例,singleton为单例

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Scope {
    String value(); //不需要给默认值
}

 (2)修改 MonsterService.java ,增加Scope注解,用于测试

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

(3)BeanDefinition.java,封装bean对象

/**
 * 1. 在扫描时,将 bean 信息,封装到 BeanDefinition 对象中
 * 2. 然后将 BeanDefinition 对象 , 放入到 spring 容器的集合中
*/
public class BeanDefinition {
    private Class clazz;//bean对应的class对象
    private String scope;//bean的作用域,singleton/prototype
}

(4)SpringApplicationContext.java, 将扫描到的 Bean 信息封装到 BeanDefinition 对象中,并保存到 BeanDefinitionMap,实现getBean方法

import com.spring.annotation.Component;
import com.spring.annotation.ComponentScan;
import com.spring.annotation.Scope;

import java.io.File;
import java.net.URL;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 该类作用类似于Spring原生ioc容器
 */
@SuppressWarnings("all")
public class SpringApplicationContext {
    //拿到配置类.class文件
    private Class configClass;
    //如果对象是单例的,就直接放在这个 单例 bean 对象池
    private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();
    //将 bean 的定义,放在这个 beanDefinitionMap 集合
    private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();

    //构造器
    public SpringApplicationContext(Class configClass) {
        //通过扫描,得到 beanDefinition 的 map
        beanDefinitionsByscan(configClass);
    }

    private void beanDefinitionsByscan(Class configClass) {
        this.configClass = configClass;
//        System.out.println("this.configClass=" + this.configClass);
        //获取要扫描的包
        //1.先获取到ComponentScan注解
        ComponentScan componentScan = (ComponentScan) this.configClass.getDeclaredAnnotation(ComponentScan.class);
        //2.获取到注解的value值
        String path = componentScan.value();
//        System.out.println("要扫描的包= " + path);

        //得到要扫描的包下的所有class文件(在target目录下)
        //1.得到类的加载器
        ClassLoader classLoader = SpringApplicationContext.class.getClassLoader();
        //2.通过类的加载器获取到要扫描的包的资源路径
        path = path.replace(".", "/");//一定要把.替换成/
        URL resource = classLoader.getResource(path);
//        System.out.println(resource);
        //3.将要加载的资源(.class) 路径下的文件进行遍历
        File file = new File(resource.getFile());
        readAllFiles(file, path, classLoader);

        //通过beanDefinitionMap , 初始化singletonObjects 单例池
        //遍历所有的beanDefinition对象
        Enumeration<String> keys = beanDefinitionMap.keys();
        while (keys.hasMoreElements()){
            //得到beanName
            String beanName = keys.nextElement();
            //通过beanName 得到对应的beanDefinition对象
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            //判断该bean是singleton还是prototype
            if ("singleton".equalsIgnoreCase(beanDefinition.getScope())) {
                //将该bean实例放入到singletonObjects 集合
                Object bean = createBean(beanDefinition);
                singletonObjects.put(beanName, bean);
            }
        }

    }

    //递归遍历所有包
    private void readAllFiles(File file, String path, ClassLoader classLoader) {
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            for (File f : files) {
                if (f.isFile()) {
                    dealFile(f,path,classLoader);
                } else if (f.isDirectory()) {
                    readAllFiles(f, path, classLoader);
                }
            }

        } else if (file.isFile()) {
            dealFile(file,path,classLoader);
        }
    }

    private void dealFile(File f, String path, ClassLoader classLoader){
        //获取到.class文件的绝对路径
        String fileAbsolutePath = f.getAbsolutePath();
        //这里只处理.class文件
        if (fileAbsolutePath.endsWith(".class")) {
            //获取到类全类名
            //1.获取到类名
            String className = fileAbsolutePath.substring
                    (fileAbsolutePath.lastIndexOf("\\") + 1, fileAbsolutePath.indexOf(".class"));
//                System.out.println(className);
            //2.拼接成全类名
            String temp = fileAbsolutePath.replace("\\","/");
            String classFullName = temp.substring(temp.indexOf(path),temp.indexOf(".class"));
            classFullName = classFullName.replace("/",".");
//                    System.out.println(classFullName);
            //3.判断该类是不是需要注入容器,看该类是不是有注解 @Component @Service @Repository @Controller
            try {
                //这时,就得到了该类的Class对象
                //1. Class clazz = Class.forName(classFullName) 可以反射加载类
                //2. classLoader.loadClass(classFullName); 也可以反射类的Class
                //3. 区别是 : 上面方式会调用该类的静态方法, 下面方法不会
                //4. aClass.isAnnotationPresent(Component.class) 判断该类是否有 @Component
                Class<?> aClass = classLoader.loadClass(classFullName);
                //判断该类是否有 @Component
                if (aClass.isAnnotationPresent(Component.class)) {
                    //org.springframework.util.StringUtils包中的静态方法uncapitalize可以将字符串首字母小写
//                            String keyVal = StringUtils.uncapitalize(className);
                    String keyVal = className.substring(0, 1).toLowerCase() + className.substring(1);
                    //获取到value值,替换指定id
                    Component annotation = aClass.getDeclaredAnnotation(Component.class);
                    String value = annotation.value();
                    if (!value.equals("")) {
                        keyVal = value;
                    }
                    //这时候就可以创建该bean的定义对象
                    Class<?> clazz = Class.forName(classFullName);
                    BeanDefinition beanDefinition = new BeanDefinition();
                    beanDefinition.setClazz(clazz);
                    //如果存在Scope注解,就设置该注解的值,如果不存在,则设为单例 singleton
                    if (clazz.isAnnotationPresent(Scope.class)){
                        Scope s = clazz.getDeclaredAnnotation(Scope.class);
                        String val = s.value();
                        beanDefinition.setScope(val);
                    }else {
                        beanDefinition.setScope("singleton");
                    }
                    //将 BeanDefinition 对象放入 map 集合中
                    beanDefinitionMap.put(keyVal, beanDefinition);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    //通过id获取到bean对象
    public Object getBean(String id){
        if (beanDefinitionMap.containsKey(id)) {
            BeanDefinition bean = beanDefinitionMap.get(id);
            String scope = bean.getScope();
            if (scope.equals("singleton")) {
                //如果是单例的,就直接从单例池中获取
                return singletonObjects.get(id);
            } else {
                //如果bean是多实例的,就临时创建一个对象
                Class clazz = bean.getClazz();
                return createBean(bean);
            }
        }else {
            throw new NullPointerException("没有该bean");
        }
    }

    private Object createBean(BeanDefinition bean){
        Class clazz = bean.getClazz();
        try {
            return clazz.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

(5)创建主程序,测试

public class AppMain {
    public static void main(String[] args) {
        SpringApplicationContext ioc = new SpringApplicationContext(SpringConfigXml.class);
        MonsterService monsterService = (MonsterService)ioc.getBean("monsterService");
        MonsterService monsterService2 = (MonsterService)ioc.getBean("monsterService");
        MonsterDao monsterDao = (MonsterDao)ioc.getBean("monsterDao");
        MonsterDao monsterDao1 = (MonsterDao)ioc.getBean("monsterDao");
        System.out.println(monsterService);
        System.out.println(monsterService2);
        System.out.println(monsterDao);
        System.out.println(monsterDao1);
        System.out.println("ok~");
    }
}

(6)运行效果

分析:可以看到,多例 bean 每次 getbean 返回的是不同的对象,而单例 bean 每次 getbean 返回的是同一个对象

4 阶段 3 -- 完成依赖注入

完成依赖注入

(1)Autowired.java注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Autowired 自动依赖注入
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD})
public @interface Autowired {
}

(2)MonsterDao.java,MonsterService.java,加入 Autowired 注解

import com.spring.annotation.Component;

@Component(value = "monsterDao")
public class MonsterDao {
    public void hi() {
        System.out.println("hi 我是 monster Dao");
    }
}
import com.spring.annotation.Autowired;
import com.spring.annotation.Component;
import com.spring.annotation.Scope;

/**
 * MonsterService 是一个 Service
 * 1.如果指定value,注入容器时,以指定的value值为准
 * 2.如果没用指定value,则默认为类名首字母小写
 */
@Scope(value = "prototype")
@Component(value = "monsterService")//把MonsterService注入到我们自己的spring容器中
public class MonsterService {

    //表示该属性,是通过容器完成依赖注入
    //说明:这里只实现了按照属性名来进行组装
    @Autowired
    private MonsterDao monsterDao;
    public void m1() {
        //调用 monsterDao 的 hi()
        monsterDao.hi();
    }
}

(3)SpringApplicationContext.java 类中的 createBean 方法,实现依赖注入

private Object createBean(BeanDefinition bean){
    Class clazz = bean.getClazz();
    try {
        Object instance = clazz.newInstance();
        //完成依赖注入
        Field[] fields = clazz.getDeclaredFields();
        //遍历当前要创建的对象的所有字段
        for (Field field : fields) {
            //判断这个字段是否有@Autowired注解
            if (field.isAnnotationPresent(Autowired.class)){
                //如果该属性有@Autowired, 就进行组装
                //得到这个字段名字
                String fieldName = field.getName();
                //因为属性是private,需要爆破
                field.setAccessible(true);
                //进行组装,通过getBean方法获取到要组装的对象
                field.set(instance, getBean(fieldName));
            }
        }
        return instance;
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

(4)编写主程序,查看依赖是否注入成功

public class AppMain {
    public static void main(String[] args) {
        SpringApplicationContext ioc = new SpringApplicationContext(SpringConfigXml.class);
        MonsterService monsterService = (MonsterService)ioc.getBean("monsterService");
        monsterService.m1();
        System.out.println("ok~");
    }
}

(5)运行效果

依赖注入成功!

5 阶段 4 -- bean后置处理器实现

实现自己的 bean 后置处理器

(1)InitializingBean.java 接口, 实现该接口的 Bean , 需要实现 Bean 初始化方法

/**
 * 1. 根据原生Spring 定义了一个InitializingBean
 * 2. 该InitializingBean接口有一个方法void afterPropertiesSet() throws Exception;
 * 3. afterPropertiesSet() 在Bean的 setter后执行,即就是初始化方法
 * 4. 当一个Bean实现这个接口后,就实现afterPropertiesSet() , 这个方法就是初始化方法
 */
public interface InitializingBean {
    void afterPropertiesSet() throws Exception;
}

(2)修改MonsterService.java、MonsterDao.java, 实现 InitializingBean 接口

import com.spring.annotation.Autowired;
import com.spring.annotation.Component;
import com.spring.annotation.Scope;
import com.spring.processor.InitializingBean;

/**
 * MonsterService 是一个 Service
 * 1.如果指定value,注入容器时,以指定的value值为准
 * 2.如果没用指定value,则默认为类名首字母小写
 */
@Scope(value = "prototype")
@Component(value = "monsterService")//把MonsterService注入到我们自己的spring容器中
public class MonsterService implements InitializingBean {

    //表示该属性,是通过容器完成依赖注入
    //说明:这里只实现了按照属性名来进行组装
    @Autowired
    private MonsterDao monsterDao;
    public void m1() {
        //调用 monsterDao 的 hi()
        monsterDao.hi();
    }

    /**
     * afterPropertiesSet就是在bean的setter方法执行完毕后被spring容器调用
     * 即就是初始化方法
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("MonsterService 初始化方法被调用 程序员在这里加入初始化的业务..");
    }
}
import com.spring.annotation.Component;
import com.spring.processor.InitializingBean;

@Component(value = "monsterDao")
public class MonsterDao implements InitializingBean {
    public void hi() {
        System.out.println("hi 我是 monster Dao");
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("MonsterDao 初始化方法被调用...");
    }
}

(3)SpringApplicationContext.java 类中的createBean方法 , 在创建好 Bean 实例后,判断是否需要进行初始化

private Object createBean(BeanDefinition bean){
    Class clazz = bean.getClazz();
    try {
        Object instance = clazz.getDeclaredConstructor().newInstance();
        //完成依赖注入
        Field[] fields = clazz.getDeclaredFields();
        //遍历当前要创建的对象的所有字段
        for (Field field : fields) {
            //判断这个字段是否有@Autowired注解
            if (field.isAnnotationPresent(Autowired.class)){
                //如果该属性有@Autowired, 就进行组装
                //得到这个字段名字
                String fieldName = field.getName();
                //因为属性是private,需要爆破
                field.setAccessible(true);
                //进行组装,通过getBean方法获取到要组装的对象
                field.set(instance, getBean(fieldName));
            }
        }

        System.out.println("====创建好实例=====" + instance);
        //这里判断是否要执行Bean初始化方法
        //1. 判断当前创建的Bean对象是否实现了InitializingBean
        //2. instanceof 可以用来判断某个对象的运行类型是不是某个类型或者
        //  某个类型的子类型
        if (instance instanceof InitializingBean){
            // 转型成接口类型调用方法
            ((InitializingBean) instance).afterPropertiesSet();
            // 也可以使用反射调用
//                Method initMethod = clazz.getDeclaredMethod("afterPropertiesSet");
//                initMethod.invoke(instance);
        }
        return instance;
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

(4)编写主程序,测试

public class AppMain {
    public static void main(String[] args) {
        SpringApplicationContext ioc = new SpringApplicationContext(SpringConfigXml.class);
        MonsterService monsterService = (MonsterService)ioc.getBean("monsterService");
        System.out.println("ok~");
    }
}

(5)运行效果

成功实现初始化方法!

(6)现在开始实现后置处理器,BeanPostProcessor.java 接口,创建MyBeanPostProcessor.java 类实现该接口,增加一个Car类,用于测试

/**
 * 1. 参考原生Spring容器定义一个接口BeanPostProcessor
 * 2. 该接口有两个方法postProcessBeforeInitialization 和 postProcessAfterInitialization
 * 3. 这两个方法,会对Spring容器的所有Bean生效
 */
public interface BeanPostProcessor {

    /**
     * postProcessBeforeInitialization在Bean的初始化方法前调用
     */
    default Object postProcessBeforeInitialization(Object bean, String beanName) {
        return bean;
    }

    /**
     * postProcessAfterInitialization在Bean的初始化方法后调用
     */
    default Object postProcessAfterInitialization(Object bean, String beanName) {
        return bean;
    }
}
package com.spring.component;

import com.spring.annotation.Component;
import com.spring.processor.InitializingBean;

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
import com.spring.annotation.Component;
import com.spring.processor.BeanPostProcessor;

/**
 * 1. 这是我们自己的一个后置处理器
 * 2. 实现了BeanPostProcessor
 * 3. 我们可以重写before和after方法
 * 4. 在Spring容器中,仍然把HspBeanPostProcessor当做一个Bean对象, 要注入到容器
 * 5. 需要加上@Component 标识
 * 6. 我们要让HspBeanPostProcessor成为真正的后置处理器, 需要在容器中加入业务代码
 * 7. 还要考虑多个后置处理器对象注入到容器问题
 */
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {

        //后置处理器会对所有的bean生效,这里就可以对Car对象进行单独的处理
        if (bean instanceof Car){
            System.out.println("这是一个Car对象,我可以处理");
        }

        System.out.println("后置处理器HspBeanPostProcessor Before调用 bean类型="
                + bean.getClass() + " bean的名字=" + beanName);
        return null;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        System.out.println("后置处理器HspBeanPostProcessor After调用 bean类型="
                + bean.getClass() + " bean的名字=" + beanName);
        return bean;
    }
}

(7)修改 SpringApplicationContext.java 容器类

添加属性,存放后置处理器对象

//定义一个属性beanPostProcessorList, => 存放后置处理器
private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();

修改dealFile方法,在以下位置加入以下代码,将后置处理器对象放入集合中

//判断该类是否有 @Component
if (aClass.isAnnotationPresent(Component.class)) {

    //1. 为了方便,这里将后置处理器放入到一个ArrayList
    //2. 如果发现是一个后置处理器, 放入到 beanPostProcessorList
    //3. 在原生的Spring容器中, 对后置处理器还是走的getBean, createBean
    //   , 但是需要我们在singletonObjects 加入相应的业务逻辑,这里进行了简化

    //判断当前的这个class对象有没有实现BeanPostProcessor
    //说明, 这里我们不能使用 instanceof 来判断clazz是否实现了BeanPostProcessor
    //原因: clazz不是一个实例对象,而是一个类对象/clazz, 可以使用isAssignableFrom
    if (BeanPostProcessor.class.isAssignableFrom(aClass)) {
        BeanPostProcessor beanPostProcessor =
                (BeanPostProcessor) aClass.newInstance();
        //放入到beanPostProcessorList
        beanPostProcessorList.add(beanPostProcessor);
        return;
    }
    。。。。。
}

修改create方法,增加一个参数beanName,并在初始化方法前后调用后置处理器方法

private Object createBean(BeanDefinition bean, String beanName)

create方法修改部分代码如下

System.out.println("====创建好实例=====" + instance);

//在Bean的初始化方法前,调用后置处理器的before方法
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
    //在后置处理器的before方法中,可以对容器的bean实例进行处理
    //然后返回处理后的实例
    Object current = beanPostProcessor.postProcessBeforeInitialization(instance,beanName);
    //这样处理,如果返回null,对bean对象不会造成影响,后置处理器中对bean做出的修改依然会生效
    if (current != null){
        instance = current;
    }
}


//这里判断是否要执行Bean初始化方法
//1. 判断当前创建的Bean对象是否实现了InitializingBean
//2. instanceof 可以用来判断某个对象的运行类型是不是某个类型或者
//  某个类型的子类型
if (instance instanceof InitializingBean){
    // 转型成接口类型调用方法
    ((InitializingBean) instance).afterPropertiesSet();
    // 也可以使用反射调用
    // Method initMethod = clazz.getDeclaredMethod("afterPropertiesSet");
    //  initMethod.invoke(instance);
}

//在Bean的初始化方法后,调用后置处理器的after方法
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
    //在后置处理器的after方法中,可以对容器的bean实例进行处理
    //然后返回处理后的实例
    Object current = beanPostProcessor.postProcessAfterInitialization(instance,beanName);
    //这样处理,如果返回null,对bean对象不会造成影响
    if (current != null){
        instance = current;
    }
}

(8)编写主类测试

package com.spring;

import com.spring.component.MonsterService;
import com.spring.ioc.SpringApplicationContext;
import com.spring.ioc.SpringConfigXml;

public class AppMain {
    public static void main(String[] args) {
        SpringApplicationContext ioc = new SpringApplicationContext(SpringConfigXml.class);
        MonsterService monsterService = (MonsterService)ioc.getBean("monsterService");
        System.out.println("ok~");
    }
}

(9)运行结果

后置处理器成功作用在所有bean上

6 阶段 5 -- AOP机制实现

实现一个简单的AOP,这里做了简化,使用的硬编码,写死了

(1)创建注解Aspect.java,Before.java,AfterReturning.java

package com.spring.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

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

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Before {
    String value();
    String argNames() default "";
}
package com.spring.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

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

    String pointcut() default "";

    String returning() default "";

    String argNames() default "";
}

(2)创建接口 SmartAnimalable.java,实现类 SmartDog.java,切面类 SmartAnimalAspect.java

package com.spring.component;

@SuppressWarnings("all")
public interface SmartAnimalable {
    //求和
    float getSum(float i, float j);
    //求差
    float getSub(float i, float j);
}
package com.spring.component;

import com.spring.annotation.Component;

@Component
public class SmartDog implements SmartAnimalable {
    @Override
    public float getSum(float i, float j) {
        float result = i + j;
        System.out.println("SmartDog-getSum方法内部打印result = " + result);
        return result;
    }

    @Override
    public float getSub(float i, float j) {
        float result = i - j;
        System.out.println("SmartDog-getSub方法内部打印result = " + result);
        return result;
    }
}
package com.spring.component;

import com.spring.annotation.AfterReturning;
import com.spring.annotation.Aspect;
import com.spring.annotation.Before;
import com.spring.annotation.Component;

/**
 * SmartAnimalAspect当做一个切面类来使用
 */
@Aspect
@Component
public class SmartAnimalAspect {

    @Before(value = "execution com.spring.aop.aspectj.SmartDog getSum")
    public static void showBeginLog() {

        System.out.println("前置通知..");
    }

    @AfterReturning(value = "execution com.spring.aop.aspectj.SmartDog getSum")
    public static void showSuccessLog() {

        System.out.println("返回通知..");
    }
}

(3)修改 MyBeanPostProcessor.java 类中的 postProcessAfterInitialization方法,实现AOP,返回代理对象

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
    System.out.println("后置处理器HspBeanPostProcessor After调用 bean类型="
            + bean.getClass() + " bean的名字=" + beanName);

    //实现AOP,返回代理对象,即对Bean进行包装
    if ("smartDog".equals(beanName)){
        //使用jdk的动态代理,返回bean的代理对象
        Object proxyInstance = Proxy.newProxyInstance(MyBeanPostProcessor.class.getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                SmartAnimalAspect.showBeginLog();
                Object res = method.invoke(bean,args);
                SmartAnimalAspect.showSuccessLog();
                return res;
            }
        });
        return proxyInstance;
    }
    return bean;
}

(4)创建 AOPTest.java

package com.spring;

import com.spring.annotation.AfterReturning;
import com.spring.annotation.Before;
import com.spring.component.SmartAnimalAspect;

import java.lang.reflect.Method;

public class AOPTest {
    public static void main(String[] args) throws Exception {

        //1. 获取SmartAnimalAspect的class对象
        Class<SmartAnimalAspect> smartAnimalAspectClass = SmartAnimalAspect.class;
        //2. 遍历该类的所有方法
        for (Method declaredMethod : smartAnimalAspectClass.getDeclaredMethods()) {
            //如果切面类的方法有Before注解
            if (declaredMethod.isAnnotationPresent(Before.class)) {
                //得到切面类的切入方法名
                System.out.println("m:= " + declaredMethod.getName());
                //得到Before(value="xxxx")
                //得到Before注解
                Before annotation = declaredMethod.getAnnotation(Before.class);
                //得到Before注解的value
                System.out.println("value:= " + annotation.value());

                //调用切入方法[通过反射调用]
                declaredMethod.invoke(smartAnimalAspectClass.newInstance(), null);

            } else if (declaredMethod.isAnnotationPresent(AfterReturning.class)) {

                //如果发现切面类有AfterReturning注解,同样可以进行处理..
                System.out.println("m:= " + declaredMethod.getName());
                AfterReturning annotation = declaredMethod.getAnnotation(AfterReturning.class);
                System.out.println("value:= " + annotation.value());

                //调用切入方法[反射调用]
                declaredMethod.invoke(smartAnimalAspectClass.newInstance(), null);
            }
        }
    }
}

(5)运行结果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1693676.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

闲鱼电商运营高级课程,一部手机学会闲鱼开店赚钱(34节课)

课程目录 1&#xff0c;闲鱼更货出售主要核心原理.mp4 2、闲鱼前期开店准备.mp4 3.账号基础信息设置1.mp4 4、提升账号权重.mp4 5、注意避免违规行为.mp4 6、实接课 应该怎么选择爆款产品.mp4 7、分析商品的闲鱼市场.mp4 8、寻找最低价货源.mp4 9、怎么寻我优质的货源…

把自己的垃圾代码发布到官方中央仓库

参考博客&#xff1a;将组件发布到maven中央仓库-CSDN博客 感谢这位博主。但是他的步骤有漏缺&#xff0c;相对进行补充 访问管理页面 网址&#xff1a;Maven Central 新注册账号&#xff0c;或者使用github快捷登录&#xff0c;建议使用github快捷登录 添加命名空间 注意&…

【会议征稿,IEEE出版】第九届信息科学、计算机技术与交通运输国际学术会议(ISCTT 2024,6月28-30)

第九届信息科学、计算机技术与交通运输国际学术会议&#xff08;ISCTT 2024&#xff09;将于2024年6月28-30日在中国绵阳举行。 ISCTT 2024将围绕 “信息科学”、"计算机技术”、“交通运输” 等最新研究领域&#xff0c;为来自国内外高等院校、科学研究所、企事业单位的专…

【idea】idea2024最新版本下载_安装_破解

1、下载 下载地址&#xff1a;下载 IntelliJ IDEA – 领先的 Java 和 Kotlin IDE 下载完成&#xff1a; idea破解脚本下载链接&#xff1a;https://pan.baidu.com/s/1L5qq26cRABw8XuEn_CngKQ 提取码&#xff1a;6666 下载完成&#xff1a; 2、安装 1、双击idea的安装包&…

MGR集群从库出现RECOVERING

一、MGR集群问题 说明&#xff1a; 1、启动MGR集群&#xff0c;发现从库转态是&#xff1a;RECOVERING&#xff0c;导致数据不同步。 2、查看MGR日志报错信息&#xff0c;发现提示从库以存在数据库linux&#xff0c;导致无法创建。 3、报错信息如下图所示&#xff1a; 二、解决…

数组-在两个长度相等的有序数组中找到上中位数

题目描述 解题思路 此题目直接遍历两个列表&#xff0c;时间复杂度为O(n)&#xff1b;使用二分法去比较两个递增列表的中位数&#xff0c;缩小两个数组中位数范围&#xff0c;时间复杂度O(logn)&#xff0c;这里我们的算法实现使用二分法。 通过举例子来说明解题算法&#xf…

git revert 和 git reset

文章目录 工作区 暂存区 本地仓库 远程仓库需求&#xff1a;已推送到远程仓库&#xff0c;想要撤销操作git revert &#xff08;添加新的提交来“反做”之前的更改&#xff0c;云端会残留上次的提交记录&#xff09;git reset&#xff08;相当于覆盖上次的提交&#xff09;1.--…

lvgl无法显示中文

环境&#xff1a; VS2019、LVGL8.3 问题&#xff1a; VS2019默认编码为GB2312&#xff0c; 解决&#xff1a; VS2022设置编码方式为utf-8的三种方式_vs utf8-CSDN博客 我用的方法2&#xff0c;设置为 utf-8无签名就行。

Java+Spring+ IDEA+MySQL云HIS系统源码 云HIS适合哪些地区的医院?

JavaSpring IDEAMySQL云HIS系统源码云HIS适合哪些地区的医院&#xff1f; 云HIS适合哪些地区的医院&#xff1f; 云HIS&#xff08;云医院信息系统&#xff09;适合多种地区的医院&#xff0c;特别是那些希望实现医疗服务的标准化、信息化和规范化&#xff0c;同时降低IT运营成…

二叉排序树的创建

二叉排序树就是节点经过排序构建起的二叉树&#xff0c;其有以下性质&#xff1a; 1. 若它的左子树不为空&#xff0c;则左子树上所有节点的值均小于它的根节点的值。 2. 若它的右子树不为空&#xff0c;则右子树上所有节点的值均大于它的根节点的值。 3. 它的左、右子树也分…

【评价类模型】层次分析法(AHP)

1.评价类思想综述&#xff1a; 明确评价主体–>评价指标确定–>计算指标权重–>方案评价 1.1指标确定&#xff1a; 可以通过一个思维导图的方式来画出一个指标系统&#xff0c;先确定方向&#xff0c;再向下细化 注意&#xff1a;指标需要具有贴合性和独立性。 贴合…

《计算机网络微课堂》1-5 计算机网络的性能指标

本节课我们介绍计算机网络的性能指标&#xff0c;性能指标可以从不同的方面来度量计算机网络的性能。 常用的计算机网络性能指标有 8 个&#xff1a;速率、带宽、吞吐量、时延、时延带宽积&#xff0c;往返时间、利用率&#xff0c;丢包率。 ‍ 速率 首先介绍速率。为了能够…

Vue3中为Ant Design Vue中table的checkbox加tooltip、popover

问题的产生 Vue版本&#xff1a;3.3.13 ant-design-vue 版本&#xff1a;3.x.x 在工作时遇到一个场景&#xff0c;需要在 ant-table 的 checkbox 被禁用的时候提示原因&#xff0c;但是在 ant-design-vue 文档中并没有发现有相关介绍。 首先我去看了issue中是否有提到相关问题…

[数据集][目标检测]抽烟喝酒检测数据集VOC+YOLO格式1026张2类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;1026 标注数量(xml文件个数)&#xff1a;1026 标注数量(txt文件个数)&#xff1a;1026 标注…

【全网最全】2024电工杯数学建模A题前三题完整解答matlab+21页初步参考论文+py代码等(后续会更新成品论文)

您的点赞收藏是我继续更新的最大动力&#xff01; 一定要点击如下的卡片链接&#xff0c;那是获取资料的入口&#xff01; 【全网最全】2024电工杯数学建模A题前三题完整解答matlab21页初步参考论文py代码等&#xff08;后续会更新成品论文&#xff09;「首先来看看目前已有的…

Android Studio实现MQTT协议的连接

1添加依赖 在项目中找到下图文件 打开文件 如下 plugins {alias(libs.plugins.android.application) }android {namespace "com.example.mqtt_04"compileSdk 34defaultConfig {applicationId "com.example.mqtt_04"minSdk 27targetSdk 34versionCo…

网络性能与流量监控:优化企业网络管理的关键策略

目录 网络性能监控的重要性 1. 提高网络可靠性 2. 优化网络资源使用 3. 提升用户体验 网络流量监控的必要性 1. 识别异常流量 2. 改善网络管理 3. 确保合规性 AnaTraf网络流量分析仪&#xff1a;提升网络监控效率的利器 如何实施有效的网络监控策略 1. 确定监控目标…

yolov10 快速使用及训练

参考: https://docs.ultralytics.com/models/yolov10/ ultralytics其实大多数系列都能加载使用: 官方: https://github.com/THU-MIG/yolov10.git 代码参考: https://colab.research.google.com/github/roboflow-ai/notebooks/blob/main/notebooks/train-yolov10-object-…

无界鼠标与键盘,如何轻松控制多台电脑

简介 在软件开发领域&#xff0c;高效地管理多台电脑是至关重要的。Mouse without Borders软件为开发人员提供了一种便捷的解决方案&#xff0c;使他们能够轻松地在多台电脑之间共享鼠标和键盘。不仅如此&#xff0c;Mouse without Borders还提供了许多高级功能&#xff0c;如…

洗地机哪个牌子最好用?2024洗地机排行榜

随着人们生活水平的提升&#xff0c;智能清洁家电已经成为日常生活中的必需品。如今的清洁家电市场上&#xff0c;洗地机、吸尘器和扫地机器人等设备各有其独特的功能和优势。洗地机结合了扫、拖、吸和自清洁等多种功能&#xff0c;不仅可以处理干湿垃圾&#xff0c;还能高效清…