Spring annotation driven notes - Lei Fengyang source level explanation

Spring annotation driven development (I)

Introduction - annotation driven development

Component registration - @ configuration & @ bean registers components in the container

Component registration - @ ComponentScan - auto scan components & specify scan rules

Component registration - Custom TypeFilter specifies filtering rules

Component registration - @ Scope - set component Scope

Video address of station B: https://www.bilibili.com/video/BV1gW411W7wy

Introduction - annotation driven development

When we are still using Spring, Spring MVC and Mybatis to integrate development, we need to use a large number of xml files for configuration; However, after the rise of Springboot and SpringCloud, it will be very necessary to learn the annotation driven and principle of Spring; Because a large number of annotations will be used for configuration in Springboot and SpringCloud; When we master the annotation driver of Spring, it will be more relaxed when we are learning the Spring boot and Spring cloud framework; Let's start the learning journey of Spring annotation driven development!

Component registration - @ configuration & @ bean registers components in the container

Create a Maven project: Spring annotation

Import the Spring context jar package – this is all the jar packages that the Spring core environment depends on

<?xml version="1.0" encoding="UTF-8"?>;
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">;
    <modelVersion>;4.0.0</modelVersion>;

    <groupId>;com.ldc</groupId>;
    <artifactId>;spring-annotation</artifactId>;
    <version>;1.0-SNAPSHOT</version>;

    <dependencies>;
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->;
        <dependency>;
            <groupId>;org.springframework</groupId>;
            <artifactId>;spring-context</artifactId>;
            <version>;4.3.12.RELEASE</version>;
        </dependency>;
    </dependencies>;

</project>;
  • xml file configuration mode

    First use Spring in the way we configured previously:

    First, there is a Person class:

public class Person {
    private String name;
    private Integer age;

    public Person() {
    }

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

Let's write another Spring xml configuration file:

<?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.ldc.bean.Person">;
		<property name="age" value="18">;</property>;
		<property name="name" value="Zhang San">;</property>;
	</bean>;
</beans>;

Test class to test:

public class MainTest {
    public static void main(String[]args){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
        Person person = (Person) applicationContext.getBean("person");
        System.out.println(person);
    }
}

The output result is:

Person{name = 'Zhang San', age=18}

  • Annotation method:
    First, we write a configuration class: equivalent to an xml configuration file
/**
 * @Description The configuration class is the same as the previous configuration file
 */
@Configuration //Tell Spring that this is a configuration class
public class MainConfig {

    //It is equivalent to the < bean > tag in the xml configuration file, which tells the container to register a bean
    //Previously, the < bean > tag in the xml file had the class type of bean, so the type of annotation now is, of course, the type of return value
    //Previously, the < bean > tag in the xml file had the id of the bean. Now, the default annotation method is to use the method name as the id of the bean
    @Bean
    public Person person() {
        return new Person("lisi",20);
    }

}

Now let's test:

public class MainTest {
    public static void main(String[]args){
        /**
         * Here is an AnnotationConfigApplicationContext object, which is the ClassPathXmlApplicationContext object of new
         * The constructor of is passed the location of the configuration file, and now the constructor of the AnnotationConfigApplicationContext object is passed the location of the configuration file
         * Type of configuration class
         */
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
        Person person = applicationContext.getBean(Person.class);
        System.out.println(person);
    }
}

The test results are as follows:

Person{name='lisi', age=20}

We can also use some methods of ApplicationContext to get some information about the bean in the container. For example, we can get the name of the bean Person in the IOC container, which is also equivalent to the id attribute in the tag in the xml configuration file;

public class MainTest {
    public static void main(String[]args){
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
        Person person = applicationContext.getBean(Person.class);
        System.out.println(person);

        //We can get the bean definition information
        String[] namesForType = applicationContext.getBeanNamesForType(Person.class);
        //IDEA enter the shortcut iter to generate an enhanced for
        for (String name : namesForType) {
            System.out.println(name);
        }
    }
}

The test results are as follows:

Person{name='lisi', age=20} person

Spring annotation takes the configured method name as the default id of the Bean by default. If we don't want the method name as the Bean id, we can specify it in the value attribute of @ Bean annotation:

@Configuration //Tell Spring that this is a configuration class
public class MainConfig {

    //It is equivalent to the < bean > tag in the xml configuration file, which tells the container to register a bean
    //Previously, the < bean > tag in the xml file had the class type of bean, so the type of annotation now is, of course, the type of return value
    //The default method is to annotate the bean id with the bean id in the xml
    @Bean(value = "person")//Through this value attribute, you can specify the id of the bean in the IOC container
    public Person person01() {
        return new Person("lisi",20);
    }

}

Let's run this test class again:

public class MainTest {
    public static void main(String[]args){
        /**
         * Here is an AnnotationConfigApplicationContext object, which is the ClassPathXmlApplicationContext object of new
         * The constructor of is passed the location of the configuration file, and now the constructor of the AnnotationConfigApplicationContext object is passed the location of the configuration file
         * Type of configuration class
         */
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
        Person person = applicationContext.getBean(Person.class);
        System.out.println(person);

        //We can get the bean definition information
        String[] namesForType = applicationContext.getBeanNamesForType(Person.class);
        for (String name : namesForType) {
            System.out.println(name);
        }
    }
}

Now the test results are as follows: the name of the Bean in the IOC container is the value of the value attribute of the @ Bean annotation, rather than the default id is the method name person01

Person{name='lisi', age=20} person

- @ component scan - @ component auto scan rules

In the way of xml file configuration, we can configure it as follows:

    <!-- Package scanning, as long as it is marked@Controller,@Service,@Repository´╝î@Component -->
    <context:component-scan base-package="com.ldc"/>

In the past, package scanning was written in the xml configuration file. Now we can write package scanning in the configuration class:

/**
 * @Description The configuration class is the same as the previous configuration file
 */
@Configuration //Tell Spring that this is a configuration class
@ComponentScan(value = "com.ldc")//It is equivalent to < context: component scan base package = "com. LDC" / > in the xml configuration file
public class MainConfig {

    //It is equivalent to the < bean > tag in the xml configuration file, which tells the container to register a bean
    //Previously, the < bean > tag in the xml file had the class type of bean, so the type of annotation now is, of course, the type of return value
    //Previously, the < bean > tag in the xml file had the id of the bean. Now, the default annotation method is to use the method name as the id of the bean
    @Bean(value = "person")//Through this value attribute, you can specify the id of the bean in the IOC container
    public Person person01() {
        return new Person("lisi",20);
    }

}

We created BookController, BookService and BookDao, and added @ Controller, @ Service and @ Repository annotations respectively:

@Controller
public class BookController {
}
@Service
public class BookService {
}
@Repository
public class BookDao {
}

We can introduce junit's jar package for testing:

    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>

Let's do the unit test:

    @Test
    public void test01() {
        //Read configuration class
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
        String[] definitionNames = applicationContext.getBeanDefinitionNames();
        for (String name : definitionNames) {
            System.out.println(name);
        }
    }

The test results are as follows: in addition to some components to be assembled by the IOC container, there are also components assembled by ourselves

org.springframework.context.annotation.internalConfigurationAnnotationProcessor org.springframework.context.annotation.internalAutowiredAnnotationProcessor org.springframework.context.annotation.internalRequiredAnnotationProcessor org.springframework.context.annotation.internalCommonAnnotationProcessor org.springframework.context.event.internalEventListenerProcessor org.springframework.context.event.internalEventListenerFactory mainConfig bookController bookDao bookService person

From the above test results, we can find that the main configuration class MainConfig is also a component in the IOC container and is also included in the management of the IOC container:

/**
 * @Description The configuration class is the same as the previous configuration file
 */
@Configuration //Tell Spring that this is a configuration class
@ComponentScan(value = "com.ldc")//It is equivalent to < context: component scan base package = "com. LDC" / > in the xml configuration file
public class MainConfig {

    //It is equivalent to the < bean > tag in the xml configuration file, which tells the container to register a bean
    //Previously, the < bean > tag in the xml file had the class type of bean, so the type of annotation now is, of course, the type of return value
    //Previously, the < bean > tag in the xml file had the id of the bean. Now, the default annotation method is to use the method name as the id of the bean
    @Bean(value = "person")//Through this value attribute, you can specify the id of the bean in the IOC container
    public Person person01() {
        return new Person("lisi",20);
    }

}

From the @ Configuration annotation point, we can find that the annotation of @ Component is also marked on the annotation, which is also included in the IOC container as a Component:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {<!-- -->

	/**
	 * Explicitly specify the name of the Spring bean definition associated
	 * with this Configuration class. If left unspecified (the common case),
	 * a bean name will be automatically generated.
	 * &lt;p&gt;The custom name applies only if the Configuration class is picked up via
	 * component scanning or supplied directly to a {@link AnnotationConfigApplicationContext}.
	 * If the Configuration class is registered as a traditional XML bean definition,
	 * the name/id of the bean element will take precedence.
	 * @return the specified bean name, if any
	 * @see org.springframework.beans.factory.support.DefaultBeanNameGenerator
	 */
	String value() default "";

}

On the @ ComponentScan annotation, we can also specify which packages to exclude or which packages to include for management: a Filter [] array is passed inside We click the excludeFilters method to get the @ Filter annotation:

	@Retention(RetentionPolicy.RUNTIME)
	@Target({})
	@interface Filter {

		/**
		 * The type of filter to use.
		 * <p>Default is {@link FilterType#ANNOTATION}.
		 * @see #classes
		 * @see #pattern
		 */
		//This is the rule to exclude: whether to exclude by annotation, class or regular expression
		FilterType type() default FilterType.ANNOTATION;

		/**
		 * Alias for {@link #classes}.
		 * @see #classes
		 */
		@AliasFor("classes")
		Class<?>[] value() default {};

		/**
		 * The class or classes to use as the filter.
		 * <p>The following table explains how the classes will be interpreted
		 * based on the configured value of the {@link #type} attribute.
		 * <table border="1">
		 * <tr><th>{@code FilterType}</th><th>Class Interpreted As</th></tr>
		 * <tr><td>{@link FilterType#ANNOTATION ANNOTATION}</td>
		 * <td>the annotation itself</td></tr>
		 * <tr><td>{@link FilterType#ASSIGNABLE_TYPE ASSIGNABLE_TYPE}</td>
		 * <td>the type that detected components should be assignable to</td></tr>
		 * <tr><td>{@link FilterType#CUSTOM CUSTOM}</td>
		 * <td>an implementation of {@link TypeFilter}</td></tr>
		 * </table>
		 * <p>When multiple classes are specified, <em>OR</em> logic is applied
		 * &amp;mdash; for example, "include types annotated with {@code @Foo} OR {@code @Bar}".
		 * <p>Custom {@link TypeFilter TypeFilters} may optionally implement any of the
		 * following {@link org.springframework.beans.factory.Aware Aware} interfaces, and
		 * their respective methods will be called prior to {@link TypeFilter#match match}:
		 * <ul>
		 * <li>{@link org.springframework.context.EnvironmentAware EnvironmentAware}</li>
		 * <li>{@link org.springframework.beans.factory.BeanFactoryAware BeanFactoryAware}
		 * <li>{@link org.springframework.beans.factory.BeanClassLoaderAware BeanClassLoaderAware}
		 * <li>{@link org.springframework.context.ResourceLoaderAware ResourceLoaderAware}
		 * </ul>
		 * <p>Specifying zero classes is permitted but will have no effect on component
		 * scanning.
		 * @since 4.2
		 * @see #value
		 * @see #type
		 */
		@AliasFor("value")
		Class<?>[] classes() default {};

		/**
		 * The pattern (or patterns) to use for the filter, as an alternative
		 * to specifying a Class {@link #value}.
		 * <p>If {@link #type} is set to {@link FilterType#ASPECTJ ASPECTJ},
		 * this is an AspectJ type pattern expression. If {@link #type} is
		 * set to {@link FilterType#REGEX REGEX}, this is a regex pattern
		 * for the fully-qualified class names to match.
		 * @see #type
		 * @see #classes
		 */
		String[] pattern() default {};

	}

At this time, we can configure it as follows:

@Configuration
@ComponentScan(value = "com.ldc",excludeFilters = {
        //Here is an @ Filter annotation array, filtertype Annotation indicates the rule of exclusion: exclude by annotation
        //classes = {Controller.class,Service.class} indicates that the classes marked with these annotations are excluded
        @Filter(type = FilterType.ANNOTATION,classes = {Controller.class,Service.class})
})
public class MainConfig {

    @Bean(value = "person")
    public Person person01() {
        return new Person("lisi",20);
    }

}

Let's test again:

public class IOCTest {

    @Test
    public void test01() {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
        String[] definitionNames = applicationContext.getBeanDefinitionNames();
        for (String name : definitionNames) {
            System.out.println(name);
        }
    }

}

The test results at this time are as follows: at this time, the bookService and bookController components have been excluded and are no longer managed by the IOC container:

org.springframework.context.annotation.internalConfigurationAnnotationProcessor org.springframework.context.annotation.internalAutowiredAnnotationProcessor org.springframework.context.annotation.internalRequiredAnnotationProcessor org.springframework.context.annotation.internalCommonAnnotationProcessor org.springframework.context.event.internalEventListenerProcessor org.springframework.context.event.internalEventListenerFactory mainConfig bookDao person

We can also configure includeFilters: specify which components only need to be included during scanning. When configuring with xml file, we also need to disable the default configuration rules to make the configuration of which components only take effect

<context:component-scan base-package="com.ldc" use-default-filters="false"/>

At this time, we can write as follows:

@Configuration
//excludeFilters = Filter[]; Specify the rules to exclude which components during scanning
//includeFilters = Filter[]; Specify which components only need to be included when scanning
@ComponentScan(value = "com.ldc",includeFilters = {
        //Here is an @ Filter annotation array, filtertype Annotation indicates the rule of exclusion: exclude by annotation
        //classes = {Controller.class} indicates that the classes marked with these annotations are included in the IOC container
        @Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
},useDefaultFilters = false)
public class MainConfig {

    @Bean(value = "person")
    public Person person01() {
        return new Person("lisi",20);
    }

}

Test class:

public class IOCTest {

    @Test
    public void test01() {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
        String[] definitionNames = applicationContext.getBeanDefinitionNames();
        for (String name : definitionNames) {
            System.out.println(name);
        }
    }

}

At this time, the test results are as follows: at this time, it is included according to the annotation. Now, only one bookController is included in the IOC container for management

org.springframework.context.annotation.internalConfigurationAnnotationProcessor org.springframework.context.annotation.internalAutowiredAnnotationProcessor org.springframework.context.annotation.internalRequiredAnnotationProcessor org.springframework.context.annotation.internalCommonAnnotationProcessor org.springframework.context.event.internalEventListenerProcessor org.springframework.context.event.internalEventListenerFactory mainConfig bookController person

@The annotation of ComponentScan can be defined repeatedly: to specify different scanning strategies

We can also use @ ComponentScans to define multiple scanning rules: there is an array of @ ComponentScan rules

@Configuration
//excludeFilters = Filter[]; Specify the rules to exclude brain components during scanning
//includeFilters = Filter[]; Specify which components only need to be included when scanning
@ComponentScans(value = {
    @ComponentScan(value = "com.ldc",includeFilters = {
            //Here is an @ Filter annotation array, filtertype Annotation indicates the rule of exclusion: exclude by annotation
            //classes = {Controller.class} indicates that the classes marked with these annotations are included in the IOC container
            @Filter(type = FilterType.ANNOTATION, classes = {<!-- -->Controller.class})
    },useDefaultFilters = false),
    @ComponentScan(value = "com.ldc")
})
public class MainConfig {

    @Bean(value = "person")
    public Person person01() {
        return new Person("lisi",20);
    }

}

You can also directly configure multiple @ ComponentScan annotations in this way: however, if it is written in this way, it must be supported by java8 or above

@Configuration
//excludeFilters = Filter[]; Specify the rules to exclude brain components during scanning
//includeFilters = Filter[]; Specify which components only need to be included when scanning
@ComponentScan(value = "com.ldc",includeFilters = {
        //Here is an @ Filter annotation array, filtertype Annotation indicates the exclusion rule: match by annotation
        //classes = {Controller.class} indicates that the classes marked with these annotations are included in the IOC container
        @Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
},useDefaultFilters = false)

@ComponentScan(value = "com.ldc")
public class MainConfig {

    @Bean(value = "person")
    public Person person01() {
        return new Person("lisi",20);
    }

}

Component registration - Custom TypeFilter specifies filtering rules

Let's take a look at the filtering rules:

public enum FilterType {

	/**
	 * Filter candidates marked with a given annotation.
	 * @see org.springframework.core.type.filter.AnnotationTypeFilter
	 */
	ANNOTATION,

	/**
	 * Filter candidates assignable to a given type.
	 * @see org.springframework.core.type.filter.AssignableTypeFilter
	 */
	ASSIGNABLE_TYPE,

	/**
	 * Filter candidates matching a given AspectJ type pattern expression.
	 * @see org.springframework.core.type.filter.AspectJTypeFilter
	 */
	ASPECTJ,

	/**
	 * Filter candidates matching a given regex pattern.
	 * @see org.springframework.core.type.filter.RegexPatternTypeFilter
	 */
	REGEX,

	/** Filter candidates using a given custom
	 * {@link org.springframework.core.type.filter.TypeFilter} implementation.
	 */
	CUSTOM

}

We can specify different matching rules in this way:

@Configuration
//excludeFilters = Filter[]; Specify the rules to exclude brain components during scanning
//includeFilters = Filter[]; Specify which components only need to be included when scanning
@ComponentScans(value = {
        @ComponentScan(value = "com.ldc",includeFilters = {
                //Here is an @ Filter annotation array, filtertype Annotation indicates the exclusion rule: match by annotation
                //classes = {Controller.class} indicates that the classes marked with these annotations are included in the IOC container

                // FilterType.ANNOTATION matches according to annotation
                // FilterType.ASSIGNABLE_TYPE matches the given type
                @Filter(type = FilterType.ANNOTATION, classes = {Controller.class}),
                //Match according to the given type
                @Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {BookService.class})
        },useDefaultFilters = false)
})

public class MainConfig {

    @Bean(value = "person")
    public Person person01() {
        return new Person("lisi",20);
    }

}

The test results are as follows:

org.springframework.context.annotation.internalConfigurationAnnotationProcessor org.springframework.context.annotation.internalAutowiredAnnotationProcessor org.springframework.context.annotation.internalRequiredAnnotationProcessor org.springframework.context.annotation.internalCommonAnnotationProcessor org.springframework.context.event.internalEventListenerProcessor org.springframework.context.event.internalEventListenerFactory mainConfig bookController bookService person

bookService component is managed by IOC container again;

The following two are our most commonly used matching rules:

FilterType.ANNOTATION matches filtertype according to annotation ASSIGNABLE_ Type matches the given type

We can also write a filtertype AspectJ expression to match, which is not commonly used; We can also follow the regular expression filtertype Match by regex:

Let's talk about the last one: Custom matching rule filtertype CUSTOM

We can write a matching rule class: MyTypeFilter. This class should implement the TypeFilter interface

public class MyTypeFilter implements TypeFilter {
    /**
     *
     * @param metadataReader  the metadata reader for the target class Read the information of the class currently being scanned
     * @param metadataReaderFactory a factory for obtaining metadata readers You can get the information of any other class
     * @return
     * @throws IOException
     */
    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        //Get the information of the current class annotation
        AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
        //Get the resource information of the current class (such as the path of the class)
        Resource resource = metadataReader.getResource();

        //Get the information of the class currently being scanned
        ClassMetadata classMetadata = metadataReader.getClassMetadata();
        String className = classMetadata.getClassName();
        System.out.println("Through custom matching rules--->"+className);
        return false;
    }
}

At this time, we can use it like this: use filtertype CUSTOM

@Configuration
//excludeFilters = Filter[]; Specify the rules to exclude brain components during scanning
//includeFilters = Filter[]; Specify which components only need to be included when scanning
@ComponentScans(value = {
        @ComponentScan(value = "com.ldc",includeFilters = {
                //Custom matching rules
                @Filter(type = FilterType.CUSTOM, classes = {MyTypeFilter.class})
        },useDefaultFilters = false)
})

public class MainConfig {

    @Bean(value = "person")
    public Person person01() {
        return new Person("lisi",20);
    }

}

The current test results are as follows:

Through customized matching rules - > com ldc. test. Ioctest uses custom matching rules - > com ldc. bean. Person uses custom matching rules - > com ldc. config. Mytypefilter uses custom matching rules - > com ldc. controller. Bookcontroller uses custom matching rules - > com ldc. dao. Bookdao uses custom matching rules - > com ldc. Maintest uses custom matching rules - > com ldc. service. BookService org. springframework. context. annotation. internalConfigurationAnnotationProcessor org. springframework. context. annotation. internalAutowiredAnnotationProcessor org. springframework. context. annotation. internalRequiredAnnotationProcessor org. springframework. context. annotation. internalCommonAnnotationProcessor org. springframework. context. event. internalEventListenerProcessor org. springframework. context. event. internalEventListenerFactory mainConfig person

Because my custom rule class returns false, none of them matches; We can modify it in this way to match the component containing "er" in clsssName to:

public class MyTypeFilter implements TypeFilter {
    /**
     *
     * @param metadataReader  the metadata reader for the target class Read the information of the class currently being scanned
     * @param metadataReaderFactory a factory for obtaining metadata readers You can get the information of any other class
     * @return
     * @throws IOException
     */
    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        //Get the information of the current class annotation
        AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
        //Get the resource information of the current class (such as the path of the class)
        Resource resource = metadataReader.getResource();

        //Get the information of the class currently being scanned
        ClassMetadata classMetadata = metadataReader.getClassMetadata();
        String className = classMetadata.getClassName();
        System.out.println("Through custom matching rules--->"+className);

        if (className.contains("er")) {
            return true;
        }
        return false;
    }
}

Through customized matching rules - > com ldc. test. Ioctest uses custom matching rules - > com ldc. bean. Person uses custom matching rules - > com ldc. config. Mytypefilter uses custom matching rules - > com ldc. controller. Bookcontroller uses custom matching rules - > com ldc. dao. Bookdao uses custom matching rules - > com ldc. Maintest uses custom matching rules - > com ldc. service. BookService org. springframework. context. annotation. internalConfigurationAnnotationProcessor org. springframework. context. annotation. internalAutowiredAnnotationProcessor org. springframework. context. annotation. internalRequiredAnnotationProcessor org. springframework. context. annotation. internalCommonAnnotationProcessor org. springframework. context. event. internalEventListenerProcessor org. springframework. context. event. internalEventListenerFactory mainConfig person myTypeFilter bookController bookService

At this time, the component containing "er" is added to the IOC container; As long as each class in the package scanned will enter this user-defined matching rule for matching;

Component registration - @ Scope - set component Scope

First, there is a configuration class:

@Configuration
public class MainConfig2 {

    //The default is single instance
    @Bean("person")
    public Person person() {
        return new Person();
    }

}

Test method:

    @Test
    public void test02() {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
        String[] definitionNames = applicationContext.getBeanDefinitionNames();
        for (String name : definitionNames) {
            System.out.println(name);
        }
        //The default is single instance
        Person person1 = (Person) applicationContext.getBean("person");
        Person person2 = (Person) applicationContext.getBean("person");
        System.out.println(person1==person2);
    }

The test results are as follows:

org.springframework.context.annotation.internalConfigurationAnnotationProcessor org.springframework.context.annotation.internalAutowiredAnnotationProcessor org.springframework.context.annotation.internalRequiredAnnotationProcessor org.springframework.context.annotation.internalCommonAnnotationProcessor org.springframework.context.event.internalEventListenerProcessor org.springframework.context.event.internalEventListenerFactory mainConfig2 person true

It shows that the instance of this bean is a singleton;

We can use the @ scope annotation to specify the scope of the scope: This is equivalent to specifying the scope = "prototype" attribute in the tag configured in the xml file;

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Scope {

	/**
	 * Alias for {@link #scopeName}.
	 * @see #scopeName
	 */
	@AliasFor("scopeName")
	String value() default "";

	/**
	 * Specifies the name of the scope to use for the annotated component/bean.
	 * &lt;p&gt;Defaults to an empty string ({@code ""}) which implies
	 * {@link ConfigurableBeanFactory#SCOPE_SINGLETON SCOPE_SINGLETON}.
	 * @since 4.2
	 * @see ConfigurableBeanFactory#SCOPE_PROTOTYPE
	 * @see ConfigurableBeanFactory#SCOPE_SINGLETON
	 * @see org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST
	 * @see org.springframework.web.context.WebApplicationContext#SCOPE_SESSION
	 * @see #value
	 */
	@AliasFor("value")
	String scopeName() default "";

	/**
	 * Specifies whether a component should be configured as a scoped proxy
	 * and if so, whether the proxy should be interface-based or subclass-based.
	 * &lt;p&gt;Defaults to {@link ScopedProxyMode#DEFAULT}, which typically indicates
	 * that no scoped proxy should be created unless a different default
	 * has been configured at the component-scan instruction level.
	 * &lt;p&gt;Analogous to {@code &lt;aop:scoped-proxy/&gt;} support in Spring XML.
	 * @see ScopedProxyMode
	 */
	ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;

}

From the comments of the source code, we can know that scopeName can take the following values: the first two are used more. Let's take a look at the values that can be taken from the first two

ConfigurableBeanFactory#SCOPE_PROTOTYPE ConfigurableBeanFactory#SCOPE_SINGLETON org.springframework.web.context.WebApplicationContext#SCOPE_REQUEST org.springframework.web.context.WebApplicationContext#SCOPE_SESSION

We can click the ConfigurableBeanFactory interface to see:

public interface ConfigurableBeanFactory extends HierarchicalBeanFactory, SingletonBeanRegistry {
    String SCOPE_SINGLETON = "singleton";
    String SCOPE_PROTOTYPE = "prototype";

    void setParentBeanFactory(BeanFactory var1) throws IllegalStateException;

    void setBeanClassLoader(ClassLoader var1);

    ClassLoader getBeanClassLoader();

    void setTempClassLoader(ClassLoader var1);

    ClassLoader getTempClassLoader();

    void setCacheBeanMetadata(boolean var1);

    boolean isCacheBeanMetadata();

    void setBeanExpressionResolver(BeanExpressionResolver var1);

    BeanExpressionResolver getBeanExpressionResolver();

    void setConversionService(ConversionService var1);

    ConversionService getConversionService();

    void addPropertyEditorRegistrar(PropertyEditorRegistrar var1);

    void registerCustomEditor(Class&lt;?&gt; var1, Class&lt;? extends PropertyEditor&gt; var2);

    void copyRegisteredEditorsTo(PropertyEditorRegistry var1);

    void setTypeConverter(TypeConverter var1);

    TypeConverter getTypeConverter();

    void addEmbeddedValueResolver(StringValueResolver var1);

    boolean hasEmbeddedValueResolver();

    String resolveEmbeddedValue(String var1);

    void addBeanPostProcessor(BeanPostProcessor var1);

    int getBeanPostProcessorCount();

    void registerScope(String var1, Scope var2);

    String[] getRegisteredScopeNames();

    Scope getRegisteredScope(String var1);

    AccessControlContext getAccessControlContext();

    void copyConfigurationFrom(ConfigurableBeanFactory var1);

    void registerAlias(String var1, String var2) throws BeanDefinitionStoreException;

    void resolveAliases(StringValueResolver var1);

    BeanDefinition getMergedBeanDefinition(String var1) throws NoSuchBeanDefinitionException;

    boolean isFactoryBean(String var1) throws NoSuchBeanDefinitionException;

    void setCurrentlyInCreation(String var1, boolean var2);

    boolean isCurrentlyInCreation(String var1);

    void registerDependentBean(String var1, String var2);

    String[] getDependentBeans(String var1);

    String[] getDependenciesForBean(String var1);

    void destroyBean(String var1, Object var2);

    void destroyScopedBean(String var1);

    void destroySingletons();
}

Let's specify a multi instance:

@Configuration
public class MainConfig2 {
    //singleton: single instance
    //prototype: multi instance
    //Request: create an instance for the same request
    //Session: an instance created by the same session
    @Scope("prototype")
    //The default is single instance
    @Bean("person")
    public Person person() {
        return new Person();
    }

}

Now let's test again:

@Test
public void test02() {
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
    String[] definitionNames = applicationContext.getBeanDefinitionNames();
    for (String name : definitionNames) {
        System.out.println(name);
    }
    //The default is single instance
    Person person1 = (Person) applicationContext.getBean("person");
    Person person2 = (Person) applicationContext.getBean("person");
    System.out.println(person1==person2);
}

The test results at this time are as follows:

org.springframework.context.annotation.internalConfigurationAnnotationProcessor org.springframework.context.annotation.internalAutowiredAnnotationProcessor org.springframework.context.annotation.internalRequiredAnnotationProcessor org.springframework.context.annotation.internalCommonAnnotationProcessor org.springframework.context.event.internalEventListenerProcessor org.springframework.context.event.internalEventListenerFactory mainConfig2 person false

At this time, the bean instance is multi instance, and one instance will be created every time the getBean() method is called;

Let's see when a bean is created in the IOC container when its scope is a singleton:

@Configuration
public class MainConfig2 {
    
    @Scope
    @Bean("person")
    public Person person() {
        System.out.println("to IOC Add to container Person...");
        return new Person();
    }

}

First, we start the IOC container, but do not call the getBean method to get the Person instance:

@Test
public void test02() {
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
}

The test results are as follows:

Add Person to IOC container

At this time, we can find that when the scope is singleton, the IOC container will create all instances of bean s with singleton scope in the container when it is started; For each subsequent acquisition, it is directly obtained from the IOC container, which is equivalent to that from the map A process of get();

However, when the scope of our bean is changed to multi instance, let's look at the results:

@Configuration
public class MainConfig2 {

    @Scope("prototype")
    @Bean("person")
    public Person person() {
        System.out.println("to IOC Add to container Person...");
        return new Person();
    }

}

When we run again:

@Test
public void test02() {
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
}

We can find that the console does not have any output results; When the IOC container was created, it did not create a bean with multi instance scope;

At this time, let's call the getBean() method to get:

@Test
public void test02() {
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
    System.out.println("IOC Container creation complete...");
    Person person = (Person) applicationContext.getBean("person");
}

At this time, the console Prints:

IOC container creation completed... Add Person to IOC container

At the same time, if I get it many times:

@Test
public void test02() {
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
    System.out.println("IOC Container creation complete...");
    Person person = (Person) applicationContext.getBean("person");
    Person person1 = (Person) applicationContext.getBean("person");
}

The test results are as follows:

IOC container creation completed... Add Person to IOC container... Add Person to IOC container

We can find that we use the getBean method to obtain several times and create bean instances several times;

In other words, when the scope of the bean is multiple instances, the IOC container will not create an instance of the bean when it is started, but will create an instance of the bean when we call getBean() to obtain it; And each time it is called, an instance of the bean will be created;

Tags: Java Spring Spring Boot

Posted by andrin on Sat, 14 May 2022 17:21:14 +0300