手写实现简易Spring框架

该文章记录的是自己实现简易Spring框架的记录,简化了源码中的大量操作,没有实现三级缓存的机制。
其中将会对@Component@ComponentScan@Scope@Autowired注解。BeanDefinition类以及ApplicationContextBeanPostProcessorInitializingBean接口进行实现。
注: 该实现参考了“伟大的Yve菌”的手写一个简单的spring框架

一、预备知识


后续会对下列的注解、类、接口进行实现,在该部分对这些注解等进行介绍,之后再下个部分进行实现。

@Component

@Component注解是定义一个组件,表示这个类会被Spring自动扫描并创建实例化。它还衍生出了其他三个注解:@Controller用在表现层,@Service用在业务层,@Repository用在数据层。这些注解与@Component的作用和属性一样,只是提供了更加明确的语义化。
当我们需要将一个类注册为Spring容器中的Bean对象时,就可以在这个类上添加@Component注解。

@Component
public class MyService {
    // ...
}

@ComponentScan

@ComponentScan注解是定义扫描的路径,从中找出被@Component在这里插入代码片及衍生注解@Component@Service@Repository等注解的类,并将它们注册为Spring应用程序上下文中的Bean。该注解可以用在配置类上,作为配置扫描组件的指令。一般来说,@ComponentScan会扫描指定路径下的所有类,并将它们注册为Bean。
其中@Configuration注解的作用是用于标识一个类是配置类,通常用于替代XML配置文件。并且其中的@Bean注解方法将会被Spring容器处理和管理。

@Configuration
@ComponentScan(basePackages = "com.example.package")
public class AppConfig {
    // ...
}

@Scope

@Scope注解是用于定义Bean的作用域。它可以修饰在类级别,表示注入的Bean在整个应用中是单例(Singleton)还是多例(Prototype),从而影响Bean的生命周期和存储方式。默认情况下,所有被Spring管理的Bean都是单例的。

  1. singleton作用域,它是默认的作用域。当一个bean的作用域被设置为singleton时,在整个应用程序生命周期内,只会存在一个实例化的bean对象,所有请求该bean的对象都会共享同一个实例。也就是说,无论是在单线程环境还是多线程环境下,都只有一个bean对象存在。
  2. prototype作用域,它与singleton作用域有所不同。当一个bean的作用域被设置为prototype时,每次请求该bean的对象都会创建一个新的实例。也就是说,每次请求该bean,都会创建一个独立的新对象。

因为不同的业务场景需要不同的作用域来满足需求。

  • singleton作用域可以节省内存资源,适用于那些无状态(stateless)的bean,例如工具类、配置类等。
  • prototype作用域适用于那些有状态(stateful)的bean,每次请求都需要一个新的实例,例如用户请求、线程任务等。
@Component
@Scope("prototype")
public class MyBean {
    // ...
}

@Autowired

@Autowired是 Spring 框架中的一个注解,用于实现自动装配(Autowired)功能。它可以标注在类的成员变量、方法、构造函数或参数上,让 Spring 完成对相应依赖的自动注入。
@Autowired作用有以下几个方面:

  1. 自动装配依赖: 通过 @Autowired 注解,Spring 可以自动识别并注入相应的依赖对象,无需手动编写繁琐的依赖注入代码。这样可以简化开发过程,提高代码的可读性和可维护性。
  2. 解决依赖冲突: 当存在多个符合条件的依赖对象时,@Autowired 注解可以根据一定的规则(如类型匹配、限定符等)来解决依赖冲突,确保正确的依赖对象被注入。
  3. 支持多种注入方式: @Autowired 注解可以用于不同的注入方式,包括字段注入、方法注入、构造函数注入和参数注入。这样可以根据具体情况选择最合适的注入方式。

常见的使用方法:

  1. 字段注入:
@Autowiredprivate SomeDependency someDependency;
  1. 方法注入:
@Autowiredpublic void setSomeDependency(SomeDependency someDependency) {
    this.someDependency = someDependency;
}
  1. 构造函数注入:
@Autowiredpublic MyClass(SomeDependency someDependency) {
    this.someDependency = someDependency;
}
  1. 参数注入:
public void doSomething(@Autowired SomeDependency someDependency) {
    // 使用注入的依赖对象进行操作
}

BeanDefinition类

BeanDefinition 类是 Spring 框架中的一个关键类,用于描述和定义一个 bean 的元数据信息。它包含了创建和配置 bean 实例所需的所有信息,如类名、属性值、构造函数参数等,是 Spring 容器实现依赖注入和对象创建的基础。
BeanDefinition 接口定义了一些常用的方法和属性,用于获取和设置 bean 的相关信息。以下是一些主要的方法和属性:

  • getBeanClassName():获取 bean 的类名。
  • setBeanClassName(String beanClassName):设置 bean 的类名。
  • getScope():获取 bean 的作用域,如 singleton、prototype 等。
  • setScope(String scope):设置 bean 的作用域。
  • getPropertyValues():获取 bean 的属性值集合。
  • getPropertyValues().addPropertyValue(PropertyValue pv):向属性值集合中添加一个属性值。
  • getConstructorArgumentValues():获取 bean 的构造函数参数值集合。
  • getConstructorArgumentValues().addIndexedArgumentValue(int index, Object value):向构造函数参数值集合中添加一个索引参数值。
  • getConstructorArgumentValues().addGenericArgumentValue(Object value):向构造函数参数值集合中添加一个通用参数值。
  • getDependsOn():获取 bean 的依赖关系。
  • setDependsOn(String[] dependsOn):设置 bean 的依赖关系。
  • isSingleton():判断 bean 是否为单例。
  • isPrototype():判断 bean 是否为原型。
  • isAbstract():判断 bean 是否为抽象。
  • getRole():获取 bean 的角色。
  • setRole(int role):设置 bean 的角色。

除了上述方法和属性,BeanDefinition 还提供了其他一些方法,用于获取和设置 bean 的其他元数据信息,如初始化方法、销毁方法、是否懒加载等。
BeanDefinition 是一个接口,具体的实现类有多个,如 GenericBeanDefinitionRootBeanDefinitionChildBeanDefinition 等。每个实现类都有不同的特点和用途,用于满足不同场景下的需求。

ApplicationContext接口

ApplicationContext是 Spring 框架中的一个关键接口,它是 Spring 容器的核心接口之一。是 Spring 框架中负责管理和组织 bean 的创建、配置和生命周期的核心接口。它提供了丰富的功能和服务,可以方便地实现依赖注入、AOP、国际化等功能,是构建企业级应用程序的重要组成部分。
ApplicationContext 接口继承了 BeanFactory 接口,因此它具备了 BeanFactory 的所有功能,并在此基础上提供了更多的特性。以下是 ApplicationContext 的一些主要特点和功能:

  1. Bean 的生命周期管理:ApplicationContext 负责管理 bean 的生命周期,包括创建、初始化和销毁。它会根据配置信息自动创建和初始化 bean,并在容器关闭时销毁 bean。
  2. 依赖注入:ApplicationContext 支持依赖注入,可以自动将依赖的 bean 注入到目标 bean 中,无需手动编写繁琐的依赖注入代码。这样可以简化开发过程,提高代码的可读性和可维护性。
  3. AOP(面向切面编程)支持:ApplicationContext 提供了对 AOP 的支持,可以通过配置和使用切面来实现横切关注点的模块化。它可以方便地实现事务管理、日志记录、性能监控等功能。
  4. 国际化支持:ApplicationContext 提供了国际化支持,可以方便地实现多语言的应用程序。它可以根据不同的语言环境加载相应的资源文件,并提供统一的访问接口。
  5. 事件机制:ApplicationContext 支持事件机制,可以发布和监听事件。通过事件机制,不同的组件可以进行解耦,实现松耦合的设计。
  6. 配置文件的加载和解析:ApplicationContext 负责加载和解析配置文件,可以使用多种格式的配置文件,如 XML、注解、Java 配置等。它可以根据配置文件中的信息创建和配置相应的 bean。
  7. 容器的扩展性:ApplicationContext 提供了容器的扩展机制,可以通过自定义扩展点来扩展容器的功能。例如,可以自定义 BeanPostProcessorBeanFactoryPostProcessor 等来对 bean 进行定制化处理。
    ApplicationContext 接口有多个实现类,如 ClassPathXmlApplicationContextAnnotationConfigApplicationContextFileSystemXmlApplicationContext 等,每个实现类都有不同的特点和用途,用于满足不同场景下的需求。

BeanPostProcessor接口

BeanPostProcessor 是 Spring 框架中的一个重要接口,用于在 bean 的实例化和初始化过程中进行扩展和定制。它允许开发者在 bean 的创建过程中干涉并修改 bean 的行为。
BeanPostProcessor 接口定义了两个方法:

  • postProcessBeforeInitialization(Object bean, String beanName):在 bean 初始化之前调用。开发者可以在此方法中对 bean 进行修改或扩展操作。例如,可以对 bean 的属性进行修改、添加一些自定义的逻辑等。
  • postProcessAfterInitialization(Object bean, String beanName):在 bean 初始化之后调用。开发者可以在此方法中对 bean 进行进一步的处理。例如,可以对 bean 进行代理、添加一些额外的功能等。
    通过实现 BeanPostProcessor 接口,开发者可以在 Spring 容器实例化和初始化 bean 的过程中插入自己的逻辑,对 bean 进行个性化的定制。这为开发者提供了很大的灵活性和扩展性。
    BeanPostProcessor 的应用场景非常广泛,常见的用途包括:
  • 属性注入:可以在 postProcessBeforeInitialization 方法中对 bean 的属性进行修改或扩展,实现自定义的属性注入逻辑。
  • AOP(面向切面编程):可以在 postProcessAfterInitialization 方法中对 bean 进行代理,实现 AOP 的功能,如事务管理、日志记录等。
  • 自定义初始化逻辑:可以在 postProcessAfterInitialization 方法中添加一些自定义的初始化逻辑,如数据初始化、资源加载等。

InitializingBean接口

InitializingBean 是 Spring 框架中的一个接口,用于在 bean 的属性设置完成后执行自定义的初始化逻辑。它定义了一个方法 afterPropertiesSet(),在该方法中可以编写需要在 bean 初始化之后执行的代码。
当一个 bean 实现了 InitializingBean 接口并被 Spring 容器管理时,当所有的属性都被设置完成后,Spring 容器会自动调用该 bean 的 afterPropertiesSet() 方法。开发者可以在该方法中进行一些初始化操作,例如数据加载、资源初始化、校验等。
使用 InitializingBean 接口的优点是,它提供了一种标准化的方式来定义 bean 的初始化逻辑,使得初始化代码与 bean 的定义紧密结合,提高了代码的可读性和可维护性。此外,通过实现 InitializingBean 接口,可以确保在 bean 的属性设置完成后执行初始化逻辑,避免了手动调用初始化方法的繁琐操作。

二、项目结构


springframe包中放的是用来实现spring框架的包。
user为用户的包
在这里插入图片描述

三、初始化项目


首先我们去实现一个自己的ApplicationContext

  1. 创建一个MyApplicationContext类定义构造方法和getBean()方法,后续都需要进行完善。
package com.example.springframe;


public class MyApplicationContext {
    private Class appConfig;

    public MyApplicationContext(Class appConfig) {
        this.appConfig = appConfig;
    }

    public Object getBean(String beanName) {
        return null;
    }
}
  1. 创建一个service包,里面包含UserServiceOrderService两个类。
package com.example.user.service;

public class UserService {
    public void test() {
        System.out.println("userService");
    }
}

package com.example.user.service;

public class OrderService {
    public void test(){
        System.out.println("orderService");
    }
}
  1. 为user创建一个AppConfig配置类。
package com.example;

public class AppConfig {
}
  1. 创建一个Test类运行测试程序来测试自己实现的spring
package com.example;

import com.example.springframe.MyApplicationContext;
import com.example.user.service.UserService;

public class Test {
    public static void main(String[] args) {
        MyApplicationContext context = new MyApplicationContext(AppConfig.class);
        System.out.println(context.getBean("userService"));
        System.out.println(context.getBean("orderService"));        
    }
}

注: 当前MyApplicationContext无法通过AppConfig获取到内容,因为没有添加注解说明扫描的路径,getBean()方法也是无法获取到userService的,因为MyApplicationContext 中的getBean()方法的返回值还是null。

  1. 现在去实现AppConfig中添加的路径扫描注解ComponentScan
package com.example.springframe;

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 {
    String value() default "";
}

其中:

  • @interface是用于声明注解的,注解是一种用于向代码中添加元数据的方式。注解是在源代码级别保留的,可以被编译器和其他工具处理。
  • @Target(ElementType.FIELD)指定了@Autowired注解可以应用于字段上。这意味着可以在类的字段上使用@Autowired注解来实现自动装配,让Spring框架自动将相应的依赖注入到字段中。
  • @Retention(RetentionPolicy.RUNTIME)指定了@Autowired注解在运行时保留。这意味着在程序运行时,可以通过反射机制获取到被@Autowired注解标记的字段,并进行相应的处理。
  • String value() default "";: 这一行代码定义了一个注解元素,名为 value,并指定其类型为 String。注解元素允许使用者在注解中指定值。在这里,value@Component 注解的一个属性。通过 default 关键字,可以为这个属性设置默认值为空字符串 "",表示当使用者不显式指定 value 属性值时,它将默认为一个空字符串。
  1. AppConfig类上添加咱们自己写的@ComponentScan注解,配置扫描路径,扫描到包,不要到具体类
package com.example;

import com.example.springframe.ComponentScan;

@ComponentScan("com.example.user.service")
public class AppConfig {
}
  1. 再配置个@Component注解来让Spring进行扫描。
package com.example.springframe;

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 {
    String value() default "";
}
  1. 在之前创建好的UserSerivce添加@Component注解,之后@Component进行扫描的时候就会将带有@Component注解的类注册为Bean。OrderService不添加注解,与UserService进行对比。
@Component
public class UserService {

现在spring已经可以扫描到包了,下面我们来写相关扫描逻辑和处理方法

四、扫描和加载逻辑


在完成了上个部分的创建Bean的过程之后,在这个部分来编写扫描和加载的逻辑。

  1. MyApplicationContext中写一个scan()方法来处理扫描操作。将扫描路径下带有@Component的类进行存放。
private void scan(Class appConfig){
    // 首先判断是否有扫描路径的注解
    if(appConfig.isAnnotationPresent(ComponentScan.class)){
        // 取出注解中的路径
        ComponentScan componentScan = (ComponentScan) appConfig.getAnnotation(ComponentScan.class);
        String path = componentScan.value();
        // 找到文件位置,修改为路径
        path = path.replace(".","/");

        // 需要解析的是编译之后的class类上的注解
        ClassLoader classLoader = MyApplicationContext.class.getClassLoader();
        URL resource = classLoader.getResource(path);

        // 获取对应路径下的所有文件
        assert resource != null;
        File file = new File(resource.getFile());

        // 判断文件是否为目录
        if (file.isDirectory()){
            for (File listFile: file.listFiles()){
                // 获取到每个文件的绝对路径
                String absolutePath = listFile.getAbsolutePath();
                absolutePath = absolutePath.substring(absolutePath.indexOf("com"),absolutePath.indexOf(".class")).replace("\\",".");
               
                try {
                    // 判断每个文件是否有@Component注解
                    Class<?> clazz = classLoader.loadClass(absolutePath);
                    if (clazz.isAnnotationPresent(Component.class)){
                       System.out.println(clazz);              
                    }
                }catch (ClassNotFoundException e){
                    e.printStackTrace();
                } 
            }
        }
    }
}

scan()方法在MyApplicationContext构造函数中进行调用。

public MyApplicationContext(Class appConfig) {
    this.appConfig = appConfig;
    scan(appConfig);
}

现在运行Test测试文件就会发现,只有UserService,这是因为只有UserService加了@Component注解所以被扫描到了,下面的两个打印都是null是因为getBean()方法没有被实现,返回值还是null
在这里插入图片描述

  1. 实现@Scope类,用来判断扫描到的Bean的作用域。作用域分为两种,singleton和prototype,在前文的预备知识中有详细介绍。
package com.example.springframe;

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 Scope {
    String value() default "";
}

UserServiceOrderService加入@Scope注解

package com.example.user.service;

import com.example.springframe.Component;
import com.example.springframe.Scope;

@Component("userService")
@Scope("prototype")
public class UserService{

    public void test(){
        System.out.println("userService");
    }
}
package com.example.user.service;

import com.example.springframe.Component;
import com.example.springframe.Scope;

@Component("orderService")
@Scope("singleton")
public class OrderService {
    public void test(){
        System.out.println("orderService");
    }
}
  1. 创建BeanDefinition类,对Bean的内部属性进行定义。在这里我使用了两个属性,一个类,一个作用域。
package com.example.springframe;

public class BeanDefinition {
    private Class type;
    private String scope;

    public Class getType() {
        return type;
    }

    public void setType(Class type) {
        this.type = type;
    }

    public String getScope() {
        return scope;
    }

    public void setScope(String scope) {
        this.scope = scope;
    }
}
  1. MyApplicationContext中定义一个map来存放这些Bean,beanNamemapkeyBeanDefinitionvalue
public class MyApplicationContext {
    private Class appConfig;
    // 用map存储bean
    private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();
  1. 完善scan()方法,扫描过后将信息存放到beanDefinitionMap
private void scan(Class appConfig){
    // 首先判断是否有扫描路径的注解
    if(appConfig.isAnnotationPresent(ComponentScan.class)){
        // 取出注解中的路径
        ComponentScan componentScan = (ComponentScan) appConfig.getAnnotation(ComponentScan.class);
        String path = componentScan.value();
        // 找到文件位置,修改为路径
        path = path.replace(".","/");

        // 需要解析的是编译之后的class类上的注解
        ClassLoader classLoader = MyApplicationContext.class.getClassLoader();
        URL resource = classLoader.getResource(path);

        // 获取对应路径下的所有文件
        assert resource != null;
        File file = new File(resource.getFile());

        // 判断文件是否为目录
        if (file.isDirectory()){
            for (File listFile: file.listFiles()){
                // 获取到每个文件的绝对路径
                String absolutePath = listFile.getAbsolutePath();
                absolutePath = absolutePath.substring(absolutePath.indexOf("com"),absolutePath.indexOf(".class")).replace("\\",".");
                try {
                    // 判断每个文件是否有@Component注解
                    Class<?> clazz = classLoader.loadClass(absolutePath);

                    if (clazz.isAnnotationPresent(Component.class)){
                        String beanName = clazz.getAnnotation(Component.class).value();
                        // 创建BeanDefinition保存Bean对象信息
                        BeanDefinition beanDefinition = new BeanDefinition();
                        beanDefinition.setType(clazz);

                        // 如果该类中有@Scope注解就保存为注解中的值,否则默认为singleton
                        if (clazz.isAnnotationPresent(Scope.class)){
                            beanDefinition.setScope(clazz.getAnnotation(Scope.class).value());
                        } else {
                           beanDefinition.setScope("singleton");
                        }

                        // 把定义好的bean放入到beanDefintionMap中
                        //如果@Comonent注解中没有值,则取首字母作为beanName
                        if (beanName.isEmpty()){
                            beanName = Introspector.decapitalize(clazz.getSimpleName());
                        }
                        beanDefinitionMap.put(beanName, beanDefinition);
                    }
                }catch (ClassNotFoundException e){
                    e.printStackTrace();
                } 
            }
        }
    }
}
  1. 完善MyApplicationContext中的getBean()方法,具体singleton和prototype处理方法后续完善。
public Object getBean(String beanName){
    // 通过beanDefinitionMap获取到beanDefinition并根据作用域来返回bean对象
    BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
    // 如果没有beanName则证明该类型bean没有被声明
    if (beanDefinition == null){
        return new NullPointerException("No bean definition");
    }

    if (beanDefinition.getScope().equals("singleton")){
        // singleton类型的bean

    }else {
        // prototype类型的bean

    }
}
  1. MyApplicationContext中创建一个单例池存放所有单例Bean,后续使用时从单例池中取用。
public class MyApplicationContext {
    private Class appConfig;
    // 用map存储bean
    private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();
    // 单例池存储所有单例的bean
    private Map<String, Object> singleObjects = new HashMap<>();
  1. 完善构造方法,扫描完成后遍历Bean,将所有Bean存放到单例池。
public MyApplicationContext(Class appConfig) {
    this.appConfig = appConfig;
    scan(appConfig);
    // 遍历bean,将所有单例的bean放入单例池中
    for (Map.Entry<String, BeanDefinition> entry: beanDefinitionMap.entrySet()){
        // 获取bean的名字和信息
        String beanName = entry.getKey();
        BeanDefinition beanDefinition = entry.getValue();
        // 如果是单例的则放入单例池中
        if(beanDefinition.getScope().equals("singleton")){
            Object bean = createBean(beanName, beanDefinition);
            singleObjects.put(beanName, bean);
        }
    }
}
  1. 完善CreateBean()方法
private Object createBean(String beanName, BeanDefinition beanDefinition) {
    // 通过type获取到类
    Class clazz = beanDefinition.getType();
    Object instance = null;
    try {
        // 构造方法创建对象
        instance = clazz.getConstructor().newInstance();

    } catch (InvocationTargetException e) {
        e.printStackTrace();
    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    }
    return instance;
}
  1. 完善getBean()方法
public Object getBean(String beanName){
    // 通过beanDefinitionMap获取到beanDefinition并根据作用域来返回bean对象
    BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
    // 如果没有beanName则证明该类型bean没有被声明
    if (beanDefinition == null){
        return new NullPointerException("No bean definition");
    }

    if (beanDefinition.getScope().equals("singleton")){
        // singleton类型的bean
        Object singletonBean = singleObjects.get(beanName);
        // 如果单例bean还没被加载,就直接创建加载
        if (singletonBean == null){
            singletonBean = createBean(beanName, beanDefinition);
            singleObjects.put(beanName, singletonBean);
        }
        return singletonBean;
    }else {
        // prototype类型的bean
        return createBean(beanName, beanDefinition);
    }
}
  1. 验证。我们再修改Test,测试不同作用域的不同反馈。
@Component("userService")
@Scope("prototype")
public class UserService  {
@Component("orderService")
@Scope("singleton")
public class OrderService {
package com.example;

import com.example.springframe.MyApplicationContext;
import com.example.user.service.UserService;

public class Test {
    public static void main(String[] args) {
        MyApplicationContext context = new MyApplicationContext(AppConfig.class);
        System.out.println("prototype: "+context.getBean("userService"));
        System.out.println("prototype: "+context.getBean("userService"));
        System.out.println("singleton: "+context.getBean("orderService"));
        System.out.println("singleton: "+context.getBean("orderService"));
    }
}

观察下面的输出结果我们可以发现,singleton两次是一个对象,prototype两次是两个对象。
在这里插入图片描述

五、实现@Autowired注解


  1. @Autowired注解
package com.example.springframe;

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

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
}
  1. UserService中添加OrderService属性并添加@Autowired,现在Spring无法为OrderService赋值,后面会对内部逻辑进行定义。
package com.example.user.service;

import com.example.springframe.Autowired;
import com.example.springframe.Component;
import com.example.springframe.InitializingBean;
import com.example.springframe.Scope;

@Component("userService")
@Scope("prototype")
public class UserService {

    @Autowired
    private  OrderService orderService;

    public void test(){
        System.out.println("userService");
        // 后续测试orderService属性注入
        System.out.println(orderService);
    }
}
  1. 创建Bean的时候为成员变量进行注入
private Object createBean(String beanName, BeanDefinition beanDefinition) {
    // 通过type获取到类
    Class clazz = beanDefinition.getType();
    Object instance = null;
    try {
        // 构造方法创造对象
        instance = clazz.getConstructor().newInstance();

        // 为添加了@Autowired注解的属性赋值
        for(Field field : clazz.getDeclaredFields()){
            if (field.isAnnotationPresent((Autowired.class))){
                field.setAccessible(true);
                // 为属性赋值
                field.set(instance, getBean(field.getName()));
            }
        }

    } catch (InvocationTargetException e) {
        e.printStackTrace();
    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    }
    return instance;
}
  1. getBean()中添加判断逻辑,因为无法保证singleton的Bean只存在一个,所以需要做补充。
public Object getBean(String beanName){
    // 通过beanDefinitionMap获取到beanDefinition并根据作用域来返回bean对象
    BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
    // 如果没有beanName则证明该类型bean没有被声明
    if (beanDefinition == null){
        return new NullPointerException("No bean definition");
    }

    if (beanDefinition.getScope().equals("singleton")){
        // singleton类型的bean
        Object singletonBean = singleObjects.get(beanName);
        // 如果单例bean还没被加载,就直接创建加载
        if (singletonBean == null){
            singletonBean = createBean(beanName, beanDefinition);
            singleObjects.put(beanName, singletonBean);
        }
        return singletonBean;
    }else {
        // prototype类型的bean
        return createBean(beanName, beanDefinition);
    }
}
  1. 现在通过test方法调用UserServicetest()方法可以顺利使用带有@AutowiredOrderService属性。
package com.example;

import com.example.springframe.MyApplicationContext;
import com.example.user.service.UserService;

public class Test {
    public static void main(String[] args) {
        MyApplicationContext context = new MyApplicationContext(AppConfig.class);
        UserService userService = (UserService) context.getBean("userService");
        userService.test();
    }
}

在这里插入图片描述

六、初始化以及前后的操作


  1. Bean的初始化操作,创建InitializingBean接口让UserService去实现。
package com.example.springframe;

public interface InitializingBean {
    void afterPropertiesSet();
}
package com.example.user.service;

import com.example.springframe.Autowired;
import com.example.springframe.Component;
import com.example.springframe.InitializingBean;
import com.example.springframe.Scope;

@Component("userService")
@Scope("prototype")
public class UserService implements InitializingBean {

    @Autowired
    private  OrderService orderService;

    @Override
    public void afterPropertiesSet() {
        System.out.println("初始化userService");
    }

    public void test(){
        System.out.println("userService");
        System.out.println(orderService);
    }
}
  1. CreateBean()方法中添加初始化逻辑。
// 初始化,没法在bean创建时调用初始化方法,所以在createBean中实现
// 判断instance是不是InitializingBean的实例
if(instance instanceof InitializingBean){
    ((InitializingBean) instance).afterPropertiesSet();
}
  1. 定义BeanPostProcesser接口,定义初始化前和初始化后的方法,之后再用一个类去实现这个接口。
package com.example.springframe;

public interface BeanPostProcessor {
    default Object postProcessBeforeInitialization(Object bean, String beanName) {
        return bean;
    }

    default Object postProcessAfterInitialization(Object bean, String beanName) {
        return bean;
    }
}
package com.example.user.service;

import com.example.springframe.BeanPostProcessor;
import com.example.springframe.Component;

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        System.out.println(beanName+"初始化前");
        return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        System.out.println(beanName+"初始化后");
        return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
    }
}
  1. 在初始化操作前后添加两个方法,去实现初始化前和初始化后的一些操作,在扫描的时候判断有哪些带有@Component的类实现了BeanPostProcessor接口,同时创建一个list来存放这些BeanPostProcessor
public class MyApplicationContext {
    private Class appConfig;
    // 用map存储bean
    private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();
    // 单例池存储所有单例的bean
    private Map<String, Object> singleObjects = new HashMap<>();

    // 存放带有@Component注解并且实现了BeanPostProcessor方法的bean处理方法
    private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();
  1. scan()方法中添加判断逻辑
// 判断文件是否为目录
if (file.isDirectory()){
    for (File listFile: file.listFiles()){
        // 获取到每个文件的绝对路径
        String absolutePath = listFile.getAbsolutePath();
        absolutePath = absolutePath.substring(absolutePath.indexOf("com"),absolutePath.indexOf(".class")).replace("\\",".");
        try {
            // 判断每个文件是否有@Component注解
            Class<?> clazz = classLoader.loadClass(absolutePath);

            if (clazz.isAnnotationPresent(Component.class)){
                // 判断哪些bean实现了BeanPostProcessor
                if(BeanPostProcessor.class.isAssignableFrom(clazz)){
                    BeanPostProcessor instance = (BeanPostProcessor) clazz.getConstructor().newInstance();
                    beanPostProcessorList.add(instance);
                }
  1. 在初始化前后添加对应的前后操作方法
// 初始化前的操作
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList){
    beanPostProcessor.postProcessBeforeInitialization(instance, beanName);
}
// 初始化,没法在bean创建时调用初始化方法,所以在createBean中实现
// 判断instance是不是InitializingBean的实例
if(instance instanceof InitializingBean){
    ((InitializingBean) instance).afterPropertiesSet();
}
// 初始化后的操作
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList){
    beanPostProcessor.postProcessAfterInitialization(instance, beanName);
}
  1. 这样我们就可以取到经过完整BeanPostProcessor以及初始化后的Bean对象,可以通过这种方法在初始化Bean的前后进行很多操作。
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值