Writing in the front

Since I am updating articles on other topics, the Spring series articles have not been updated for a long time. Many friends have left messages on the background of the official account or directly sent private messages to me on wechat to urge me to update the Spring series articles.

It seems to be time to continue updating the Spring article. After a while, write an article about annotations in Spring, because previous updates to the Spring series have been updates to Spring annotation-driven development. This article is also a small summary of the previous article, it is expected that after updating this article, we will move on to the Spring AOP chapter update.

If you haven’t read other Spring articles, you can go to the Spring series of Glacier Technology.

The article has been included:

Github.com/sunshinelyz…

Gitee.com/binghe001/t…

XML configuration versus class configuration

1. The XML configuration


      
<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/sp person" class="com.binghe.spring.Person"></bean>
</beans>
Copy the code

Get the Person instance as shown below.

public static void main( String[] args ){
	ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
	System.out.println(ctx.getBean("person"));
}
Copy the code

2. The type of configuration

@Configuration
public class MainConfig {
    @Bean
    public Person person(a){
    	return newPerson(); }}Copy the code

There is one caveat here: the default name of the Bean is the method name if it is used through the @bean form. If @bean (value=” Bean name “) then the name of the Bean is specified.

Get the Person instance as shown below.

public static void main( String[] args ){
	AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig.class);
	System.out.println(ctx.getBean("person"));
}
Copy the code

@ CompentScan annotations

We can use the @compentScan annotation to do a packet scan, as shown below.

@Configuration
@ComponentScan(basePackages = {"com.binghe.spring"})
	public class MainConfig {}Copy the code

ExcludeFilters properties

When we use the @CompentScan annotation to scan, we can use the excludeFilters attribute of the @CompentScan annotation to exclude certain classes, as shown below.

@Configuration
@ComponentScan(basePackages = {"com.binghe.spring"},excludeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Controller.class}),
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,value = {PersonService.class})
})
public class MainConfig {}Copy the code

IncludeFilters properties

When we use the @CompentScan annotation to scan, we can use the includeFilters attribute of the @CompentScan annotation to include certain classes. Note here that you need to set the useDefaultFilters property to false (true means to scan all filters)

@Configuration
@ComponentScan(basePackages = {"com.binghe.spring"},includeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Controller.class, PersonService.class})
},useDefaultFilters = false)
public class MainConfig {}Copy the code

@ComponentScan.Filter Type Specifies the type

  • ANNOTATION in the form of FilterType.ANNOTATION @controller @service @repository @compent
  • Specify type filterType.assignable_type @componentScan.Filter(type = filterType.assignable_type,value = {person.class})
  • Aspectj filterType.aspectj (not often used)
  • Filtertype.regex for regular expressions (not often used)
  • CUSTOM filterType.custom
public enum FilterType {
    // Annotations in the form @controller @service @repository @compent
    ANNOTATION,
    // The specified type
    ASSIGNABLE_TYPE,
    / / in the form of aspectJ
    ASPECTJ,
    // regular expression
    REGEX,
    // Custom
    CUSTOM
}
Copy the code

Filtertype. CUSTOM Indicates a CUSTOM type

public class CustomFilterType implements TypeFilter {
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
    // Get the annotation source information for the current class
    AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
    // Get the source information for the current class
    ClassMetadata classMetadata = metadataReader.getClassMetadata();
    // Get resource information for the current class
    Resource resource = metadataReader.getResource();
 	return classMetadata.getClassName().contains("Service");
}
    
@ComponentScan(basePackages = {"com.binghe.spring"},includeFilters = {
@ComponentScan.Filter(type = FilterType.CUSTOM,value = CustomFilterType.class)
},useDefaultFilters = false)
public class MainConfig {}Copy the code

Configure the scope object of the Bean

Do not specify a @ the Scope

Without specifying @scope, all beans are singleton beans and are loaded on demand (instances are created by container startup)

@Bean
public Person person(a) {
	return new Person();
}	
Copy the code

@ the Scope of the prototype

Specifying @scope to prototype means multi-instance and lazy mode loading (when the IOC container is started, objects are not created, but when first used)

@Bean
@Scope(value = "prototype")
public Person person(a) {
    return new Person();
}
Copy the code

@ Scope values

  • Singleton single instance (default)
  • Prototype multi-instance
  • Request Indicates the same request
  • Session Indicates the same session level

Lazy loading

Lazy loading of beans @Lazy(the object is not created when the Bean container is started, mainly for single instances, and is only created when it is first used)

@Bean
@Lazy
public Person person(a) {
	return new Person();
}
Copy the code

@conditional Conditional judgment

In this scenario, there are two components, CustomAspect and CustomLog. My CustomLog component is a component application that depends on the CustomAspect: I create a CustomCondition class that implements the Condition interface

public class CustomCondition implements Condition {
/ * * * *@param context
* @param metadata
* @return* /
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        // Determine if there are any CustomAspect components in the container
        return context.getBeanFactory().containsBean("customAspect"); }}public class MainConfig {
    @Bean
    public CustomAspect customAspect(a) {
        return new CustomAspect();
    } 
    @Bean
    @Conditional(value = CustomCondition.class)
    public CustomLog customLog(a) {
   		return newCustomLog(); }}Copy the code

Add components to the IOC container

(1) Through @compentScan + @controller @service @respository @compent. Application scenario: Components written by ourselves can be loaded into containers in this way.

(2) Import components via @bean (for classes that import third-party components)

(3) Import component via @import (id of Import component is full class name path)

@Configuration
@Import(value = {Person.class})
public class MainConfig {}Copy the code

Import components with the @import ImportSeletor class (the id of the imported component is the full class name path)

public class CustomImportSelector implements ImportSelector {	
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
    	return new String[]{"com.binghe.spring"};
    }
} 
Configuration
@Import(value = {Person.class}
public class MainConfig {
}
Copy the code

Through the @ Import ImportBeanDefinitionRegister Import components (can specify the name of the bean)

public class DogBeanDefinitionRegister implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        // Create a bean definition object
        RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Dog.class);
        // Import the bean definition object into the container
        registry.registerBeanDefinition("dog",rootBeanDefinition); }}@Configuration
@Import(value = {Person.class, Car.class, CustomImportSelector.class, DogBeanDefinitionRegister.class})
public class MainConfig {}Copy the code

The registered component is implemented by implementing the FacotryBean interface

public class CarFactoryBean implements FactoryBean<Car> {
    @Override
    public Car getObject(a) throws Exception {
    	return new Car();
    } 
    @Override
    publicClass<? > getObjectType() {return Car.class;
    } 

    @Override
    public boolean isSingleton(a) {
    	return true; }}Copy the code

Initialization and destruction of beans

Specify the bean’s initialization method and the bean’s destruction method

The container manages the Bean life cycle, and we can specify the Bean initialization method and Bean destruction method ourselves

@Configuration
public class MainConfig {
    // Specify the bean's life-cycle initialization and destruction methods.@Bean(initMethod = "init",destroyMethod = "destroy")
    public Car car(a) {
    	return newCar(); }}Copy the code

For single-instance beans, when the container is started, the bean object is created, and when the container is destroyed, the bean’s destruction method is called

In the case of multi-instance beans, the bean is not created when the container is started but when the bean is retrieved, and bean destruction is not managed by the IOC container

This is done through InitializingBean and DisposableBean

Implement the bean initialization and destruction methods through the InitializingBean and DisposableBean interfaces

@Component
public class Person implements InitializingBean.DisposableBean {
    public Person(a) {
    	System.out.println("Constructor of Person");
    } 
    @Override
    public void destroy(a) throws Exception {
    	System.out.println("DisposableBean's destroy() method");
    } 
    @Override
    public void afterPropertiesSet(a) throws Exception {
    	System.out.println("AfterPropertiesSet method for InitializingBean"); }}Copy the code

By JSR250 specification

Annotate @postconstruct and @ProdeStory annotations provided by the JSR250 specification

@Component
public class Book {
    public Book(a) {
    	System.out.println("Constructor of book");
    } 
    @PostConstruct
    public void init(a) {
    	System.out.println("Method of PostConstruct flag for book");
    } 
    @PreDestroy
    public void destory(a) {
    	System.out.println("Method of PreDestory annotation in Book"); }}Copy the code

Implemented via BeanPostProcessor

The backend processor of beans that pass through Spring’s BeanPostProcessor intercepts all bean creation

  • PostProcessBeforeInitialization before the init method call
  • PostProcessAfterInitialization called after the init method
@Component
public class CustomBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    	System.out.println("CustomBeanPostProcessor... postProcessBeforeInitialization:"+beanName);
   		return bean;
    } 
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("CustomBeanPostProcessor... postProcessAfterInitialization:"+beanName);
        returnbean; }}Copy the code

Execution time of BeanPostProcessor

populateBean(beanName, mbd, instanceWrapper) initializeBean{ applyBeanPostProcessorsBeforeInitialization() invokeInitMethods{ IsInitializingBean. AfterPropertiesSet () the custom of the init method} applyBeanPostProcessorsAfterInitialization} () methodCopy the code

Assign values to components via @Value + @propertysource

public class Person {
    // In the normal way
    @ Value (" lonely ")
    private String firstName;
    // assign to spEL
    @Value("#{28-8}")
    privateInteger age; By reading the value of the external configuration file@Value("${person.lastName}")
    private String lastName;
} 
@Configuration
@PropertySource(value = {"classpath:person.properties"}) // Specify the location of the external file
public class MainConfig {
    @Bean
    public Person person(a) {
        return newPerson(); }}Copy the code

Automatically.

The use of the @autowired

Automatic injection

@Repository
public class CustomDao {}@Service
public class CustomService {
    @Autowired
    privateCustomDao customDao; }Copy the code

Conclusion: (1) Automatic assembly is firstly assembled according to type. If multiple components of the same type are found in IOC container, assembly is carried out according to attribute name

@Autowired
private CustomDao customDao;
Copy the code

For example, if I have two components of type CustomDao in the container one named CustomDao and the other named CustomDao2, then when we modify the property name CustomDao with @AutoWired, then we load the container’s CustomDao component, If the property name is tulignDao2 then it loads the CustomDao2 component

(2) Assuming we need to specify a specific component for assembly, we can specify the component to be assembled by using @Qualifier(“CustomDao”) or annotating @primary on the @bean on the configuration class

@Autowired
@Qualifier("CustomDao")
private CustomDao customDao2
Copy the code

(3) If we do not have either CustomDao or CustomDao2 in the container, we will throw an exception during assembly

No qualifying bean of type 'com.binghhe.spring.dao.CustomDao' available
Copy the code

If we don’t want to throw exceptions, we need to specify required to be false

@Autowired(required = false)
@Qualifier("customDao")
private CustomDao CustomDao2;
Copy the code

@Resource(JSR250 specification) has almost the same functionality as @AutoWired, but @primary and @Qualifier are not supported

(5) @inject (JSR330 specification) needs to import jar package dependency, function and support @primary function, but does not Require=false function

<dependency>
    <groupId>javax.inject</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
</dependency>
Copy the code

(6) Use @autowired to mark the method

  • Labeled on the set method
//@Autowired
public void setCustomLog(CustomLog customLog) {
	this.customLog = customLog;
}
Copy the code
  • The notation is on the constructor
@Autowired
public CustomAspect(CustomLog customLog) {
	this.customLog = customLog;
}
Copy the code

Annotation in input parameter on configuration class (optional)

@Bean
public CustomAspect CustomAspect(@Autowired CustomLog customLog) {
    CustomAspect customAspect = new CustomAspect(customLog);
    return ustomAspect;
}
Copy the code

XXXAwarce interface

When our own components need to use spring IOC’s underlying components, such as ApplicationContext, we can implement the XXXAware interface to do so

@Component
public class CustomCompent implements ApplicationContextAware.BeanNameAware {
    private ApplicationContext applicationContext;
    @Override
    public void setBeanName(String name) {
    	System.out.println("current bean name is :【"+name+"】");
    } 
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    	this.applicationContext = applicationContext; }}Copy the code

@ Profile annotations

The @profile annotation is used to activate and identify different beans depending on the environment

  • @profile identifies the class, so the entire configuration class takes effect only if the current environment matches
  • @profile identifies a Bean, so only beans in the current environment will be activated
  • Can beans that are not marked @profile be activated in any environment
@Configuration
@PropertySource(value = {"classpath:ds.properties"})
public class MainConfig implements EmbeddedValueResolverAware {
    @Value("${ds.username}")
    private String userName;
    @Value("${ds.password}")
    private String password;
    private String jdbcUrl;
    private String classDriver;
    @Override
    public void setEmbeddedValueResolver(StringValueResolver resolver) {
        this.jdbcUrl = resolver.resolveStringValue("${ds.jdbcUrl}");
        this.classDriver = resolver.resolveStringValue("${ds.classDriver}");
    } 
    @Bean
    @Profile(value = "test")
    public DataSource testDs(a) {
   		return buliderDataSource(new DruidDataSource());
    }
    @Bean
    @Profile(value = "dev")
    public DataSource devDs(a) {
    	return buliderDataSource(new DruidDataSource());
    } 
    @Bean
    @Profile(value = "prod")
    public DataSource prodDs(a) {
    	return buliderDataSource(new DruidDataSource());
    } 
    private DataSource buliderDataSource(DruidDataSource dataSource) {
        dataSource.setUsername(userName);
        dataSource.setPassword(password);
        dataSource.setDriverClassName(classDriver);
        dataSource.setUrl(jdbcUrl);
    	returndataSource; }}Copy the code

Method to activate the switch environment

(1) Run time JVM parameters to switch

 -Dspring.profiles.active=test|dev|prod  
Copy the code

(2) Through the code to activate

public static void main(String[] args) {
    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
    ctx.getEnvironment().setActiveProfiles("test"."dev");
    ctx.register(MainConfig.class);
    ctx.refresh();
    printBeanName(ctx);
}
Copy the code

If you have any questions, you can leave a comment below or add me to wechat: SUN_shine_LYz. I will pull you into the group. We can exchange technology together, advance together, and make great force together