Примечания к изучению исходного кода Spring Framework (1)

задняя часть
Примечания к изучению исходного кода Spring Framework (1)

Глава 01. Основы Spring и использование компонентов

Раздел 01 - Что такое весна?

  Spring поддерживает различные решения J2EE, а не только определенный уровень решений. Можно сказать, что Spring является универсальным выбором для разработки корпоративных приложений. Spring работает на уровне представления, бизнес-уровне и уровне сохраняемости. эти существующие рамки, рамки для интеграции.

В первые дни Spring чаще всего использовались контейнер IoC и внедрение зависимостей DI.Путем настройки тега bean-компонента в файле application.xml компоненты или объекты в проекте передавались в контейнер IoC для управления. Когда контейнер инициализируется, Bean будет создан или инстанцирован, что приводит к огромному количеству bean-тегов, которые необходимо настроить в огромном проекте, который очень громоздок в обслуживании.

Пружинная основная система

  • Spring Core: то есть ядро ​​Spring, которое является самой базовой частью фреймворка, обеспечивающим функции ввода-вывода и внедрения зависимостей.
  • Контекст Spring: Контейнер контекста Spring, который представляет собой подинтерфейс, улучшенный функцией BeanFactory.
  • Spring Web: обеспечивает поддержку разработки веб-приложений.
  • Spring MVC: направлен на реализацию идей MVC в веб-приложениях.
  • Spring DAO: обеспечивает уровень абстракции для JDBC, который упрощает кодирование JDBC и в то же время делает его более надежным.
  • Spring ORM: поддерживает интеграцию с популярными платформами ORM, такими как: Spring + Hibernate, Spring + iBatis, интеграция Spring + JDO и т. д.

Общие компоненты Spring

image.png

Раздел 02 — Компоненты регистрации XML и аннотаций

Создайте проект maven, добавить зависимости Добавить пакет сущностей, добавить класс сущностей Person

public class Person {
    private String name;
    private Integer age;
    //省略getter/setter/toString
}

метод конфигурации xml для регистрации bean-компонентов

Создайте новый файл конфигурации beans.xml в каталоге ресурсов.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
 http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="person" class="com.citi.entity.Person">
        <property name="name" value="Peter"></property>
        <property name="age" value="18"></property>
    </bean>
</beans>

Создайте новый тестовый класс SingleBeanContainerTest в тестовом пакете, добавьте метод main, вызовите метод getBeanByXml() и протестируйте компонент, полученный из контейнера.

public static void getBeanByXml(){
    ApplicationContext context = new ClassPathXmlApplicationContext("classpath:beans.xml");
    Person person = (Person) context.getBean("person");
    System.out.println(person);
}

консольная печать

image.png

Регистрация компонентов с помощью аннотацийСоздайте новый пакет конфигурации и добавьте класс конфигурации BeanConfig.

@Configuration
public class BeanConfig {

    @Bean
    public Person person(){
        Person person = new Person();
        person.setName("Stark");
        person.setAge(40);
        return person;
    }
}

@Configuration: определяется как класс конфигурации, эквивалентный файлу конфигурации. @Bean: зарегистрируйте Bean-компонент для контейнера, тип — это тип возвращаемого значения, а идентификатор bean-компонента — это возвращаемое имя. Новый метод в SingleBeanContainerTest, выполняемый в основном методе

public static void getBeanByAnno(){
    ApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class);
    Person person = (Person) context.getBean("person");
    System.out.println(person);

    String[] beanNamesForType = context.getBeanNamesForType(Person.class);
    for (String s : beanNamesForType) {
        System.out.println("Bean id:" + s);
    }
}

консольный вывод

image.png

Имя метода изменения метода под аннотацией @Bean в BeanConfig совершенно

@Bean
public Person stark(){
    Person person = new Person();
    person.setName("Stark");
    person.setAge(40);
    return person;
}

Снова запустите метод mian SingleBeanContainerTest, и консоль сообщит об ошибке Нет доступного компонента с именем «человек».

image.pngЭто связано с тем, что идентификатор bean-компонента, полученный в коде, — это человек, а имя метода под аннотацией @Bean в конфигурации указывает на то, что идентификатор bean-компонента является абсолютным, поэтому возникает ошибка, измените getBean("person") на getBean("stark") , выполнить снова, получить успех

image.pngПомимо изменения имени метода в аннотации @Bean для определения идентификатора компонента, вы также можете определить имя компонента с помощью @Bean("thor").

image.png

Результатом данных контейнера IoC является карта. Регистрация компонента заключается в добавлении данных на карту. Используйте put ("ключ", значение), ключ — это идентификатор компонента, а значение — объект.

Раздел 03 — XML и аннотации для пакетной регистрации бинов

@CompponmentScan вместо @Bean Добавьте три пакета, контроллер, сервис и dao соответственно, а также классы ответа. Три класса объявлены как bean-компоненты с помощью трех аннотаций @Controller, @Service и @Repository.

@Controller
public class PersonController {
}
@Service
public class PersonService {
}
@Repository
public class PersonDao {
}

Измените BeanConfig, добавьте аннотацию @ComponentScan и просканируйте все bean-компоненты в пакете com.citi в классе конфигурации BeanConfig, что эквивалентно наличию множества тегов bean-компонентов в XML-файле конфигурации.Преимущество заключается в том, что вам не нужно писать теги bean-компонентов один за другим. Может сканировать все bean-компоненты

@Configuration
@ComponentScan(basePackages = "com.citi")
public class BeanConfig {

}

Чтобы проверить, создает ли контейнер IoC экземпляр отсканированного компонента, добавьте тестовый класс ComponentScanTest.

public class ComponentScanTest {

    @Test
    public void getBeansByScan(){
        ApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class);
        String[] beanDefinitionNames = context.getBeanDefinitionNames();
        for (String beanDefinitionName : beanDefinitionNames) {
            System.out.println(beanDefinitionName);
        }
    }
}

Вывод консоли выглядит следующим образом: три пользовательских класса, включая сам BeanConfi, создаются контейнером.

image.png

Класс Person в пакете сущностей не создается, потому что к классу Person не добавлена ​​аннотация, то есть тег bean-компонента не настроен, поэтому он не зарегистрирован в контейнере и не создан. к аннотации класса сущностей Person, идентифицированной как Bean, снова выполните тестовый метод, консоль выводит результат следующим образом, создается экземпляр класса Person

image.pngИсходный код @ComponentScan

image.pngИспользование includeFilters()

image.pngОбе функции includeFilters() и excludeFilters() возвращают массив фильтров.

FilterType — это класс перечисления, по умолчанию — ANNOTATION, метод аннотации

image.png

Измените код BeanConfig, добавьте includeFilters(), сканируйте только bean-компоненты, отмеченные аннотациями @Controller и @Service в пакете com.citi, и не забудьте useDefaultFilters = false

@Configuration
@ComponentScan(basePackages = "com.citi",
        includeFilters = {
            @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class})
        },
        useDefaultFilters = false)
public class BeanConfig {

}

Выполните тест, вывод консоли содержит только bean-компоненты, аннотированные с помощью @Controller, @Service, а классы, идентифицированные аннотациями @Componet и @Repository, не управляются контейнером.

image.png

Plus: Зачем настраивать useDefaultFilters = false при использовании includeFilters?

image.pngКогда useDefaultFilters = true, введите метод registerDefaultFilters()

image.pngВ этом методе bean-компоненты с аннотацией @Component добавляются в includeFilters, а @Controller, @Service и @Repository основаны на аннотациях @Component. @Service.вступить в силу

Используйте excludeFilters(), измените includeFilters в BeanConfig на excludeFilters() и useDefaultFilters = true, выполните тест, видно, что включены только созданные объекты других bean-компонентов, кроме bean-компонентов, отмеченных @Controller и @Serviceimage.png

Используйте ASSIGNABLE_TYPE в классе перечисления FilterType для фильтрации bean-компонентов и измените BeanConfig следующим образом, указав, что затронуты другие bean-компоненты, отличные от PersonController и PersonService.

@Configuration
@ComponentScan(basePackages = "com.citi",
        excludeFilters = {
            @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {PersonController.class, PersonService.class})
        },
        useDefaultFilters = true)
public class BeanConfig {

}

image.png

Используйте пользовательский FilterType, добавьте CustTypeFilter в пакет конфигурации, наследуйте TypeFilter

public class CustTypeFilter implements TypeFilter {

    /**
     * @param metadataReader 读取到当前正在扫描的类的信息
     * @param metadataReaderFactory 可以获取到其他任何类的信息
     * @return
     * @throws IOException
     */
    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        // 获取当前类的注解信息
        AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
        // 获取当前正在扫描的类信息
        ClassMetadata classMetadata = metadataReader.getClassMetadata();
        // 获取当前类资源(类的路径)
        Resource resource = metadataReader.getResource();

        // 获取类的名称
        String className = classMetadata.getClassName();
        // 扫描的类
        System.out.println("------>" + className);

        // 过滤条件,return true表示过滤掉符合这个条件的Bean
        if (className.contains("service")){
            return true;
        }
        return false;
    }
}

image.png

В дополнение к @ComponentScan вы также можете добавить @ComponentScans, то есть несколько @ComponentScan

image.png

Используются две аннотации @ComponentScan: одна предназначена для удаления пользовательского компонента, то есть компонента, идентификатор которого содержит строку «service», а другой использует фильтр типа аннотации для удаления компонента, отмеченного аннотацией @Controller.

@ComponentScans({@ComponentScan(basePackages = "com.citi",
        excludeFilters = {
                @ComponentScan.Filter(type = FilterType.CUSTOM, classes = {CustTypeFilter.class})
        },
        useDefaultFilters = true),
        @ComponentScan(basePackages = "com.citi",
                excludeFilters = {
                        @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
                },
                useDefaultFilters = true)})

public class BeanConfig {

}

Здесь консоль должна выводить экземпляры объектов, отличных от PeresonController и PersonService.Согласно печати консоли видно, что два настроенных @ComponentScan не действуют.Это еще предстоит изучить здесь.

image.png

Раздел 04 - Одиночные и множественные экземпляры бинов

Независимо от того, использует ли контейнер IoC аннотации или методы xml, по умолчанию используется один экземпляр, который будет создан при инициализации контейнера IoC, а несколько экземпляров будут созданы только при использовании bean-компонента. Измените BeanConfi на

@Configuration
public class BeanConfig {

        @Bean
        public Person person(){
                Person person = new Person();
                person.setName("Stark");
                person.setAge(40);
                return person;
        }
}

Добавьте новый тестовый класс SingleOrMultInstanceTest и выполните метод testSingleBean().

public class SingleOrMultiInstanceTest {

    @Test
    public void testSingleBean(){
        ApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class);
        Person person = (Person) context.getBean("person");
        Person person1 = (Person) context.getBean("person");
        System.out.println("是否为单实例:" + (person == person1));
    }
}

Просмотрите результат вывода, сравнив, являются ли два bean-компонента, полученные из контейнера, одним bean-компонентом, по умолчанию это один экземпляр.

image.png

Аннотация @Scope может настроить bean-компонент как один экземпляр или несколько экземпляров. Исходный код @Scope

image.png

image.png

Изменить конфигурацию компонента

@Configuration
public class BeanConfig {

        @Bean
        @Scope("prototype")
        public Person person(){
                Person person = new Person();
                person.setName("Stark");
                person.setAge(40);
                return person;
        }
}

Выполните тест, который на данный момент уже является мультиэкземпляром.

image.png

Раздел 05 - Ленивая загрузка @Lazy

Измените BeanConfig, добавьте аннотацию @Lazy, указатель ленивой загрузки для нескольких компонентов с одним экземпляром, поскольку объекты с одним экземпляром создаются при инициализации контейнера.После добавления аннотации @Lazy контейнер будет создавать экземпляр компонента только при вызове getBean() для получения экземпляра объекта. Сначала аннотируйте аннотацию @Scope в BeanConfig

@Configuration
public class BeanConfig {

        //@Lazy
        @Bean
        //@Scope("prototype")
        public Person person(){
                System.out.println("Bean被实例化");
                Person person = new Person();
                person.setName("Stark");
                person.setAge(40);
                return person;
        }
}

Добавлен LazyLoadTest, метод testLazyLoad() содержит только код для инициализации контейнера, выполнить этот метод

public class LazyLoadTest {

    @Test
    public void testLazyLoad(){
        ApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class);
        //Person person = (Person) context.getBean("person");
        //System.out.println(person);
    }
}

Проверьте консоль и напечатайте, что экземпляр Bean создан, указывая на то, что операция создания экземпляра Bean выполняется при инициализации контейнера.

image.png

Отпустите аннотацию @Lazy в BeanConfig и выполните ее снова, экземпляр Bean не создается.

image.png

Добавьте код для получения компонента в методе testLazyLoad() класса LazyLoadTest.

public class LazyLoadTest {

    @Test
    public void testLazyLoad(){
        ApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class);
        Person person = (Person) context.getBean("person");
        System.out.println(person);
    }
}

Выполнить снова, в сочетании с последним результатом выполнения, указывающим, что после добавления аннотации @Lazy инициализация контейнера не создает экземпляр Бин, но будет создавать Бин, когда Бин будет получен.

image.png