Spring 주석 기반 개발---Spring Ioc 컨테이너에 Bean을 등록하는 7가지 방법

머리말

Spring은 프로젝트 구성 요소 간의 종속성을 분리하는 데 도움이 되는 매우 강력한 IOC(Inversion of Control) 프레임워크입니다. 따라서 Spring 컨테이너에 의한 Bean 등록 및 관리는 Spring 컨테이너의 핵심 내용이자 가장 중요한 기능적 부분이라 할 수 있다.

따라서 이 기사에서는 주로 다음을 소개합니다. Spring 컨테이너에 Bean을 등록하는 다양한 방법

Spring IOC 컨테이너에 빈을 등록하는 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 방법을 지정할 수 있습니다. 그러나 참고하세요: 단일 인스턴스 빈 컨테이너는 빈의 init 및 destroy 메소드를 관리하지만 다중 인스턴스 빈 컨테이너는 생성 및 초기화에만 도움이 되며 Spring은 그 이후에는 이에 대해 신경 쓰지 않습니다.

@Bean 관련 관심: @Scope, @Lazy 등

단일 인스턴스 Bean인 경우 IOC 컨테이너가 시작될 때 Bean이 즉시 생성되고 이후의 모든 획득은 컨테이너에서 가져옵니다. (물론 단일 인스턴스 Bean을 지연 로딩하도록 @Lazy 주석을 추가할 수도 있습니다.) . 다중 인스턴스 Bean인 경우 해당 Bean을 획득하면 해당 Bean이 한 번 생성됩니다.

3. @ComponentScan을 사용하여 등록된 구성 요소를 검색합니다.

주석이 표시되어 있는 한 다음과 같이 검색할 수 있습니다. @Controller @Service @Repository @comComponent

구성 클래스에 다음 주석을 추가합니다.

@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, includeFilters,lazyInit, useDefaultFilters 등. includeFilters를 적용하려면 useDefaultFilters=false가 필요합니다. 그렇지 않으면 기본값은 모든 항목을 검색하는 것입니다.

FilterType에 의해 열거된 필터 유형은 주석, 정규식 등의 정확한 일치를 달성할 수 있습니다. 물론 필터링할 인터페이스를 직접 구현하여 사용자 정의할 수도 있는데 이는 매우 강력합니다.

4. @Conditional은 조건에 따라 Spring 중기에 Bean을 등록합니다.

 /*
 * @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 하단에서 광범위하게 사용됩니다.

Annotation의 속성 값으로 판단하면 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 클래스가 임포트되었음을 ​​알 수 있습니다.

ImportBean정의등록기관:

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에는 두 가지 유형의 Bean이 있습니다. 하나는 일반 Bean이고 다른 하나는 Factory Bean인 FactoryBean입니다. FactoryBean은 반환된 객체가 지정된 클래스의 인스턴스가 아니라 FactoryBean의 getObject 메소드에 의해 반환된 객체라는 점에서 일반 Bean과 다릅니다. 생성된 객체가 싱글톤인지 여부는 isSingleton의 반환에 의해 결정됩니다.

공식적인 설명:
FactoryBean은 일반적으로 보다 복잡한 Bean을 생성하는 데 사용됩니다. 일반 Bean은 xml을 사용하여 직접 구성할 수 있지만, Bean 생성 프로세스에 다른 Bean이 많고 복잡한 로직이 포함되는 경우 xml로 구성하기가 더 어렵습니다. FactoryBean 사용을 고려할 수 있습니다.

내 설명:
간단히 말해서 초기화 프로세스 중에 많은 작업을 수행해야 하는 복잡한 빈(예: MyBatis의 SqlSessionFactoryBean 등)을 처리하는 데 사용됩니다. 이를 통해 내부 구현과 호출자에게 친숙한 솔루션을 보호합니다. /사용자 계획.
이런 Bean을 xml을 이용하여 구성한다면 거의 불가능하다. @Bean을 구동하기 위해 주석을 사용하는 경우, 가능하더라도 호출하기에는 매우 비우호적입니다(MyBatis를 사용할 때 초기화 중에 수행해야 하는 작업을 알고 싶지 않기 때문입니다). 따라서 이 방법을 사용하는 것이 가장 우아한 솔루션입니다. Spring은 1.0부터 이 인터페이스를 지원했는데, 정말 훌륭하네요~

요약
Spring은 다양한 요구 사항을 충족하기 위해 컨테이너에 Bean을 등록하는 다양한 방법을 제공합니다. 각 방법에는 고유한 사용 시나리오가 있습니다. 예를 들어 @Bean이 가장 일반적으로 사용되며 @Import import Bean은 SpringBoot의 자동 어셈블리에 널리 사용됩니다.

성숙한 프레임워크는 폐쇄적이고 불완전한 기능을 제공하는 것을 매우 금기시합니다. Spring은 분명히 "열림 및 닫힘 원리"에 매우 능숙하며 깊이 학습할 가치가 있습니다.

Supongo que te gusta

Origin blog.csdn.net/tian830937/article/details/132922649
Recomendado
Clasificación