Основная ссылка этого блога:Чтение официального веб-сайта Spring (4) BeanDefinition (часть 1)
Представлять темы
Почему вы хотите читать исходный код Spring?Некоторые люди хотят изучать передовые идеи в Spring, а некоторые люди хотят лучше понимать шаблоны проектирования.Конечно, большое количество мелких партнеров должны справиться с интервью, жизненный цикл Spring Bean, Spring AOP Принцип Spring IoC, принцип Spring IoC, чтобы справиться с интервью, прочитайте несколько блогов и посмотрите исходный код, проблем быть не должно, но если вы действительно хотите понять Spring, вам нужно потратить много времени, нужно обратить внимание Начнем с самого основного, сегодня мы рассмотрим основу Spring — BeanDefinition.
Что такое BeanDefinition
На официальном сайте Spring есть подробная инструкция, переведем: Контейнер SpringIoc управляет bean-компонентом или bean-компонентами, созданными из метаданных конфигурации, которые мы предоставляем контейнеру (например, определения в xml). В контейнере эти определения компонентов представлены объектами BeanDefinition, которые содержат следующие метаданные:
- Полное имя класса, обычно фактический класс реализации компонента;
- Элементы конфигурации поведения бина, которые описывают поведение бина в контейнере (область действия, обратные вызовы жизненного цикла и т. д.);
- Ссылки на другие bean-компоненты, которые необходимы bean-компоненту для выполнения своей работы, также известные как соавторы или зависимости;
- Другая информация о конфигурации, например компонент, управляющий пулом соединений, ограничивает размер пула или количество используемых соединений.
Объяснение BeanDefinition на официальном веб-сайте Spring по-прежнему очень подробное, но его не так просто понять На самом деле BeanDefinition относительно легко объяснить: BeanDefinition используется для описания Bean или BeanDefinition — это определение Bean.
Создайте Java Bean, вероятно, следующий соус:Написанный нами файл Java будет скомпилирован в файл класса, запустите программу, загрузчик классов загрузит файл класса, поместит его в область методов JVM, и мы сможем с радостью создавать новые объекты.
Приготовьте Spring Bean, вероятно, со следующим соусом:Написанный нами файл Java будет скомпилирован в файл класса, запустите программу, и загрузчик классов загрузит файл класса и поместит его в область методов JVM.Этот шаг остается неизменным (конечно, это не может быть измененный...) Следующее касается Spring, Spring будет анализировать наш класс конфигурации (файл конфигурации), предполагая, что сейчас настроен только A, после разбора Spring поместит BeanDefinition A в карту, а затем по одному BeanPostProcessor один за другим После обработки bean-компоненты, прошедшие полный жизненный цикл Spring, окончательно помещаются в singleObjects.
Вид с высоты птичьего полета на диаграмму классов BeanDefinition
Как видите, диаграмма классов BeanDefinition в Spring довольно сложна.Когда я впервые начал читать исходный код Spring, я думал, что BeanDefinition должен быть очень простым, но позже обнаружил, что это не так.
Ниже я буду интерпретировать задействованные классы один за другим.
AttributeAccessor
AttributeAccessor — это интерфейс:
/**
* Interface defining a generic contract for attaching and accessing metadata
* to/from arbitrary objects.
*
* @author Rob Harrop
* @since 2.0
*/
public interface AttributeAccessor {
void setAttribute(String name, @Nullable Object value);
Object getAttribute(String name);
Object removeAttribute(String name);
boolean hasAttribute(String name);
String[] attributeNames();
}
Давайте посмотрим на комментарии над классом: интерфейс определяет общие методы для сохранения или чтения метаданных. Поскольку это интерфейс, должен быть класс реализации, так что давайте отложим это в сторону.
BeanMetadataElement
BeanMetadataElement также является интерфейсом, который определяет только один метод:
/**
* Interface to be implemented by bean metadata elements
* that carry a configuration source object.
*
* @author Juergen Hoeller
* @since 2.0
*/
public interface BeanMetadataElement {
@Nullable
Object getSource();
}
Давайте взглянем на аннотации к классу: интерфейс предоставляет метод для получения исходного объекта бина. Этот исходный объект является исходным файлом. Как насчет этого? Это не очень просто понять, это не имеет значения, давайте напишем код, чтобы увидеть:
@Configuration
@ComponentScan
public class AppConfig {
}
@Service
public class BookService {
}
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
System.out.println(context.getBeanDefinition("bookService").getSource());
}
}
file [D:\cycleinject\target\classes\com\codebear\springcycle\BookService.class]
Как, теперь я понимаю.
AttributeAccessorSupport
Класс AttributeAccessorSupport — это абстрактный класс, реализующий интерфейс AttributeAccessor. Помните этот AttributeAccessor, который определяет общий метод для сохранения или чтения виртуальных методов метаданных. AttributeAccessorSupport реализует этот виртуальный метод, а AttributeAccessorSupport определяет контейнер карты. Метаданные хранятся в этой карте. .
Зачем эта карта
Когда я впервые прочитал исходный код Spring, я почувствовал себя немного странно, увидев эту карту: разве метаданные не должны храниться в полях beanClass, scope и lazyInit BeanDefinition? Разве эта карта не одна вещь за раз?
Позже я узнал, что Spring для удобства расширения. В противном случае, если BeanDefinition имеет новые возможности, необходимо добавить новые поля. Это одно из них. Во-вторых, если программист хочет расширить Spring, поля, определенные в BeanDefinition, могут больше не удовлетворяют расширения.
Использует ли Spring эту карту сам по себе? Ответ положительный. Давайте посмотрим, какие данные Spring помещает в эту карту:
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
BeanDefinition appConfig = context.getBeanDefinition("appConfig");
for (String item : appConfig.attributeNames()) {
System.out.println(item + ":" + appConfig.getAttribute(item));
}
}
org.springframework.context.annotation.ConfigurationClassPostProcessor.configurationClass:full
org.springframework.aop.framework.autoproxy.AutoProxyUtils.preserveTargetClass:true
Как видите, Spring помещает в него два элемента:
- Первый элемент сохраняет, является ли класс конфигурации полным классом конфигурации.О классе полной конфигурации я кратко представил его в предыдущем блоге:Что вы могли не знать о весне (2)
- Второй пункт, как видно из названия, связан с АОП.
BeanDefinition
BeanDefinition — это интерфейс, который наследует AttributeAccessor и BeanMetadataElement, которые были представлены выше.
BeanDefinition определяет множество методов, таких как setBeanClassName, getBeanClassName, setScope, getScope, setLazyInit, isLazyInit и т. д. Эти методы понятны с первого взгляда и не будут здесь объясняться.
BeanMetadataAttributeAccessor
BeanMetadataAttributeAccessor наследует AttributeAccessorSupport и дополнительно инкапсулирует метод сохранения или чтения метаданных.
AbstractBeanDefinition
AbstractBeanDefinition — это абстрактный класс, который наследует BeanMetadataAttributeAccessor и реализует BeanDefinition.
BeanDefinition реализует большинство виртуальных методов, определенных BeanDefinition, и определяет множество констант и значений по умолчанию.
AbstractBeanDefinition имеет три подкласса, давайте рассмотрим эти три подкласса.
ChildBeanDefinition
Начиная с Spring 2.5, ChildBeanDefinition больше не используется, его заменяет GenericBeanDefinition.
GenericBeanDefinition
GenericBeanDefinition заменяет ChildBeanDefinition. Конечно, есть.
public class ChildService {
private int id;
private String name;
public ChildService(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class ParentService {
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public ParentService(int id, String name) {
this.id = id;
this.name = name;
}
}
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
GenericBeanDefinition parentBeanDefinition = new GenericBeanDefinition();
parentBeanDefinition.setScope(BeanDefinition.SCOPE_SINGLETON);
parentBeanDefinition.setAttribute("name", "codebear");
parentBeanDefinition.setAbstract(true);
parentBeanDefinition.getConstructorArgumentValues().addGenericArgumentValue(1);
parentBeanDefinition.getConstructorArgumentValues().addGenericArgumentValue("CodeBear");
GenericBeanDefinition childBeanDefinition = new GenericBeanDefinition();
childBeanDefinition.setParentName("parent");
childBeanDefinition.setBeanClass(ChildService.class);
context.registerBeanDefinition("parent", parentBeanDefinition);
context.registerBeanDefinition("child", childBeanDefinition);
context.refresh();
BeanDefinition child = context.getBeanFactory().getMergedBeanDefinition("child");
for (String s : child.attributeNames()) {
System.out.println(s + ":" + child.getAttribute(s));
}
System.out.println("scope:" + child.getScope());
System.out.println("-------------------");
ChildService service = context.getBean(ChildService.class);
System.out.println(service.getName());
System.out.println(service.getId());
}
результат операции:
name:codebear
scope:singleton
-------------------
CodeBear
1
Проанализируем код:
- Создал объект GenericBeanDefinition parentBeanDefinition, установил его в одноэлементный режим, установил Атрибут и объявил два значения параметров конструктора;
- Создал объект GenericBeanDefinition childBeanDefinition, установил parentName в parent, BeanClass в ChildService;
- Зарегистрируйте parentBeanDefinition, beanName — родительский, childBeanDefinition, beanName — дочерний;
- обновить контейнер;
- Дочерний объект извлекается из mergedBeanDefinitions, а в mergedBeanDefinitions сохраняется объединенный BeanDefinition;
- Распечатайте дочерний атрибут, область действия и два значения параметров конструктора.
Как видите, childBeanDefinition наследует parentBeanDefinition.
Если отношения родитель-потомок отсутствуют, как только BeanDefinition, оно также может быть представлено GenericBeanDefinition:
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
GenericBeanDefinition genericBeanDefinition = new GenericBeanDefinition();
genericBeanDefinition.setBeanClass(AuthorService.class);
genericBeanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);
context.registerBeanDefinition("authorService", genericBeanDefinition);
context.refresh();
BeanDefinition mergedBeanDefinition = context.getBeanFactory().getMergedBeanDefinition("authorService");
BeanDefinition beanDefinition = context.getBeanFactory().getMergedBeanDefinition("authorService");
System.out.println(mergedBeanDefinition);
System.out.println(beanDefinition);
результат операции:
Root bean: class [com.codebear.springcycle.AuthorService]; scope=prototype; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null
Root bean: class [com.codebear.springcycle.AuthorService]; scope=prototype; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null
Можно видеть, что когда отношения родитель-потомок отсутствуют, beanDefinition по-прежнему будет храниться в mergedBeanDefinitions, но сохраненное содержимое точно такое же, как и в beanDefinitions.
GenericBeanDefinitionСводка
GenericBeanDefinition заменяет ChildBeanDefinition более ранней версии Spring. GenericBeanDefinition более гибок, чем ChildBeanDefinition и RootBeanDefinition. Его можно использовать как отдельное BeanDefinition, как родительское BeanDefinition или как дочернее GenericBeanDefinition.
RootBeanDefinition
Представляя GenericBeanDefinition, я написал два фрагмента кода.
Поставьте точку останова на первый код, наблюдайте за mergedBeanDefinitions, вы найдете parentBeanDefinition и childBeanDefinition становится RootBeanDefinition в mergedBeanDefinitions:
Поставьте точку останова на втором коде и наблюдайте за mergedBeanDefinitions, и вы обнаружите, что authorService также стал RootBeanDefinition в mergedBeanDefinitions:
Видно, что все RootBeanDefinitions хранятся в mergedBeanDefinitions.
RootBeanDefinition также можно использовать в качестве родительского BeanDefinition, как в следующем соусе:
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
RootBeanDefinition genericBeanDefinition = new RootBeanDefinition();
genericBeanDefinition.setBeanClass(ParentService.class);
genericBeanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);
context.registerBeanDefinition("parent", genericBeanDefinition);
GenericBeanDefinition rootBeanDefinition = new GenericBeanDefinition();
rootBeanDefinition.setBeanClass(ChildService.class);
rootBeanDefinition.setParentName("parent");
context.refresh();
}
Но RootBeanDefinition не может действовать как дочерний BeanDefinition:
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
RootBeanDefinition genericBeanDefinition = new RootBeanDefinition();
genericBeanDefinition.setBeanClass(ParentService.class);
genericBeanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);
context.registerBeanDefinition("parent", genericBeanDefinition);
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition();
rootBeanDefinition.setBeanClass(ChildService.class);
rootBeanDefinition.setParentName("parent");
context.refresh();
}
результат операции:
Exception in thread "main" java.lang.IllegalArgumentException: Root bean cannot be changed into a child bean with parent reference
at org.springframework.beans.factory.support.RootBeanDefinition.setParentName(RootBeanDefinition.java:260)
at com.codebear.springcycle.Main.main(Main.java:20)
Было выброшено исключение.
Исходный код запроса:
@Override
public void setParentName(@Nullable String parentName) {
if (parentName != null) {
throw new IllegalArgumentException("Root bean cannot be changed into a child bean with parent reference");
}
}
Обнаружено, что вызов метода setParentName RootBeanDefinition напрямую вызывает исключение.
RootBeanDefinitionСводка
RootBeanDefinition может использоваться как родительский BeanDefinition для других BeanDefinition, а также может использоваться только как BeanDefinition, но не может использоваться как дочерний BeanDefinition для других BeanDefinition.
ScannedGenericBeanDefinition
@Configuration
@ComponentScan
public class AppConfig {
}
@Service
public class AuthorService {
}
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
System.out.println(context.getBeanDefinition("authorService").getClass());
}
}
результат операции:
class org.springframework.context.annotation.ScannedGenericBeanDefinition
BeanDefinition бина, отсканированного аннотацией, представлен ScannedGenericBeanDefinition.
AnnotatedGenericBeanDefinition
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
System.out.println(context.getBeanDefinition("appConfig").getClass());
}
результат операции:
class org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition
BeanDefinition класса конфигурации представлен AnnotatedGenericBeanDefinition.
ConfigurationClassBeanDefinition
public class AuthorService {
}
@Configuration
@ComponentScan
public class AppConfig {
@Bean
public AuthorService authorService() {
return null;
}
}
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
System.out.println(context.getBeanDefinition("authorService").getClass());
}
результат операции:
class org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader$ConfigurationClassBeanDefinition
BeanDefinition бина, объявленного с помощью @Bean, представлено ConfigurationClassBeanDefinition.
Разве вы не ожидали, что BeanDefinition может включать в себя так много содержимого, которое бесполезно, если о нем говорят, что оно бесполезно; если оно полезно, оно также полезно. Если вы не понимаете этого содержания, вам будет сложно читать исходный код Spring, почему существует так много BeanDefinitions. В это время вы застрянете и будете отчаянно пытаться понять, для чего используются эти BeanDefinitions, но в Интернете не так уж много блогов о BeanDefinitions, а хороших блогов еще меньше, надеюсь, эта статья сможет восполнить это. Блок пустой.