«Вы сегодня брали интервью» - Весна

Spring
«Вы сегодня брали интервью» - Весна

предисловие

Сегодня была хорошая погода, и я с уверенной улыбкой пришел в научно-исследовательский центр крупного завода, чтобы начать день интервью. Во-первых, я не готов, я хорошо подготовлен к java concurrency, multithreading, jvm, Distributed и Database, я не ожидал, что тема сегодняшнего интервью — весна. Но, к счастью, я готов... Дверь открылась, и вошел молодой парень в макинтоше и очках, примерно моего возраста. Затем он жестом пригласил меня сесть и вежливо сказал: «Добро пожаловать в нашу компанию на собеседование, поговорим сегодня о весне»...

Сессия интервью

  • Интервьюер: Что вы имеете в виду под весной?

  • Я: Spring — это легкая среда разработки, предназначенная для повышения эффективности разработки разработчиков и удобства обслуживания системы. Spring Framework, на который мы обычно ссылаемся, относится к Spring Framework, который представляет собой набор многих модулей. Использование этих модулей может легко помочь нам в разработке. Этими модулями являются: основные контейнеры, доступ к данным/интеграция, веб, АОП (аспектно-ориентированное программирование), инструменты, обмен сообщениями и тестовые модули. Например, компонент Core в контейнере Core является ядром всех компонентов Spring, компонент Beans и компонент Context являются основой для реализации IOC и внедрения зависимостей, а компонент AOP используется для реализации аспектно-ориентированного программирования.

  • Интервьюер: Каковы преимущества использования среды Spring?

  • Я: Фреймворки позволяют нам программировать более эффективно и легче поддерживать наши системы.

  1. Легкий: Spring легкий по сравнению с другими фреймворками.
  2. Инверсия управления: Spring обеспечивает слабую связь через инверсию управления, когда объекты предоставляют свои зависимости вместо создания или поиска зависимых объектов.
  3. Аспектно-ориентированное программирование (АОП): Spring поддерживает аспектно-ориентированное программирование и отделяет бизнес-логику от системных служб.
  4. Контейнер: Spring содержит и управляет жизненным циклом и конфигурацией объектов в приложении.
  5. Фреймворк MVC: WEB-фреймворк Spring — это хорошо спроектированный фреймворк и хорошая альтернатива WEB-фреймворку.
  6. Управление транзакциями: Spring предоставляет постоянный интерфейс управления транзакциями, обеспечивая декларативные и программные транзакции.
  7. Обработка исключений: Spring предоставляет удобный API для преобразования исключений, зависящих от технологии, в согласованные непроверенные исключения.
  • Интервьюер: Вы упомянули инверсию управления пружиной во втором пункте, можете ли вы это объяснить?
  • Я: Во-первых, давайте объясним Инверсию Контроля. Инверсия управления (сокращенно IOC) — важный принцип объектно-ориентированного программирования, позволяющий уменьшить проблему связанности программ, а также ядро ​​Spring Framework. Применяя инверсию управления, при создании объекта ссылка на объект, от которого он зависит, передается ему внешней сущностью, управляющей всеми объектами в системе. Можно также сказать, что зависимости внедряются в объекты. Итак, инверсия управления — это то, как объект получает ссылки на объекты, от которых он зависит, инверсия ответственности. Кроме того, инверсия управления обычно делится на два типа: внедрение зависимостей (DI) и поиск зависимостей. Внедрение зависимостей широко используется.
    Также есть несколько часто задаваемых вопросов:
  1. Кто от кого зависит — конечно приложение зависит от контейнера IOC.
  2. Зачем нужна зависимость. Приложению нужен контейнер IOC для предоставления внешних ресурсов, необходимых объекту.
  3. Кто кого инжектит - очевидно IOC-контейнер инжектит объект приложения, объект, от которого зависит приложение
  4. Что инжектится - это инжект внешних ресурсов (включая объекты, ресурсы, константные данные) необходимых объекту
  • Интервьюер: В чем разница между IOC и новым объектом?

  • Я: Это разница между прямым и обратным ходом. Традиционные приложения активно контролируются нами в объекте, чтобы напрямую получить зависимый объект, то есть вращение вперед. Инверсия — это контейнер, помогающий нам создавать и внедрять зависимые объекты.

  • Интервьюер: Итак, каковы преимущества и недостатки МОК?

  • Я: Преимущества: Очевидно, реализована развязка между компонентами, улучшена гибкость и ремонтопригодность программы. Недостатки: Генерация объектов имеет некоторые потери в эффективности, потому что это рефлективное программирование. Но по сравнению с улучшенной ремонтопригодностью и гибкостью IOC эта потеря незначительна, если только генерация объекта не требует особо высокой эффективности.

  • Интервьюер: Spring управляет таким количеством объектов и определенно нуждается в контейнере. Можете ли вы рассказать о своем понимании контейнеров IOC?

  • Я: Во-первых, давайте объясним контейнер: в каждом фреймворке есть понятие контейнера, так называемый контейнер предназначен для инкапсуляции часто используемых сервисов, а затем пользователям нужно только следовать определенным правилам для достижения единства, гибкости, безопасности, удобства. и удобство быстрая цель.

  • Я: Тогда контейнер IOC — это контейнер с внедрением зависимостей, который отвечает за создание экземпляров, размещение, настройку объектов в приложении и установление зависимостей между этими объектами.

  • Интервьюер: Тогда не могли бы вы рассказать мне, как работает контейнер IOC?

  • Я: Во-первых, давайте поговорим о следующих двух концепциях.

  1. Концепция Bean: Bean — это объект, который инициализируется, собирается и управляется контейнером Spring, кроме того, bean ничем не отличается от других объектов в приложении.
  2. BeanDefinition метаданных: определите, как создавать экземпляры bean-компонентов, управлять зависимостями между bean-компонентами и управлять bean-компонентами, для чего требуются метаданные конфигурации, представленные BeanDefinition в spring.
  • Я: Вот как это работает:
  1. Подготовьте файл конфигурации: Объявление определения bean-компонента в файле конфигурации означает настройку метаданных для bean-компонента.
  2. Метаданные анализируются контейнером IOC: Bean Reader контейнера IOC считывает и анализирует файл конфигурации и создает объект метаданных конфигурации BeanDefinition в соответствии с определением Контейнер IOC создает экземпляры, конфигурирует и собирает компоненты в соответствии с BeanDefinition.
  3. Создание экземпляра контейнера IOC: клиент создает экземпляр контейнера и получает необходимые компоненты.
    Вот пример:
@Test  
       public void testHelloWorld() {  
             //1、读取配置文件实例化一个IoC容器  
             ApplicationContext context = new ClassPathXmlApplicationContext("helloworld.xml");  
             //2、从容器中获取Bean,注意此处完全“面向接口编程,而不是面向实现”  
              HelloApi helloApi = context.getBean("hello", HelloApi.class);  
              //3、执行业务逻辑  
              helloApi.sayHello();  
       }
  • Интервьюер: Тогда вы знаете разницу между BeanFactory и ApplicationContext?
  • Я:
  1. BeanFactory — самый простой интерфейс Spring. Он отвечает за чтение и чтение документов конфигурации bean-компонентов, управление загрузкой bean-компонентов, создание экземпляров, поддержание зависимостей между bean-компонентами и отвечает за жизненный цикл bean-компонентов.
  2. ApplicationContext является подинтерфейсом BeanFactory.В дополнение к предоставлению всех функций BeanFactory, указанных выше, он также предоставляет более полные функции структуры: такие как поддержка интернационализации, доступ к ресурсам, доставка событий и т. д. Обычно используемые методы для получения ApplicationContext: 2.1 FileSystemXmlApplicationContext: создается из файла конфигурации xml, указанного файловой системой или URL-адресом, параметром является имя файла конфигурации или массив имен файлов. 2.2 ClassPathXmlApplicationContext: создается из файла конфигурации xml пути к классам, файл конфигурации можно прочитать из пакета jar. 2.3 WebApplicationContextUtils: чтобы прочитать файл конфигурации из корневого каталога веб-приложения, его необходимо сначала настроить в файле web.xml, чего можно добиться путем настройки прослушивателя или сервлета.
  3. Существует большое различие между инициализацией ApplicationContext и BeanFactory: BeanFactory не создает экземпляры bean-компонентов при инициализации контейнера и создает экземпляры bean-компонентов только тогда, когда знает, что к bean-компоненту обращаются в первый раз; в то время как ApplicationContext создает экземпляры всех синглетонов при инициализации приложения. context.Example Bean, поэтому время инициализации ApplicationContext будет немного больше, чем у BeanFactory.
  • Интервьюер: Очень хорошо, кажется, вы хорошо разбираетесь в IOC-контейнере Spring. Тогда давайте поговорим о спринг-аоп, расскажите мне, что вы знаете о спринг-аоп.
  • Я: АОП (аспектно-ориентированное программирование) может инкапсулировать логику или обязанности (такие как обработка транзакций, управление журналами, контроль разрешений и т. д.), которые не связаны с бизнесом, но обычно вызываются бизнес-модулями, чтобы уменьшить повторяющиеся действия. код системы и уменьшить количество модулей.Степень связи между ними способствует будущей масштабируемости и ремонтопригодности. Вот сбор каштанов для обработки брёвен:
обработка журнала Метод реализации Преимущества и недостатки
жестко закодированный
Код обработки тот же, и код сильно связан
Метод извлечения, повторное использование кода
Введите код вручную, код сильно связан
aop
Горизонтальные функции извлекаются для формирования независимого модуля с низкой связью.
  • Интервьюер: Тогда вы знаете принцип весеннего аоп?

  • Я: spring aop основан на динамическом прокси.Если проксируемый объект реализует интерфейс, то spring aop будет использовать прокси jdk для создания прокси-объектов, но для объектов, которые не реализуют интерфейсы, динамический прокси jdk использовать нельзя.При этом время Spring aop будет использовать динамический прокси-сервер cglib.В это время spring aop будет использовать cglib для создания подкласса прокси-объекта в качестве прокси.

  • Я: Принцип динамического прокси можно посмотреть в этой моей статье:nuggets.capable/post/684490…

  • Интервьюер: Тогда вы знаете разницу между Spring Aop и AspecJ Aop?

  • Я: Spring AOP — это усовершенствование времени выполнения, а AspectJ — усовершенствование времени компиляции. Spring Aop основан на прокси, а AspectJ основан на манипулировании байт-кодом. Spring Aop интегрировал AspectJ, который следует рассматривать как наиболее полную структуру АОП в экосистеме Java. AspectJ более мощный, чем Spring AOP, но Spring AOP относительно проще. Если у нас меньше граней, то между ними нет большой разницы в производительности. Однако, когда аспектов слишком много, лучше выбрать AspectJ, который намного быстрее, чем Spring Aop.

  • Интервьюер: Вы знаете что-нибудь о бобах весной? Какие прицелы есть?

  • Я: У Beans in Spring есть пять типов областей видимости:

  1. singleton: единственный экземпляр bean-компонента. По умолчанию bean-компоненты в Spring являются синглтонами.
  2. прототип: новый экземпляр компонента создается при каждом запросе.
  3. request: Каждый HTTP-запрос будет генерировать новый Bean-компонент, который действителен только в рамках текущего HTTP-запроса.
  4. сеанс: новый bean-компонент генерируется каждый раз, когда выполняются HTTP-запросы, и bean-компонент действителен только в рамках текущего сеанса HTTP.
  5. global-session: глобальная область сеанса, которая имеет смысл только в веб-приложениях на основе портлетов, больше не доступна в Spring 5.
  • Интервьюер: Вы понимаете безопасность потоков singleton bean-компонентов в Spring?
  • Я: Большую часть времени мы не используем многопоточность в системе. Одноэлементные компоненты имеют проблемы с безопасностью потоков, в основном потому, что когда несколько потоков работают с одним и тем же объектом, операция записи в нестатические переменные-члены этого объекта будут иметь проблемы с безопасностью потоков. Есть два распространенных решения:
  1. Старайтесь избегать определения изменяемых переменных-членов в Beans (нереалистично).
  2. Определите переменную-член ThreadLocal в классе и сохраните необходимые переменные-члены в Threadlocal.
  • Интервьюер: Вы понимаете жизненный цикл Beans in Spring?
  • (Я подумал про себя, что этот процесс довольно сложен, но, к счастью, я записал его до того, как пришел.) Beans in Spring, вероятно, пройдет эти шаги от создания до уничтожения:
  1. Контейнер Bean находит определение Spring Bean в файле конфигурации.
  2. Контейнер Bean использует механизм отражения Java для создания экземпляра Bean.
  3. Если задействованы некоторые значения свойств, используйте метод set() для установки некоторых значений свойств.
  4. Если Бин реализует интерфейс BeanNameAware, вызовите метод setBeanName(), передав имя Бин.
  5. Если Бин реализует интерфейс BeanClassLoaderAware, вызовите метод setBeanClassLoader(), передав экземпляр объекта ClassLoader.
  6. Если Бин реализует интерфейс BeanFactoryAware, вызовите метод setBeanClassLoader() и передайте экземпляр объекта ClassLoader.
  7. Аналогично предыдущему, если реализованы другие интерфейсы *.Aware, вызываются соответствующие методы.
  8. Если есть объект BeaPostProcessor, связанный с контейнером Spring, который загрузил этот компонент, выполните метод postProcessBeforeInitialization().
  9. Если Бин реализует интерфейс InitializingBean, выполните метод afterPropertiesSet().
  10. Если определение Бина в файле конфигурации содержит атрибут init-method, выполнить указанный метод.
  11. Если есть объект BeanPostProcessor, связанный с контейнером Spring, который загрузил этот компонент, выполните метод postProcessAfterInitialization().
  12. При уничтожении компонента, если компонент реализует интерфейс DisposableBean, выполните метод destroy().
  13. Когда bean-компонент должен быть уничтожен, если определение bean-компонента в файле конфигурации содержит атрибут destroy-method, выполняется указанный метод.

  • Интервьюер: Знаете ли вы аннотации для объявления класса как Spring Bean?
  • Я: Обычно мы используем аннотацию @Autowried для автоподключения bean-компонентов.Чтобы идентифицировать класс как Bean-компонент, который можно использовать для автоподключения, можно использовать следующие аннотации:
  1. @Component: общая аннотация, которая может пометить любой класс как компонент Spring. Если компонент не знает, к какому слою он принадлежит, его можно пометить аннотацией @Component.
  2. @Repository: соответствующий уровень сохраняемости — это уровень Dao, который в основном используется для операций с базой данных.
  3. @Service: соответствует сервисному уровню, в основном включает некоторую сложную логику.
  4. @Controller: соответствует уровню управления Spring MVC, который в основном используется для получения пользовательских запросов и вызова уровня службы для возврата данных на интерфейсную страницу.
  • Интервьюер: В чем разница между @Component и @Bean?
  • Я: Тогда позвольте мне резюмировать:
  1. Действующие объекты разные: @Component действует на классы, @Bean действует на методы.
  2. @Component обычно автоматически обнаруживается и автоматически подключается к контейнеру Spring посредством сканирования пути к классу (используйте аннотацию @ComponentScan, чтобы определить путь для сканирования, чтобы определить классы, которые необходимо собрать и автоматически подключить к контейнеру Spring Bean). ). Аннотация @Bean обычно определяется в методе, отмеченном аннотацией для создания bean-компонента. @Bean сообщает Spring, что это экземпляр определенного класса, и возвращает его мне, когда мне нужно его использовать.
  3. Аннотация @Bean более настраиваема, чем аннотация @Component, и во многих местах можно зарегистрировать компоненты только через аннотацию @Bean, например классы в сторонних библиотеках.
  • Интервьюер: Кажется, вы хорошо разбираетесь в bean-компонентах Spring, поэтому не могли бы вы рассказать нам, что вы знаете о Spring MVC?
  • Я: Когда дело доходит до этой проблемы, я должен сказать, что Model1 и Model2 — это две эпохи без весеннего MVC.
  1. Эпоха Model1: все веб-приложение почти состоит из JSP-страниц, и только небольшое количество JavaBeans используется для обработки подключения к базе данных, доступа и других операций. В этом режиме JSP является как уровнем управления, так и уровнем представления. Очевидно, что с этой моделью связано много проблем: например, логика уровня управления и уровня представления смешивается вместе, что приводит к очень низкой частоте повторного использования кода; интерфейс и сервер взаимозависимы, что трудно тестировать и анализировать. эффективность разработки крайне низкая.
  2. Эпоха Model2: Друзья, изучавшие Servlet, должны понимать, что режим разработки «Java Bean (Model) + JSP (VIEW) + Servlet (Controller)» — это ранний режим веб-разработки на Java. В режиме Model2 еще много проблем.Степень абстракции и инкапсуляции далеко не достаточна.При использовании Model2 для разработки неизбежно повторение колес.
  • Я подумал об этом, а потом сказал: MVC — это шаблон проектирования, а Spring MVC — очень хороший фреймворк MVC. Spring MVC может помочь нам разработать более краткий веб-уровень, и он естественным образом интегрирован с инфраструктурой Spring. В Spring MVC мы обычно делим внутренние проекты на уровень службы (обработка бизнеса), уровень Dao (операция с базой данных), уровень сущности (класс сущности) и уровень контроллера (уровень управления, возвращающий данные во внешний интерфейс). Я рисую простую схему Spring MVC:

  • Интервьюер: Не могли бы вы рассказать обо всем процессе Spring MVC от приема запросов до возврата данных?
  • (Я подумал про себя: к счастью, я не забыл) Я: Да. Хотя этот процесс сложен, его нетрудно понять.
  1. Клиент (браузер) отправляет запрос непосредственно DispatcherServlet.
  2. DispatcherServlet вызывает HandlerMapping в соответствии с запросом и анализирует соответствующий обработчик запроса.
  3. После синтаксического анализа соответствующему обработчику (то есть контроллеру) он начинает обрабатываться адаптером HandlerAdapter.
  4. HandlerAdapter вызовет реальный обработчик в соответствии с обработчиком для обработки запроса и обработки соответствующей бизнес-логики.
  5. После того, как процессор обработает бизнес, он вернет объект ModelAndView, Model — это возвращаемый объект данных, а View — это логическое представление.
  6. ViewResolver будет искать фактическое представление на основе представления.
  7. DispatcherServlet передает возвращенную модель в представление (представление рендеринга).
  8. Вернуть представление запрашивающей стороне (браузеру).
  • Интервьюер: Знаете ли вы, какие шаблоны проектирования используются в среде Spring?
  • Я: Тогда позвольте мне подытожить.
  1. Фабричный шаблон проектирования: Spring использует фабричный шаблон для создания объектов Bean через BeanFactory и ApplicationContext.
  2. Шаблон проектирования прокси: реализация функциональности Spring AOP.
  3. Шаблон проектирования Singleton: Bean-компоненты в Spring по умолчанию являются одноэлементными.
  4. Режим метода шаблона: в Spring классы, работающие с базой данных, такие как jdbcTemplate и hibernateTemplate, заканчивающиеся на Template, используют режим шаблона.
  5. Шаблон проектирования оболочки: нашему проекту необходимо связать несколько баз данных, и разные клиенты будут обращаться к разным базам данных по мере необходимости при каждом посещении. Этот режим позволяет нам переключаться между различными источниками данных в соответствии с потребностями клиента.
  6. Шаблон наблюдателя. Модель Spring, управляемая событиями, является классическим применением шаблона наблюдателя.
  7. Шаблон адаптера: расширения или уведомления Spring AOP используют шаблон адаптера. SpringMVC также использует режим адаптера для адаптации контроллера.
  • Интервьюер: Вы использовали транзакции Spring? Как это используется?
  • Я: Конечно. Spring управляет транзакциями двумя способами:
  1. Программные транзакции: жестко закодированы в коде (устарели)
  2. Декларативные транзакции. Декларативные транзакции, настроенные в файле конфигурации, делятся на два типа: на основе XML и на основе аннотаций (рекомендуется). Чтобы использовать транзакцию Spring в проекте, вам нужно только добавить аннотацию @Transaction к нужному вам методу транзакции, затем этот метод добавляется с транзакцией.Если возникнет исключение, логика модификации данных во всем методе будет откат. Избегайте несоответствий данных.
  • Интервьюер: Какие уровни изоляции имеют транзакции Spring?
  • I: В интерфейсе TransactionDefinition определены пять констант уровня изоляции:
  1. ISOLATION_DEFAULT: используйте уровень изоляции по умолчанию для серверной базы данных (обычно просто используйте это), MySQL по умолчанию использует уровень изоляции REPEATABLE_READ, а Oracle по умолчанию использует уровень изоляции READ_COMMITTED.
  2. ISOLATION_READ_UNCOMMITTED: самый низкий уровень изоляции, который разрешает чтение незафиксированных данных, что может привести к грязному чтению, фантомному чтению или неповторяемому чтению.
  3. ISOLATION_READ_COMMITTED: разрешает чтение параллельных транзакций и зафиксированных данных, что может предотвратить грязное чтение, но фантомное чтение или неповторяющееся чтение все еще может происходить.
  4. ISOLATION_REPEATABLE_READ: результаты многократного чтения одного и того же поля согласуются, если только данные не изменены самой транзакцией, что может предотвратить грязное чтение и неповторяющееся чтение, но фантомное чтение все же может происходить.
  5. ISOLATION_SERIALIZABLE: Самый высокий уровень изоляции, все транзакции выполняются одна за другой, так что абсолютно исключена возможность интерференции между транзакциями, то есть этот уровень может предотвращать грязные чтения, неповторяемые чтения и фантомные чтения. Но это серьезно повлияет на производительность программы и обычно не используется.
  • Интервьюер: Тогда вы знаете, какие типы поведения распространения транзакций имеют транзакции Spring?(Интервьюер загадочно улыбается)
  • Я: (Я подумал: Ям здесь больше всего в транзакциях Spring, как это может быть непонятно) В TransactionDefinition определено 7 вариантов поведения распространения транзакций: Ситуация, поддерживающая текущую транзакцию
  1. PROPAGATION_REQUIRED: если транзакция уже существует, присоединиться к ней; если текущей транзакции нет, создать новую транзакцию.
  2. PROPAGATION_SUPPORTS: если в данный момент есть транзакция, присоединиться к транзакции; если текущей транзакции нет, продолжить выполнение в нетранзакционном режиме.
  3. PROPAGATION_MANDATORY: если в данный момент есть транзакция, присоединиться к транзакции; если текущей транзакции нет, выдать исключение (обязательно: обязательно) Случаи, когда текущая транзакция не поддерживается
  4. PROPAGATION_REQUIRES_NEW: создать новую транзакцию, если есть текущая транзакция, приостановить текущую транзакцию.
  5. PROPAGATION_NOT_SUPPORTED: работать без транзакций, если есть текущая транзакция, приостановить текущую транзакцию.
  6. PROPAGATION_NEVER: работает без транзакций, вызывая исключение, если в данный момент есть транзакция. Другие случаи
  7. PROPAGATION_NESTED: если транзакция в настоящее время существует, создайте транзакцию для выполнения как вложенную транзакцию текущей транзакции; если текущей транзакции нет, это значение эквивалентно PROPAGATION_REQUIRED.
  • Я: ценный блог, в котором перечислены несколько причин, по которым транзакции Spring не работают https://blog.csdn.net/f641385712/article/details/80445933. Здесь я перечисляю:
  1. Причина 1: Это вызвано неправильными настройками ядра базы данных. Например, наши часто используемые mysql и движок MyISAM не поддерживают операции транзакций и должны быть изменены на InnoDB для их поддержки.
  2. Причина 2: метод входа должен быть общедоступным, иначе транзакция не будет работать (это определяется функцией АОП Spring).Приватные методы, финальные методы и статические методы не могут добавлять транзакции, и они не вступят в силу, если они будут добавлены.
  3. Причина 3: управление транзакциями Spring по умолчанию откатывает только исключения времени выполнения (kava.lang.RuntimeException и его подклассы) (относительно того, почему Spring разработан таким образом: поскольку Spring считает, что проверенные исключения относятся к бизнесу, программисты должны давать решения, а не должны быть брошены непосредственно в фреймворк).
  4. Причина 4: @EnableTransactionManagement // Включить управление транзакциями аннотаций, что эквивалентно в конфигурации xml. Ни одна транзакция не была запущена с использованием этой аннотации.
  5. Причина 5: Убедитесь, что ваш класс проксирован. (Поскольку принцип реализации транзакций в Spring — это АОП, только вызов методов через прокси-объекты может быть перехвачен, и транзакции могут вступить в силу).
  6. Причина 6. Убедитесь, что ваш бизнес и транзакция находятся в одном потоке, иначе транзакция не вступит в силу, например следующий код:
@Transactional
@Override
public void save(User user1, User user2) {
    new Thread(() -> {
          saveError(user1, user2);
          System.out.println(1 / 0);
    }).start();
}

  1. Причина 7: нетранзакционный метод в том же классе вызывает другой транзакционный метод, и транзакция не будет работать. Например, следующий код:

  • Интервьюер: Кажется, вы хорошо разбираетесь в ключевых знаниях о структуре Spring, и основа очень прочная.Сегодня мы сначала поговорим здесь, и я надеюсь, что завтра вы сможете работать лучше.
  • Я улыбнулся и сказал: (втайне рад: прохождение уровня есть уровень) Что ж, я буду хорошо готовиться.