首页 > 其他分享 >手动实现 Spring 底层机制【初始化 IOC容器+依赖注入+BeanPostProcessor 机制+AOP】【附完整代码】

手动实现 Spring 底层机制【初始化 IOC容器+依赖注入+BeanPostProcessor 机制+AOP】【附完整代码】

时间:2024-08-18 17:53:46浏览次数:12  
标签:BeanPostProcessor Spring hykedu bean spring import 机制 com class

手动实现 Spring 底层机制【初始化 IOC容器+依赖注入+BeanPostProcessor 机制+AOP】【任务1-6整合版】

引言:Spring框架的ioc容器、依赖注入、BeanPostProcessor后置处理器、AOP面向切面编程等特点为我们的开发带来了极大的便利,但是我们不能只学其中的api,更要懂得Spring的底层机制。
思考Spring框架是如何实现以下功能
Spring 注解方式注入 bean
原生 Spring 如何实现依赖注入和 singleton、prototype
Spring 底层实现,如何实现 IOC 容器创建和初始化
Spring 底层实现, 如何实现 getBean, 根据 singleton 和 prototype 来返回 bean 实例
Spring 如何实现 BeanPostProcessor后置处理器机制
Spring AOP 动态代理实现
下面我来手动实现一下Spring的底层机制【初始化 IOC容器+依赖注入+BeanPostProcessor 机制+AOP】

前置知识:java反射+IO+动态代理+Spring

目录

程序框架图

一、实现任务阶段 1- 编写自己 Spring 容器,实现扫描包, 得到 bean 的 class 对象


1.先创建一个名为mySpring的Maven项目
目录结构

2.在annotation包下定义ComponentScan注解,该注解的value就是要扫描的包

package com.hykedu.spring.annotation;

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

/**
 * @author 程序员蛇皮
 * @version 1.0
 * 1.@Target(ElementType.TYPE)指定我们的 ComponentScan注解可以修饰 Type程序元素
 * 2.@Retention(RetentionPolicy.RUNTIME) ComponentScan注解 保留范围
 * 3.String value() default ""; 表示 ComponentScan注解 可以传入value
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ComponentScan {
    String value() default "";
}

3.在ioc包下定义CodeSnakeSpringConfig.java,在该类实现ComponentScan注解,该注解的value就是要扫描的包

package com.hykedu.spring.ioc;

import com.hykedu.spring.annotation.ComponentScan;

/**
 * @author 程序员蛇皮
 * @version 1.0
 * 这是一个配置类,作用类似于原生Spring的 beans.xml 容器配置文件
 */
@ComponentScan(value = "com.hykedu.spring.component")
public class CodeSnakeSpringConfig {
}

4.我们知道Spring扫描包下的类的注解有Component、Controller、Repository、Service,这里我们只在annotation包下定义Component注解做一个示范,其他注解作用非常类似,为了简化流程,这里不写代码示范了

package com.hykedu.spring.annotation;

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

/**
 * @author 程序员蛇皮
 * @version 1.0
 * 1.@Target(ElementType.TYPE)指定我们的 ComponentScan注解可以修饰 Type程序元素
 * 2.@Retention(RetentionPolicy.RUNTIME) ComponentScan注解 保留范围
 * 3.String value() default ""; 表示 ComponentScan注解 可以传入value
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
    String value() default "";
}

5.在ioc包定义CodeSnakeSpringApplicationContext类,CodeSnakeSpringApplicationContext 类的作用类似Spring原生ioc容器

package com.hykedu.spring.ioc;

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

import java.io.File;
import java.net.URL;

/**
 * @author 程序员蛇皮
 * @version 1.0
 * CodeSnakeSpringApplicationContext 类的作用类似Spring原生ioc容器
 */
@SuppressWarnings("all")
public class CodeSnakeSpringApplicationContext {
    private Class configClass;

    public CodeSnakeSpringApplicationContext(Class configClass) {
        this.configClass = configClass;
        System.out.println(configClass);
        //获取要扫描的包
        //1.先得到SpringConfig配置的@ComponentScan(value = "com.hykedu.spring.component")
        ComponentScan componentScan = (ComponentScan) this.configClass.getDeclaredAnnotation(ComponentScan.class);
        //2.componentScan的value就是我们要扫描的包
        String path = componentScan.value();
        System.out.println("要扫描的包:" + path);
        //得到要扫描的包下所有的资源(类.class)
        //1.得到类的加载器
        ClassLoader classLoader = CodeSnakeSpringApplicationContext.class.getClassLoader();
        //2.通过类加载器获取到要扫描包的url
        path = path.replace(".", "/");//把 . 替换成路径间隔符 /
        URL resource = classLoader.getResource(path);
        System.out.println("要扫描包的url:" + resource);
        //3.将要加载的资源(.class) 路径下的文件进行遍历=>io
        File file = new File(resource.getFile());
        if (file.isDirectory()) {//pand
            File[] files = file.listFiles();
            for (File f : files) {
                String absolutePath = f.getAbsolutePath();
                System.out.println("文件的绝对路径:" + absolutePath);
                //这里我们只处理.class文件
                if (absolutePath.endsWith(".class")) {
                    //1.获取到类名
                    String className = absolutePath.substring
                            (absolutePath.lastIndexOf("\\") + 1, absolutePath.lastIndexOf(".class"));
                    System.out.println("类名:" + className);
                    //2.获取类的完整路径(全类名)
                    String classFullName = path.replace("/", ".") + "." + className;
                    System.out.println("全类名:" + classFullName);
                    //3.判断该类是否需要注入容器,判断该类是不是有@Component/@Controller/@Repository/@Service注解
                    try {
                        Class<?> clazz = classLoader.loadClass(classFullName);
                        if (clazz.isAnnotationPresent(Component.class)) {
                            //如果在注解指定了value,将其赋值给className
                            System.out.println("这是一个Spring bean" + clazz);
                        } else {
                            System.out.println("这不是一个Spring bean" + clazz);
                        }
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
    }
}

6.测试能否识别带有Component注解的类
在Component包下分别定义没有Component注解的Car类和带有Component注解的StudentDao和StudentService
类,看看程序能否识别出来哪个是Bean对象,哪个不是Bean对象

package com.hykedu.spring.component;

/**
 * @author 程序员蛇皮
 * @version 1.0
 */
public class Car {
}
package com.hykedu.spring.component;

import com.hykedu.spring.annotation.Component;

/**
 * @author 程序员蛇皮
 * @version 1.0
 */
@Component
public class StudentDao {
}
package com.hykedu.spring.component;

import com.hykedu.spring.annotation.Component;

/**
 * @author 程序员蛇皮
 * @version 1.0
 */
@Component
public class StudentService {
}

测试方法AppMain

package com.hykedu.spring;

import com.hykedu.spring.ioc.CodeSnakeSpringApplicationContext;
import com.hykedu.spring.ioc.CodeSnakeSpringConfig;

/**
 * @author 程序员蛇皮
 * @version 1.0
 */
public class AppMain {
    public static void main(String[] args) {
        CodeSnakeSpringApplicationContext codeSnakeSpringApplicationContext = new CodeSnakeSpringApplicationContext(CodeSnakeSpringConfig.class);
        System.out.println("ok");
    }
}

测试结果(成功扫描到了Bean对象)

二、实现任务阶段 2- 扫描将 bean 信息封装到 BeanDefinition 对象, 并放入到 Map


1.在ioc包定义BeanDefinition类,用于封装/记录Bean的信息[1.scope 2.Bean对应的Class对象,反射可以生成对应的对象

package com.hykedu.spring.ioc;

/**
 * @author 程序员蛇皮
 * @version 1.0
 * BeanDefinition 用于封装/记录Bean的信息[1.scope 2.Bean对应的Class对象,反射可以生成对应的对象
 */
@SuppressWarnings("all")
public class BeanDefinition {
    private String scope;
    private Class clazz;

    public String getScope() {
        return scope;
    }

    public void setScope(String scope) {
        this.scope = scope;
    }

    public Class getClazz() {
        return clazz;
    }

    public void setClazz(Class clazz) {
        this.clazz = clazz;
    }

    @Override
    public String toString() {
        return "BeanDefinition{" +
                "scope='" + scope + '\'' +
                ", clazz=" + clazz +
                '}';
    }
}

2.在annotation包定义Scope注解,可以指定bean的作用范围[singleton(单例),prototype(多例)]

package com.hykedu.spring.annotation;

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

/**
 * @author 程序员蛇皮
 * @version 1.0
 * Scope可以指定bean的作用范围[singleton,prototype]
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Scope {
    String value() default "";
}

3.在pom.xml引入commons-lang包(需要用到里面的StringUtils工具类)

<dependencies>
    <dependency>
        <groupId>commons-lang</groupId>

        <artifactId>commons-lang</artifactId>

        <version>2.6</version>

    </dependency>

</dependencies>

4.在CodeSnakeSpringApplicationContext类中定义存放BeanDefinition的容器,并将Bean对象放入其中,同时将其封装成scanBeanDefinition(Class configClass)方法,在构造器中调用

package com.hykedu.spring.ioc;

import com.hykedu.spring.annotation.Component;
import com.hykedu.spring.annotation.ComponentScan;
import com.hykedu.spring.annotation.Scope;
import org.apache.commons.lang.StringUtils;

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

/**
 * @author 程序员蛇皮
 * @version 1.0
 * SpringApplicationContext 类的作用类似Spring原生ioc容器
 */
@SuppressWarnings("all")
public class CodeSnakeSpringApplicationContext {
    private Class configClass;
    //定义beanDefinitionMap,存放BeanDefinition对象
    private final ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();

    public CodeSnakeSpringApplicationContext(Class configClass) {
        scanBeanDefinition(configClass);
    }

    //该方法完成的对指定包的扫描,并将Bean信息封装到BeanDefinition对象,再放入map
    private void scanBeanDefinition(Class configClass) {
        this.configClass = configClass;
        System.out.println(configClass);
        //获取要扫描的包
        //1.先得到SpringConfig配置的@ComponentScan(value = "com.hykedu.spring.component")
        ComponentScan componentScan = (ComponentScan) this.configClass.getDeclaredAnnotation(ComponentScan.class);
        //2.componentScan的value就是我们要扫描的包
        String path = componentScan.value();
        System.out.println("要扫描的包:" + path);
        //得到要扫描的包下所有的资源(类.class)
        //1.得到类的加载器
        ClassLoader classLoader = CodeSnakeSpringApplicationContext.class.getClassLoader();
        //2.通过类加载器获取到要扫描包的url
        path = path.replace(".", "/");//把 . 替换成路径间隔符 /
        URL resource = classLoader.getResource(path);
        System.out.println("要扫描包的url:" + resource);
        //3.将要加载的资源(.class) 路径下的文件进行遍历=>io
        File file = new File(resource.getFile());
        if (file.isDirectory()) {//pand
            File[] files = file.listFiles();
            for (File f : files) {
                String absolutePath = f.getAbsolutePath();
                System.out.println("文件的绝对路径:" + absolutePath);
                //这里我们只处理.class文件
                if (absolutePath.endsWith(".class")) {
                    //1.获取到类名
                    String className = absolutePath.substring
                            (absolutePath.lastIndexOf("\\") + 1, absolutePath.lastIndexOf(".class"));
                    System.out.println("类名:" + className);
                    //2.获取类的完整路径(全类名)
                    String classFullName = path.replace("/", ".") + "." + className;
                    System.out.println("全类名:" + classFullName);
                    //3.判断该类是否需要注入容器,判断该类是不是有@Component/@Controller/@Repository/@Service注解
                    try {
                        Class<?> clazz = classLoader.loadClass(classFullName);
                        if (clazz.isAnnotationPresent(Component.class)) {
                            //如果在注解指定了value,将其赋值给className
                            System.out.println("这是一个Spring bean" + clazz);
                            Component componentAnnotation = clazz.getDeclaredAnnotation(Component.class);
                            String beanName = componentAnnotation.value();
                            if (beanName.equals("")) {
                                //将类名首字母小写作为beanName
                                beanName = StringUtils.uncapitalize(className);
                            }
                            BeanDefinition beanDefinition = new BeanDefinition();
                            beanDefinition.setClazz(clazz);
                            if (clazz.isAnnotationPresent(Scope.class)) {
                                //如果配置了Scope,获取他配置的值
                                Scope scopeAnnotation = clazz.getDeclaredAnnotation(Scope.class);
                                beanDefinition.setScope(scopeAnnotation.value());
                            } else {
                                //如果没有配置值,就默认Singleton
                                beanDefinition.setScope("singleton");
                            }
                            //将beanDefinition放入beanDefinitionMap
                            beanDefinitionMap.put(beanName, beanDefinition);
                        } else {
                            System.out.println("这不是一个Spring bean" + clazz);
                        }
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
    }
}

我们在AppMain测试方法中进行Debug,Bean对象成功存放至beanDefinitionMap中

三、实现任务阶段 3- 初始化 bean 单例池,并完成 getBean 方法 , createBean 方法

1…在CodeSnakeSpringApplicationContext类中定义singletonObjects容器,并将单例的Bean对象创建好实例放入其中(scanSingletonObjects()方法),实现getBean(String beanName)方法,没有@Scope(value = “prototype”)的对象默认设为@Scope(value = “singleton”)

package com.hykedu.spring.ioc;

import com.hykedu.spring.annotation.Component;
import com.hykedu.spring.annotation.ComponentScan;
import com.hykedu.spring.annotation.Scope;
import org.apache.commons.lang.StringUtils;

import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author 程序员蛇皮
 * @version 1.0
 * SpringApplicationContext 类的作用类似Spring原生ioc容器
 */
@SuppressWarnings("all")
public class CodeSnakeSpringApplicationContext {
    private Class configClass;
    //定义beanDefinitionMap,存放BeanDefinition对象
    private final ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
    //定义singletonObjects,存放单例对象
    private final ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();

    public CodeSnakeSpringApplicationContext(Class configClass) {
        scanBeanDefinition(configClass);
    }
    
    //初始化单例池方法
    private void scanSingletonObjects() {
        //通过 beanDefinitionMap,初始化 singletonObjects 单例池
        ConcurrentHashMap.KeySetView<String, BeanDefinition> keys = beanDefinitionMap.keySet();
        for (String key : keys) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(key);
            //判断该bean是singleton还是prototype
            if ("singleton".equalsIgnoreCase(beanDefinition.getScope())) {
                Object bean = createBean(beanDefinition, key);
                singletonObjects.put(key, bean);
            }
        }
    }
    
    //该方法完成的对指定包的扫描,并将Bean信息封装到BeanDefinition对象,再放入map
    private void scanBeanDefinition(Class configClass) {
        this.configClass = configClass;
        System.out.println(configClass);
        //获取要扫描的包
        //1.先得到SpringConfig配置的@ComponentScan(value = "com.hykedu.spring.component")
        ComponentScan componentScan = (ComponentScan) this.configClass.getDeclaredAnnotation(ComponentScan.class);
        //2.componentScan的value就是我们要扫描的包
        String path = componentScan.value();
        System.out.println("要扫描的包:" + path);
        //得到要扫描的包下所有的资源(类.class)
        //1.得到类的加载器
        ClassLoader classLoader = CodeSnakeSpringApplicationContext.class.getClassLoader();
        //2.通过类加载器获取到要扫描包的url
        path = path.replace(".", "/");//把 . 替换成路径间隔符 /
        URL resource = classLoader.getResource(path);
        System.out.println("要扫描包的url:" + resource);
        //3.将要加载的资源(.class) 路径下的文件进行遍历=>io
        File file = new File(resource.getFile());
        if (file.isDirectory()) {//pand
            File[] files = file.listFiles();
            for (File f : files) {
                String absolutePath = f.getAbsolutePath();
                System.out.println("文件的绝对路径:" + absolutePath);
                //这里我们只处理.class文件
                if (absolutePath.endsWith(".class")) {
                    //1.获取到类名
                    String className = absolutePath.substring
                            (absolutePath.lastIndexOf("\\") + 1, absolutePath.lastIndexOf(".class"));
                    System.out.println("类名:" + className);
                    //2.获取类的完整路径(全类名)
                    String classFullName = path.replace("/", ".") + "." + className;
                    System.out.println("全类名:" + classFullName);
                    //3.判断该类是否需要注入容器,判断该类是不是有@Component/@Controller/@Repository/@Service注解
                    try {
                        Class<?> clazz = classLoader.loadClass(classFullName);
                        if (clazz.isAnnotationPresent(Component.class)) {
                            //如果在注解指定了value,将其赋值给className
                            System.out.println("这是一个Spring bean" + clazz);
                            Component componentAnnotation = clazz.getDeclaredAnnotation(Component.class);
                            String beanName = componentAnnotation.value();
                            if (beanName.equals("")) {
                                //将类名首字母小写作为beanName
                                beanName = StringUtils.uncapitalize(className);
                            }
                            BeanDefinition beanDefinition = new BeanDefinition();
                            beanDefinition.setClazz(clazz);
                            if (clazz.isAnnotationPresent(Scope.class)) {
                                //如果配置了Scope,获取他配置的值
                                Scope scopeAnnotation = clazz.getDeclaredAnnotation(Scope.class);
                                beanDefinition.setScope(scopeAnnotation.value());
                            } else {
                                //如果没有配置值,就默认Singleton
                                beanDefinition.setScope("singleton");
                            }
                            //将beanDefinition放入beanDefinitionMap
                            beanDefinitionMap.put(beanName, beanDefinition);
                        } else {
                            System.out.println("这不是一个Spring bean" + clazz);
                        }
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
    }
    //完成createBean(BeanDefinition beanDefinition)方法
    private Object createBean(BeanDefinition beanDefinition, String beanName) {
        //得到Bean的clazz对象
        Class clazz = beanDefinition.getClazz();
        try {
            //使用反射得到对象实例
            Object instance = clazz.getDeclaredConstructor().newInstance();
            System.out.println("------------------------------------------------------------------------------------------------------------------------------------------------");
            System.out.println("=====创建好实例=====" + instance);
            if (instance != null) {
                return instance;
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return null;
    }

    //编写方法,返回容器的对象
    public Object getBean(String name) {
        if (beanDefinitionMap.containsKey(name)) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(name);
            if ("singleton".equalsIgnoreCase(beanDefinition.getScope())) {
                //说明是单例配置
                return singletonObjects.get(name);
            } else {//如果不是单例的,就反射一个新的对象
                return createBean(beanDefinition, name);
            }
        }
        throw new NullPointerException("没有该bean");
    }
}

2.将studentService.java设置成多例的(@Scope(value = “prototype”)),方便测试

package com.hykedu.spring.component;

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

/**
 * @author 程序员蛇皮
 * @version 1.0
 */
@Scope(value = "prototype")
@Component(value = "monsterService")
public class MonsterService implements InitializingBean {
}

测试方法AppMain,对其进行debug

package com.hykedu.spring;

import com.hykedu.spring.component.StudentDao;
import com.hykedu.spring.component.StudentService;
import com.hykedu.spring.ioc.CodeSnakeSpringApplicationContext;
import com.hykedu.spring.ioc.CodeSnakeSpringConfig;

/**
 * @author 程序员蛇皮
 * @version 1.0
 */
public class AppMain {
    public static void main(String[] args) {
        CodeSnakeSpringApplicationContext codeSnakeSpringApplicationContext = new CodeSnakeSpringApplicationContext(CodeSnakeSpringConfig.class);
        StudentDao studentDao1 = (StudentDao) codeSnakeSpringApplicationContext.getBean("studentDao");
        StudentDao studentDao = (StudentDao) codeSnakeSpringApplicationContext.getBean("studentDao");
        StudentService studentService1 = (StudentService) codeSnakeSpringApplicationContext.getBean("studentService");
        StudentService studentService2 = (StudentService) codeSnakeSpringApplicationContext.getBean("studentService");
        System.out.println("ok");
    }
}

测试结果:只有单例的StudentDao存放到了singletonObjects容器中,实现了@Scope(value = “prototype”)注解的的多例对象StudentService没有存放到单例池中,而单例的StudentDao对象创建了两个对象地址相同,多例的StudentService的两个对象地址不同,与原生Spring一致

四、实现任务阶段 4- 完成依赖注入


说明:整个实现思路,就是参考 Spring 规范
1.定义Autowired注解,实现了该注解的属性将会被进行依赖注入(看下面代码实现)

package com.hykedu.spring.annotation;

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

/**
 * @author 程序员蛇皮
 * @version 1.0
 */
@Target({ElementType.METHOD, ElementType.FIELD,})
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
}

2.编辑CodeSnakeSpringApplicationContext类,在createBean方法增加识别Autowired注解进行依赖注入的功能

//完成createBean(BeanDefinition beanDefinition)方法
private Object createBean(BeanDefinition beanDefinition, String beanName) {
    //得到Bean的clazz对象
    Class clazz = beanDefinition.getClazz();
    try {
        //使用反射得到对象实例
        Object instance = clazz.getDeclaredConstructor().newInstance();
        //依赖注入
        //1.遍历当前要创建的对象的所有字段
        for (Field declaredField : clazz.getDeclaredFields()) {
            //2.判断这个字段是否有@Autowired
            if (declaredField.isAnnotationPresent(Autowired.class)) {
                //3.得到这个字段的名字
                String name = declaredField.getName();
                System.out.println("字段名:" + name);
                //4.通过getBean()获取要组装的对象
                Object bean = getBean(name);
                //5.进行组装
                //私有属性需要进行反射暴破
                declaredField.setAccessible(true);
                declaredField.set(instance, bean);
            }
        }
        System.out.println("------------------------------------------------------------------------------------------------------------------------------------------------");
        System.out.println("=====创建好实例=====" + instance);
        if (instance != null) {
            return instance;
        }
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
    return null;
}

3.在StudentDao类中定义hi()方法,在StudentService类中定义m1()方法,在m1()调用StudentDao类的hi()方法
StudentDao.java

package com.hykedu.spring.component;

import com.hykedu.spring.annotation.Component;

/**
 * @author 程序员蛇皮
 * @version 1.0
 */
@Component
public class StudentDao {
    public void hi() {
        System.out.println("StudentDao hi~");
    }
}

StudentService.java

package com.hykedu.spring.component;

import com.hykedu.spring.annotation.Autowired;
import com.hykedu.spring.annotation.Component;
import com.hykedu.spring.annotation.Scope;

/**
 * @author 程序员蛇皮
 * @version 1.0
 */
@Scope(value = "prototype")
@Component
public class StudentService {
    //这里我们使用自己的 @Autowired 来修饰属性
    //表示该属性,是通过容器完成依赖注入
    //我们这里实现按照名字进行组装
    @Autowired
    private StudentDao studentDao;
    public void m1(){
        studentDao.hi();
    }
}

测试方法AppMain

package com.hykedu.spring;

import com.hykedu.spring.component.StudentDao;
import com.hykedu.spring.component.StudentService;
import com.hykedu.spring.ioc.CodeSnakeSpringApplicationContext;
import com.hykedu.spring.ioc.CodeSnakeSpringConfig;

/**
 * @author 程序员蛇皮
 * @version 1.0
 */
public class AppMain {
    public static void main(String[] args) {
        CodeSnakeSpringApplicationContext codeSnakeSpringApplicationContext = new CodeSnakeSpringApplicationContext(CodeSnakeSpringConfig.class);
        StudentService studentService = (StudentService) codeSnakeSpringApplicationContext.getBean("studentService");
        studentService.m1();
        System.out.println("ok");
    }
}

测试结果:StudentService中的StudentDao对象成功地进行了依赖注入

五、实现任务阶段 5- bean 后置处理器实现


1.在processor包定义InitializingBean接口
实现该接口的 Bean , 需要实现 Bean 初始化方法, 可以参考 原生 Spring 规范来定义这个接口

package com.hykedu.spring.processor;

/**
 * @author 程序员蛇皮
 * @version 1.0
 * 1.我们根据原生Spring 定义了一个InitializingBean
 * 2.该 InitializingBean接口有一个void afterPropertiesSet() throws Exception;
 * 3.afterPropertiesSet() 在bean的setter后执行,即等价于Spring原来的初始化方法init()
 * 4.当一个Bean实现这个接口后,就实现afterPropertiesSet(),这个方法就是初始化方法
 */
public interface InitializingBean {
    void afterPropertiesSet() throws Exception;
}

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

//完成createBean(BeanDefinition beanDefinition)方法
private Object createBean(BeanDefinition beanDefinition, String beanName) {
    //得到Bean的clazz对象
    Class clazz = beanDefinition.getClazz();
    try {
        //使用反射得到对象实例
        Object instance = clazz.getDeclaredConstructor().newInstance();
        //依赖注入
        //1.遍历当前要创建的对象的所有字段
        for (Field declaredField : clazz.getDeclaredFields()) {
            //2.判断这个字段是否有@Autowired
            if (declaredField.isAnnotationPresent(Autowired.class)) {
                //3.得到这个字段的名字
                String name = declaredField.getName();
                System.out.println("字段名:" + name);
                //4.通过getBean()获取要组装的对象
                Object bean = getBean(name);
                //5.进行组装
                //私有属性需要进行反射暴破
                declaredField.setAccessible(true);
                declaredField.set(instance, bean);
            }
        }
        System.out.println("------------------------------------------------------------------------------------------------------------------------------------------------");
        System.out.println("=====创建好实例=====" + instance);
        //这里要判断是否要执行Bean的初始化方法
        //1.判断当前创建的Bean对象是否实现了InitializingBean
        //2.实现了InitializingBean就调用初始化方法
        if (instance instanceof InitializingBean) {
            try {
                ((InitializingBean) instance).afterPropertiesSet();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        if (instance != null) {
            return instance;
        }
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
    return null;
}

3.测试:在StudentService类中实现InitializingBean接口并重新初始化方法

package com.hykedu.spring.component;

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

/**
 * @author 程序员蛇皮
 * @version 1.0
 */
@Scope(value = "prototype")
@Component
public class StudentService implements InitializingBean {
    //这里我们使用自己的 @Autowired 来修饰属性
    //表示该属性,是通过容器完成依赖注入
    //我们这里实现按照名字进行组装
    @Autowired
    private StudentDao studentDao;
    public void m1(){
        studentDao.hi();
    }

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

测试方法AppMain

package com.hykedu.spring;

import com.hykedu.spring.component.StudentDao;
import com.hykedu.spring.component.StudentService;
import com.hykedu.spring.ioc.CodeSnakeSpringApplicationContext;
import com.hykedu.spring.ioc.CodeSnakeSpringConfig;

/**
 * @author 程序员蛇皮
 * @version 1.0
 */
public class AppMain {
    public static void main(String[] args) {
        CodeSnakeSpringApplicationContext codeSnakeSpringApplicationContext = new CodeSnakeSpringApplicationContext(CodeSnakeSpringConfig.class);
        StudentDao studentDao1 = (StudentDao) codeSnakeSpringApplicationContext.getBean("studentDao");
        StudentDao studentDao = (StudentDao) codeSnakeSpringApplicationContext.getBean("studentDao");
        StudentService studentService1 = (StudentService) codeSnakeSpringApplicationContext.getBean("studentService");
        StudentService studentService2 = (StudentService) codeSnakeSpringApplicationContext.getBean("studentService");
        System.out.println("ok");
    }
}

测试结果:实现了InitializingBean接口的对象都在创建好实例后执行了初始化方法

4.在processor包定义后置处理器BeanPostProcessor接口

package com.hykedu.spring.processor;

/**
 * @author 程序员蛇皮
 * @version 1.0
 * 1.参考原生Spring容器,定义一个BeanPostProcessor接口
 * 2.该接口有两个方法 postProcessBeforeInitialization(Object bean, String beanName)、postProcessAfterInitialization(Object bean, String beanName)
 * 3.这两个方法会对Spring容器的所有Bean生效
 */
public interface BeanPostProcessor {
    /**
     * 1.postProcessBeforeInitialization在Bean的初始化前调用
     * @param bean
     * @param beanName
     * @return
     */
    default Object postProcessBeforeInitialization(Object bean, String beanName) {
        return bean;
    }

    /**
     * 1.postProcessAfterInitializatio在Bean的初始化后调用
     * @param bean
     * @param beanName
     * @return
     */
    default Object postProcessAfterInitialization(Object bean, String beanName) {
        return bean;
    }
}

5.在component包中定义MyProcessor类,该类实现了BeanPostProcessor接口,重写其中的方法

package com.hykedu.spring.component;

import com.hykedu.spring.annotation.Component;
import com.hykedu.spring.processor.BeanPostProcessor;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @author 程序员蛇皮
 * @version 1.0
 * 1.这是我们自己的一个后置处理器
 * 2.实现了BeanPostProcessor
 * 3.我们可以重新before和after
 * 4.在Spring容器中,仍然把MyBeanPostProcessor当作一个Bean对象,注入到容器中
 * 5.@Component
 */
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        System.out.println("后置处理器postProcessBeforeInitialization()被调用 bean类型=" + bean.getClass() + " bean的名字=" + beanName);
        return bean;
    }

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

6.编辑CodeSnakeSpringApplicationContext类的scanBeanDefinition(Class configClass)和createBean(BeanDefinition beanDefinition)方法,在我们Bean的初始化方法前,调用后置处理器的before方法,在我们Bean的初始化方法后,调用后置处理器的after方法

package com.hykedu.spring.ioc;

import com.hykedu.spring.annotation.Autowired;
import com.hykedu.spring.annotation.Component;
import com.hykedu.spring.annotation.ComponentScan;
import com.hykedu.spring.annotation.Scope;
import com.hykedu.spring.processor.BeanPostProcessor;
import com.hykedu.spring.processor.InitializingBean;
import org.apache.commons.lang.StringUtils;

import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author 程序员蛇皮
 * @version 1.0
 * SpringApplicationContext 类的作用类似Spring原生ioc容器
 */
@SuppressWarnings("all")
public class CodeSnakeSpringApplicationContext {
    private Class configClass;
    //定义beanDefinitionMap,存放BeanDefinition对象
    private final ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
    //定义singletonObjects,存放单例对象
    private final ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();
    //定义beanPostProcessors=>存放后置处理器
    private final List<BeanPostProcessor> beanPostProcessors = new ArrayList<>();

    public CodeSnakeSpringApplicationContext(Class configClass) {
        scanBeanDefinition(configClass);
        scanSingletonObjects();
    }

    //初始化单例池方法
    private void scanSingletonObjects() {
        //通过 beanDefinitionMap,初始化 singletonObjects 单例池
        ConcurrentHashMap.KeySetView<String, BeanDefinition> keys = beanDefinitionMap.keySet();
        for (String key : keys) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(key);
            //判断该bean是singleton还是prototype
            if ("singleton".equalsIgnoreCase(beanDefinition.getScope())) {
                Object bean = createBean(beanDefinition, key);
                singletonObjects.put(key, bean);
            }
        }
    }

    //该方法完成的对指定包的扫描,并将Bean信息封装到BeanDefinition对象,再放入map
    private void scanBeanDefinition(Class configClass) {
        this.configClass = configClass;
        System.out.println(configClass);
        //获取要扫描的包
        //1.先得到SpringConfig配置的@ComponentScan(value = "com.hykedu.spring.component")
        ComponentScan componentScan = (ComponentScan) this.configClass.getDeclaredAnnotation(ComponentScan.class);
        //2.componentScan的value就是我们要扫描的包
        String path = componentScan.value();
        System.out.println("要扫描的包:" + path);
        //得到要扫描的包下所有的资源(类.class)
        //1.得到类的加载器
        ClassLoader classLoader = CodeSnakeSpringApplicationContext.class.getClassLoader();
        //2.通过类加载器获取到要扫描包的url
        path = path.replace(".", "/");//把 . 替换成路径间隔符 /
        URL resource = classLoader.getResource(path);
        System.out.println("要扫描包的url:" + resource);
        //3.将要加载的资源(.class) 路径下的文件进行遍历=>io
        File file = new File(resource.getFile());
        if (file.isDirectory()) {//pand
            File[] files = file.listFiles();
            for (File f : files) {
                String absolutePath = f.getAbsolutePath();
                System.out.println("文件的绝对路径:" + absolutePath);
                //这里我们只处理.class文件
                if (absolutePath.endsWith(".class")) {
                    //1.获取到类名
                    String className = absolutePath.substring
                            (absolutePath.lastIndexOf("\\") + 1, absolutePath.lastIndexOf(".class"));
                    System.out.println("类名:" + className);
                    //2.获取类的完整路径(全类名)
                    String classFullName = path.replace("/", ".") + "." + className;
                    System.out.println("全类名:" + classFullName);
                    //3.判断该类是否需要注入容器,判断该类是不是有@Component/@Controller/@Repository/@Service注解
                    try {
                        Class<?> clazz = classLoader.loadClass(classFullName);
                        if (clazz.isAnnotationPresent(Component.class)) {
                            //如果在注解指定了value,将其赋值给className
                            System.out.println("这是一个Spring bean" + clazz);
                            //1.这里为了方便,将后置处理器放入到ArrayList集合中
                            //2.如果发现是一个后置处理器,放入到beanPostProcessors
                            //3.在原生的Spring中,对后置处理器还是走的getBean,createBean,但是我们需要在singletonObjects加入相应的业务逻辑
                            //4.这里我们简化,用的ArrayList

                            //判断这个clazz有没有实现BeanPostProcessor
                            //说明:这里我们不能使用 instanceof 来判断clazz是否实现了BeanPostProcessor
                            //原因:clazz不是一个实例对象,而是一个类对象/clazz
                            if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
                                BeanPostProcessor beanPostProcessor = (BeanPostProcessor) clazz.newInstance();
                                beanPostProcessors.add(beanPostProcessor);
                                continue;
                            }
                            Component componentAnnotation = clazz.getDeclaredAnnotation(Component.class);
                            String beanName = componentAnnotation.value();
                            if (beanName.equals("")) {
                                //将类名首字母小写作为beanName
                                beanName = StringUtils.uncapitalize(className);
                            }
                            BeanDefinition beanDefinition = new BeanDefinition();
                            beanDefinition.setClazz(clazz);
                            if (clazz.isAnnotationPresent(Scope.class)) {
                                //如果配置了Scope,获取他配置的值
                                Scope scopeAnnotation = clazz.getDeclaredAnnotation(Scope.class);
                                beanDefinition.setScope(scopeAnnotation.value());
                            } else {
                                //如果没有配置值,就默认Singleton
                                beanDefinition.setScope("singleton");
                            }
                            //将beanDefinition放入beanDefinitionMap
                            beanDefinitionMap.put(beanName, beanDefinition);
                        } else {
                            System.out.println("这不是一个Spring bean" + clazz);
                        }
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
    }

    //完成createBean(BeanDefinition beanDefinition)方法
    private Object createBean(BeanDefinition beanDefinition, String beanName) {
        //得到Bean的clazz对象
        Class clazz = beanDefinition.getClazz();
        try {
            //使用反射得到对象实例
            Object instance = clazz.getDeclaredConstructor().newInstance();
            //依赖注入
            //1.遍历当前要创建的对象的所有字段
            for (Field declaredField : clazz.getDeclaredFields()) {
                //2.判断这个字段是否有@Autowired
                if (declaredField.isAnnotationPresent(Autowired.class)) {
                    //3.得到这个字段的名字
                    String name = declaredField.getName();
                    System.out.println("字段名:" + name);
                    //4.通过getBean()获取要组装的对象
                    Object bean = getBean(name);
                    //5.进行组装
                    //私有属性需要进行反射暴破
                    declaredField.setAccessible(true);
                    declaredField.set(instance, bean);
                }
            }
            System.out.println("------------------------------------------------------------------------------------------------------------------------------------------------");
            System.out.println("=====创建好实例=====" + instance);
            //在我们Bean的初始化方法前,调用后置处理器的before方法
            for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
                Object current = beanPostProcessor.postProcessBeforeInitialization(instance, beanName);
                if (current != null) {
                    instance = current;
                }
            }
            //这里要判断是否要执行Bean的初始化方法
            //1.判断当前创建的Bean对象是否实现了InitializingBean
            //2.实现了InitializingBean就调用初始化方法
            if (instance instanceof InitializingBean) {
                try {
                    ((InitializingBean) instance).afterPropertiesSet();
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
            //在我们Bean的初始化方法后,调用后置处理器的after方法
            for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
                Object current = beanPostProcessor.postProcessAfterInitialization(instance, beanName);
                if (current != null) {
                    instance = current;
                }
            }
            if (instance != null) {
                return instance;
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return null;
    }

    //编写方法,返回容器的对象
    public Object getBean(String name) {
        if (beanDefinitionMap.containsKey(name)) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(name);
            if ("singleton".equalsIgnoreCase(beanDefinition.getScope())) {
                //说明是单例配置
                return singletonObjects.get(name);
            } else {//如果不是单例的,就反射一个新的对象
                return createBean(beanDefinition, name);
            }
        }
        throw new NullPointerException("没有该bean");
    }
}

7.在AppMain中进行测试
测试结果:成功在初始化方法前后调用后置处理器方法

六、实现任务阶段 6- AOP 机制实现


AOP作用示意图
因为原生Spring的AOP是在初始化方法后进行的动态代理,所有我们在后置处理器中的postProcessAfterInitialization(Object bean, String beanName)中编写动态代理代码
1.分别定义SmartAnimalable接口、实现了SmartAnimalable接口的SmartDog类、切面类SmartAnimalAspect

package com.hykedu.spring.component;

/**
 * @author 程序员蛇皮
 * @version 1.0
 */
public interface SmartAnimalable {
    float getSum(float i,float j);
    float getSub(float i,float j);
}
package com.hykedu.spring.component;

import com.hykedu.spring.annotation.Component;

/**
 * @author 程序员蛇皮
 * @version 1.0
 */
@Component
public class SmartDog implements SmartAnimalable {
    @Override
    public float getSum(float i, float j) {
        float result = i + j;
//        result = 1/0;//模拟一个算术异常
        System.out.println("方法内部打印result = " + result);
        return result;
    }

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

import com.hykedu.spring.annotation.Component;

/**
 * @author 程序员蛇皮
 * @version 1.0
 * SmartAnimalAspect我们当作一个切面类来使用
 */
@Component
public class SmartAnimalAspect {
    public static void showBeginLog(){
        System.out.println("前置通知..");
    }
    public static void showSuccessLog(){
        System.out.println("返回通知..");
    }
}

说明:这里我们只对SmartDog的getSum(float i, float j)方法进行切入,写得比较死,如果要写灵活还是使用注解那一套,只不过跟aop关系不大了,这里就用写死的办法
2.修改后置处理器中的postProcessAfterInitialization(Object bean, String beanName)方法

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
    System.out.println("后置处理器postProcessAfterInitialization()被调用 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 {
                        Object result = null;
                        if ("getSum".equals(method.getName())) {
                            SmartAnimalAspect.showBeginLog();
                            result = method.invoke(bean, args);
                            SmartAnimalAspect.showSuccessLog();
                        } else {
                            result = method.invoke(bean, args);
                        }
                        return result;
                    }
                });
        //如果Bean需要返回代理对象
        return proxyInstance;
    }
    //如果不需要AOP,返回原生Bean对象
    return bean;
}

测试方法AppMain

package com.hykedu.spring;

import com.hykedu.spring.component.SmartAnimalAspect;
import com.hykedu.spring.component.SmartAnimalable;
import com.hykedu.spring.component.StudentDao;
import com.hykedu.spring.component.StudentService;
import com.hykedu.spring.ioc.CodeSnakeSpringApplicationContext;
import com.hykedu.spring.ioc.CodeSnakeSpringConfig;

/**
 * @author 程序员蛇皮
 * @version 1.0
 */
public class AppMain {
    public static void main(String[] args) {
        CodeSnakeSpringApplicationContext codeSnakeSpringApplicationContext = new CodeSnakeSpringApplicationContext(CodeSnakeSpringConfig.class);
        SmartAnimalable smartDog = (SmartAnimalable) codeSnakeSpringApplicationContext.getBean("smartDog");
        smartDog.getSum(10,10);
        System.out.println("ok");
    }
}

测试结果:成功在SmartDog的getSum(float i, float j)方法前后切入前置通知和后置通知

七、手动实现 Spring 底层机制【初始化 IOC容器+依赖注入+BeanPostProcessor 机制+AOP】完整代码

annotation包

package com.hykedu.spring.annotation;

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

/**
 * @author 程序员蛇皮
 * @version 1.0
 */
@Target({ElementType.METHOD, ElementType.FIELD,})
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
}
package com.hykedu.spring.annotation;

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

/**
 * @author 程序员蛇皮
 * @version 1.0
 * 1.@Target(ElementType.TYPE)指定我们的 ComponentScan注解可以修饰 Type程序元素
 * 2.@Retention(RetentionPolicy.RUNTIME) ComponentScan注解 保留范围
 * 3.String value() default ""; 表示 ComponentScan注解 可以传入value
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
    String value() default "";
}
package com.hykedu.spring.annotation;

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

/**
 * @author 程序员蛇皮
 * @version 1.0
 * 1.@Target(ElementType.TYPE)指定我们的 ComponentScan注解可以修饰 Type程序元素
 * 2.@Retention(RetentionPolicy.RUNTIME) ComponentScan注解 保留范围
 * 3.String value() default ""; 表示 ComponentScan注解 可以传入value
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ComponentScan {
    String value() default "";
}
package com.hykedu.spring.annotation;

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

/**
 * @author 程序员蛇皮
 * @version 1.0
 * Scope可以指定bean的作用范围[singleton,prototype]
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Scope {
    String value() default "";
}

component包

package com.hykedu.spring.component;

/**
 * @author 程序员蛇皮
 * @version 1.0
 */
public class Car {
}
package com.hykedu.spring.component;

import com.hykedu.spring.annotation.Component;
import com.hykedu.spring.processor.BeanPostProcessor;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @author 程序员蛇皮
 * @version 1.0
 * 1.这是我们自己的一个后置处理器
 * 2.实现了BeanPostProcessor
 * 3.我们可以重新before和after
 * 4.在Spring容器中,仍然把MyBeanPostProcessor当作一个Bean对象,注入到容器中
 * 5.@Component
 */
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        System.out.println("后置处理器postProcessBeforeInitialization()被调用 bean类型=" + bean.getClass() + " bean的名字=" + beanName);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        System.out.println("后置处理器postProcessAfterInitialization()被调用 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 {
                                                                  Object result = null;
                                                                  if ("getSum".equals(method.getName())) {
                                                                      SmartAnimalAspect.showBeginLog();
                                                                      result = method.invoke(bean, args);
                                                                      SmartAnimalAspect.showSuccessLog();
                                                                  } else {
                                                                      result = method.invoke(bean, args);
                                                                  }
                                                                  return result;
                                                              }
                                                          });
            //如果Bean需要返回代理对象
            return proxyInstance;
        }
        //如果不需要AOP,返回原生Bean对象
        return bean;
    }
}
package com.hykedu.spring.component;

/**
 * @author 程序员蛇皮
 * @version 1.0
 */
public interface SmartAnimalable {
    float getSum(float i,float j);
    float getSub(float i,float j);
}
package com.hykedu.spring.component;

import com.hykedu.spring.annotation.Component;

/**
 * @author 程序员蛇皮
 * @version 1.0
 * SmartAnimalAspect我们当作一个切面类来使用
 */
//@Component
public class SmartAnimalAspect {
    public static void showBeginLog(){
        System.out.println("前置通知..");
    }
    public static void showSuccessLog(){
        System.out.println("返回通知..");
    }
}
package com.hykedu.spring.component;

import com.hykedu.spring.annotation.Component;

/**
 * @author 程序员蛇皮
 * @version 1.0
 */
@Component
public class SmartDog implements SmartAnimalable {
    @Override
    public float getSum(float i, float j) {
        float result = i + j;
        //        result = 1/0;//模拟一个算术异常
        System.out.println("方法内部打印result = " + result);
        return result;
    }

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

import com.hykedu.spring.annotation.Component;

/**
 * @author 程序员蛇皮
 * @version 1.0
 */
@Component
public class StudentDao {
    public void hi() {
        System.out.println("StudentDao hi~");
    }
}
package com.hykedu.spring.component;

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

/**
 * @author 程序员蛇皮
 * @version 1.0
 */
@Scope(value = "prototype")
@Component
public class StudentService implements InitializingBean {
    //这里我们使用自己的 @Autowired 来修饰属性
    //表示该属性,是通过容器完成依赖注入
    //我们这里实现按照名字进行组装
    @Autowired
    private StudentDao studentDao;
    public void m1(){
        studentDao.hi();
    }

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

ioc包

package com.hykedu.spring.ioc;

/**
 * @author 程序员蛇皮
 * @version 1.0
 * BeanDefinition 用于封装/记录Bean的信息[1.scope 2.Bean对应的Class对象,反射可以生成对应的对象
 */
@SuppressWarnings("all")
public class BeanDefinition {
    private String scope;
    private Class clazz;

    public String getScope() {
        return scope;
    }

    public void setScope(String scope) {
        this.scope = scope;
    }

    public Class getClazz() {
        return clazz;
    }

    public void setClazz(Class clazz) {
        this.clazz = clazz;
    }

    @Override
    public String toString() {
        return "BeanDefinition{" +
        "scope='" + scope + '\'' +
        ", clazz=" + clazz +
        '}';
    }
}
package com.hykedu.spring.ioc;

import com.hykedu.spring.annotation.Autowired;
import com.hykedu.spring.annotation.Component;
import com.hykedu.spring.annotation.ComponentScan;
import com.hykedu.spring.annotation.Scope;
import com.hykedu.spring.processor.BeanPostProcessor;
import com.hykedu.spring.processor.InitializingBean;
import org.apache.commons.lang.StringUtils;

import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author 程序员蛇皮
 * @version 1.0
 * CodeSnakeSpringApplicationContext 类的作用类似Spring原生ioc容器
 */
@SuppressWarnings("all")
public class CodeSnakeSpringApplicationContext {
    private Class configClass;
    //定义beanDefinitionMap,存放BeanDefinition对象
    private final ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
    //定义singletonObjects,存放单例对象
    private final ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();
    //定义beanPostProcessors=>存放后置处理器
    private final List<BeanPostProcessor> beanPostProcessors = new ArrayList<>();

    public CodeSnakeSpringApplicationContext(Class configClass) {
        scanBeanDefinition(configClass);
        scanSingletonObjects();
    }

    //初始化单例池方法
    private void scanSingletonObjects() {
        //通过 beanDefinitionMap,初始化 singletonObjects 单例池
        ConcurrentHashMap.KeySetView<String, BeanDefinition> keys = beanDefinitionMap.keySet();
        for (String key : keys) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(key);
            //判断该bean是singleton还是prototype
            if ("singleton".equalsIgnoreCase(beanDefinition.getScope())) {
                Object bean = createBean(beanDefinition, key);
                singletonObjects.put(key, bean);
            }
        }
    }

    //该方法完成的对指定包的扫描,并将Bean信息封装到BeanDefinition对象,再放入map
    private void scanBeanDefinition(Class configClass) {
        this.configClass = configClass;
        System.out.println(configClass);
        //获取要扫描的包
        //1.先得到SpringConfig配置的@ComponentScan(value = "com.hykedu.spring.component")
        ComponentScan componentScan = (ComponentScan) this.configClass.getDeclaredAnnotation(ComponentScan.class);
        //2.componentScan的value就是我们要扫描的包
        String path = componentScan.value();
        System.out.println("要扫描的包:" + path);
        //得到要扫描的包下所有的资源(类.class)
        //1.得到类的加载器
        ClassLoader classLoader = CodeSnakeSpringApplicationContext.class.getClassLoader();
        //2.通过类加载器获取到要扫描包的url
        path = path.replace(".", "/");//把 . 替换成路径间隔符 /
        URL resource = classLoader.getResource(path);
        System.out.println("要扫描包的url:" + resource);
        //3.将要加载的资源(.class) 路径下的文件进行遍历=>io
        File file = new File(resource.getFile());
        if (file.isDirectory()) {//pand
            File[] files = file.listFiles();
            for (File f : files) {
                String absolutePath = f.getAbsolutePath();
                System.out.println("文件的绝对路径:" + absolutePath);
                //这里我们只处理.class文件
                if (absolutePath.endsWith(".class")) {
                    //1.获取到类名
                    String className = absolutePath.substring
                            (absolutePath.lastIndexOf("\\") + 1, absolutePath.lastIndexOf(".class"));
                    System.out.println("类名:" + className);
                    //2.获取类的完整路径(全类名)
                    String classFullName = path.replace("/", ".") + "." + className;
                    System.out.println("全类名:" + classFullName);
                    //3.判断该类是否需要注入容器,判断该类是不是有@Component/@Controller/@Repository/@Service注解
                    try {
                        Class<?> clazz = classLoader.loadClass(classFullName);
                        if (clazz.isAnnotationPresent(Component.class)) {
                            //如果在注解指定了value,将其赋值给className
                            System.out.println("这是一个Spring bean" + clazz);
                            //1.这里为了方便,将后置处理器放入到ArrayList集合中
                            //2.如果发现是一个后置处理器,放入到beanPostProcessors
                            //3.在原生的Spring中,对后置处理器还是走的getBean,createBean,但是我们需要在singletonObjects加入相应的业务逻辑
                            //4.这里我们简化,用的ArrayList

                            //判断这个clazz有没有实现BeanPostProcessor
                            //说明:这里我们不能使用 instanceof 来判断clazz是否实现了BeanPostProcessor
                            //原因:clazz不是一个实例对象,而是一个类对象/clazz
                            if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
                                BeanPostProcessor beanPostProcessor = (BeanPostProcessor) clazz.newInstance();
                                beanPostProcessors.add(beanPostProcessor);
                                continue;
                            }
                            Component componentAnnotation = clazz.getDeclaredAnnotation(Component.class);
                            String beanName = componentAnnotation.value();
                            if (beanName.equals("")) {
                                //将类名首字母小写作为beanName
                                beanName = StringUtils.uncapitalize(className);
                            }
                            BeanDefinition beanDefinition = new BeanDefinition();
                            beanDefinition.setClazz(clazz);
                            if (clazz.isAnnotationPresent(Scope.class)) {
                                //如果配置了Scope,获取他配置的值
                                Scope scopeAnnotation = clazz.getDeclaredAnnotation(Scope.class);
                                beanDefinition.setScope(scopeAnnotation.value());
                            } else {
                                //如果没有配置值,就默认Singleton
                                beanDefinition.setScope("singleton");
                            }
                            //将beanDefinition放入beanDefinitionMap
                            beanDefinitionMap.put(beanName, beanDefinition);
                        } else {
                            System.out.println("这不是一个Spring bean" + clazz);
                        }
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        }
    }

    //完成createBean(BeanDefinition beanDefinition)方法
    private Object createBean(BeanDefinition beanDefinition, String beanName) {
        //得到Bean的clazz对象
        Class clazz = beanDefinition.getClazz();
        try {
            //使用反射得到对象实例
            Object instance = clazz.getDeclaredConstructor().newInstance();
            //依赖注入
            //1.遍历当前要创建的对象的所有字段
            for (Field declaredField : clazz.getDeclaredFields()) {
                //2.判断这个字段是否有@Autowired
                if (declaredField.isAnnotationPresent(Autowired.class)) {
                    //3.得到这个字段的名字
                    String name = declaredField.getName();
                    System.out.println("字段名:" + name);
                    //4.通过getBean()获取要组装的对象
                    Object bean = getBean(name);
                    //5.进行组装
                    //私有属性需要进行反射暴破
                    declaredField.setAccessible(true);
                    declaredField.set(instance, bean);
                }
            }
            System.out.println("------------------------------------------------------------------------------------------------------------------------------------------------");
            System.out.println("=====创建好实例=====" + instance);
            //在我们Bean的初始化方法前,调用后置处理器的before方法
            for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
                Object current = beanPostProcessor.postProcessBeforeInitialization(instance, beanName);
                if (current != null) {
                    instance = current;
                }
            }
            //这里要判断是否要执行Bean的初始化方法
            //1.判断当前创建的Bean对象是否实现了InitializingBean
            //2.实现了InitializingBean就调用初始化方法
            if (instance instanceof InitializingBean) {
                try {
                    ((InitializingBean) instance).afterPropertiesSet();
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
            //在我们Bean的初始化方法后,调用后置处理器的after方法
            for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {
                Object current = beanPostProcessor.postProcessAfterInitialization(instance, beanName);
                if (current != null) {
                    instance = current;
                }
            }
            if (instance != null) {
                return instance;
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return null;
    }

    //编写方法,返回容器的对象
    public Object getBean(String name) {
        if (beanDefinitionMap.containsKey(name)) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(name);
            if ("singleton".equalsIgnoreCase(beanDefinition.getScope())) {
                //说明是单例配置
                return singletonObjects.get(name);
            } else {//如果不是单例的,就反射一个新的对象
                return createBean(beanDefinition, name);
            }
        }
        throw new NullPointerException("没有该bean");
    }
}
package com.hykedu.spring.ioc;

import com.hykedu.spring.annotation.ComponentScan;

/**
 * @author 程序员蛇皮
 * @version 1.0
 * 这是一个配置类,作用类似于原生Spring的 beans.xml 容器配置文件
 */
@ComponentScan(value = "com.hykedu.spring.component")
public class CodeSnakeSpringConfig {
}

processor包

package com.hykedu.spring.processor;

/**
 * @author 程序员蛇皮
 * @version 1.0
 * 1.参考原生Spring容器,定义一个BeanPostProcessor接口
 * 2.该接口有两个方法 postProcessBeforeInitialization(Object bean, String beanName)、postProcessAfterInitialization(Object bean, String beanName)
 * 3.这两个方法会对Spring容器的所有Bean生效
 */
public interface BeanPostProcessor {
    /**
     * 1.postProcessBeforeInitialization在Bean的初始化前调用
     * @param bean
     * @param beanName
     * @return
     */
    default Object postProcessBeforeInitialization(Object bean, String beanName) {
        return bean;
    }

    /**
     * 1.postProcessAfterInitializatio在Bean的初始化后调用
     * @param bean
     * @param beanName
     * @return
     */
    default Object postProcessAfterInitialization(Object bean, String beanName) {
        return bean;
    }
}
package com.hykedu.spring.processor;

/**
 * @author 程序员蛇皮
 * @version 1.0
 * 1.我们根据原生Spring 定义了一个InitializingBean
 * 2.该 InitializingBean接口有一个void afterPropertiesSet() throws Exception;
 * 3.afterPropertiesSet() 在bean的setter后执行,即等价于Spring原来的初始化方法init()
 * 4.当一个Bean实现这个接口后,就实现afterPropertiesSet(),这个方法就是初始化方法
 */
public interface InitializingBean {
    void afterPropertiesSet() throws Exception;
}

测试类

package com.hykedu.spring;

import com.hykedu.spring.component.SmartAnimalAspect;
import com.hykedu.spring.component.SmartAnimalable;
import com.hykedu.spring.component.StudentDao;
import com.hykedu.spring.component.StudentService;
import com.hykedu.spring.ioc.CodeSnakeSpringApplicationContext;
import com.hykedu.spring.ioc.CodeSnakeSpringConfig;

/**
 * @author 程序员蛇皮
 * @version 1.0
 */
public class AppMain {
    public static void main(String[] args) {
        CodeSnakeSpringApplicationContext codeSnakeSpringApplicationContext = new CodeSnakeSpringApplicationContext(CodeSnakeSpringConfig.class);
        SmartAnimalable smartDog = (SmartAnimalable) codeSnakeSpringApplicationContext.getBean("smartDog");
        StudentDao studentDao1 = (StudentDao) codeSnakeSpringApplicationContext.getBean("studentDao");
        StudentDao studentDao = (StudentDao) codeSnakeSpringApplicationContext.getBean("studentDao");
        StudentService studentService1 = (StudentService) codeSnakeSpringApplicationContext.getBean("studentService");
        StudentService studentService2 = (StudentService) codeSnakeSpringApplicationContext.getBean("studentService");
        smartDog.getSum(10,10);
        System.out.println("ok");
    }

标签:BeanPostProcessor,Spring,hykedu,bean,spring,import,机制,com,class
From: https://blog.csdn.net/2301_77647448/article/details/141302865

相关文章

  • 基于SpringBoot的农村风貌展示平台的设计与实现(源码+LW+调试文档)
     目录:程序视频演示:程序功能截图:程序部分代码参考:数据库sql:程序技术介绍:后端springboot介绍:mysql介绍:程序论文:​选择我的理由:程序获取:......
  • Spring Boot 中如何解决跨域问题
    SpringBoot中跨域问题是什么   在SpringBoot中,跨域问题是指在浏览器上发送跨源请求时可能会遇到的问题。跨源请求是指通过浏览器发送请求到不同域的服务器。同源策略是浏览器的一种安全机制,限制了跨域请求的行为。如果请求的域与当前页面的域不同,浏览器会阻止请求的发......
  • springboot项目中mybatis的dao接口实现类是如何添加到spring容器中的
    一、@Mapper注解在springboot+mybatis的工程中,如果不做特殊配置,mybatis会查找有@Mapper的接口创建其代理对象添加到spring容器中,接下来就来分析下这个是如何实现的。关键点就在MybatisAutoConfiguration这个自动配置类中publicclassMybatisAutoConfiguration{//这个配......
  • 基于SpringBoot3框架-数据库乐观锁、悲观锁、Redis、Zookeeper分布式锁的简单案例实现
    1.分布式锁的定义分布式锁是一种在分布式系统中用来协调多个进程或线程对共享资源进行访问的机制。它确保在分布式环境下,多个节点(如不同的服务器或进程)不会同时访问同一个共享资源,从而避免数据不一致、资源竞争等问题。2.分布式锁的工作原理分布式锁的工作原理与单机锁......
  • springboot+vue前后端分离项目-项目搭建19-ElementUI图标+聊天室
    一、ElementUI图标按照官网这两步,注册所有图标,然后就能直接使用 1.安装后在vue/package.json里能看到包 2.注册所有图标 3.点击自动复制,直接就能使用 4.效果: ......
  • 【防忘笔记】Spring+Struts2古董框架学习
    Spring+Struts2项目框架梳理若基于Spring+Struts2的方式进行开发,前后端的交互逻辑会与boot系以及MCV的组织结构有所不同这里是对于学习过程的一些记录前置通用知识Struts2框架资料Struts2基础篇之基本概念Java之struts2框架学习一般情况的Spring前后端调试流程要理解基于......
  • Springboot项目的War包部署在tomcat上
    使用场景:使用springboot框架+mybatis+html开发的项目将软件服务打成war包,将war包部署在tomcat上。使用前提:电脑已经安装jdk1.8、tomcat8.5环境。开始部署:步骤1:Java启动类上加SpringApplicationBuilder()方法,且需继承类SpringBootServletInitializer@SpringBootApplication@Ma......
  • 基于Java+SpringBoot+Mysql实现的共享厨房平台功能设计与实现六
    一、前言介绍:1.1项目摘要随着城市化进程的加快和人们对生活品质要求的提升,共享经济模式在全球范围内迅速兴起。共享厨房平台作为共享经济的一种创新形式,旨在通过整合闲置的厨房资源,为用户提供一个便捷、经济且富有创意的烹饪空间。现代都市生活中,许多年轻人、创业者及小......
  • 基于Java+SpringBoot+Mysql实现的共享厨房平台功能设计与实现七
    一、前言介绍:1.1项目摘要随着城市化进程的加快和人们对生活品质要求的提升,共享经济模式在全球范围内迅速兴起。共享厨房平台作为共享经济的一种创新形式,旨在通过整合闲置的厨房资源,为用户提供一个便捷、经济且富有创意的烹饪空间。现代都市生活中,许多年轻人、创业者及小......
  • java guide Spring Cloud Gateway 答疑6
    使用SpringCloudGateway的时候,官方文档提供的方案总是基于配置文件或代码配置的方式。SpringCloudGateway作为微服务的入口,需要尽量避免重启,而现在配置更改需要重启服务不能满足实际生产过程中的动态刷新、实时变更的业务需求,所以我们需要在SpringCloudGateway运行......