分类:01_设计模式
Spring的设计理念
Spring是面向Bean的编程(BOP:Bean Oriented Programming),Bean在Spring中才是真正的主角。Bean在Spring中作用就像Object对OOP的意义一样,没有对象的概念就像没有面向对象编程,Spring中没有Bean也就没有Spring存在的意义。Spring提供了IoC 容器通过配置文件或者注解的方式来管理对象之间的依赖关系。
控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体,将其所依赖的对象的引用传递给它。
Bean组件定义在Spring的org.springframework.beans包下,解决了以下几个问题:
这个包下的所有类主要解决了三件事:
Bean的定义
Bean的创建
Bean的解析
Spring Bean的创建是典型的工厂模式,它的顶级接口是BeanFactory。
BeanFactory有三个子类:ListableBeanFactory、HierarchicalBeanFactory和AutowireCapableBeanFactory。目的是为了区分Spring内部对象处理和转化的数据限制。
但是从图中可以发现最终的默认实现类是DefaultListableBeanFactory,它实现了所有的接口
Spring中的BeanFactory就是简单工厂模式的体现,根据传入一个唯一的标识来获得Bean对象
BeanFactory,以Factory结尾,表示它是一个工厂(接口), 它负责生产和管理bean的一个工厂。在Spring中,BeanFactory是工厂的顶层接口,也是IOC容器的核心接口,因此BeanFactory中定义了管理Bean的通用方法,如 getBean 和 containsBean 等.
它的职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。
BeanFactory只是个接口,并不是IOC容器的具体实现,所以Spring容器给出了很多种实现,如 DefaultListableBeanFactory、XmlBeanFactory、ApplicationContext等,其中XmlBeanFactory就是常用的一个,该实现将以XML方式描述组成应用的对象及对象间的依赖关系。
1) BeanFactory源码解析
public interface BeanFactory {
/**
对FactoryBean的转移定义,因为如果使用bean的名字来检索FactoryBean得到的是对象是工厂生成的对象,
如果想得到工厂本身就需要转移
*/
String FACTORY_BEAN_PREFIX = "&";
//根据Bean的名字 获取IOC容器中对应的实例
Object getBean(String var1) throws BeansException;
//根据Bean的名字和class类型得到bean实例,增加了类型安全验证机制
<T> T getBean(String var1, Class<T> var2) throws BeansException;
Object getBean(String var1, Object... var2) throws BeansException;
<T> T getBean(Class<T> var1) throws BeansException;
<T> T getBean(Class<T> var1, Object... var2) throws BeansException;
<T> ObjectProvider<T> getBeanProvider(Class<T> var1);
<T> ObjectProvider<T> getBeanProvider(ResolvableType var1);
//查看Bean容器中是否存在对应的实例,存在返回true 否则返回false
boolean containsBean(String var1);
//根据Bean的名字 判断这个bean是不是单例
boolean isSingleton(String var1) throws NoSuchBeanDefinitionException;
boolean isPrototype(String var1) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String var1, ResolvableType var2) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String var1, Class<?> var2) throws NoSuchBeanDefinitionException;
//得到bean实例的class类型
@Nullable
Class<?> getType(String var1) throws NoSuchBeanDefinitionException;
@Nullable
Class<?> getType(String var1, boolean var2) throws NoSuchBeanDefinitionException;
//得到bean的别名
String[] getAliases(String var1);
}
BeanFactory的使用场景
从IOC容器中获取Bean(Name or Type)
检索IOC容器中是否包含了指定的对象
判断Bean是否为单例
2) BeanFactory的使用
public class User {
private int id;
private String name;
private Friends friends;
public User() {
}
public User(Friends friends) {
this.friends = friends;
}
//get set......
}
public class Friends {
private List<String> names;
public Friends() {
}
public List<String> getNames() {
return names;
}
public void setNames(List<String> names) {
this.names = names;
}
}
配置文件
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-4.2.xsd">
<bean id="User" class="com.example.factory.User">
<property name="friends" ref="UserFriends" />
</bean>
<bean id="UserFriends" class="com.example.factory.Friends">
<property name="names">
<list>
<value>"LiLi"</value>
<value>"LuLu"</value>
</list>
</property>
</bean>
</beans>
测试
public class SpringTest01 { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml"); User user = context.getBean("user", User.class); user.setId(1001); user.setName("大远"); List<String> names = user.getFriends().getNames(); for (String name : names) { System.out.println(user.getName() + "的朋友包括: " + name); } } }
首先FactoryBean是一个Bean,但又不仅仅是一个Bean,这样听起来矛盾,但为啥又这样说呢?其实在Spring中,所有的Bean都是由BeanFactory(也就是IOC容器)来进行管理的。但对FactoryBean而言,这个FactoryBean不是简单的Bean,而是一个能生产或者修饰对象生成的工厂Bean,它的实现与设计模式中的工厂方法模式和修饰器模式类似
1) 为什么需要FactoryBean?
在某些情况下,实例化Bean过程比较复杂,如果按照传统的方式,则需要在中提供大量的配置信息。配置方式的灵活性是受限的,这时采用编码的方式可能会得到一个简单的方案。Spring为此提供了一个org.springframework.bean.factory.FactoryBean
的工厂类接口,用户可以通过实现该接口定制实例化Bean的逻辑。FactoryBean接口对于Spring框架来说占用重要的地位,Spring自身就提供了70多个FactoryBean的实现。它们隐藏了实例化一些复杂Bean的细节,给上层应用带来了便利。
由于第三方库不能直接注册到spring容器,于是可以实现org.springframework.bean.factory.FactoryBean
接口,然后给出自己对象的实例化代码即可。
2 ) FactoryBean的使用特点
当用户使用容器本身时,可以使用转义字符"&"来得到FactoryBean本身,以区别通过FactoryBean产生的实例对象和FactoryBean对象本身。
在BeanFactory中通过如下代码定义了该转义字符:
StringFACTORY_BEAN_PREFIX = "&";
举例
如果MyObject是一个FactoryBean,则使用&MyObject得到的是MyObject对象,而不是MyObject产生出来的对象。
3) FactoryBean的代码示例
@Configuration
@ComponentScan("com.example.factory_bean")
public class AppConfig {
}
@Component("studentBean")
public class StudentBean implements FactoryBean {
//返回工厂中的实例
@Override
public Object getObject() throws Exception {
//这里并不一定要返回MyBean自身的实例,可以是其他任何对象的实例。
return new TeacherBean();
}
//该方法返回的类型是在IOC容器中getBean所匹配的类型
@Override
public Class<?> getObjectType() {
return StudentBean.class;
}
public void study(){
System.out.println("学生学习......");
}
}
public class TeacherBean {
public void teach(){
System.out.println("老师教书......");
}
}
public class Test01 {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
//StudentBean studentBean = (StudentBean)context.getBean("studentBean");
//加上&符号,返回工厂中的实例
// StudentBean studentBean = (StudentBean)context.getBean("&studentBean");
// studentBean.study();
TeacherBean teacherBean = (TeacherBean) context.getBean("studentBean");
teacherBean.teach();
}
}
3) FactoryBean源码分析
public interface FactoryBean<T> {
String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
/**
getObject()方法: 会返回该FactoryBean生产的对象实例,我们需要实现该方法,以给出自己的对象实例化逻辑
这个方法也是FactoryBean的核心.
*/
@Nullable
T getObject() throws Exception;
/**
getObjectType()方法: 仅返回getObject() 方法所返回的对象类型,如果预先无法确定,返回NULL,
这个方法返回类型是在IOC容器中getBean所匹配的类型
*/
@Nullable
Class<?> getObjectType();
//该方法的结果用于表明 工厂方法getObject() 所生产的 对象是否要以单例形式存储在容器中如果以单例存在就返回true,否则返回false
default boolean isSingleton() {
return true;
}
}
FactoryBean表现的是一个工厂的职责,如果一个BeanA 是实现FactoryBean接口,那么A就是变成了一个工厂,根据A的名称获取到的实际上是工厂调用getObject()方法返回的对象,而不是对象本身,如果想获取工厂对象本身,需要在名称前面加上 '&'符号
getObject('name') 返回的是工厂中工厂方法生产的实例
getObject('&name') 返回的是工厂本身实例
使用场景
FactoryBean的最为经典的使用场景,就是用来创建AOP代理对象,这个对象在Spring中就是 ProxyFactoryBean
BeanFactory与FactoryBean区别
他们两个都是工厂,但是FactoryBean本质还是一个Bean,也归BeanFactory管理
BeanFactory是Spring容器的顶层接口,FactoryBean更类似于用户自定义的工厂接口
BeanFactory和ApplicationContext的区别
BeanFactory是Spring容器的顶层接口,而ApplicationContext应用上下文类 他是BeanFactory的子类,他是Spring中更高级的容器,提供了更多的功能
国际化
访问资源
载入多个上下文
消息发送 响应机制
两者的装载bean的时机不同
优点: 应用启动的时候占用的资源比较少,对资源的使用要求比较高的应用 ,比较有优势
ApplicationContext:在启动的时候就把所有的Bean全部实例化.
lazy-init= true 可以使bean延时实例化
优点: 所有的Bean在启动的时候就加载,系统运行的速度快,还可以及时的发现系统中配置的问题.
观察者模式它是用于建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将相应的作出反应.
在观察者模式中发生改变的对象称为观察目标,而被通知的对象称为观察者,一个观察目标可以应对多个观察者,而且这些观察者之间可以没有任何相互联系,可以根据需要增加和删除观察者,使得系统更易于扩展.
观察者模式的别名有发布-订阅(Publish/Subscribe)模式, 我们来看一下观察者模式与发布订阅模式结构上的区别
在设计模式结构上,发布订阅模式继承自观察者模式,是观察者模式的一种实现的变体。
在设计模式意图上,两者关注点不同,一个关心数据源,一个关心的是事件消息。
观察者模式里,只有两个角色 —— 观察者 + 被观察者; 而发布订阅模式里,却不仅仅只有发布者和订阅者两个角色,还有一个管理并执行消息队列的 “经纪人Broker”
观察者和被观察者,是松耦合的关系;发布者和订阅者,则完全不存在耦合
观察者模式:数据源直接通知订阅者发生改变。
发布订阅模式:数据源告诉第三方(事件通道)发生了改变,第三方再通知订阅者发生了改变。
Spring 基于观察者模式,实现了自身的事件机制也就是事件驱动模型,事件驱动模型通常也被理解成观察者或者发布/订阅模型。
spring事件模型提供如下几个角色
ApplicationEvent
ApplicationListener
ApplicationEventPublisher
ApplicationEventMulticaster
1) 事件:ApplicationEvent
是所有事件对象的父类。ApplicationEvent 继承自 jdk 的 EventObject, 所有的事件都需要继承 ApplicationEvent, 并且通过 source 得到事件源。
public abstract class ApplicationEvent extends EventObject {
private static final long serialVersionUID = 7099057708183571937L;
private final long timestamp = System.currentTimeMillis();
public ApplicationEvent(Object source) {
super(source);
}
public final long getTimestamp() {
return this.timestamp;
}
}
Spring 也为我们提供了很多内置事件:
ContextRefreshEvent,当ApplicationContext容器初始化完成或者被刷新的时候,就会发布该事件。
ContextStartedEvent,当ApplicationContext启动的时候发布事件.
ContextStoppedEvent,当ApplicationContext容器停止的时候发布事件.
RequestHandledEvent,只能用于DispatcherServlet的web应用,Spring处理用户请求结束后,系统会触发该事件。
2) 事件监听:ApplicationListener
ApplicationListener(应用程序事件监听器) 继承自jdk的EventListener,所有的监听器都要实现这个接口,这个接口只有一个onApplicationEvent()方法,该方法接受一个ApplicationEvent或其子类对象作为参数
在方法体中,可以通过不同对Event类的判断来进行相应的处理.当事件触发时所有的监听器都会收到消息,如果你需要对监听器的接收顺序有要求,可是实现该接口的一个实现SmartApplicationListener,通过这个接口可以指定监听器接收事件的顺序.
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
void onApplicationEvent(E var1);
}
实现了ApplicationListener接口之后,需要实现方法onApplicationEvent(),在容器将所有的Bean都初始化完成之后,就会执行该方法。
3) 事件源:ApplicationEventPublisher
事件的发布者,封装了事件发布功能方法接口,是Applicationcontext接口的超类
事件机制的实现需要三个部分,事件源,事件,事件监听器,在上面介绍的ApplicationEvent就相当于事件,ApplicationListener相当于事件监听器,这里的事件源说的就是ApplicationEventPublisher.
public interface ApplicationEventPublisher {
default void publishEvent(ApplicationEvent event) {
this.publishEvent((Object)event);
}
//调用publishEvent方法,传入一个ApplicationEvent的实现类对象作为参数,每当ApplicationContext发布ApplicationEvent时,所有的ApplicationListener就会被自动的触发.
void publishEvent(Object var1);
}
我们常用的ApplicationContext都继承了AbstractApplicationContext,像我们平时常见ClassPathXmlApplicationContext、XmlWebApplicationContex也都是继承了它,AbstractApplicationcontext是ApplicationContext接口的抽象实现类,在该类中实现了publishEvent方法:
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
} else {
//事件发布委托给applicationEventMulticaster
this.getApplicationEventMulticaster().multicastEvent((ApplicationEvent)applicationEvent, eventType);
}
}
在这个方法中,我们看到了一个getApplicationEventMulticaster().这就要牵扯到另一个类ApplicationEventMulticaster.
4) 事件管理:ApplicationEventMulticaster
用于事件监听器的注册和事件的广播。监听器的注册就是通过它来实现的,它的作用是把 Applicationcontext 发布的 Event 广播给它的监听器列表。
public interface ApplicationEventMulticaster {
//添加事件监听器
void addApplicationListener(ApplicationListener<?> var1);
//添加事件监听器,使用容器中的bean
void addApplicationListenerBean(String var1);
//移除事件监听器
void removeApplicationListener(ApplicationListener<?> var1);
void removeApplicationListenerBean(String var1);
//移除所有
void removeAllListeners();
//发布事件
void multicastEvent(ApplicationEvent var1);
void multicastEvent(ApplicationEvent var1, @Nullable ResolvableType var2);
}
在AbstractApplicationcontext中有一个applicationEventMulticaster的成员变量,提供了监听器Listener的注册方法.
public abstract class AbstractApplicationContext extends DefaultResourceLoader
implements ConfigurableApplicationContext, DisposableBean {
private ApplicationEventMulticaster applicationEventMulticaster;
protected void registerListeners() {
// Register statically specified listeners first.
for (ApplicationListener<?> listener : getApplicationListeners()) {
getApplicationEventMulticaster().addApplicationListener(listener);
}
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let post-processors apply to them!
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
for (String lisName : listenerBeanNames) {
getApplicationEventMulticaster().addApplicationListenerBean(lisName);
}
}
}
实现一个需求:当调用一个类的方法完成时,该类发布事件,事件监听器监听该类的事件并执行的自己的方法逻辑
假设这个类是Request、发布的事件是ReuqestEvent、事件监听者是ReuqestListener。当调用Request的doRequest方法时,发布事件。
代码如下
/**
* 定义事件
* @author spikeCong
* @date 2022/10/24
**/
public class RequestEvent extends ApplicationEvent {
public RequestEvent(Object source) {
super(source);
}
}
/**
* 发布事件
* @author spikeCong
* @date 2022/10/24
**/
@Component
public class Request {
@Autowired
private ApplicationContext applicationContext;
public void doRequest(){
System.out.println("调用Request类的doRequest方法发送一个请求......");
applicationContext.publishEvent(new RequestEvent(this));
}
}
/**
* 监听事件
* @author spikeCong
* @date 2022/10/24
**/
@Component
public class RequestListener implements ApplicationListener<RequestEvent> {
@Override
public void onApplicationEvent(RequestEvent requestEvent) {
System.out.println("监听到RequestEvent事件,执行本方法");
}
}
public class SpringEventTest {
public static void main(String[] args) {
ApplicationContext context =
new AnnotationConfigApplicationContext("com.mashibing.pubsub");
Request request = (Request) context.getBean("request");
//调用方法发布事件
request.doRequest();
}
}
//打印日志
调用Request类的doRequest方法发送一个请求......
监听到RequestEvent事件,执行本方法
上面代码的执行流程
监听器什么时候注册到IOC容器
注册的开始逻辑是在AbstractApplicationContext类的refresh方法,该方法包含了整个IOC容器初始化所有方法。其中有一个registerListeners()方法就是注册系统监听者(spring自带的)和自定义监听器的。
public void refresh() throws BeansException, IllegalStateException {
//BeanFactory准备工作完成后进行的后置处理工作
this.postProcessBeanFactory(beanFactory);
//执行BeanFactoryPostProcessor的方法;
this.invokeBeanFactoryPostProcessors(beanFactory);
//注册BeanPostProcessor(Bean的后置处理器),在创建bean的前后等执行
this.registerBeanPostProcessors(beanFactory);
//初始化MessageSource组件(做国际化功能;消息绑定,消息解析);
this.initMessageSource();
//初始化事件派发器
this.initApplicationEventMulticaster();
////子类重写这个方法,在容器刷新的时候可以自定义逻辑;如创建Tomcat,Jetty等WEB服务器
this.onRefresh();
//注册应用的监听器。就是注册实现了ApplicationListener接口的监听器bean,这些监听器是注册到ApplicationEventMulticaster中的
this.registerListeners();
//初始化所有剩下的非懒加载的单例bean
this.finishBeanFactoryInitialization(beanFactory);
//完成context的刷新
this.finishRefresh();
}
看registerListeners的关键方法体,其中的两个方法addApplicationListener和addApplicationListenerBean
,从方法可以看出是添加监听者。
protected void registerListeners() {
Iterator var1 = this.getApplicationListeners().iterator();
while(var1.hasNext()) {
ApplicationListener<?> listener = (ApplicationListener)var1.next();
this.getApplicationEventMulticaster().addApplicationListener(listener);
}
String[] listenerBeanNames = this.getBeanNamesForType(ApplicationListener.class, true, false);
String[] var7 = listenerBeanNames;
int var3 = listenerBeanNames.length;
for(int var4 = 0; var4 < var3; ++var4) {
String listenerBeanName = var7[var4];
this.getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
}
}
那么最后将监听者放到哪里了呢?就是ApplicationEventMulticaster接口的子类
该接口主要两个职责,维护ApplicationListener相关类和发布事件。
实现在默认实现类AbstractApplicationEventMulticaster,最后将Listener放到了内部类ListenerRetriever两个set集合中
private class ListenerRetriever {
public final Set<ApplicationListener<?>> applicationListeners = new LinkedHashSet();
public final Set<String> applicationListenerBeans = new LinkedHashSet();
}
ListenerRetriever被称为监听器注册表。
Spring如何发布的事件并通知监听者
这个注意的有两个方法
1) publishEvent方法
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");
Object applicationEvent;
//尝试转换为ApplicationEvent或者PayloadApplicationEvent,如果是PayloadApplicationEvent则获取eventType
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent)event;
} else {
applicationEvent = new PayloadApplicationEvent(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent)applicationEvent).getResolvableType();
}
}
if (this.earlyApplicationEvents != null) {
//判断earlyApplicationEvents是否为空(也就是早期事件还没有被发布-说明广播器还没有实例化好),如果不为空则将当前事件放入集合
this.earlyApplicationEvents.add(applicationEvent);
} else {
//否则获取ApplicationEventMulticaster调用其multicastEvent将事件广播出去。本文这里获取到的广播器实例是SimpleApplicationEventMulticaster。
this.getApplicationEventMulticaster().multicastEvent((ApplicationEvent)applicationEvent, eventType);
}
//将事件交给父类处理
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext)this.parent).publishEvent(event, eventType);
} else {
this.parent.publishEvent(event);
}
}
}
2) multicastEvent方法
继续进入到multicastEvent方法
,该方法有两种方式调用invokeListener,通过线程池和直接调用,进一步说就是通过异步和同步两种方式调用.
public void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType) {
//解析事件类型
ResolvableType type = eventType != null ? eventType : this.resolveDefaultEventType(event);
//获取执行器
Executor executor = this.getTaskExecutor();
// 获取合适的ApplicationListener,循环调用监听器的onApplicationEvent方法
Iterator var5 = this.getApplicationListeners(event, type).iterator();
while(var5.hasNext()) {
ApplicationListener<?> listener = (ApplicationListener)var5.next();
if (executor != null) {
//如果executor不为null,则交给executor去调用监听器
executor.execute(() -> {
this.invokeListener(listener, event);
});
} else {
//否则,使用当前主线程直接调用监听器;
this.invokeListener(listener, event);
}
}
}
3) invokeListener方法
// 该方法增加了错误处理逻辑,然后调用doInvokeListener
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
ErrorHandler errorHandler = this.getErrorHandler();
if (errorHandler != null) {
try {
this.doInvokeListener(listener, event);
} catch (Throwable var5) {
errorHandler.handleError(var5);
}
} else {
this.doInvokeListener(listener, event);
}
}
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
//直接调用了listener接口的onApplicationEvent方法
listener.onApplicationEvent(event);
}
AbstractApplicationContext
实现了ApplicationEventPublisher
接口的publishEvent
方法
1) BeanFactory
BeanFactory作为最顶层的一个接口,定义了IoC容器的基本功能规范
从类图中我们可以发现最终的默认实现类是DefaultListableBeanFactory,它实现了所有的接口。那么为何要定义这么多层次的接口呢?每个接口都有它的使用场合,主要是为了区分在Spring内部操作过程中对象的传递和转化,对对象的数据访问所做的限制。
例如,
ListableBeanFactory接口表示这些Bean可列表化。
HierarchicalBeanFactory表示这些Bean 是有继承关系的,也就是每个 Bean 可能有父 Bean
AutowireCapableBeanFactory 接口定义Bean的自动装配规则。
这三个接口共同定义了Bean的集合、Bean之间的关系及Bean行为。
在BeanFactory里只对IoC容器的基本行为做了定义,根本不关心你的Bean是如何定义及怎样加载的。正如我们只关心能从工厂里得到什么产品,不关心工厂是怎么生产这些产品的。
2 ) ApplicationContext
BeanFactory有一个很重要的子接口,就是ApplicationContext接口,该接口主要来规范容器中的bean对象是非延时加载,即在创建容器对象的时候就对象bean进行初始化,并存储到一个容器中。
//延时加载
BeanFactory factory = new XmlBeanFactory(new ClassPathResource("bean.xml"));
//立即加载
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
User user = context.getBean("user", User.class);
ApplicationContext 的子类主要包含两个方面:
ConfigurableApplicationContext 表示该 Context 是可修改的,也就是在构建 Context 中用户可以动态添加或修改已有的配置信息
WebApplicationContext 顾名思义,就是为 web 准备的 Context 他可以直接访问到 ServletContext,通常情况下,这个接口使用少
要知道工厂是如何产生对象的,我们需要看具体的IoC容器实现,Spring提供了许多IoC容器实现,比如:
ClasspathXmlApplicationContext : 根据类路径加载xml配置文件,并创建IOC容器对象。
FileSystemXmlApplicationContext :根据系统路径加载xml配置文件,并创建IOC容器对象。
AnnotationConfigApplicationContext :加载注解类配置,并创建IOC容器。
总体来说 ApplicationContext 必须要完成以下几件事:
标识一个应用环境
利用 BeanFactory 创建 Bean 对象
保存对象关系表
能够捕获各种事件
3) Bean定义:BeanDefinition
这里的 BeanDefinition 就是我们所说的 Spring 的 Bean,我们自己定义的各个 Bean 其实会转换成一个个 BeanDefinition 存在于 Spring 的 BeanFactory 中
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
//DefaultListableBeanFactory 中使用 Map 结构保存所有的 BeanDefinition 信息
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
}
BeanDefinition 中保存了我们的 Bean 信息,比如这个 Bean 指向的是哪个类、是否是单例的、是否懒加载、这个 Bean 依赖了哪些 Bean 等等。
4) BeanDefinitionReader
Bean的解析过程非常复杂,功能被分得很细,因为这里需要被扩展的地方很多,必须保证足够的灵活性,以应对可能的变化。Bean的解析主要就是对Spring配置文件的解析。
这个解析过程主要通过BeanDefinitionReader来完成,看看Spring中BeanDefinitionReader的类结构图,如下图所示。
BeanDefinitionReader接口定义的功能
public interface BeanDefinitionReader {
/*
下面的loadBeanDefinitions都是加载bean定义,从指定的资源中
*/
int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException;
int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException;
int loadBeanDefinitions(String location) throws BeanDefinitionStoreException;
int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException;
}
5) BeanFactory后置处理器
后置处理器是一种拓展机制,贯穿Spring Bean的生命周期
后置处理器分为两类:
BeanFactory后置处理器:BeanFactoryPostProcessor
实现该接口,可以在spring的bean创建之前,修改bean的定义属性
public interface BeanFactoryPostProcessor {
/*
* 该接口只有一个方法postProcessBeanFactory,方法参数是ConfigurableListableBeanFactory,通过该
参数,可以获取BeanDefinition
*/
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
6) Bean后置处理器:BeanPostProcessor
BeanPostProcessor是Spring IOC容器给我们提供的一个扩展接口
实现该接口,可以在spring容器实例化bean之后,在执行bean的初始化方法前后,添加一些处理逻辑
public interface BeanPostProcessor {
//bean初始化方法调用前被调用
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
//bean初始化方法调用后被调用
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
容器环境的初始化(系统、JVM 、解析器、类加载器等等)
Bean工厂的初始化(IOC容器首先会销毁旧工厂,旧Bean、创建新的工厂)
读取:通过BeanDefinitonReader读取我们项目中的配置(application.xml)
定义:通过解析xml文件内容,将里面的Bean解析成BeanDefinition(未实例化、未初始化)
将解析得到的BeanDefinition,存储到工厂类的Map容器中
调用 BeanFactoryPostProcessor 该方法是一种功能增强,可以在这个步骤对已经完成初始化的 BeanFactory 进行属性覆盖,或是修改已经注册到 BeanFactory 的 BeanDefinition
通过反射实例化bean对象
进入到Bean实例化流程,首先设置对象属性
检查Aware相关接口,并设置相关依赖
前置处理器,执行BeanPostProcesser的before方法对bean进行扩展
检查是否有实现initializingBean 回调接口,如果实现就要回调其中的AftpropertiesSet() 方法,(通过可以完成一些配置的加载)
检查是否有配置自定义的init-method ,
后置处理器执行BeanPostProcesser 的after方法 --> AOP就是在这个阶段完成的, 在这里判断bean对象是否实现接口,实现就使用JDK代理,否则选择CGLIB
对象创建完成,添加到BeanFactory的单例池中
对下面的配置文件进行解析,并自定义SpringIOC, 对涉及到的对象进行管理。
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="courseService" class="com.mashibing.service.impl.CourseServiceImpl">
<property name="courseDao" ref="courseDao"></property>
</bean>
<bean id="courseDao" class="com.mashibing.dao.impl.CourseDaoImpl"></bean>
</beans>
PropertyValue类: 用于封装bean的属性,体现到上面的配置文件就是封装bean标签的子标签property标签数据。
package com.mashibing.framework.beans;
/**
* 该类用来封装bean标签下的property子标签的属性
* 1.name属性
* 2.ref属性
* 3.value属性: 给基本数据类型及string类型数据赋的值
* @author spikeCong
* @date 2022/10/26
**/
public class PropertyValue {
private String name;
private String ref;
private String value;
public PropertyValue() {
}
public PropertyValue(String name, String ref, String value) {
this.name = name;
this.ref = ref;
this.value = value;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getRef() {
return ref;
}
public void setRef(String ref) {
this.ref = ref;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
MutablePropertyValues类: 一个bean标签可以有多个property子标签,所以再定义一个MutablePropertyValues类,用来存储并管理多个PropertyValue对象。
package com.mashibing.framework.beans;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
/**
* 该类用来存储和遍历多个PropertyValue对象
* @author spikeCong
* @date 2022/10/26
**/
public class MutablePropertyValues implements Iterable<PropertyValue>{
//定义List集合,存储PropertyValue的容器
private final List<PropertyValue> propertyValueList;
//空参构造中 初始化一个list
public MutablePropertyValues() {
this.propertyValueList = new ArrayList<PropertyValue>();
}
//有参构造 接收一个外部传入的list,赋值propertyValueList属性
public MutablePropertyValues(List<PropertyValue> propertyValueList) {
if(propertyValueList == null){
this.propertyValueList = new ArrayList<PropertyValue>();
}else{
this.propertyValueList = propertyValueList;
}
}
//获取当前容器对应的迭代器对象
@Override
public Iterator<PropertyValue> iterator() {
//直接获取List集合中的迭代器
return propertyValueList.iterator();
}
//获取所有的PropertyValue
public PropertyValue[] getPropertyValues(){
//将集合转换为数组并返回
return propertyValueList.toArray(new PropertyValue[0]); //new PropertyValue[0]声明返回的数组类型
}
//根据name属性值获取PropertyValue
public PropertyValue getPropertyValue(String propertyName){
//遍历集合对象
for (PropertyValue propertyValue : propertyValueList) {
if(propertyValue.getName().equals(propertyName)){
return propertyValue;
}
}
return null;
}
//判断集合是否为空,是否存储PropertyValue
public boolean isEmpty(){
return propertyValueList.isEmpty();
}
//向集合中添加
public MutablePropertyValues addPropertyValue(PropertyValue value){
//判断集合中存储的propertyvalue对象.是否重复,重复就进行覆盖
for (int i = 0; i < propertyValueList.size(); i++) {
//获取集合中每一个 PropertyValue
PropertyValue currentPv = propertyValueList.get(i);
//判断当前的pv的name属性 是否与传入的相同,如果相同就覆盖
if(currentPv.getName().equals(value.getName())){
propertyValueList.set(i,value);
return this;
}
}
//没有重复
this.propertyValueList.add(value);
return this; //目的是实现链式编程
}
//判断是否有指定name属性值的对象
public boolean contains(String propertyName){
return getPropertyValue(propertyName) != null;
}
}
BeanDefinition类: 用来封装bean信息的,主要包含id(即bean对象的名称)、class(需要交由spring管理的类的全类名)及子标签property数据。
/**
* 封装Bean标签数据的类,包括id与class以及子标签的数据
* @author spikeCong
* @date 2022/10/27
**/
public class BeanDefinition {
private String id;
private String className;
private MutablePropertyValues propertyValues;
public BeanDefinition() {
propertyValues = new MutablePropertyValues();
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public MutablePropertyValues getPropertyValues() {
return propertyValues;
}
public void setPropertyValues(MutablePropertyValues propertyValues) {
this.propertyValues = propertyValues;
}
}
BeanDefinition对象存取的操作, 其实是在BeanDefinitionRegistry接口中定义的,它被称为是BeanDefinition的注册中心.
//源码
public interface BeanDefinitionRegistry extends AliasRegistry {
void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException;
void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
boolean containsBeanDefinition(String beanName);
String[] getBeanDefinitionNames();
int getBeanDefinitionCount();
boolean isBeanNameInUse(String beanName);
}
BeanDefinitionRegistry继承结构图如下:
BeanDefinitionRegistry接口的子实现类主要有以下两个:
DefaultListableBeanFactory
在该类中定义了如下代码,就是用来注册bean
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
SimpleBeanDefinitionRegistry
在该类中定义了如下代码,就是用来注册bean
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(64);
自定义BeanDefinitionRegistry接口定义了注册表的相关操作,定义如下功能:
public interface BeanDefinitionRegistry {
//注册BeanDefinition对象到注册表中
void registerBeanDefinition(String beanName, BeanDefinition beanDefinition);
//从注册表中删除指定名称的BeanDefinition对象
void removeBeanDefinition(String beanName) throws Exception;
//根据名称从注册表中获取BeanDefinition对象
BeanDefinition getBeanDefinition(String beanName) throws Exception;
//判断注册表中是否包含指定名称的BeanDefinition对象
boolean containsBeanDefinition(String beanName);
//获取注册表中BeanDefinition对象的个数
int getBeanDefinitionCount();
//获取注册表中所有的BeanDefinition的名称
String[] getBeanDefinitionNames();
}
SimpleBeanDefinitionRegistry类, 该类实现了BeanDefinitionRegistry接口,定义了Map集合作为注册表容器。
public class SimpleBeanDefinitionRegistry implements BeanDefinitionRegistry {
private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<String, BeanDefinition>();
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {
beanDefinitionMap.put(beanName,beanDefinition);
}
@Override
public void removeBeanDefinition(String beanName) throws Exception {
beanDefinitionMap.remove(beanName);
}
@Override
public BeanDefinition getBeanDefinition(String beanName) throws Exception {
return beanDefinitionMap.get(beanName);
}
@Override
public boolean containsBeanDefinition(String beanName) {
return beanDefinitionMap.containsKey(beanName);
}
@Override
public int getBeanDefinitionCount() {
return beanDefinitionMap.size();
}
@Override
public String[] getBeanDefinitionNames() {
return beanDefinitionMap.keySet().toArray(new String[1]);
}
}
BeanDefinitionReader接口
BeanDefinitionReader用来解析配置文件并在注册表中注册bean的信息。定义了两个规范:
获取注册表的功能,让外界可以通过该对象获取注册表对象
加载配置文件,并注册bean数据
/**
* 该类定义解析配置文件规则的接口
* @author spikeCong
* @date 2022/10/28
**/
public interface BeanDefinitionReader {
//获取注册表对象
BeanDefinitionRegistry getRegistry();
//加载配置文件并在注册表中进行注册
void loadBeanDefinitions(String configLocation) throws Exception;
}
XmlBeanDefinitionReader类
XmlBeanDefinitionReader是专门用来解析xml配置文件的。该类实现BeanDefinitionReader接口并实现接口中的两个功能。
/**
* 该类是对XML文件进行解析的类
* @author spikeCong
* @date 2022/10/28
**/
public class XmlBeanDefinitionReader implements BeanDefinitionReader {
//声明注册表对象(将配置文件与注册表解耦,通过Reader降低耦合性)
private BeanDefinitionRegistry registry;
public XmlBeanDefinitionReader() {
registry = new SimpleBeanDefinitionRegistry();
}
@Override
public BeanDefinitionRegistry getRegistry() {
return registry;
}
//加载配置文件
@Override
public void loadBeanDefinitions(String configLocation) throws Exception {
//使用dom4j解析xml
SAXReader reader = new SAXReader();
//获取配置文件,类路径下
InputStream is = XmlBeanDefinitionReader.class.getClassLoader().getResourceAsStream(configLocation);
//获取document文档对象
Document document = reader.read(is);
Element rootElement = document.getRootElement();
//解析bean标签
parseBean(rootElement);
}
private void parseBean(Element rootElement) {
//获取所有的bean标签
List<Element> elements = rootElement.elements();
//遍历获取每个bean标签的属性值和子标签property
for (Element element : elements) {
String id = element.attributeValue("id");
String className = element.attributeValue("class");
//封装到beanDefinition
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setId(id);
beanDefinition.setClassName(className);
//获取property
List<Element> list = element.elements("property");
MutablePropertyValues mutablePropertyValues = new MutablePropertyValues();
//遍历,封装propertyValue,并保存到mutablePropertyValues
for (Element element1 : list) {
String name = element1.attributeValue("name");
String ref = element1.attributeValue("ref");
String value = element1.attributeValue("value");
PropertyValue propertyValue = new PropertyValue(name,ref,value);
mutablePropertyValues.addPropertyValue(propertyValue);
}
//将mutablePropertyValues封装到beanDefinition
beanDefinition.setPropertyValues(mutablePropertyValues);
System.out.println(beanDefinition);
//将beanDefinition注册到注册表
registry.registerBeanDefinition(id,beanDefinition);
}
}
public static void main(String[] args) throws Exception {
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader();
reader.loadBeanDefinitions("bean.xml");
}
}
1) BeanFactory接口
在该接口中定义IOC容器的统一规范和获取bean对象的方法。
/**
* IOC容器父接口
* @author spikeCong
* @date 2022/10/28
**/
public interface BeanFactory {
Object getBean(String name)throws Exception;
//泛型方法,传入当前类或者其子类
<T> T getBean(String name ,Class<? extends T> clazz)throws Exception;
}
2) ApplicationContext接口
该接口的所有的子实现类对bean对象的创建都是非延时的,所以在该接口中定义 refresh()
方法,该方法主要完成以下两个功能:
加载配置文件。
根据注册表中的BeanDefinition对象封装的数据进行bean对象的创建。
/**
* 定义非延时加载功能
* @author spikeCong
* @date 2022/10/28
**/
public interface ApplicationContext extends BeanFactory {
//进行配置文件加载,并进行对象创建
void refresh();
}
3) AbstractApplicationContext类
作为ApplicationContext接口的子类,所以该类也是非延时加载,所以需要在该类中定义一个Map集合,作为bean对象存储的容器。
声明BeanDefinitionReader类型的变量,用来进行xml配置文件的解析,符合单一职责原则。
BeanDefinitionReader类型的对象创建交由子类实现,因为只有子类明确到底创建BeanDefinitionReader哪儿个子实现类对象。
/**
* ApplicationContext接口的子实现类
* 创建容器对象时,加载配置文件,对bean进行初始化
* @author spikeCong
* @date 2022/10/28
**/
public abstract class AbstractApplicationContext implements ApplicationContext {
//声明解析器变量
protected BeanDefinitionReader beanDefinitionReader;
//定义存储bean对象的Map集合
protected Map<String,Object> singletonObjects = new HashMap<>();
//声明配置文件类路径的变量
protected String configLocation;
@Override
public void refresh() {
//加载beanDefinition对象
try {
beanDefinitionReader.loadBeanDefinitions(configLocation);
//初始化bean
finishBeanInitialization();
} catch (Exception e) {
e.printStackTrace();
}
}
//bean初始化
protected void finishBeanInitialization() throws Exception {
//获取对应的注册表对象
BeanDefinitionRegistry registry = beanDefinitionReader.getRegistry();
//获取beanDefinition对象
String[] beanNames = registry.getBeanDefinitionNames();
for (String beanName : beanNames) {
//进行bean的初始化
getBean(beanName);
}
};
}
4) ClassPathXmlApplicationContext类
该类主要是加载类路径下的配置文件,并进行bean对象的创建,主要完成以下功能:
在构造方法中,创建BeanDefinitionReader对象。
在构造方法中,调用refresh()方法,用于进行配置文件加载、创建bean对象并存储到容器中。
重写父接口中的getBean()方法,并实现依赖注入操作。
/**
* IOC容器具体的子实现类,加载XML格式配置文件
* @author spikeCong
* @date 2022/10/28
**/
public class ClassPathXmlApplicationContext extends AbstractApplicationContext{
public ClassPathXmlApplicationContext(String configLocation) {
this.configLocation = configLocation;
//构建解析器对象
this.beanDefinitionReader = new XmlBeanDefinitionReader();
this.refresh();
}
//跟据bean的对象名称获取bean对象
@Override
public Object getBean(String name) throws Exception {
//判断对象容器中是否包含指定名称的bean对象,如果包含就返回,否则自行创建
Object obj = singletonObjects.get(name);
if(obj != null){
return obj;
}
//自行创建,获取beanDefinition对象
BeanDefinitionRegistry registry = beanDefinitionReader.getRegistry();
BeanDefinition beanDefinition = registry.getBeanDefinition(name);
//通过反射创建对象
String className = beanDefinition.getClassName();
Class<?> clazz = Class.forName(className);
Object beanObj = clazz.newInstance();
//CourseService与UserDao存依赖,所以要将UserDao一同初始化,进行依赖注入
MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
for (PropertyValue propertyValue : propertyValues) {
//获取name属性值
String propertyName = propertyValue.getName();
//获取Value属性
String value = propertyValue.getValue();
//获取ref属性
String ref = propertyValue.getRef();
//ref与value只能存在一个
if(ref != null && !"".equals(ref)){
//获取依赖的bean对象,拼接set set+Course
Object bean = getBean(ref);
String methodName = StringUtils.getSetterMethodFieldName(propertyName);
//获取所有方法对象
Method[] methods = clazz.getMethods();
for (Method method : methods) {
if(methodName.equals(method.getName())){
//执行该set方法
method.invoke(beanObj,bean);
}
}
}
if(value != null && !"".equals(value)){
String methodName = StringUtils.getSetterMethodFieldName(propertyName);
//获取method
Method method = clazz.getMethod(methodName, String.class);
method.invoke(beanObj,value);
}
}
//在返回beanObj之前 ,需要将对象存储到Map容器中
this.singletonObjects.put(name,beanObj);
return beanObj;
}
@Override
public <T> T getBean(String name, Class<? extends T> clazz) throws Exception {
Object bean = getBean(name);
if(bean == null){
return null;
}
return clazz.cast(bean);
}
}
第一步: 将我们写好的自定义IOC容器项目,安装到maven仓库中,使其他项目可以引入其依赖
//依赖信息
<dependencies>
<dependency>
<groupId>com.mashibing</groupId>
<artifactId>user_defined_springioc</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
第二步: 创建一个新的maven项目,引入上面的依赖,项目结构如下
第三步: 完成代码编写
dao
public interface CourseDao {
public void add();
}
public class CourseDaoImpl implements CourseDao {
//value注入
private String courseName;
public String getCourseName() {
return courseName;
}
public void setCourseName(String courseName) {
this.courseName = courseName;
}
public CourseDaoImpl() {
System.out.println("CourseDaoImpl创建了......");
}
@Override
public void add() {
System.out.println("CourseDaoImpl的add方法执行了......" + courseName);
}
}
service
public interface CourseService {
public void add();
}
public class CourseServiceImpl implements CourseService {
public CourseServiceImpl() {
System.out.println("CourseServiceImpl创建了......");
}
private CourseDao courseDao;
public void setCourseDao(CourseDao courseDao) {
this.courseDao = courseDao;
}
@Override
public void add() {
System.out.println("CourseServiceImpl的add方法执行了......");
courseDao.add();
}
}
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="courseService" class="com.mashibing.test_springioc.service.impl.CourseServiceImpl">
<property name="courseDao" ref="courseDao"></property>
</bean>
<bean id="courseDao" class="com.mashibing.test_springioc.dao.impl.CourseDaoImpl">
<property name="courseName" value="java"></property>
</bean>
</beans>
Controller
public class CourseController{
public static void main(String[] args) {
//1.创建Spring的容器对象
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//2.从容器对象中获取CourseService对象
CourseService courseService = context.getBean("courseService", CourseService.class);
//3.调用UserService的add方法
courseService.add();
}
}
工厂模式。这个使用工厂模式 + 配置文件的方式。
单例模式。Spring IOC管理的bean对象都是单例的,此处的单例不是通过构造器进行单例的控制的,而是spring框架对每一个bean只创建了一个对象。
模板方法模式。AbstractApplicationContext类中的finishBeanInitialization()方法调用了子类的getBean()方法,因为getBean()的实现和环境息息相关。
迭代器模式。对于MutablePropertyValues类定义使用到了迭代器模式,因为此类存储并管理PropertyValue对象,也属于一个容器,所以给该容器提供一个遍历方式。