序文
Spring は、プロジェクト コンポーネント間の依存関係を分離するのに役立つ、非常に強力な Inversion of Control (IOC) フレームワークです。したがって、Spring コンテナによる Bean の登録と管理は、その中核的な内容であり、最も重要な機能部分であると言えます。
したがって、この記事では主に Spring コンテナに Bean を登録するさまざまな方法を紹介します。
Spring IOC コンテナに Bean を登録する 7 つの方法
1. XML 方式 (古い方式で、現在はほとんど使用されていません)
リソースのクラスパスにファイル「beans.xml」を作成します。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="person" class="com.sayabc.boot2demo1.bean.Person">
<property name="name" value="cuzz"></property>
<property name="age" value="18"></property>
</bean>
</beans>
次に、メイン関数は ClassPathXmlApplicationContext を使用して Spring コンテナーを開始します。
public static void main(String[] args) {
ApplicationContext applicationContext = createNewApplicationContext();
Person bean = applicationContext.getBean(Person.class);
System.out.println(bean); //Person(name=fsx, age=18)
}
//创建、启动Spring容器
private static ApplicationContext createNewApplicationContext() {
return new ClassPathXmlApplicationContext("classpath:beans.xml");
}
このことから、この Bean が Spring コンテナーに直接配置されていることがわかります。
2. @Configuration @Bean 設定クラスのメソッド
構成クラスを作成する
/**
* @author fangshixiang
* @description
* @date 2019-01-30 14:28
*/
@Configuration //该注解就相当于一个xml配置文件
public class MainConfig {
@Bean(value = "person")
public Person person() {
return new Person("fsx", 18);
}
}
このように、AnnotationConfigApplicationContext を使用してコンテナを起動します。
//创建、启动Spring容器
private static ApplicationContext createNewApplicationContext() {
return new AnnotationConfigApplicationContext(MainConfig.class);
}
効果は上記と同じで、コンテナにBeanを入れることもできます。
@Bean が値を指定しない場合、Bean の ID はデフォルトでメソッド名の名前になります。init-method メソッドと destroy-method メソッドを指定できます。ただし、注意してください。単一インスタンスの Bean コンテナは Bean の init メソッドと destroy メソッドを管理しますが、マルチインスタンス Bean コンテナは作成と初期化を支援するだけであり、Spring はそれ以降は気にしません。
@Bean 関連の注意: @Scope、@Lazy など。
単一インスタンス Bean の場合、IOC コンテナの開始時に Bean がすぐに作成され、その後のすべての取得はコンテナから取得されます (もちろん @Lazy アノテーションを追加して単一インスタンス Bean を遅延ロードすることもできます) 。マルチインスタンスBeanの場合、Bean取得時に一度だけBeanが作成されます。
3. @ComponentScan を使用して登録されたコンポーネントをスキャンします
アノテーションがマークされている限り、@Controller @Service @Repository @component のようにスキャンできます。
このアノテーションを構成クラスに追加します。
@Configuration //该注解就相当于一个xml配置文件
@ComponentScan("com.fsx")
public class MainConfig {
}
コンポーネント コンポーネントをエンティティ クラスに追加して、スキャンできるようにします。
@Component
public class Person {
private String name;
private Integer age;
}
コンポーネント コンポーネントをエンティティ クラスに追加して、スキャンできるようにします。
@Component
public class Person {
private String name;
private Integer age;
}
Spring コンテナーの開始の出力を確認できます。
//创建、启动Spring容器
private static ApplicationContext createNewApplicationContext() {
return new AnnotationConfigApplicationContext(MainConfig.class);
}
输出为:
Person(name=null, age=null)
注: このスキャン方法では、空のコンストラクターがあることを確認してください。そうでない場合は、エラーが報告されます。。。
@ComponentScan には、より正確なスキャンを実現するための多くのプロパティがあります。例:basePackageClasses、includeFilters、excludeFilters、lazyInit、useDefaultFilters など。includeFilters を有効にするには、useDefaultFilters=false が必要であることに注意してください。それ以外の場合、デフォルトではすべてのフィルタがスキャンされます。
FilterType で列挙されたフィルター タイプは、注釈、正規表現などの正確な一致を実現できます。もちろん、フィルターするインターフェイスの独自の実装をカスタマイズすることもできます。これは非常に強力です。
4. @Conditionalは条件に従ってBeanをSpringに途中登録する
/*
* @author Phillip Webb
* @author Sam Brannen
* @since 4.0
* @see Condition
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
/**
* All {@link Condition}s that must {@linkplain Condition#matches match}
* in order for the component to be registered.
*/
Class<? extends Condition>[] value();
}
このインターフェイスは Spirng4 によって提供されます。このインターフェイスは、条件に従って Bean を登録するために SpringBoot の下部で広範囲に使用されます。
アノテーションの属性値から判断すると、Condition条件を渡すことができるので、システム独自のものを渡すこともできますし、このインターフェースを自分で実装して必要に応じてBeanを登録することもできます。SpringBoot プロジェクト 中国では、このインターフェイスの実装が多数あります。この記事では、独自の実装を使用して、条件に基づいて Bean を登録する機能を確認します。
たとえば、次の関数を実装したいとします:
システムが Windows の場合は、「bill」をコンテナに挿入し、システムが Linux の場合は、「linus」をコンテナに挿入します。
public class WindowCondition implements Condition{
/**
* @param context 判断条件
* @param metadata 注释信息
* @return boolean
*/
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();
String property = environment.getProperty("os.name");
if (property.contains("Windows")) {
return true;
}
return false;
}
}
context には次のメソッドもあることに注意してください。
// 能获取ioc使用的beanfactory
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
// 能获取到类加载器
ClassLoader classLoader = context.getClassLoader();
// 获取到环境变量
Environment environment = context.getEnvironment();
// 获取到Bean定义的注册类
BeanDefinitionRegistry registry = context.getRegistry();
LinuxConditionクラスの記述方法を簡単に説明します。構成クラスは次のとおりです。
@Configuration
public class MainConfig2 {
@Conditional({WindowCondition.class})
@Bean("bill")
public Person person01() {
return new Person("Bill Gates", 60);
}
@Conditional({LinuxCondition.class})
@Bean("linux")
public Person person02() {
return new Person("linus", 45);
}
}
実行: (テスト中に実行時パラメータを設定できます: -Dos.name=linux)
结果我们会发现,注册的Bean已经按照我们的条件去注册了
注: @Conditonal アノテーションはメソッドだけでなくクラスにもアノテーションを付けることができます。アノテーションが構成クラスにある場合、それが有効にならないと、構成クラスも有効になりません。
5. @Improt はコンポーネントを迅速にインポートします
@Improt の高速インポートは特に重要であり、SpringBoot の自動アセンブリ プロセスにおいて非常に重要な役割を果たします。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
/**
* {@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar}
* or regular component classes to import.
*/
Class<?>[] value();
}
@Import では、サードパーティのパッケージや自分で作成したクラスをインポートでき、より便利です。ID はデフォルトで完全なクラス名になります (これには注意する必要があります)。
たとえば、新しいクラス Color を作成します。
public class Color {
}
構成クラスについて:
@Import({Color.class})
@Configuration
public class MainConfig2 {}
6、ImportSelectorとImportBeanDefinitionRegistrar
インポートセレクター:
public class MyImportSelector implements ImportSelector{
// 返回值就导入容器组件的全类名
// AnnotationMetadata:当前类标注的@Import注解类的所有注解信息
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[] {"com.cuzz.bean.Car"};
}
}
構成クラスで、@Import 経由でインポートします。
@Import({Color.class, MyImportSelector.class})
@Configuration
public class MainConfig2 {}
このようにして、Car クラスがインポートされたことがわかります。
ImportBeanDefinitionRegistrar:
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/**
* @param importingClassMetadata 当前类的注解信息
* @param registry 注册类
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 查询容器
boolean b = registry.containsBeanDefinition("com.cuzz.bean.Car");
// 如果有car, 注册一个汽油类
if (b == true) {
// 需要添加一个bean的定义信息
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Petrol.class);
// 注册一个bean, 指定bean名
registry.registerBeanDefinition("petrol", rootBeanDefinition);
}
}
}
構成クラス:
@Import({Color.class, MyImportSelector.class, MyImportBeanDefinitionRegistrar.class})
@Configuration
public class MainConfig2 {}
たとえば、Spring Boot で使用する場合:
次のコードからわかるように、アノテーション @ServletComponentScan の解析。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(ServletComponentScanRegistrar.class)
public @interface ServletComponentScan {}
たとえば、Spring Boot で使用する場合:
次のコードからわかるように、アノテーション @ServletComponentScan の解析。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(ServletComponentScanRegistrar.class)
public @interface ServletComponentScan {}
これは、ServletComponentScanRegistrar によって登録および解析されます。このクラスを見てください。
class ServletComponentScanRegistrar implements ImportBeanDefinitionRegistrar {}
これは標準の ImportBeanDefinitionRegistrar です。次に、registerBeanDefinitions メソッドで、アノテーション付きポストプロセッサの追加など、さまざまな作業を行いました。
7. FactoryBean を使用してコンポーネントを登録する
ファクトリービーンズ。Spring と統合するサードパーティ フレームワークのほとんどはこのインターフェイスの実装によって実現されるため、この Bean は非常に重要です。
public interface FactoryBean<T> {
T getObject() throws Exception;
Class<?> getObjectType();
default boolean isSingleton() {
return true;
}
}
たとえば、この Bean インターフェイスを自分で実装します。
public class ColorFactoryBean implements FactoryBean<Color> {
// 返回一个Color对象
@Override
public Color getObject() throws Exception {
return new Color();
}
@Override
public Class<?> getObjectType() {
return Color.class;
}
// 是否为单例
@Override
public boolean isSingleton() {
return true;
}
}
@Bean を通じてコンテナに注入します。
@Bean
public ColorFactoryBean colorFactoryBean() {
return new ColorFactoryBean();
}
テストを受けてください:
@Test
public void test05() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
Object bean = applicationContext.getBean("colorFactoryBean");
// 工厂bean调用的是getClass()方法
System.out.println("colorFactoryBean的类型是: " + bean.getClass());
}
これを出力すると、この時点で Bean によって呼び出されたメソッドが getObjectType メソッドであることがわかります。出力は次のようになります: class com.fsx.boot2demo1.bean.Color
FactoryBean 自体を取得する必要がある場合は、ID の前に「&」ロゴを追加できます。
Object bean2 = applicationContext.getBean("&colorFactoryBean");
この時点の出力は次のとおりです: com.fsx.boot2demo1.bean.ColorFactoryBean
質問の提起: なぜ @Bean を直接使用せず、FactoryBean を使用するのでしょうか?
Spring には 2 種類の Bean があり、1 つは一般的な Bean で、もう 1 つはファクトリー Bean、つまり FactoryBean です。FactoryBean は、返されるオブジェクトが指定されたクラスのインスタンスではなく、FactoryBean の getObject メソッドによって返されるオブジェクトであるという点で通常の Bean とは異なります。作成されたオブジェクトがシングルトンであるかどうかは、isSingleton の戻り値によって決まります。
公式説明:
FactoryBean は通常、より複雑な Bean を作成するために使用されます。一般的な Bean は xml で直接設定できます。ただし、Bean の作成プロセスに他の多くの Bean や複雑なロジックが含まれる場合、xml で設定するのはより困難です。 FactoryBean の使用を検討できます。
私の説明:
簡単に言えば、初期化プロセス中に多くのことを行う必要がある複雑な Bean (MyBatis の SqlSessionFactoryBean など) を処理するために使用され、それによって内部実装と呼び出し元に優しいソリューションが保護されます。 /ユーザープラン。
この種の Bean を XML を使用して構成する場合、それはほぼ不可能です。アノテーションを使用して @Bean を駆動する場合、可能ではありますが、呼び出しには明らかに非常に不親切です (MyBatis を使用する場合、初期化中にそれが何をしなければならないのか知りたくないからです)。したがって、この方法を使用するのが最もエレガントな解決策です。Springは1.0からこのインターフェースをサポートしています。これは素晴らしいです~
まとめ
Spring では、さまざまなニーズを満たすために、Bean をコンテナに登録するためのさまざまな方法が提供されています。各メソッドには独自の使用シナリオがあります。たとえば、@Bean が最も一般的に使用されており、@Import インポート Bean は SpringBoot の自動アセンブリで広く使用されています。
成熟したフレームワークでは、閉じられた不完全な機能を提供することは非常にタブーです。Spring は明らかに「開閉原理」に優れており、深く学ぶ価値があります。