Spring source code chapters from shallow to deep, explain the detailed use of spring in actual combat

First of all, this article will not go into the underlying source code in detail. It will only talk about the use of our spring based on annotations and configuration. Don't underestimate the basics, make sure that there are annotations and configurations you haven't used before, and let's go.

Let's first create a maven project and introduce spring files. If you don't like it, there is a code address at the bottom of the article to download. Look first, then download the code and try it yourself. Give it to you first, try it and watch it.

1. How the IOC container registers components

1. Basic XML injection bean

 

<?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="car" class="com.springIOC.bean.CarBean"></bean><!--id是唯一表示,class是全路径 -->
</beans>

 

package com.springIOC;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class MainTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext cac = new ClassPathXmlApplicationContext("config.xml");
        Object car = cac.getBean("car");
    }
}

Is it super simple? Let's start from the shallower to the deeper.

2. Configure based on annotations

 

package com.springIOC2.config;

import com.springIOC.bean.CarBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MainConfig {
    @Bean
        public CarBean car(){
        //注意方法名
        return new CarBean();
    }
}

 

package com.springIOC2;

import com.springIOC2.config.MainConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MainTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext aca = new AnnotationConfigApplicationContext(MainConfig.class);
        Object car = aca.getBean("car");
    }
}

We can get our object directly through the method name, and the default is to assemble according to the method. You can also specify the name of the assembly by @Bean(value="newName").

3. Assemble in the way of package scanning (emphasis), use @ComponentScan(basePackages={"full path of package"})

 

package com.springIOC3.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan(basePackages = {"com.springIOC3"})
public class MainConfig {
}

 

package com.springIOC3;

import com.springIOC3.config.MainConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MainTest {
    /**
     * 基础@CompentScan,包扫描方式来配置
     * @param args
     */
    public static void main(String[] args) {
        AnnotationConfigApplicationContext aca = new AnnotationConfigApplicationContext(MainConfig.class);
        String[] beanDefinitionNames = aca.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            System.out.println("beanDefinitionName = " + beanDefinitionName);
        }
    }
}

Here are a few parameters, excludeFilters exclude certain objects, the syntax is as follows

 

excludeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Controller.class}),  //排除所有Controller的注解类,多个value可以用逗号分隔
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,value = {RepositoryBean.class}) //排除RepositoryBean类对象
}

There are five types of FilterType, namely ANNOTATION (annotation class), ASSIGNABLE_TYPE (class name), ASPECTJ (not commonly used, the document says AspectJ type pattern expression matching), REGEX (regular expression matching), CUSTOM (custom), commonly used I marked the three in red. Let's take a look at the specific writing

 

package com.springIOC3b.config;

import com.springIOC3b.repository.RepositoryBean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;

@Configuration
@ComponentScan(basePackages = {"com.springIOC3b"},excludeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Controller.class}),
        @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,value = {RepositoryBean.class})
}
)
/**
 * 1:排除用法 excludeFilters(排除@Controller注解的,和RepositoryBean类)
 */
public class MainConfig {
}

Just now we talked about custom filtering, let’s take a look at how to write custom filtering, implement our TypeFilter interface, rewrite our match, and only focus on the returned true. Here is an example

 

package com.springIOC3c.config;

import org.springframework.core.io.Resource;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;
import java.io.IOException;

public class CustomFilterType implements TypeFilter {
    @Override
        public Boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        //获取当前类的注解源信息
        AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
        //获取当前类的class的源信息
        ClassMetadata classMetadata = metadataReader.getClassMetadata();
        //获取当前类的资源信息
        Resource resource = metadataReader.getResource();
        if (classMetadata.getClassName().contains("RepositoryBean")) {
            //这里注意,他是一个包含的匹配规则,即使我写成下面注释掉那样也可以的
            return true;
        }
        //        if (classMetadata.getClassName().contains("ryBean")) {
        //            return true;
        //        }
        return false;
    }
}

Contrary to inclusion, there is another, only what is allowed to be introduced, that is, our includeFilters, we need to pay attention to the need to set the useDefaultFilters property to false (true means scan all). The syntax is exactly the same as excludeFilters

 

package com.springIOC3d.config;

import com.springIOC3d.service.ServiceBean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.stereotype.Controller;

@Configuration
@ComponentScan(basePackages = {"com.springIOC3d"},includeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Controller.class}),
        @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,value = ServiceBean.class)
}
,useDefaultFilters = false)
/**
 * 只许引入*** includeFilters(只许引入@Controller注解和ServiceBean类),useDefaultFilters设置为false,关闭全包扫描
 */
public class MainConfig {
}

4. Looking back, let's look at the scope of Bean.

@Lazy lazy loading, instantiation only after use, look at the code, we add a construction method to the bean, it is more convenient to figure out when the instantiation.

 

package com.springIOC4.config;

import com.springIOC4.bean.CarBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;

@Configuration
public class MainConfig {
    @Bean
        @Lazy
        public CarBean car(){
        return new CarBean();
    }
}

Specify @Scpoe can have four scopes

a) Singleton single instance (default), the life cycle of the singleton is controlled by the spring container. When non-lazy loading, the object is generated after the spring instantiation, and the container is destroyed when the object is destroyed

 

package com.springIOC4b.config;

import com.springIOC4b.bean.CarBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;

@Configuration
public class MainConfig {
    @Bean
        @Scope(value = "singleton")
        public CarBean car(){
        return new CarBean();
    }
}

 

package com.springIOC4b;

import com.springIOC4b.bean.CarBean;
import com.springIOC4b.config.MainConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MainTest {
    /**
     * @Scope(value = "singleton")单例,默认也是@Scope(value = "singleton")
     * @param args
     */
    public static void main(String[] args) {
        AnnotationConfigApplicationContext aca = new AnnotationConfigApplicationContext(MainConfig.class);
        CarBean car = (CarBean)aca.getBean("car");
        CarBean car2 = (CarBean)aca.getBean("car");
        System.out.println(car == car2);
        // # true
    }
}

The output result is true, indicating that our object is singleton, and the life cycle of singleton objects is managed by spring.

b) prototype multi-instance

 

package com.springIOC4c.config;

import com.springIOC4c.bean.CarBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;

@Configuration
public class MainConfig {
    @Bean
        @Scope(value = "prototype")
        public CarBean car(){
        return new CarBean();
    }
}

 

package com.springIOC4c;

import com.springIOC4c.bean.CarBean;
import com.springIOC4c.config.MainConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MainTest {
    /**
     * @Scope(value = "prototype")多例
     * @param args
     */
    public static void main(String[] args) {
        AnnotationConfigApplicationContext aca = new AnnotationConfigApplicationContext(MainConfig.class);
        CarBean car = (CarBean)aca.getBean("car");
        CarBean car2 = (CarBean)aca.getBean("car");
        System.out.println(car == car2);
        // # false
    }
}

Many cases are not managed by the ioc container, and are cleaned up by the GC when they are destroyed. There are also requests for the same request and the same session level as the session. I will not demonstrate them here.

5. @Configuration annotation to determine whether to inject Bean.

 

package com.springIOC5.config;

import com.springIOC5.bean.CarBean;
import com.springIOC5.bean.UserBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MainConfig {
    @Bean(value = "user")
        public UserBean userBean() {
        return new UserBean();
    }
    @Bean
        @Conditional(value = IOCConditional.class)
        public CarBean carBean() {
        return new CarBean();
    }
}

 

package com.springIOC5.config;

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;

public class IOCConditional implements Condition {
    @Override
        public Boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        if (context.getBeanFactory().containsBean("user")) {
            //这里必须和Bean的名称完全一致。
            return true;
        }
        return false;
    }
}

What does the above code mean? It is whether we need to inject the carBean. If it contains the user object, we will inject our carBean. If it does not contain it, we will not inject it. There is an interesting thing here. The injection of classes in the Configuration configuration is ordered. We must use us as the judgment condition. Put the Bean on it, otherwise Conditional will recognize that you don’t have a Bean with that judgment condition.

6. @Import introduction method to inject Bean

 

package com.springIOC6.config;

import com.springIOC6.bean.CarBean;
import com.springIOC6.bean.UserBean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
@Import({
    CarBean.class, UserBean.class
}
)
public class MainConfig {
}

Just write the class we want to inject directly in the annotation, or use the interface to implement it, let's change it.

 

package com.springIOC6b.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
@Import({
    ImportSelector.class
}
)
public class MainConfig {
}

 

package com.springIOC6b.config;

import org.springframework.core.type.AnnotationMetadata;

public class ImportSelector implements org.springframework.context.annotation.ImportSelector {
    @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{
            "com.springIOC6b.bean.CarBean","com.springIOC6b.bean.UserBean"
        }
        ;
    }
}

Implement the ImportSelector class, and then return the full path of the class name. Automatic assembly is based on @Import.

It is also possible to implement ImportBeanDefinitionRegistrar and rewrite the registerBeanDefinitions method.

 

package com.springIOC6c.config;

import com.springIOC6c.bean.CarBean;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;

public class ImportSelectorRegister implements ImportBeanDefinitionRegistrar {
    @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(CarBean.class);
        registry.registerBeanDefinition("CarBean",rootBeanDefinition);
    }
}

7. Injection through FactoryBean

 

package com.springIOC7.config;

import com.springIOC7.bean.UserBean;
import org.springframework.beans.factory.FactoryBean;

public class IOCFactoryBean implements FactoryBean<UserBean> {
    @Override
        public UserBean getObject() throws Exception {
        //指定对象
        return new UserBean();
    }
    @Override
        public Class<?> getObjectType() {
        //指定类型
        return UserBean.class;
    }
    @Override
        public Boolean isSingleton() {
        //指定是否为单例
        return true;
    }
}

 

package com.springIOC7.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MainConfig {
    @Bean
        public IOCFactoryBean iocFactoryBean(){
        return new IOCFactoryBean();
    }
}

 

package com.springIOC7;

import com.springIOC7.config.MainConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MainTest {
    /**
     * FactoryBean注入
     * @param args
     */
    public static void main(String[] args) {
        AnnotationConfigApplicationContext aca = new AnnotationConfigApplicationContext(MainConfig.class);
        Object carBean = aca.getBean("iocFactoryBean");
        //取出userBean
        System.out.println(carBean);
        Object iocFactoryBean = aca.getBean("&iocFactoryBean");
        //取得FactoryBean
        System.out.println(iocFactoryBean);
    }
}

Speaking of all the ways to add components to the IOC container, I'm all over, let's briefly summarize:

  • @Bean injection, you can specify four scopes, singleton, multiple instances (the life cycle is not managed by the IOC container), one request and one session, you can also set lazy loading,
  • @ComponentScan specifies the method of package scanning to inject, and use it with @Controller, @Repository, @Service, and @Component annotations.
  • @Import injection, two implementations of ImportSelector and ImportBeanDefinitionRegistrar.
  • @FactoryBean, engineering Bean can also be injected. Note that without & is to get the final object, with & to get the real Bean. Three methods, one specifies the object, one specifies the type, and one specifies whether it is a singleton.

Two, Bean's life cycle-initialization method and destruction method

For single-instance beans, when the container starts, the bean object is created, and when the container is destroyed, the bean destruction method is also called.

For multi-instance beans, when the container starts, the bean will not be created but when the bean is obtained, and the destruction of the bean is not managed by the IOC container.

1. Let's first look at the simplest method, using initMethod and destroyMethod to specify our initialization method and destruction method

 

package com.springlifeCycle1a.config;

import com.springlifeCycle1a.bean.CarBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MainConfig {
    @Bean(initMethod = "init",destroyMethod = "destroy")
        public CarBean car() {
        return new CarBean();
    }
}

We are specifying our initial init method, and the destroy method is the destroy method. The calling sequence is, Car's construction method, Car's init method, Car's destroy method. You can also try the @Lazy annotation yourself. You can try it yourself in the code cloud code.

2. Implement bean initialization and destruction methods through the two interfaces of InitializingBean and DisposableBean

 

package com.springlifeCycle2a.bean;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;

public class CarBean implements InitializingBean, DisposableBean {
    public CarBean() {
        System.out.println("我是Car");
    }
    public void afterPropertiesSet() {
        System.out.println("我是初始化init");
    }
    public void destroy() {
        System.out.println("我是销毁destroy");
    }
}

3. The method of annotation @PostConstruct and @ProDestory provided by JSR250 specification

 

package com.springlifeCycle3.bean;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

public class CarBean {
    public CarBean() {
        System.out.println("我是Car");
    }
    @PostConstruct
    public void init() {
        System.out.println("我是初始化init--PostConstruct");
    }
    @PreDestroy
    public void destory() {
        System.out.println("我是销毁destroy--PreDestroy");
    }    
}

4. The post processor of the bean through Spring's BeanPostProcessor will intercept all bean creation processes (this method will talk about the internal implementation when the source code is described later, and I think it is necessary to read the source code of this)

 

package com.springlifeCycle4.bean;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

@Component
public class LifeBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("初始化方法" + beanName);
        return bean;
    }
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("销毁方法" + beanName);
        return bean;
    }
}

Here is also a summary, let us specify the initialization method and destruction method of the object in the container, there are four ways

  • Use @Bean's initMethod and destroyMethod to give initialization and destruction methods.
  • Realize bean initialization and destruction methods through the two interfaces of InitializingBean and DisposableBean.
  • The methods annotated by @PostConstruct and @ProDestory provided by JSR250 specification.
  • The post processor of the bean through Spring's BeanPostProcessor will intercept all bean creation processes.

Three, assign values ​​to attributes

There are not many things here, so I will talk about it as soon as possible. There are three ways to assign values. Let's take a look.

 

package com.springValue.config;

import com.springValue.bean.CarBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

@Configuration
@PropertySource(value = "classpath:carBean.properties",encoding = "utf-8") //指定外部文件的位置
public class MainConfig {
    @Bean
    public CarBean carBean() {
        return new CarBean();
    }
}

 

import org.springframework.beans.factory.annotation.Value;

public class CarBean {
    @Value("宝马")//通过普通的方式
    private String name;
    @Value("#{5-2}")//spel方式来赋值
    private int carNum;
    @Value("${carBean.realName}")//通过读取外部配置文件的值
    private String realName;        
}//自己记得加get set方法。

It is worth mentioning here that it is best to set encoding = "utf-8" in the imported file, otherwise the Chinese characters will be garbled.

Four, automatic assembly:

Active assembly is usually the one we are most familiar with and the one we use the most. Let's review it.

1.@Autowired Auto-assembly is first assembled according to the type. If multiple components of the same type are found in the IOC container, then the assembly is carried out according to the attribute name

 

package com.springxAutowired1.config;

import com.springxAutowired1.dao.AutowiredDao;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan(value = "com.springxAutowired1.service")
public class MainConfig {
    @Bean
    public AutowiredDao autowiredDao1(){
        return new AutowiredDao(1);
    }
    @Bean
    public AutowiredDao autowiredDao2(){
        return new AutowiredDao(2);
    }
}

 

package com.springxAutowired1.service;

import com.springxAutowired1.dao.AutowiredDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class ServiceBean {
    @Autowired
    private AutowiredDao autowiredDao2;
    public ServiceBean() {
        System.out.println("我是serviceBean");
    }
    @Override
    public String toString() {
        return "ServiceBean{" +
                "AutowiredDao=" + autowiredDao2 +
                '}';
    }
}

Here we set up two AutowiredDao objects, one identified as 1, and the other identified as 2. ServiceBean is assembled by name by default.

2. We can also specify the assembly name through @Qualifier.

 

package com.springxAutowired1b.service;

import com.springxAutowired1b.dao.AutowiredDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service
public class ServiceBean {
    @Autowired
    @Qualifier(value = "autowiredDao1")
    private AutowiredDao autowiredDao2;
    public ServiceBean() {
        System.out.println("我是serviceBean");
    }
    @Override
    public String toString() {
        return "ServiceBean{" +
                "AutowiredDao=" + autowiredDao2 +
                '}';
    }
}

3. We use Qualifier because if the name is wrong, an assembly error may be reported. At this time, we can use required = false to prevent the exception from being thrown

 

package com.springxAutowired3.service;

import com.springxAutowired3.dao.AutowiredDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service
public class ServiceBean {
    @Qualifier(value = "autowiredDaoError")
    @Autowired(required = false)
    private AutowiredDao autowiredDao2;
    public ServiceBean() {
        System.out.println("我是serviceBean");
    }
    @Override
    public String toString() {
        return "ServiceBean{" +
                "AutowiredDao=" + autowiredDao2 +
                '}';
    }
}

Note: I did not mention the @Resource annotation here. This annotation is actually not in spring, it is JSR250 specification, but it does not support @Primary and @Qualifier.

Five, environment switching:

We sometimes need to switch our configuration through different environments. We use @Profile annotations to activate and identify different beans according to the environment.

@Profile is marked on the class, then the entire configuration class will take effect only if the current environment matches

@Profile is marked on the Bean, then only the Bean in the current environment will be activated

 

package com.springxAutowired4.config;

import com.springxAutowired4.bean.Environment;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

@Configuration
public class MainConfig {
    @Bean
    @Profile(value = "test")
    public Environment environment_test() {
        return new Environment("test");
    }
    @Bean
    @Profile(value = "dev")
    public Environment environment_dev() {
        return new Environment("dev");
    }
    @Bean
    @Profile(value = "pro")
    public Environment environment_pro() {
        return new Environment("pro");
    }
}

How to activate the switching environment

  • Method 1: Switch by jvm parameter at runtime-Dspring.profiles.active=test,dev,prod multiple parameter tables are separated by English commas
  • Method 2: Activate through code

 

package com.springxAutowired4;

import com.springxAutowired4.config.MainConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MainTest {
    /**
     * 环境切换
     * @param args
     */
    public static void main(String[] args) {
        AnnotationConfigApplicationContext aca = new AnnotationConfigApplicationContext();
        aca.getEnvironment().setActiveProfiles("test","dev");//方便演示,我写了两个,一般都是一个的.
        aca.register(MainConfig.class);
        aca.refresh();
        for (String beanDefinitionName : aca.getBeanDefinitionNames()) {
            System.out.println(beanDefinitionName);
        }
    }
}

If both are written, follow the code in the implementation, the parameters no longer work


 

Guess you like

Origin blog.csdn.net/mrchaochao/article/details/108586792