предисловие
Сегодня была хорошая погода, и я с уверенной улыбкой пришел в научно-исследовательский центр крупного завода, чтобы начать день интервью. Во-первых, я не готов, я хорошо подготовлен к java concurrency, multithreading, jvm, Distributed и Database, я не ожидал, что тема сегодняшнего интервью — весна. Но, к счастью, я готов... Дверь открылась, и вошел молодой парень в макинтоше и очках, примерно моего возраста. Затем он жестом пригласил меня сесть и вежливо сказал: «Добро пожаловать в нашу компанию на собеседование, поговорим сегодня о весне»...
Сессия интервью
-
Интервьюер: Что вы имеете в виду под весной?
-
Я: Spring — это легкая среда разработки, предназначенная для повышения эффективности разработки разработчиков и удобства обслуживания системы. Spring Framework, на который мы обычно ссылаемся, относится к Spring Framework, который представляет собой набор многих модулей. Использование этих модулей может легко помочь нам в разработке. Этими модулями являются: основные контейнеры, доступ к данным/интеграция, веб, АОП (аспектно-ориентированное программирование), инструменты, обмен сообщениями и тестовые модули. Например, компонент Core в контейнере Core является ядром всех компонентов Spring, компонент Beans и компонент Context являются основой для реализации IOC и внедрения зависимостей, а компонент AOP используется для реализации аспектно-ориентированного программирования.
-
Интервьюер: Каковы преимущества использования среды Spring?
-
Я: Фреймворки позволяют нам программировать более эффективно и легче поддерживать наши системы.
- Легкий: Spring легкий по сравнению с другими фреймворками.
- Инверсия управления: Spring обеспечивает слабую связь через инверсию управления, когда объекты предоставляют свои зависимости вместо создания или поиска зависимых объектов.
- Аспектно-ориентированное программирование (АОП): Spring поддерживает аспектно-ориентированное программирование и отделяет бизнес-логику от системных служб.
- Контейнер: Spring содержит и управляет жизненным циклом и конфигурацией объектов в приложении.
- Фреймворк MVC: WEB-фреймворк Spring — это хорошо спроектированный фреймворк и хорошая альтернатива WEB-фреймворку.
- Управление транзакциями: Spring предоставляет постоянный интерфейс управления транзакциями, обеспечивая декларативные и программные транзакции.
- Обработка исключений: Spring предоставляет удобный API для преобразования исключений, зависящих от технологии, в согласованные непроверенные исключения.
- Интервьюер: Вы упомянули инверсию управления пружиной во втором пункте, можете ли вы это объяснить?
- Я: Во-первых, давайте объясним Инверсию Контроля. Инверсия управления (сокращенно IOC) — важный принцип объектно-ориентированного программирования, позволяющий уменьшить проблему связанности программ, а также ядро Spring Framework. Применяя инверсию управления, при создании объекта ссылка на объект, от которого он зависит, передается ему внешней сущностью, управляющей всеми объектами в системе. Можно также сказать, что зависимости внедряются в объекты. Итак, инверсия управления — это то, как объект получает ссылки на объекты, от которых он зависит, инверсия ответственности. Кроме того, инверсия управления обычно делится на два типа: внедрение зависимостей (DI) и поиск зависимостей. Внедрение зависимостей широко используется.
Также есть несколько часто задаваемых вопросов:
- Кто от кого зависит — конечно приложение зависит от контейнера IOC.
- Зачем нужна зависимость. Приложению нужен контейнер IOC для предоставления внешних ресурсов, необходимых объекту.
- Кто кого инжектит - очевидно IOC-контейнер инжектит объект приложения, объект, от которого зависит приложение
- Что инжектится - это инжект внешних ресурсов (включая объекты, ресурсы, константные данные) необходимых объекту
-
Интервьюер: В чем разница между IOC и новым объектом?
-
Я: Это разница между прямым и обратным ходом. Традиционные приложения активно контролируются нами в объекте, чтобы напрямую получить зависимый объект, то есть вращение вперед. Инверсия — это контейнер, помогающий нам создавать и внедрять зависимые объекты.
-
Интервьюер: Итак, каковы преимущества и недостатки МОК?
-
Я: Преимущества: Очевидно, реализована развязка между компонентами, улучшена гибкость и ремонтопригодность программы. Недостатки: Генерация объектов имеет некоторые потери в эффективности, потому что это рефлективное программирование. Но по сравнению с улучшенной ремонтопригодностью и гибкостью IOC эта потеря незначительна, если только генерация объекта не требует особо высокой эффективности.
-
Интервьюер: Spring управляет таким количеством объектов и определенно нуждается в контейнере. Можете ли вы рассказать о своем понимании контейнеров IOC?
-
Я: Во-первых, давайте объясним контейнер: в каждом фреймворке есть понятие контейнера, так называемый контейнер предназначен для инкапсуляции часто используемых сервисов, а затем пользователям нужно только следовать определенным правилам для достижения единства, гибкости, безопасности, удобства. и удобство быстрая цель.
-
Я: Тогда контейнер IOC — это контейнер с внедрением зависимостей, который отвечает за создание экземпляров, размещение, настройку объектов в приложении и установление зависимостей между этими объектами.
-
Интервьюер: Тогда не могли бы вы рассказать мне, как работает контейнер IOC?
-
Я: Во-первых, давайте поговорим о следующих двух концепциях.
- Концепция Bean: Bean — это объект, который инициализируется, собирается и управляется контейнером Spring, кроме того, bean ничем не отличается от других объектов в приложении.
- BeanDefinition метаданных: определите, как создавать экземпляры bean-компонентов, управлять зависимостями между bean-компонентами и управлять bean-компонентами, для чего требуются метаданные конфигурации, представленные BeanDefinition в spring.
- Я: Вот как это работает:
- Подготовьте файл конфигурации: Объявление определения bean-компонента в файле конфигурации означает настройку метаданных для bean-компонента.
- Метаданные анализируются контейнером IOC: Bean Reader контейнера IOC считывает и анализирует файл конфигурации и создает объект метаданных конфигурации BeanDefinition в соответствии с определением Контейнер IOC создает экземпляры, конфигурирует и собирает компоненты в соответствии с BeanDefinition.
- Создание экземпляра контейнера 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?
- Я:
- BeanFactory — самый простой интерфейс Spring. Он отвечает за чтение и чтение документов конфигурации bean-компонентов, управление загрузкой bean-компонентов, создание экземпляров, поддержание зависимостей между bean-компонентами и отвечает за жизненный цикл bean-компонентов.
- ApplicationContext является подинтерфейсом BeanFactory.В дополнение к предоставлению всех функций BeanFactory, указанных выше, он также предоставляет более полные функции структуры: такие как поддержка интернационализации, доступ к ресурсам, доставка событий и т. д. Обычно используемые методы для получения ApplicationContext: 2.1 FileSystemXmlApplicationContext: создается из файла конфигурации xml, указанного файловой системой или URL-адресом, параметром является имя файла конфигурации или массив имен файлов. 2.2 ClassPathXmlApplicationContext: создается из файла конфигурации xml пути к классам, файл конфигурации можно прочитать из пакета jar. 2.3 WebApplicationContextUtils: чтобы прочитать файл конфигурации из корневого каталога веб-приложения, его необходимо сначала настроить в файле web.xml, чего можно добиться путем настройки прослушивателя или сервлета.
- Существует большое различие между инициализацией 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 есть пять типов областей видимости:
- singleton: единственный экземпляр bean-компонента. По умолчанию bean-компоненты в Spring являются синглтонами.
- прототип: новый экземпляр компонента создается при каждом запросе.
- request: Каждый HTTP-запрос будет генерировать новый Bean-компонент, который действителен только в рамках текущего HTTP-запроса.
- сеанс: новый bean-компонент генерируется каждый раз, когда выполняются HTTP-запросы, и bean-компонент действителен только в рамках текущего сеанса HTTP.
- global-session: глобальная область сеанса, которая имеет смысл только в веб-приложениях на основе портлетов, больше не доступна в Spring 5.
- Интервьюер: Вы понимаете безопасность потоков singleton bean-компонентов в Spring?
- Я: Большую часть времени мы не используем многопоточность в системе. Одноэлементные компоненты имеют проблемы с безопасностью потоков, в основном потому, что когда несколько потоков работают с одним и тем же объектом, операция записи в нестатические переменные-члены этого объекта будут иметь проблемы с безопасностью потоков. Есть два распространенных решения:
- Старайтесь избегать определения изменяемых переменных-членов в Beans (нереалистично).
- Определите переменную-член ThreadLocal в классе и сохраните необходимые переменные-члены в Threadlocal.
- Интервьюер: Вы понимаете жизненный цикл Beans in Spring?
- (Я подумал про себя, что этот процесс довольно сложен, но, к счастью, я записал его до того, как пришел.) Beans in Spring, вероятно, пройдет эти шаги от создания до уничтожения:
- Контейнер Bean находит определение Spring Bean в файле конфигурации.
- Контейнер Bean использует механизм отражения Java для создания экземпляра Bean.
- Если задействованы некоторые значения свойств, используйте метод set() для установки некоторых значений свойств.
- Если Бин реализует интерфейс BeanNameAware, вызовите метод setBeanName(), передав имя Бин.
- Если Бин реализует интерфейс BeanClassLoaderAware, вызовите метод setBeanClassLoader(), передав экземпляр объекта ClassLoader.
- Если Бин реализует интерфейс BeanFactoryAware, вызовите метод setBeanClassLoader() и передайте экземпляр объекта ClassLoader.
- Аналогично предыдущему, если реализованы другие интерфейсы *.Aware, вызываются соответствующие методы.
- Если есть объект BeaPostProcessor, связанный с контейнером Spring, который загрузил этот компонент, выполните метод postProcessBeforeInitialization().
- Если Бин реализует интерфейс InitializingBean, выполните метод afterPropertiesSet().
- Если определение Бина в файле конфигурации содержит атрибут init-method, выполнить указанный метод.
- Если есть объект BeanPostProcessor, связанный с контейнером Spring, который загрузил этот компонент, выполните метод postProcessAfterInitialization().
- При уничтожении компонента, если компонент реализует интерфейс DisposableBean, выполните метод destroy().
- Когда bean-компонент должен быть уничтожен, если определение bean-компонента в файле конфигурации содержит атрибут destroy-method, выполняется указанный метод.
- Интервьюер: Знаете ли вы аннотации для объявления класса как Spring Bean?
- Я: Обычно мы используем аннотацию @Autowried для автоподключения bean-компонентов.Чтобы идентифицировать класс как Bean-компонент, который можно использовать для автоподключения, можно использовать следующие аннотации:
- @Component: общая аннотация, которая может пометить любой класс как компонент Spring. Если компонент не знает, к какому слою он принадлежит, его можно пометить аннотацией @Component.
- @Repository: соответствующий уровень сохраняемости — это уровень Dao, который в основном используется для операций с базой данных.
- @Service: соответствует сервисному уровню, в основном включает некоторую сложную логику.
- @Controller: соответствует уровню управления Spring MVC, который в основном используется для получения пользовательских запросов и вызова уровня службы для возврата данных на интерфейсную страницу.
- Интервьюер: В чем разница между @Component и @Bean?
- Я: Тогда позвольте мне резюмировать:
- Действующие объекты разные: @Component действует на классы, @Bean действует на методы.
- @Component обычно автоматически обнаруживается и автоматически подключается к контейнеру Spring посредством сканирования пути к классу (используйте аннотацию @ComponentScan, чтобы определить путь для сканирования, чтобы определить классы, которые необходимо собрать и автоматически подключить к контейнеру Spring Bean). ). Аннотация @Bean обычно определяется в методе, отмеченном аннотацией для создания bean-компонента. @Bean сообщает Spring, что это экземпляр определенного класса, и возвращает его мне, когда мне нужно его использовать.
- Аннотация @Bean более настраиваема, чем аннотация @Component, и во многих местах можно зарегистрировать компоненты только через аннотацию @Bean, например классы в сторонних библиотеках.
- Интервьюер: Кажется, вы хорошо разбираетесь в bean-компонентах Spring, поэтому не могли бы вы рассказать нам, что вы знаете о Spring MVC?
- Я: Когда дело доходит до этой проблемы, я должен сказать, что Model1 и Model2 — это две эпохи без весеннего MVC.
- Эпоха Model1: все веб-приложение почти состоит из JSP-страниц, и только небольшое количество JavaBeans используется для обработки подключения к базе данных, доступа и других операций. В этом режиме JSP является как уровнем управления, так и уровнем представления. Очевидно, что с этой моделью связано много проблем: например, логика уровня управления и уровня представления смешивается вместе, что приводит к очень низкой частоте повторного использования кода; интерфейс и сервер взаимозависимы, что трудно тестировать и анализировать. эффективность разработки крайне низкая.
- Эпоха 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 от приема запросов до возврата данных?
- (Я подумал про себя: к счастью, я не забыл) Я: Да. Хотя этот процесс сложен, его нетрудно понять.
- Клиент (браузер) отправляет запрос непосредственно DispatcherServlet.
- DispatcherServlet вызывает HandlerMapping в соответствии с запросом и анализирует соответствующий обработчик запроса.
- После синтаксического анализа соответствующему обработчику (то есть контроллеру) он начинает обрабатываться адаптером HandlerAdapter.
- HandlerAdapter вызовет реальный обработчик в соответствии с обработчиком для обработки запроса и обработки соответствующей бизнес-логики.
- После того, как процессор обработает бизнес, он вернет объект ModelAndView, Model — это возвращаемый объект данных, а View — это логическое представление.
- ViewResolver будет искать фактическое представление на основе представления.
- DispatcherServlet передает возвращенную модель в представление (представление рендеринга).
- Вернуть представление запрашивающей стороне (браузеру).
- Интервьюер: Знаете ли вы, какие шаблоны проектирования используются в среде Spring?
- Я: Тогда позвольте мне подытожить.
- Фабричный шаблон проектирования: Spring использует фабричный шаблон для создания объектов Bean через BeanFactory и ApplicationContext.
- Шаблон проектирования прокси: реализация функциональности Spring AOP.
- Шаблон проектирования Singleton: Bean-компоненты в Spring по умолчанию являются одноэлементными.
- Режим метода шаблона: в Spring классы, работающие с базой данных, такие как jdbcTemplate и hibernateTemplate, заканчивающиеся на Template, используют режим шаблона.
- Шаблон проектирования оболочки: нашему проекту необходимо связать несколько баз данных, и разные клиенты будут обращаться к разным базам данных по мере необходимости при каждом посещении. Этот режим позволяет нам переключаться между различными источниками данных в соответствии с потребностями клиента.
- Шаблон наблюдателя. Модель Spring, управляемая событиями, является классическим применением шаблона наблюдателя.
- Шаблон адаптера: расширения или уведомления Spring AOP используют шаблон адаптера. SpringMVC также использует режим адаптера для адаптации контроллера.
- Интервьюер: Вы использовали транзакции Spring? Как это используется?
- Я: Конечно. Spring управляет транзакциями двумя способами:
- Программные транзакции: жестко закодированы в коде (устарели)
- Декларативные транзакции. Декларативные транзакции, настроенные в файле конфигурации, делятся на два типа: на основе XML и на основе аннотаций (рекомендуется). Чтобы использовать транзакцию Spring в проекте, вам нужно только добавить аннотацию @Transaction к нужному вам методу транзакции, затем этот метод добавляется с транзакцией.Если возникнет исключение, логика модификации данных во всем методе будет откат. Избегайте несоответствий данных.
- Интервьюер: Какие уровни изоляции имеют транзакции Spring?
- I: В интерфейсе TransactionDefinition определены пять констант уровня изоляции:
- ISOLATION_DEFAULT: используйте уровень изоляции по умолчанию для серверной базы данных (обычно просто используйте это), MySQL по умолчанию использует уровень изоляции REPEATABLE_READ, а Oracle по умолчанию использует уровень изоляции READ_COMMITTED.
- ISOLATION_READ_UNCOMMITTED: самый низкий уровень изоляции, который разрешает чтение незафиксированных данных, что может привести к грязному чтению, фантомному чтению или неповторяемому чтению.
- ISOLATION_READ_COMMITTED: разрешает чтение параллельных транзакций и зафиксированных данных, что может предотвратить грязное чтение, но фантомное чтение или неповторяющееся чтение все еще может происходить.
- ISOLATION_REPEATABLE_READ: результаты многократного чтения одного и того же поля согласуются, если только данные не изменены самой транзакцией, что может предотвратить грязное чтение и неповторяющееся чтение, но фантомное чтение все же может происходить.
- ISOLATION_SERIALIZABLE: Самый высокий уровень изоляции, все транзакции выполняются одна за другой, так что абсолютно исключена возможность интерференции между транзакциями, то есть этот уровень может предотвращать грязные чтения, неповторяемые чтения и фантомные чтения. Но это серьезно повлияет на производительность программы и обычно не используется.
- Интервьюер: Тогда вы знаете, какие типы поведения распространения транзакций имеют транзакции Spring?(Интервьюер загадочно улыбается)
- Я: (Я подумал: Ям здесь больше всего в транзакциях Spring, как это может быть непонятно) В TransactionDefinition определено 7 вариантов поведения распространения транзакций: Ситуация, поддерживающая текущую транзакцию
- PROPAGATION_REQUIRED: если транзакция уже существует, присоединиться к ней; если текущей транзакции нет, создать новую транзакцию.
- PROPAGATION_SUPPORTS: если в данный момент есть транзакция, присоединиться к транзакции; если текущей транзакции нет, продолжить выполнение в нетранзакционном режиме.
- PROPAGATION_MANDATORY: если в данный момент есть транзакция, присоединиться к транзакции; если текущей транзакции нет, выдать исключение (обязательно: обязательно) Случаи, когда текущая транзакция не поддерживается
- PROPAGATION_REQUIRES_NEW: создать новую транзакцию, если есть текущая транзакция, приостановить текущую транзакцию.
- PROPAGATION_NOT_SUPPORTED: работать без транзакций, если есть текущая транзакция, приостановить текущую транзакцию.
- PROPAGATION_NEVER: работает без транзакций, вызывая исключение, если в данный момент есть транзакция. Другие случаи
- PROPAGATION_NESTED: если транзакция в настоящее время существует, создайте транзакцию для выполнения как вложенную транзакцию текущей транзакции; если текущей транзакции нет, это значение эквивалентно PROPAGATION_REQUIRED.
- Я: ценный блог, в котором перечислены несколько причин, по которым транзакции Spring не работают https://blog.csdn.net/f641385712/article/details/80445933. Здесь я перечисляю:
- Причина 1: Это вызвано неправильными настройками ядра базы данных. Например, наши часто используемые mysql и движок MyISAM не поддерживают операции транзакций и должны быть изменены на InnoDB для их поддержки.
- Причина 2: метод входа должен быть общедоступным, иначе транзакция не будет работать (это определяется функцией АОП Spring).Приватные методы, финальные методы и статические методы не могут добавлять транзакции, и они не вступят в силу, если они будут добавлены.
- Причина 3: управление транзакциями Spring по умолчанию откатывает только исключения времени выполнения (kava.lang.RuntimeException и его подклассы) (относительно того, почему Spring разработан таким образом: поскольку Spring считает, что проверенные исключения относятся к бизнесу, программисты должны давать решения, а не должны быть брошены непосредственно в фреймворк).
- Причина 4: @EnableTransactionManagement // Включить управление транзакциями аннотаций, что эквивалентно
в конфигурации xml. Ни одна транзакция не была запущена с использованием этой аннотации. - Причина 5: Убедитесь, что ваш класс проксирован. (Поскольку принцип реализации транзакций в Spring — это АОП, только вызов методов через прокси-объекты может быть перехвачен, и транзакции могут вступить в силу).
- Причина 6. Убедитесь, что ваш бизнес и транзакция находятся в одном потоке, иначе транзакция не вступит в силу, например следующий код:
@Transactional
@Override
public void save(User user1, User user2) {
new Thread(() -> {
saveError(user1, user2);
System.out.println(1 / 0);
}).start();
}
- Причина 7: нетранзакционный метод в том же классе вызывает другой транзакционный метод, и транзакция не будет работать. Например, следующий код:
- Интервьюер: Кажется, вы хорошо разбираетесь в ключевых знаниях о структуре Spring, и основа очень прочная.Сегодня мы сначала поговорим здесь, и я надеюсь, что завтра вы сможете работать лучше.
- Я улыбнулся и сказал: (втайне рад: прохождение уровня есть уровень) Что ж, я буду хорошо готовиться.