Автор: Брат Сяофу
Блог:bugstack.cn
Осаждайте, делитесь, растите и позвольте себе и другим что-то получить! 😄
Введение
忒复杂,没等搞明白大促都过去了!
вы испытали618
и双11
? Вы когда-нибудь зарабатывали несколько центов на стольких сложных маркетинговых кампаниях, присоединившись к крупной рекламной акции? Вы когда-нибудь разрабатывали большую акцию, которая требует недели, чтобы понять, как играть, но использует ее только в течение 3 дней? Иногда требования к некоторым продуктам действительно слишком сложны.Это настолько сложно, что разработка и тестирование требуют непрерывного обучения в течение всего процесса.Наконец-то можно понять, почему продукт так играет.Если это долгосрочная деятельность , это может быть хорошо. Развивайте ум пользователя! Но весь этот набор новых операций, продвижение, активация, оформление заказа, страховка, сбор талонов, потребление, открытие красного конверта и т.д., если это займет всего 3 дня онлайн, или только 1 день, то ТМ даже если участвующие пользователи не не понял, событие закончилось.Какие хорошие данные можно назвать в итоге?Для такого сложного процесса подсчитано, что даже шерсти не видно! ! !
Выше это просто пример, в большинстве случаев будет не так противно, да и обзор будет сложным! Тот же принцип применим к проектированию, разработке и использованию программы. Если вы реализуете логику своего кода слишком разрозненно, внешним вызывающим сторонам придется вызывать ваш интерфейс несколько раз и много раз при его использовании. Когда приходит сообщение, вы можете только периодически обучать свой интерфейс проверить статус заказа.Вы можете проверить только 10 элементов за раз.Если вы отметите слишком много, вы скажете «нет», и другие античеловеческие конструкции принесут звонящему опыт работы с вами.
Поэтому, если мы сможем выполнить поставленную задачу, мы все надеемся, что процесс будет максимально простым, режим понятным, а обслуживание будет автоматическим. Затем это также отражено в фреймворке Spring. Популярность этого фреймворка неотделима от удобства, которое он может принести. Если мы можем добиться такого удобства, это должно быть хорошо, дизайн и реализация.
2. Цели
На самом деле в этой главе мы уже реализовали все основное содержимое IOC и AOP, но использование немного похоже на более раннюю версию Spring, которую нужно настраивать в spring.xml один за другим. Это сильно отличается от реальной среды Spring, используемой в настоящее время, и это отличие фактически основано на базовой функциональной логике и используется более простым способом с меньшим количеством настроек.
Это включает в себя: сканирование регистрации пакетов, использование конфигурации аннотаций, заполнение атрибутов-заполнителей и т. д., и наша цель — заполнить некоторые автоматизированные функции текущей основной логикой, чтобы каждый мог изучить эту часть дизайна и реализации, из которого я понял некоторый процесс реализации логики кода и обобщил некоторый опыт кодирования.
3. Схема
Прежде всего, нам нужно учитывать 🤔, чтобы упростить настройку объектов bean-компонента и позволить автоматически сканировать регистрацию всего объекта bean-компонента, необходимые основные элементы включают: запись пути сканирования, информацию о сканировании синтаксического анализа XML и аннотации для Объекты bean-компонентов, которые необходимо сканировать Отметьте и отсканируйте объект Class, чтобы извлечь основную информацию о регистрации Bean-компонента, собрать регистрационную информацию и зарегистрировать его как объект Bean-компонента. Затем при поддержке этих условных элементов можно завершить регистрацию объекта Bean, настроив аннотацию и настроив путь сканирования. Кроме того, решите точку знаний атрибута заполнителя в конфигурации, например, вы можете передать${token}
Внедрить информацию об атрибутах в объект Bean, затем эта операция должна использовать BeanFactoryPostProcessor, поскольку она может обрабатыватьПредоставляет механизм для изменения свойств BeanDefinition после загрузки всех BeanDefinition и перед созданием экземпляра объекта Bean.Эта часть контента реализована для последующего включения такого контента в автоматизированную обработку конфигурации. Общая структура конструкции выглядит следующим образом:
В сочетании с жизненным циклом компонента сканирование пакетов представляет собой не что иное, как сканирование конкретных аннотированных классов, извлечение соответствующей информации о классах и их сборка в BeanDefinitions для регистрации в контейнере.
Анализ в XmlBeanDefinitionReader<context:component-scan />
Метка, сканирование класса для сборки BeanDefinition и последующая регистрация в контейнере реализованы в ClassPathBeanDefinitionScanner#doScan.
- Регистрация автоматического сканирования в основном сканирует классы с добавленными пользовательскими аннотациями, извлекает информацию классов во время процесса загрузки XML и собирает GENADEFINITION и регистрирует его в контейнере пружины.
- Поэтому мы будем использовать
<context:component-scan />
Настройте путь к пакету и проанализируйте его в XmlBeanDefinitionReader и выполните соответствующую обработку.Обработка здесь будет включать сканирование классов, получение аннотационной информации и т. д. - Наконец, часть о
BeanFactoryPostProcessor
Поскольку нам необходимо завершить загрузку информации о конфигурации заполнителя, нам нужно использовать BeanFactoryPostProcessor для изменения информации об атрибутах BeanDefinition после загрузки всех BeanDefinitions и до создания объекта Bean.Для этого часть также размещается на заполнителе для последующей обработки для подготовки аннотаций.
4. Реализация
1. Инженерное сооружение
small-spring-step-13
└── src
├── main
│ └── java
│ └── cn.bugstack.springframework
│ ├── aop
│ │ ├── aspectj
│ │ │ └── AspectJExpressionPointcut.java
│ │ │ └── AspectJExpressionPointcutAdvisor.java
│ │ ├── framework
│ │ │ ├── adapter
│ │ │ │ └── MethodBeforeAdviceInterceptor.java
│ │ │ ├── autoproxy
│ │ │ │ └── MethodBeforeAdviceInterceptor.java
│ │ │ ├── AopProxy.java
│ │ │ ├── Cglib2AopProxy.java
│ │ │ ├── JdkDynamicAopProxy.java
│ │ │ ├── ProxyFactory.java
│ │ │ └── ReflectiveMethodInvocation.java
│ │ ├── AdvisedSupport.java
│ │ ├── Advisor.java
│ │ ├── BeforeAdvice.java
│ │ ├── ClassFilter.java
│ │ ├── MethodBeforeAdvice.java
│ │ ├── MethodMatcher.java
│ │ ├── Pointcut.java
│ │ ├── PointcutAdvisor.java
│ │ └── TargetSource.java
│ ├── beans
│ │ ├── factory
│ │ │ ├── config
│ │ │ │ ├── AutowireCapableBeanFactory.java
│ │ │ │ ├── BeanDefinition.java
│ │ │ │ ├── BeanFactoryPostProcessor.java
│ │ │ │ ├── BeanPostProcessor.java
│ │ │ │ ├── BeanReference.java
│ │ │ │ ├── ConfigurableBeanFactory.java
│ │ │ │ ├── InstantiationAwareBeanPostProcessor.java
│ │ │ │ └── SingletonBeanRegistry.java
│ │ │ ├── support
│ │ │ │ ├── AbstractAutowireCapableBeanFactory.java
│ │ │ │ ├── AbstractBeanDefinitionReader.java
│ │ │ │ ├── AbstractBeanFactory.java
│ │ │ │ ├── BeanDefinitionReader.java
│ │ │ │ ├── BeanDefinitionRegistry.java
│ │ │ │ ├── CglibSubclassingInstantiationStrategy.java
│ │ │ │ ├── DefaultListableBeanFactory.java
│ │ │ │ ├── DefaultSingletonBeanRegistry.java
│ │ │ │ ├── DisposableBeanAdapter.java
│ │ │ │ ├── FactoryBeanRegistrySupport.java
│ │ │ │ ├── InstantiationStrategy.java
│ │ │ │ └── SimpleInstantiationStrategy.java
│ │ │ ├── support
│ │ │ │ └── XmlBeanDefinitionReader.java
│ │ │ ├── Aware.java
│ │ │ ├── BeanClassLoaderAware.java
│ │ │ ├── BeanFactory.java
│ │ │ ├── BeanFactoryAware.java
│ │ │ ├── BeanNameAware.java
│ │ │ ├── ConfigurableListableBeanFactory.java
│ │ │ ├── DisposableBean.java
│ │ │ ├── FactoryBean.java
│ │ │ ├── HierarchicalBeanFactory.java
│ │ │ ├── InitializingBean.java
│ │ │ ├── ListableBeanFactory.java
│ │ │ └── PropertyPlaceholderConfigurer.java
│ │ ├── BeansException.java
│ │ ├── PropertyValue.java
│ │ └── PropertyValues.java
│ ├── context
│ │ ├── annotation
│ │ │ ├── ClassPathBeanDefinitionScanner.java
│ │ │ ├── ClassPathScanningCandidateComponentProvider.java
│ │ │ └── Scope.java
│ │ ├── event
│ │ │ ├── AbstractApplicationEventMulticaster.java
│ │ │ ├── ApplicationContextEvent.java
│ │ │ ├── ApplicationEventMulticaster.java
│ │ │ ├── ContextClosedEvent.java
│ │ │ ├── ContextRefreshedEvent.java
│ │ │ └── SimpleApplicationEventMulticaster.java
│ │ ├── support
│ │ │ ├── AbstractApplicationContext.java
│ │ │ ├── AbstractRefreshableApplicationContext.java
│ │ │ ├── AbstractXmlApplicationContext.java
│ │ │ ├── ApplicationContextAwareProcessor.java
│ │ │ └── ClassPathXmlApplicationContext.java
│ │ ├── ApplicationContext.java
│ │ ├── ApplicationContextAware.java
│ │ ├── ApplicationEvent.java
│ │ ├── ApplicationEventPublisher.java
│ │ ├── ApplicationListener.java
│ │ └── ConfigurableApplicationContext.java
│ ├── core.io
│ │ ├── ClassPathResource.java
│ │ ├── DefaultResourceLoader.java
│ │ ├── FileSystemResource.java
│ │ ├── Resource.java
│ │ ├── ResourceLoader.java
│ │ └── UrlResource.java
│ ├── stereotype
│ │ └── Component.java
│ └── utils
│ └── ClassUtils.java
└── test
└── java
└── cn.bugstack.springframework.test
├── bean
│ ├── IUserService.java
│ └── UserService.java
└── ApiTest.java
Исходный код проекта:公众号「bugstack虫洞栈」,回复:Spring 专栏,获取完整源码
Автоматически загружать пакет в жизненном цикле компонента для сканирования отношения классов зарегистрированных объектов компонента и установки атрибутов-заполнителей, как показано на рис. 14-2.
- С точки зрения структуры отношений всего класса, задействовано не так много контента, в основном включая использование ClassPathBeanDefinitionScanner#doScan классом синтаксического анализа xml XmlBeanDefinitionReader.
- В методе doScan обрабатываются все аннотированные классы по указанному пути, дизассемблируется информация о классе: имя, область действия и т. д. и создается BeanDefinition для операции регистрации объекта Bean.
- PropertyPlaceholderConfigurer в настоящее время выглядит как отдельный фрагмент содержимого, и в будущем содержимое этого фрагмента будет интегрировано с автоматически загружаемым объектом Bean, то есть вы можете использовать заполнители в аннотациях для настройки некоторой информации о свойствах в файле конфигурации.
2. Обработка конфигурации заполнителя
cn.bugstack.springframework.beans.factory.PropertyPlaceholderConfigurer
public class PropertyPlaceholderConfigurer implements BeanFactoryPostProcessor {
/**
* Default placeholder prefix: {@value}
*/
public static final String DEFAULT_PLACEHOLDER_PREFIX = "${";
/**
* Default placeholder suffix: {@value}
*/
public static final String DEFAULT_PLACEHOLDER_SUFFIX = "}";
private String location;
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// 加载属性文件
try {
DefaultResourceLoader resourceLoader = new DefaultResourceLoader();
Resource resource = resourceLoader.getResource(location);
Properties properties = new Properties();
properties.load(resource.getInputStream());
String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
for (String beanName : beanDefinitionNames) {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
PropertyValues propertyValues = beanDefinition.getPropertyValues();
for (PropertyValue propertyValue : propertyValues.getPropertyValues()) {
Object value = propertyValue.getValue();
if (!(value instanceof String)) continue;
String strVal = (String) value;
StringBuilder buffer = new StringBuilder(strVal);
int startIdx = strVal.indexOf(DEFAULT_PLACEHOLDER_PREFIX);
int stopIdx = strVal.indexOf(DEFAULT_PLACEHOLDER_SUFFIX);
if (startIdx != -1 && stopIdx != -1 && startIdx < stopIdx) {
String propKey = strVal.substring(startIdx + 2, stopIdx);
String propVal = properties.getProperty(propKey);
buffer.replace(startIdx, stopIdx + 1, propVal);
propertyValues.addPropertyValue(new PropertyValue(propertyValue.getName(), buffer.toString()));
}
}
}
} catch (IOException e) {
throw new BeansException("Could not load properties", e);
}
}
public void setLocation(String location) {
this.location = location;
}
}
- В зависимости от свойств BeanFactoryPostProcessor во время жизненного цикла компонента информация о свойствах может быть изменена до создания экземпляра объекта компонента. Таким образом, за счет реализации интерфейса BeanFactoryPostProcessor загрузка файла конфигурации и конфигурации в файле свойств в заполнителе извлечения завершена.
- Таким образом, извлеченная информация о конфигурации может быть помещена в конфигурацию атрибута,
buffer.replace(startIdx, stopIdx + 1, propVal); propertyValues.addPropertyValue
3. Определите аннотации перехвата
cn.bugstack.springframework.context.annotation.Scope
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Scope {
String value() default "singleton";
}
- Пользовательская аннотация, используемая для настройки области, удобна для получения области действия объекта компонента путем настройки аннотации объекта компонента.Однако обычно используется синглтон по умолчанию.
cn.bugstack.springframework.stereotype.Component
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Component {
String value() default "";
}
- Пользовательские аннотации компонентов хорошо знакомы всем и используются для их настройки в классе Class. Кроме того, есть Сервис и Контроллер, но все методы обработки в основном одинаковы, здесь показан только один Компонент.
4. Обработка сборки сканирования объекта
cn.bugstack.springframework.context.annotation.ClassPathScanningCandidateComponentProvider
public class ClassPathScanningCandidateComponentProvider {
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<>();
Set<Class<?>> classes = ClassUtil.scanPackageByAnnotation(basePackage, Component.class);
for (Class<?> clazz : classes) {
candidates.add(new BeanDefinition(clazz));
}
return candidates;
}
}
- Здесь мы должны сначала указать путь, который можно настроить через
basePackage=cn.bugstack.springframework.test.bean
Найдя инструментальный метод информации о классах FindcandidAmomponents, вы можете сканировать все аннотированные объекты bean-компонентов @component с помощью этого метода.
cn.bugstack.springframework.context.annotation.ClassPathBeanDefinitionScanner
public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider {
private BeanDefinitionRegistry registry;
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {
this.registry = registry;
}
public void doScan(String... basePackages) {
for (String basePackage : basePackages) {
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition beanDefinition : candidates) {
// 解析 Bean 的作用域 singleton、prototype
String beanScope = resolveBeanScope(beanDefinition);
if (StrUtil.isNotEmpty(beanScope)) {
beanDefinition.setScope(beanScope);
}
registry.registerBeanDefinition(determineBeanName(beanDefinition), beanDefinition);
}
}
}
private String resolveBeanScope(BeanDefinition beanDefinition) {
Class<?> beanClass = beanDefinition.getBeanClass();
Scope scope = beanClass.getAnnotation(Scope.class);
if (null != scope) return scope.value();
return StrUtil.EMPTY;
}
private String determineBeanName(BeanDefinition beanDefinition) {
Class<?> beanClass = beanDefinition.getBeanClass();
Component component = beanClass.getAnnotation(Component.class);
String value = component.value();
if (StrUtil.isEmpty(value)) {
value = StrUtil.lowerFirst(beanClass.getSimpleName());
}
return value;
}
}
- ClassPathBeanDefinitionScanner унаследован от класса ClassPathScanningCandidateComponentProvider, специфичного для обработки пакетов сканирования, в дополнение к информации о классе, полученной после сканирования, вам также необходимо получить имя класса и область действия Bean doScan, если имя класса не настроено в основном как акроним.
5. Вызов сканирования при разборе xml
cn.bugstack.springframework.beans.factory.xml.XmlBeanDefinitionReader
public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
protected void doLoadBeanDefinitions(InputStream inputStream) throws ClassNotFoundException, DocumentException {
SAXReader reader = new SAXReader();
Document document = reader.read(inputStream);
Element root = document.getRootElement();
// 解析 context:component-scan 标签,扫描包中的类并提取相关信息,用于组装 BeanDefinition
Element componentScan = root.element("component-scan");
if (null != componentScan) {
String scanPath = componentScan.attributeValue("base-package");
if (StrUtil.isEmpty(scanPath)) {
throw new BeansException("The value of base-package attribute can not be empty or null");
}
scanPackage(scanPath);
}
// ... 省略其他
// 注册 BeanDefinition
getRegistry().registerBeanDefinition(beanName, beanDefinition);
}
private void scanPackage(String scanPath) {
String[] basePackages = StrUtil.splitToArray(scanPath, ',');
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(getRegistry());
scanner.doScan(basePackages);
}
}
- Что касается XmlBeanDefinitionReader, он в основном обрабатывает недавно добавленные настраиваемые свойства конфигурации после загрузки файла конфигурации.
component-scan
, вызовите метод scanPackage после синтаксического анализа, что собственно и делается в функции ClassPathBeanDefinitionScanner#doScan. - Кроме того, здесь следует отметить, что для облегчения загрузки и разбора xml XmlBeanDefinitionReader был заменен на dom4j для разбора.
5. Тест
1. Подготовьтесь заранее
@Component("userService")
public class UserService implements IUserService {
private String token;
public String queryUserInfo() {
try {
Thread.sleep(new Random(1).nextInt(100));
} catch (InterruptedException e) {
e.printStackTrace();
}
return "小傅哥,100001,深圳";
}
public String register(String userName) {
try {
Thread.sleep(new Random(1).nextInt(100));
} catch (InterruptedException e) {
e.printStackTrace();
}
return "注册用户:" + userName + " success!";
}
@Override
public String toString() {
return "UserService#token = { " + token + " }";
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
}
- Добавьте пользовательскую аннотацию в класс UserService.
@Component("userService")
и атрибутивная информацияString token
. Это необходимо для проверки свойств сканирования пакета и заполнителя по отдельности.
2. Файл конфигурации свойств
token=RejDlI78hu223Opo983Ds
- Настройте здесь информацию об атрибутах токена, которая используется для его получения с помощью заполнителей.
3. объект конфигурации spring.xml
spring-property.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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context">
<bean class="cn.bugstack.springframework.beans.factory.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:token.properties"/>
</bean>
<bean id="userService" class="cn.bugstack.springframework.test.bean.UserService">
<property name="token" value="${token}"/>
</bean>
</beans>
- нагрузка
classpath:token.properties
Установить значение свойства-заполнителя${token}
spring-scan.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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context">
<context:component-scan base-package="cn.bugstack.springframework.test.bean"/>
</beans>
- Добавить к
component-scan
свойства, установите корневой путь сканирования пакета
4. Модульный тест (заполнитель)
@Test
public void test_property() {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring-property.xml");
IUserService userService = applicationContext.getBean("userService", IUserService.class);
System.out.println("测试结果:" + userService);
}
Результаты теста
测试结果:UserService#token = { RejDlI78hu223Opo983Ds }
Process finished with exit code 0
- По результатам теста мы видим, что атрибут токена в UserService был установлен в файле конфигурации с помощью заполнителей.
token.properties
стоимость имущества.
5. Модульное тестирование (сканирование пакетов)
@Test
public void test_scan() {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring-scan.xml");
IUserService userService = applicationContext.getBean("userService", IUserService.class);
System.out.println("测试结果:" + userService.queryUserInfo());
}
Результаты теста
测试结果:小傅哥,100001,深圳
Process finished with exit code 0
- По результатам этого теста вы можете видеть, что то, как вы используете аннотации, может сделать регистрацию класса для завершения объекта bean.
6. Резюме
- Из реализации всего контента видно, что текущие добавления функций не сложны, и все они основаны на ядре IOC и АОП для завершения функций. Эти функции завершения также улучшают жизненный цикл Bean-компонентов, упрощая использование всей функции.
- Когда вы постоянно реализуете различные функции Spring, вы также можете включить некоторые функциональные идеи, которые вы обычно используете в Spring, например, как Spring динамически переключает источники данных и как пул потоков обеспечивает конфигурацию. но и очень важное.
- Могут быть случаи, когда эти классы реализуют много контента для новичков, и вы можете постепенно понять их с помощью небольшого практического применения.После реализации немного более сложного контента на самом деле не так сложно понять позже.
Семь, рекомендация серии
- Выпускник в 2013 году, потребовалось два года от аутсорсинга, чтобы войти в крупную интернет-фабрику.
- Я работаю уже два-три года, но не понимаю, при чем тут схемы архитектуры?
- Сцена интервью: обмен и анализ стороны маленького партнера Meituan (включая ответы)
- LinkedList вставляет быстрее, чем ArrayList? уверены ли вы?
- Битва Netty + JavaFx: имитация микроканала настольного чата