[Spring-1]Bean定义,注册,获取中体现的设计模式思考
1.创建简单的Bean容器
a.BeanDefinition定义
typescript
代码解读
复制代码
public class BeanDefinition{ private Object bean; public BeanDefinition(Object bean){ this.bean = bean; } public Object getBean(){ return bean; } }
b.关于BeanDefinition的工厂
typescript
代码解读
复制代码
public class BeanFactory{ private Map<String,BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(); public Object getBean(String name){ return beanDefinitions.get(name).getBean(); } public void registerBeanDefinition(String name,BeanDefinition beanDefinition){ beanDefinitionMap.put(name,beanDefinition); } }
c.测试用例
java
代码解读
复制代码
public class Test{ @Test public void test_BeanFactory(){ //1.初始化BeanFactory; BeanFactory beanFactory = new BeanFactory(); //2.注册bean BeanDefinition beanDefinition = new BeanDefinition(new UserService()); //3.获取bean UserService userService = (UserService)beanFactory.getBean("userService"); userService.queryUserInfo(); } }
d.测试结果
vbnet
代码解读
复制代码
查询用户信息 Process finished with exit code 0
本案例中体现了:工厂模式
-
BeanDefinition是原材料
-
BeanDefinitionFactory提供一个Map作为容器
-
BeanDefinitionFactory提供getBean和registerBeanDefinition方法
- 用getBean通过name拿到BeanDefinition
- 用registerBeanDefinition将name和BeanDefinition的K-V组合注册到Map容器中
注明:
-
本例中BeanDefinition存在Object类型的bean对象
- 测试方法中:BeanDefinition beanDefinition = new BeanDefinition(new UserService()); 通过new了对象
- 但在真实的Spring框架中,会用Class替代Object作为Bean
-
替代的好处:
a. 懒加载和延迟初始化
使用类定义 bean 可以在需要时实例化对象,从而节省内存资源。这种懒加载机制可以提高应用的性能,尤其是当某些 bean 在应用启动时并不立即需要时。
- 单例模式
Spring 默认使用单例模式来管理 bean。当使用类定义 bean 时,Spring 容器确保每个 bean 只有一个实例,这样可以避免不必要的对象创建和资源浪费,并提供全局共享的状态。
- 依赖注入
通过类定义 bean,Spring 可以更容易地处理依赖注入。容器能够根据构造函数、字段或方法参数自动注入依赖项,提高了代码的解耦性和可测试性。
- 配置灵活性
使用类描述 bean 使得通过 XML 或 Java 配置类灵活地配置 bean 的属性和依赖关系成为可能。这种灵活性允许开发者在不修改代码的情况下调整应用的行为。
- 生命周期管理
Spring 能够更好地管理类定义的 bean 的生命周期,包括初始化、销毁等回调,这对于资源管理和清理至关重要。
- 类型安全
通过类定义 bean,Spring 在编译时可以进行类型检查,减少了运行时错误的可能性。使用类时,IDE 提供的自动补全和提示功能也增强了开发体验。
- 支持 AOP
使用类定义 bean 使得 Spring 能够应用面向切面编程(AOP),从而为 bean 提供横切关注点(如日志记录、事务管理等)的支持。
- 可扩展性
使用类定义 bean,使得应用更易于扩展。如果需要添加新功能,只需创建新类或扩展现有类,而不必修改现有的实现。
2.实现 Bean 的定义、注册、获取
a.BeanDefinition的定义
kotlin
代码解读
复制代码
public class BeanDefinition { private Class beanClass; public BeanDefinition(Class beanClass) { this.beanClass = beanClass; } // ...get/set }
b.单例注册接口定义和实现
实际上:Spring支持Singleton Prototype Request Session Application Websocket六种bean的作用域
我们用到了:策略模式和工厂模式
在 Spring 源码中,bean 的作用域注册器主要使用了策略模式(Strategy Pattern)和工厂模式(Factory Pattern)来实现。
1. 策略模式
定义: 策略模式允许在运行时选择算法的行为。它通过定义一系列策略,并将每个策略封装起来,从而使得它们可以互换。
应用
: 在 Spring 中,针对不同的 bean 作用域(如 Singleton、Prototype 等),会定义不同的作用域策略接口和具体实现。每种作用域都有自己的实现类,例如:
SingletonScope
PrototypeScope
RequestScope
SessionScope
这些具体的作用域实现类都遵循相同的接口,使得容器能够根据 bean 的定义动态选择合适的作用域处理逻辑。
2. 工厂模式
定义: 工厂模式提供了一个创建对象的接口,但让子类决定实例化哪一个类。
应用
: 在 Spring 中,
sql
代码解读
复制代码
Scope
接口通常与
代码解读
复制代码
BeanFactory
结合使用。每个作用域都有自己的实例管理逻辑,通过工厂方法来创建和管理 bean 实例。例如:
DefaultSingletonBeanRegistry
管理 Singleton 范围的 beanPrototypeBeanDefinition
会在每次请求时返回新的实例这种设计使得 Spring 能够在不同作用域之间灵活切换,同时保持代码的高内聚性和低耦合性。
这里我们仅给出单例模式
arduino
代码解读
复制代码
public interface SingletonBeanRegistry{ Object getSingleton(String beanName); /** 我认为此处需要做两个区分: 1.Singleton等多种策略,创建的是对象,是运行在Jvm中的实例 2.BeanFactory中创建的,是BeanDefinition,其中包含了beanClassName, ProperityValues, scope, constructorArgumentValues, autowireMode(自动装配的模式), primary(该bean是否为首选的自动装配候选者), factoryBeanName(倘若bean是用工厂方法创建的,得存一下工厂bean名称), factoryMethodName( 如果使用工厂方法创建 bean, 这里存储工厂方法的名称。), initMethodName(初始化方法名称), destroyMethodName(指定销毁前执行的方法名称), description(对bean的描述信息), lazyInit(描述bean是否被懒加载), role(bean在应用中的角色), source(该bean的来源,是XML或者注释) */ }
typescript
代码解读
复制代码
public class DefaultSingletonBeanRegistry implements SingletonBeanRegistery{ private Map<String,Object> singletonObjects = new HashMap<>(); //1.此处用的是HashMap,而BeanDefinitions的Factory用的是ConcurrentHashMap @Override public Object getSingleton(String beanName) { return singletonObjects.get(beanName); } protected void addSingleton(String beanName, Object singletonObject) { singletonObjects.put(beanName, singletonObject); } }
c.抽象类定义模版方法(AbstractBeanFactory)
注意:抽象类中的抽象方法(没有方法体的方法)在其子类中是必须被@Override的
scala
代码解读
复制代码
public abstract class AbstractBeanFactory extends DefaultSingletonBeanRegistry implements BeanFactory { @Override public Object getBean(String name) throws BeansException { Object bean = getSingleton(name); if (bean != null) { return bean; } BeanDefinition beanDefinition = getBeanDefinition(name); return createBean(name, beanDefinition); } protected abstract BeanDefinition getBeanDefinition(String beanName) throws BeansException; protected abstract Object createBean(String beanName, BeanDefinition beanDefinition) throws BeansException; }
1.定义算法的框架
- 固定流程:
getBean
方法定义了获取 bean 的基本流程,包括检查单例缓存、获取 bean 定义和创建 bean。这个方法提供了一个清晰的算法步骤。
- 延迟实现细节
- 抽象方法:
getBeanDefinition
和createBean
是抽象方法,它们的具体实现由子类提供。这允许子类根据不同的需求实现不同的逻辑,而不需要修改流程本身。
- 代码复用
- 核心逻辑集中: 子类只需实现获取 bean 定义和创建 bean 的细节,而无需重复编写获取 bean 的核心逻辑。这种结构提升了代码的复用性,避免了冗余。
- 控制流程
- 调用顺序: 在
getBean
方法中,控制了执行的顺序:先检查单例缓存,再获取 bean 定义,最后调用创建逻辑。这个顺序是固定的,确保了获取 bean 的一致性。
- 高内聚低耦合
- 模块化设计:
AbstractBeanFactory
与其子类之间通过抽象方法进行交互,降低了耦合度。AbstractBeanFactory
只关心如何调用子类的实现,而不需要了解具体的创建逻辑。
- 易于扩展
- 多种实现: 由于
getBeanDefinition
和createBean
是抽象的,不同的子类可以实现不同的策略(例如,从 XML、注解或其他源加载 bean 定义),这为框架的扩展提供了极大的灵活性。
d.实例化Bean类(AbstractAutowireCapableBeanFactory)
- 在 AbstractAutowireCapableBeanFactory 类中实现了 Bean 的实例化操作
newInstance
,其实这块会埋下一个坑,有构造函数入参的对象怎么处理?可以提前思考 - 在处理完 Bean 对象的实例化后,直接调用
addSingleton
方法存放到单例对象的缓存中去。
scala
代码解读
复制代码
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory { @Override protected Object createBean(String beanName, BeanDefinition beanDefinition) throws BeansException { Object bean = null; try { bean = beanDefinition.getBeanClass().newInstance(); } catch (InstantiationException | IllegalAccessException e) { throw new BeansException("Instantiation of bean failed", e); } addSingleton(beanName, bean); return bean; } }
- 工厂模式 (Factory Pattern)
- 实例化对象:
createBean
方法负责创建 bean 实例,这符合工厂模式的核心理念,即将对象的创建逻辑封装在一个专门的类中。这使得 bean 的创建过程与其他业务逻辑分离,提供了清晰的构造方式。
- 单例模式 (Singleton Pattern)
- 缓存管理: 在
createBean
中,创建的对象通过addSingleton(beanName, bean)
被添加到单例缓存中。这种方式确保了对同一个 bean 名称的请求总是返回相同的实例,符合单例模式的行为。
- 模板方法模式 (Template Method Pattern)
- 复用和扩展: 继承自
AbstractBeanFactory
的AbstractAutowireCapableBeanFactory
利用模板方法getBean
中定义的流程,同时实现了具体的createBean
方法。这样可以复用获取 bean 的核心逻辑,而具体的 bean 创建逻辑则由子类定义。
4.为什么使用 protected
修饰符
- 控制访问范围:
createBean
被声明为protected
,这意味着只有AbstractAutowireCapableBeanFactory
的子类能够直接访问该方法。这种设计允许子类在不暴露实现细节的情况下重写或扩展createBean
的行为。 - 保护核心功能: 通过将
createBean
声明为protected
,可以防止外部代码直接调用这个方法,从而保护了 bean 创建的核心机制。这使得整个 bean 创建过程更加安全和可控。 - 支持扩展性: 子类可以重写
createBean
方法,以添加自己的逻辑(例如,依赖注入、属性填充等),同时保持AbstractBeanFactory
的核心流程。此外,确保了其他不相关的类不会干扰到 bean 的创建过程。
e.核心类实现(DefaultListableBeanFactory)
scala
代码解读
复制代码
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements BeanDefinitionRegistry { private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>(); @Override public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) { beanDefinitionMap.put(beanName, beanDefinition); } @Override public BeanDefinition getBeanDefinition(String beanName) throws BeansException { BeanDefinition beanDefinition = beanDefinitionMap.get(beanName); if (beanDefinition == null) throw new BeansException("No bean named '" + beanName + "' is defined"); return beanDefinition; } }
- 工厂模式 (Factory Pattern)
- 创建和管理 Bean:
DefaultListableBeanFactory
作为一个工厂类,负责注册和获取 bean 定义。通过registerBeanDefinition
方法,可以将 bean 的定义信息存储在beanDefinitionMap
中,而getBeanDefinition
方法则提供了根据 bean 名称获取相应定义的功能。这种模式清晰地将 bean 的创建和管理逻辑封装在一个单独的类中,从而使得其他代码不必关心具体的实现细节。
- 策略模式 (Strategy Pattern)
- 灵活的 Bean 管理:
DefaultListableBeanFactory
可以与不同的 bean 定义策略配合使用。例如,可以根据不同的需求提供不同类型的 bean 定义(如单例、原型等),而具体的创建逻辑则可以在AbstractAutowireCapableBeanFactory
中实现。这种设计允许在运行时动态选择策略,使得 bean 的管理更加灵活。
- 模板方法模式 (Template Method Pattern)
- 复用逻辑:
DefaultListableBeanFactory
可能会继承自AbstractAutowireCapableBeanFactory
,并利用其实现的一些通用逻辑,比如自动注入等。通过这种方式,它可以重写特定的方法以实现自己的逻辑,而无需重复实现所有的步骤。
- 适配器模式 (Adapter Pattern)
- 接口适应: 作为
BeanDefinitionRegistry
接口的实现,DefaultListableBeanFactory
适应了 bean 定义的注册和获取功能。这种适配器设计使得这个工厂类可以被用作不同上下文中的 bean 管理器,而不需要改变原有的逻辑。