Springソースコードシリーズの総合コラム
コンテンツ | リンクアドレス |
---|---|
[1] Springソースコードの全体概要 | https://blog.csdn.net/zhenghuishengq/article/details/130940885 |
Spring の基礎となるソース コードの全体概要
1. Springソースコードの全体概要
1. 暫定的な概要
ソース コードを分析する前に、まず公式 Web サイトにアクセスして Spring ソース パッケージをダウンロードする必要があります。5.xx バージョンをダウンロードすることをお勧めします。ここにインストールしたものは次のとおりです: https://github.com/spring-projects/spring-framework/tree/5.2.x
私たちの認識では、スプリングフレームワークはフレームワークであり、生態学です。春には 2 つの主要な部分があります。1つは IOC で、もう 1 つは AOP です。IOCは制御の反転と呼ばれるもので、オブジェクトをコンテナ管理に引き渡すという一種の考え方であり、それに対応するのが依存性注入と呼ばれるIOCの具体的な実装であるDIです。AOP は IOC の基盤を昇華したものであるため、IOC を十分に理解した上で AOP を理解する必要があります。
IOC は一般に Bean オブジェクトのコンテナとして使用されます. オブジェクトを構築する基本的なプロセスは次のとおりです. 一部のポストプロセッサーやエンハンサーなど、一部の拡張ポイントは途中で省略され、順次追加されますその後の注文。次の図は、主にアノテーションの方法で Bean を取得するフローチャートです。
1. 取得コンテキストは一般に 2 つの方法で取得されます。1 つは XML を介して取得され、もう 1 つはアノテーションを介して取得されます。XML は一般的な Springboot ではほとんど使用されないため、ここでもこのコンテキスト ApplicationContext を取得するためにアノテーションを使用する方法を選択します。
public class MainClass {
public static void main(String[] args) {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(MainCofig.class);
User user = (User)context.getBean("user");
System.out.println("user的类型:"+user.getClass());
}
}
@Configuration
@ComponentScan(basePackages = {
"com.zhs.study"})
public class MainCofig {
}
もちろん、この XML メソッドを通じてコンテキストを取得することもできます。最初に spring にアクセスするときに、xml ファイル内の Bean をアンロードします。
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("xxx.xml");
2. コンテキストを取得するためにこの XML 構成ファイルを使用するか、このアノテーションを使用するかに関係なく、getBean を呼び出す前に BeanDefinition Bean 定義を生成する必要があります。特定の Bean 定義を取得した後、この Bean ファクトリを通過できます。生成または取得このBeanオブジェクト。統一された BeanDefinition を取得する前に、BeanDefinitionReader インターフェースを実装して、異なるコンテキスト構成情報を統一された BeanDefinition に変換する必要があります。
public interface BeanDefinitionReader {
...}
BeanDefinitionReader インターフェースは、以下の図に示すように、統合パーサーに相当する、前述の XML やアノテーション生成された Bean 定義リーダーなどを備えた特定の実装クラスを持っています。
そして、BeanDefinitionRegistry レジスタを介して、読み込んだものを BeanDefinition として登録することで、Bean ファクトリが認識できる BeanDefinition オブジェクトを均一に生成できます
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
try {
Document doc = doLoadDocument(inputSource, resource);
return registerBeanDefinitions(doc, resource);
}
}
生成された beanDefinition は多数あるため、最初に Bean 定義が beanDefinitionMap に追加されます。
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
3. その後、getBean() メソッドが呼び出され、Bean ファクトリから値を取得します。Bean 定義が存在する場合は直接取得され、存在しない場合は、最初に作成されてから値が返されます。 。
Student student = (Student) context.getBean("student");
この Bean を取得するプロセスでは、主にインスタンス化、プロパティの入力、および初期化の 3 つの主要なプロセスを経ます。インスタンス化とは、ヒープ メモリ内にスペースを開き、オブジェクトのプロパティ値にデフォルト値を割り当てることです。プロパティの充填とは、このオブジェクトに最終値を設定することです。そして、最後に init 初期化メソッドを実行します。そして、これら 3 つのステップの順序は変更できません。
この初期化に属性を入力するプロセスで、Aware インターフェイスのいくつかの属性を設定でき、これらの Aware インターフェイスを実装することでコンテキストを取得できます。オブジェクトがコンテキスト全体の情報を取得したい場合は、ApplicationContextAware
このインスタンスを外部から取得した後、 getApplicationContext メソッドを通じて直接コンテキストを取得できます。
//实现上下文的aware接口
@Component
public class First implements ApplicationContextAware {
private ApplicationContext applicationContext;
//实现方法
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
//将值返回
private ApplicationContext getApplicationContext(){
return applicationContext;
}
}
Aware の役割は次のとおりです。Springコンテナによって作成された Bean オブジェクトが特定の操作を実行しているときに、コンテナ内の他のオブジェクトが必要な場合、その時点でオブジェクトは現在のニーズを満たすために Aware インターフェイスを実装できます。
Aware の設定に加えて、いくつかのBeanPostProcessor
プロセッサを設定することもできます。最初に BeanPostProcessor.before() メソッドが呼び出され、最後に BeanPostProcessor.after() メソッドが呼び出されます。これら 2 つのプロセッサ間では、前述の init 初期化メソッドが使用されます。が実行されます。これら 2 つのハンドラーのメソッドは次のとおりです。
//前置处理器
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
//后置处理器
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
4. 次に、完全な Bean オブジェクトを生成し、それを concurrentHashMap の 1 次キャッシュに格納して、マップ内のインスタンスを直接取得します。これは大まかなIOCプロセスです。
5. Springコンテナでは、Spring Beanオブジェクトが通常オブジェクトとコンテナオブジェクトに分かれており、コンテナオブジェクトは組み込みオブジェクトに相当し、通常オブジェクトはカスタムオブジェクトに相当します。組み込みオブジェクトは、事前にロードする必要がある一部のクラスに似ており、明示的にカスタマイズされないオブジェクトもあります。カスタム オブジェクトは、XML または注釈でカスタマイズされたインスタンス オブジェクトです。
2. 拡張ポイントのメカニズム
IOC の全体的なプロセスは上記で大まかに説明されています。上記のコンポーネントに加えて、いくつかのポストプロセッサ、主に BeanFactoryPostProcessor および BeanPostProcessor プロセッサなど、いくつかの拡張ポイント メカニズムがあります。前者は主に beanDefinition 情報を拡張するために使用されます。はい、後者PostProcessor
はaop などの Bean 情報を強化するために使用できます。
BeanFactoryPostProcessorのインタフェース情報は以下の通りであり、このインタフェースは関数型インタフェースであり、Springコンテナではこのインタフェースがコレクションの形で存在し、このBeanファクトリのポストプロセッサが複数存在することになる。
//函数式接口
@FunctionalInterface
public interface BeanFactoryPostProcessor {
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
BeanPostProcessorのインタフェース情報は以下の通りで、ポストプロセッサのプリメソッドとポストプロセッサのポストメソッドと呼ばれる2つのメソッドが存在します。
public interface BeanPostProcessor {
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
次の BeanFactoryPostProcessor の例のように、このインターフェースを実装した後、内部の postProcessBeanFactory メソッドを書き換えることで beanDefination を取得し、この Bean 定義の拡張関数を作成できます。Bean の説明を設定したり、説明情報を追加したり、クラスロードとして設定するかどうかなどを設定できます。
/**
* 自定义bean工厂的后置处理器类
* 对beanDefinition做增强功能
* @author zhenghuisheng
* @date : 2023/5/25
*/
@Component
public class BeanFactoryTest implements BeanFactoryPostProcessor {
/**
* @param beanFactory : beanDefinition
* @throws BeansException
*/
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition("taskTestController");
taskTestController.setDescription("hhhhha");
System.out.println("=================taskTestController增强完成!");
System.out.println(taskTestController.getDescription());
}
}
3. コアメソッドのリフレッシュ
なお、xmlメソッドの構築方法とアノテーションの構築方法には重要なメソッドがあり、refresh
それらの構築方法は以下のとおりです。
//XML的方式获取到上下文
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException {
super(parent); // 初始化父类 ,获得xml路径资源解析器
setConfigLocations(configLocations); // 通过环境变量解析 xml路径
if (refresh) {
refresh(); // 这个方法时spring是最终要的一个方法,甚至体系整个ioc的声明周期
}
}
//注解的方式获取上下文
public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
//调用构造函数
this();
//注册我们的配置类
register(annotatedClasses);
//IOC容器刷新接口
refresh();
}
このリフレッシュメソッドはSpringの最下層全体の中核ともいえるメソッドであり、そのコードは以下の通りですので、以下の記事でのSpringのソースコードの解析の続きは基本的に以下のメソッドを中心に進めていきます。
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//1:准备刷新上下文环境
prepareRefresh();
//2:获取告诉子类初始化Bean工厂 不同工厂不同实现
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
//3:对bean工厂进行填充属性
prepareBeanFactory(beanFactory);
try {
// 第四:留个子类去实现该接口,bean工厂的后置处理器
postProcessBeanFactory(beanFactory);
// 调用我们的bean工厂的后置处理器.
//1. 会在此将class扫描成beanDefinition 2.bean工厂的后置处理器调用
invokeBeanFactoryPostProcessors(beanFactory);
// 注册我们bean的后置处理器
registerBeanPostProcessors(beanFactory);
// 初始化国际化资源处理器.
initMessageSource();
// 创建事件多播器
initApplicationEventMulticaster();
// 这个方法同样也是留个子类实现的springboot也是从这个方法进行启动tomcat的.
onRefresh();
//把我们的事件监听器注册到多播器上
registerListeners();
// 实例化我们剩余的单实例bean.
finishBeanFactoryInitialization(beanFactory);
// 最后容器刷新 发布刷新事件(Spring cloud也是从这里启动的)
finishRefresh();
}
}
}
4. BeanFactory と FactoryBean の違い
BeanFactoy は大きなコンテナ インターフェイスであり、ApplicationContext はその固有の実装インターフェイスであり、主にオブジェクトの作成と取得に使用されます。この BeanFacory を使用する場合は、Spring によって制御および管理される完全な作成プロセスに従う必要があります。
FactoryBean は、特定のオブジェクトを返すために getObject を呼び出すだけで済みます。オブジェクト作成プロセス全体はユーザーによって制御され、より柔軟で、特殊な Bean やより複雑な Bean の作成にも使用できます。この FactoryBean オブジェクトには、 IsSingleton、getObject、getObjectType という3 つの主なメソッドがあります。これら 3 つのメソッドのうち、最も頻繁に使用されるメソッドは getObject です。
preInstantiateSingletonsのメソッドでは、Bean がファクトリー Bean であるかどうかを判定するために次のような判定ロジックがあります。
//是不是工厂bean
if (isFactoryBean(beanName)) {
...}
オブジェクトが取得されない場合、オブジェクトは事前に作成されていないため、オブジェクトの具体的な値は見えません。次に、コードの一部を見てみましょう。エンティティ クラスがこの Bean ファクトリを実装し、教師オブジェクトと生徒オブジェクトのエンティティ クラスを作成します。
/**
* @author zhenghuisheng
* @date : 2023/5/29
*/
@Component
public class TeacherFactory implements FactoryBean<Teacher> {
@Override
public Teacher getObject() throws Exception {
return new Teacher();
}
@Override
public Class<?> getObjectType() {
return Teacher.class;
}
}
次に、このファクトリ内のエンティティクラスをコンテキスト経由で取得し、次のように結果を確認します。
//获取上下文容器对象
ApplicationContext context = new AnnotationConfigApplicationContext(MainCofig.class);
//返回的是学生对象
Student stu = (Student)context.getBean("teacherFactory");
//返回的是老师的对象,不能强转成学生,代码运行会报错
Student stu = (Student)context.getBean("&teacherFactory");
//获取上下文容器对象
ApplicationContext context = new AnnotationConfigApplicationContext(MainCofig.class);
//返回的是学生对象
Student stu = (Student)context.getBean("teacherFactory");
//返回的是老师的对象,不能强转成学生,代码运行会报错
Student stu = (Student)context.getBean("&teacherFactory");
&
つまり、この FactoryBean によって返されるオブジェクトは現在のオブジェクトではなく、元の値もthis を通じて取得できるため、値の取得方法がより柔軟になります。この FactoryBean を使用する意味は、Bean のライフサイクルに完全に準拠することなく、対応する Bean オブジェクトを取得するために getObject メソッドを書き換えるだけで済むことです。