该文章记录的是自己实现简易Spring框架的记录,简化了源码中的大量操作,没有实现三级缓存的机制。
其中将会对@Component
,@ComponentScan
,@Scope
,@Autowired
注解。BeanDefinition
类以及ApplicationContext
、BeanPostProcessor
、InitializingBean
接口进行实现。
注: 该实现参考了“伟大的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都是单例的。
- singleton作用域,它是默认的作用域。当一个bean的作用域被设置为singleton时,在整个应用程序生命周期内,只会存在一个实例化的bean对象,所有请求该bean的对象都会共享同一个实例。也就是说,无论是在单线程环境还是多线程环境下,都只有一个bean对象存在。
- prototype作用域,它与singleton作用域有所不同。当一个bean的作用域被设置为prototype时,每次请求该bean的对象都会创建一个新的实例。也就是说,每次请求该bean,都会创建一个独立的新对象。
因为不同的业务场景需要不同的作用域来满足需求。
- singleton作用域可以节省内存资源,适用于那些无状态(stateless)的bean,例如工具类、配置类等。
- prototype作用域适用于那些有状态(stateful)的bean,每次请求都需要一个新的实例,例如用户请求、线程任务等。
@Component
@Scope("prototype")
public class MyBean {
// ...
}
@Autowired
@Autowired
是 Spring 框架中的一个注解,用于实现自动装配(Autowired)功能。它可以标注在类的成员变量、方法、构造函数或参数上,让 Spring 完成对相应依赖的自动注入。
@Autowired
作用有以下几个方面:
- 自动装配依赖: 通过
@Autowired
注解,Spring 可以自动识别并注入相应的依赖对象,无需手动编写繁琐的依赖注入代码。这样可以简化开发过程,提高代码的可读性和可维护性。 - 解决依赖冲突: 当存在多个符合条件的依赖对象时,
@Autowired
注解可以根据一定的规则(如类型匹配、限定符等)来解决依赖冲突,确保正确的依赖对象被注入。 - 支持多种注入方式:
@Autowired
注解可以用于不同的注入方式,包括字段注入、方法注入、构造函数注入和参数注入。这样可以根据具体情况选择最合适的注入方式。
常见的使用方法:
- 字段注入:
@Autowiredprivate SomeDependency someDependency;
- 方法注入:
@Autowiredpublic void setSomeDependency(SomeDependency someDependency) {
this.someDependency = someDependency;
}
- 构造函数注入:
@Autowiredpublic MyClass(SomeDependency someDependency) {
this.someDependency = someDependency;
}
- 参数注入:
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
是一个接口,具体的实现类有多个,如 GenericBeanDefinition
、RootBeanDefinition
、ChildBeanDefinition
等。每个实现类都有不同的特点和用途,用于满足不同场景下的需求。
ApplicationContext接口
ApplicationContext
是 Spring 框架中的一个关键接口,它是 Spring 容器的核心接口之一。是 Spring 框架中负责管理和组织 bean 的创建、配置和生命周期的核心接口。它提供了丰富的功能和服务,可以方便地实现依赖注入、AOP、国际化等功能,是构建企业级应用程序的重要组成部分。
ApplicationContext
接口继承了 BeanFactory
接口,因此它具备了 BeanFactory
的所有功能,并在此基础上提供了更多的特性。以下是 ApplicationContext
的一些主要特点和功能:
- Bean 的生命周期管理:
ApplicationContext
负责管理 bean 的生命周期,包括创建、初始化和销毁。它会根据配置信息自动创建和初始化 bean,并在容器关闭时销毁 bean。 - 依赖注入:
ApplicationContext
支持依赖注入,可以自动将依赖的 bean 注入到目标 bean 中,无需手动编写繁琐的依赖注入代码。这样可以简化开发过程,提高代码的可读性和可维护性。 - AOP(面向切面编程)支持:
ApplicationContext
提供了对 AOP 的支持,可以通过配置和使用切面来实现横切关注点的模块化。它可以方便地实现事务管理、日志记录、性能监控等功能。 - 国际化支持:
ApplicationContext
提供了国际化支持,可以方便地实现多语言的应用程序。它可以根据不同的语言环境加载相应的资源文件,并提供统一的访问接口。 - 事件机制:
ApplicationContext
支持事件机制,可以发布和监听事件。通过事件机制,不同的组件可以进行解耦,实现松耦合的设计。 - 配置文件的加载和解析:
ApplicationContext
负责加载和解析配置文件,可以使用多种格式的配置文件,如 XML、注解、Java 配置等。它可以根据配置文件中的信息创建和配置相应的 bean。 - 容器的扩展性:
ApplicationContext
提供了容器的扩展机制,可以通过自定义扩展点来扩展容器的功能。例如,可以自定义BeanPostProcessor
、BeanFactoryPostProcessor
等来对 bean 进行定制化处理。
ApplicationContext
接口有多个实现类,如ClassPathXmlApplicationContext
、AnnotationConfigApplicationContext
、FileSystemXmlApplicationContext
等,每个实现类都有不同的特点和用途,用于满足不同场景下的需求。
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
- 创建一个
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;
}
}
- 创建一个service包,里面包含
UserService
和OrderService
两个类。
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");
}
}
- 为user创建一个
AppConfig
配置类。
package com.example;
public class AppConfig {
}
- 创建一个
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。
- 现在去实现
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
属性值时,它将默认为一个空字符串。
- 在
AppConfig
类上添加咱们自己写的@ComponentScan
注解,配置扫描路径,扫描到包,不要到具体类
package com.example;
import com.example.springframe.ComponentScan;
@ComponentScan("com.example.user.service")
public class AppConfig {
}
- 再配置个
@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 "";
}
- 在之前创建好的
UserSerivce
添加@Component
注解,之后@Component
进行扫描的时候就会将带有@Component
注解的类注册为Bean。OrderService
不添加注解,与UserService
进行对比。
@Component
public class UserService {
现在spring已经可以扫描到包了,下面我们来写相关扫描逻辑和处理方法
四、扫描和加载逻辑
在完成了上个部分的创建Bean的过程之后,在这个部分来编写扫描和加载的逻辑。
- 在
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
。
- 实现
@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 "";
}
UserService
和OrderService
加入@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");
}
}
- 创建
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;
}
}
- 在
MyApplicationContext
中定义一个map
来存放这些Bean,beanName
为map
的key
,BeanDefinition
为value
。
public class MyApplicationContext {
private Class appConfig;
// 用map存储bean
private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();
- 完善
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();
}
}
}
}
}
- 完善
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
}
}
- 在
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<>();
- 完善构造方法,扫描完成后遍历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);
}
}
}
- 完善
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;
}
- 完善
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);
}
}
- 验证。我们再修改
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
注解
@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 {
}
- 在
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);
}
}
- 创建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;
}
- 在
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);
}
}
- 现在通过test方法调用
UserService
的test()
方法可以顺利使用带有@Autowired
的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);
UserService userService = (UserService) context.getBean("userService");
userService.test();
}
}
六、初始化以及前后的操作
- 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);
}
}
- 在
CreateBean()
方法中添加初始化逻辑。
// 初始化,没法在bean创建时调用初始化方法,所以在createBean中实现
// 判断instance是不是InitializingBean的实例
if(instance instanceof InitializingBean){
((InitializingBean) instance).afterPropertiesSet();
}
- 定义
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);
}
}
- 在初始化操作前后添加两个方法,去实现初始化前和初始化后的一些操作,在扫描的时候判断有哪些带有
@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<>();
- 在
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);
}
- 在初始化前后添加对应的前后操作方法
// 初始化前的操作
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);
}
- 这样我们就可以取到经过完整
BeanPostProcessor
以及初始化后的Bean对象,可以通过这种方法在初始化Bean的前后进行很多操作。