We know that if you want to put beans in the spring container management, you need to register beans in the spring container first, and beans can be registered by xml or annotations. The configuration based on xml is usually configurated by xml tags such as < bean >, < context: component-scan >, and then the spring container scans xml files for registration; annotation-based registration is mainly through several Annotations defined by spring are also scanned by spring containers and beans are registered in the containers. Annotation-based development of spring has become more and more popular. Annotations are also widely used in spring boot and other spring family frameworks to drive development. Learning how to develop spring annotations is very helpful for understanding and learning spring boot.
This article introduces annotation-based registration bean s:
- Way 1: Combine @Configuration with @Bean
@Configuration public class CarConfig{ @Bean public Car car() { return new Car(); } }
Classes identified by the @Configuration annotation automatically acquire the @Component feature, because the annotation itself uses the @Component annotation, which allows you to view the source code definition of @Configuration, and this class acts as a configuration class for spring. When creating a bean of this type, spring scans all the methods of @Bean annotation, executes automatically, and automatically registers the return value. In containers, the default method name is used as the name of the bean. You can also name beans by providing the value value of @Bean or by setting the name attribute of the bean.
- Mode 2: Automatic registration using @ComponentScan annotation
@ComponentScan("cn.wyn") @Configuration public class BookConfig { } package cn.wyn; @Component public class Book { }
Similar to configuring <context: component-scan base-package="> in xml, spring scans all classes labeled with @Component or related annotations (such as @Controller, @Configuration, @Service) using the @ComponentScan annotation, automatically creates instances of all classes labeled with @Component related annotations according to the specified scanning package path, and registers them with sp. The @Bean method is also executed in the ring container if it is @Configuration annotated.
We can also filter scanned classes, such as scanners excluding classes containing @Controller:
@ComponentScan(value = "cn.wyn", excludeFilters = { @ComponentScan.Filter( type = FilterType.ANNOTATION, classes = {Controller.class})}) @Configuration public class MyConfig { }
The above configuration is equivalent to the xml configuration:
<context:component-scan base-package="cn.wyn"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan>
For example, only classes with @Service annotations are scanned:
@ComponentScan(value = "cn.wyn", includeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Service.class)}, useDefaultFilters = false) @Configuration public class MyConfig { }
The above configuration is equivalent to the xml configuration:
<context:component-scan base-package="cn.wyn" use-default-filters="false"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/> </context:component-scan>
Configuring include also requires specifying useDefaultFilters to be false, so spring does not automatically register all Component s under the package.
Note: @ComponentScan can be reused on the same class for multiple scans, but this feature requires JDK versions of jdk8 and above. If the JDK version is lower than jdk8, you can use @ComponentScans to achieve multiple scans.
We can also customize the scanning rules by specifying the type value of include or exclude as CUSTOM and the TypeFilter class of processing rules. We need to customize a class that implements the TypeFilter interface and override the match method:
class MyTypeFilte implements TypeFilter { @Override public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { return false; } } @Configuration @ComponentScan(value = "cn.wyn", includeFilters = { @ComponentScan.Filter(type = FilterType.CUSTOM, classes = MyTypeFilter.class)}, useDefaultFilters = false) class MyConfig { }
Description: The metadata Reader, a parameter of the match method, can obtain metadata information of the class being scanned, such as class name, annotation information on the class, etc. If the return value of the match method is true, it will match. When true is returned, if include, it is registered, and if exclude, it is ignored. Return false and vice versa.
- Mode 3: Import a class into the spring container using the @Import annotation
@Configuration @Import({Car.class, Book.class}) class MyConfig { }
By annotating the @Import annotation on the configuration class, you can quickly create an instance of a class and import it into the spring container.
Mode 3 Extension 1: @Import uses ImportSelector to import in batches:
The specific method is to specify the value of @Import as a class that implements the ImportSelector interface, which overrides the selectImports method, and the selectImports method returns a String array containing the fully qualified class name to be imported. ImportSelector does not import ImportSelector implementation classes, but imports only the classes specified in the array returned by the selectImports method.
class MyImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { return new String[]{"java.lang.String"}; } } @Configuration @Import({MyImportSelector.class}) class BeanConfig { }
Mode 3 Extension 2: @Import uses ImportBean Definition Registrar
Customize an ImportBeanDefinitionRegistrar class, implement the ImportBeanDefinitionRegistrar interface, rewrite the registerBeanDefinitions method, and register bean s through parameter registry, such as:
class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { //Create a BeanDefinition object RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Book.class); //Register a BeanDefinition named xixi registry.registerBeanDefinition("xixi", rootBeanDefinition); } } @Configuration @Import({MyImportBeanDefinitionRegistrar.class}) class BeanConfig { }
ImportBean Definition Registrar is also imported using the @Import annotation. Similarly, only bean s registered in the registerBean Definitions method are registered, and the Import Definition Registrar class is not registered.
- Way 4: Register bean s using FactoryBean
Implement the FactoryBean interface and implement the following three methods:
Method name | Effect |
---|---|
getObject | Getting bean s in this way |
getObjectType | This method obtains Bean's lass object |
isSingleton | This method specifies whether the scope of a bean is an instance or not. |
Example:
class MyFactoryBean implements FactoryBean { //Specific methods for obtaining bean s @Override public Object getObject() throws Exception { return new String("Ha ha ha"); } //Get the Class type of the bean @Override public Class<?> getObjectType() { return String.class; } //Use this method to specify whether bean s are singletons @Override public boolean isSingleton() { return true; } } @Configuration class BeanConfig { @Bean("beanName") public MyFactoryBean createBean() { return new MyFactoryBean(); } }
spring determines that the return value of the @Bean annotated method is a factory bean, and executes the getObject method of the factory bean to get an instance and register it in the container. If it is a singleton, it registers only once. Instead of registering the implementation class of FactoryBean. If you want to get an instance of the factory bean itself, you can prefix the bean name specified when you get the bean with a "&" prefix, such as context. getBean ("&bean").
Setting the Scope of Beans -- Annotation Configuration
The scope of a bean is:
- singleton: singleton, there is only one instance bean in the whole application
- prototype: As opposed to a singleton, each getBean regenerates a Bean.
- Request: In a web environment, each request creates a bean, and there is only one bean in one request. Different requests have different beans.
- Session: In the web environment, the same bean is captured in the session lifecycle
By default, a singleton single instance can specify the scope of the Bean in the following way.
@Configuration class BeanConfig { @Bean("book") @Scope("prototype") public Book createBean() { return new Book(); } }
Lazy Loading of Beans--Annotation Configuration
By default, all single instance beans are created when the spring container is created, and if created when the bean is first used, we call it lazy loading.
The configuration is simple, just add the @Lazy annotation to the method of creating bean s.
@Configuration class BeanConfig { @Bean("book") @Lazy public Book createBean() { return new Book(); } }
Register Bean s on condition
We can choose whether or not to register beans by some criteria, which can be achieved by the @Condition annotation.
@Configuration class BeanConfig { @Bean("book") @Conditional(MyCondition.class) public Book createBean() { return new Book(); } } //Implement the Condition interface and override the matches method to decide whether to register beans based on the Boolean values returned by the method class MyCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { /** * According to the existence of my.env=hello attribute in the environment variable, the creation of the environment variable is determined. * You can test it by specifying - Dmy.env=hello as the startup parameter. **/ Environment environment = context.getEnvironment(); String property = environment.getProperty("my.env"); if ("hello".equals(property)) { return true; } return false; } } //test public class MainTest { @Test public void test() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class); } } //Classes for testing class Book { public Book() { System.out.println("init book ..."); } }
@ Conditional annotations can be added either to methods or to classes, where all @Bean methods in a class are set uniformly.
Use of Profile
In daily development, we may need to register a set of different beans according to different environments, such as: our production environment, test environment, development environment will use different data sources. With Profile configuration, you can specify that the Bean is registered in the spring container when a Profile is activated, which is the same as the profile in maven.
As follows:
@Profile("dev") public Book book1(){ return new Book(); } @Profile("test") Book book2() { return new Book(); } @Profile("prod") Book book3() { return new Book(); }
Profiles can be activated by specifying environment variables, or jvm startup parameters: - Dspring. profile s. active = dev. Profiles can also be activated in code, such as:
public class MainTest { @Test public void test() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); //Setting Activated profile context.getEnvironment().setActiveProfiles("dev"); context.register(BeanConfig.class); context.refresh(); } } @Configuration class BeanConfig { @Bean public Book book(){ return new Book(); } @Bean @Profile("dev") public Book book1(){ return new Book(); } @Bean @Profile("test") Book book2() { return new Book(); } @Bean @Profile("prod") Book book3() { return new Book(); } }
@ Profile annotations can also be written on configuration classes, and all configurations of the entire configuration class are activated under the specified profile.
Reprinted please indicate the source
Author: wyn-lin
Links to the original text: https://www.jianshu.com/p/bdca18850673