Введение в Spring
Фреймворк Spring был разработан Родом Джонсоном, а первая версия была выпущена в 2004 году. Spring — это фреймворк, выделенный из реальной разработки, поэтому он берёт на себя многие типовые шаги разработки, оставляя разработчикам только части, специфичные для приложения, что значительно повышает эффективность разработки корпоративных приложений.
Преимущества Spring можно кратко сформулировать так:
- Дизайн с низкой инвазивностью и очень низким загрязнением кода.
- Не зависит от различных серверов приложений. Приложения на основе Spring framework могут действительно реализовать обещание «написать один раз — запускать где угодно».
- IoC-контейнер Spring снижает сложность замены бизнес-объектов и уменьшает связанность между компонентами.
- Поддержка AOP в Spring позволяет централизованно управлять общими задачами, такими как безопасность, транзакции и логирование, обеспечивая тем самым лучшее повторное использование.
- ORM и DAO в Spring обеспечивают хорошую интеграцию со сторонними фреймворками персистентности и упрощают низкоуровневый доступ к базе данных.
- Spring отличается высокой открытостью и не принуждает приложения полностью полагаться на Spring. Разработчики свободны выбирать часть или весь фреймворк Spring.
Схема структуры компоновки фреймворка Spring выглядит следующим образом:

Основной механизм Spring
Управление бинами
Программы в основном получают доступ к бинам в контейнере через контейнер Spring. ApplicationContext — наиболее часто используемый интерфейс контейнера Spring. Этот интерфейс имеет следующие два класса реализации:
- ClassPathXmlApplicationContext: ищет файлы конфигурации по пути загрузки классов и создаёт контейнер Spring на основе этих файлов конфигурации.
- FileSystemXmlApplicationContext: ищет файлы конфигурации по относительному или абсолютному пути в файловой системе и создаёт контейнер Spring на основе этих файлов конфигурации.
public class BeanTest{
public static void main(String args[]) throws Exception{
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
Person p = ctx.getBean("person", Person.class);
p.say();
}
}
Использование Spring в Eclipse
В таких инструментах IDE, как Eclipse, пользователи могут создать свой собственныйUser Library, а затем поместить в него все JAR-пакеты Spring. Конечно, они также могут поместить JAR-пакеты непосредственно в/WEB-INF/libкаталог проекта. Однако, если использоватьUser Library, при публикации проекта JAR-файлы, на которые ссылается пользовательская библиотека, должны публиковаться вместе с приложением, то есть скопируйте JAR-файлы, используемые пользовательской библиотекой, в/WEB-INF/libкаталог. Это потому, что для веб-приложения, когда Eclipse развертывает веб-приложение, он не копирует JAR-файлы из пользовательской библиотеки в/WEB-INF/libкаталог, и требуется скопировать вручную.
Внедрение зависимостей
Фреймворк Spring имеет две основные функции:
- Контейнер Spring действует как суперфабрика, отвечающая за создание и управление всеми объектами Java. Эти объекты Java называются Beans.
- Контейнер Spring управляет зависимостями между Beans в контейнере. Spring использует метод, называемый "внедрением зависимостей", для управления зависимостями между Beans.
Используя внедрение зависимостей, можно не только внедрять в Beans обычные значения свойств, но и внедрять ссылки на другие Beans. Внедрение зависимостей — это превосходный метод ослабления связности, который позволяет организовывать Beans вместе через конфигурационные файлы, а не связывать их жёстко закодированным образом.
Понимание внедрения зависимостей
Род Джонсон первым придал большое значение управлению взаимосвязями экземпляров Java через конфигурационные файлы. Он дал этому методу название: инверсия управления (IoC). Позже Мартин Фаулер дал ему другое название: внедрение зависимостей. Поэтому, будь то внедрение зависимостей или инверсия управления, смысл абсолютно одинаков. Когда объекту Java (вызывающему) нужно вызвать метод другого объекта Java (зависимого объекта), в традиционной модели обычно существует два подхода:
- Исходный подход: вызывающий объектактивносоздаёт зависимый объект, а затем вызывает метод зависимого объекта.
- Шаблон простая фабрика: вызывающий объект сначала находит фабрику зависимого объекта, затемактивнополучает зависимый объект через фабрику и наконец вызывает метод зависимого объекта.
Обратите внимание на"активно"в вышесказанном. Это неизбежно приведёт к жёсткой связи между вызывающей стороной и классом реализации зависимого объекта, что крайне неблагоприятно для обновления и сопровождения проекта. После использования фреймворка Spring вызывающей стороне не нужноактивнополучать зависимый объект. Вызывающей стороне нужно лишьпассивнопринимать, что контейнер Spring присваивает значения переменным-членам вызывающей стороны. Видно, что после использования Spring способ получения зависимого объекта вызывающей стороной меняется с активного получения на пассивное принятие — поэтому Род Джонсон назвал это инверсией управления.
Кроме того, с точки зрения контейнера Spring, контейнер Spring отвечает за присваивание зависимого объекта переменным-членам вызывающей стороны — это эквивалентно внедрению экземпляра, от которого она зависит, в вызывающую сторону. Поэтому Мартин Фаулер назвал это внедрением зависимостей.
Внедрение через сеттер
Внедрение через сеттер означает, что контейнер IoC внедряет зависимые объекты через методы-сеттеры переменных-членов. Этот способ внедрения прост и интуитивно понятен, поэтому он широко используется во внедрении зависимостей Spring.
Внедрение через конструктор
Способ установки зависимостей с помощью конструкторов называется внедрением через конструктор. Проще говоря, он заставляет Spring на нижнем уровне выполнить конструктор с указанными параметрами через рефлексию. Когда выполняется параметризованный конструктор, параметры конструктора можно использовать для инициализации переменных-членов — в этом суть внедрения через конструктор.
Сравнение двух способов внедрения
Внедрение через сеттер имеет следующие преимущества:
- Оно больше похоже на традиционное написание JavaBean, что облегчает разработчикам понимание и принятие. Установка зависимостей через методы-сеттеры более интуитивна и естественна.
- Для сложных зависимостей, если используется внедрение через конструктор, конструктор может стать слишком перегруженным и трудным для чтения. Когда Spring создаёт экземпляр бина, ему также приходится одновременно создавать экземпляры всех его зависимостей, что приводит к снижению производительности. Внедрение через сеттер позволяет избежать этих проблем.
- Особенно когда некоторые переменные-члены являются необязательными, конструкторы с несколькими параметрами становятся ещё более громоздкими.
Преимущества внедрения через конструктор следующие:
- Внедрение через конструктор позволяет определить порядок внедрения зависимостей в конструкторе, при этом зависимости с более высоким приоритетом внедряются первыми.
- Для бинов, зависимости которых не должны меняться, внедрение через конструктор более полезно. Поскольку методы-сеттеры отсутствуют, все зависимости устанавливаются в конструкторе, поэтому не нужно беспокоиться о том, что последующий код нарушит отношения зависимостей.
- Если зависимости могут быть установлены только в конструкторе, то только создатель компонента может изменять зависимости компонента. Для вызывающего кода компонента внутренние отношения зависимостей компонента полностью прозрачны, что в большей степени соответствует принципу высокой связности.
Примечание:
Рекомендуется использовать внедрение через сеттер как основной подход, а внедрение через конструктор — как дополнение. Для внедрений, где отношение зависимости не должно меняться, старайтесь использовать внедрение через конструктор; для других внедрений зависимостей рассмотрите возможность использования внедрения через сеттер.
Бины в контейнере Spring
Для разработчиков использование фреймворка Spring в основном сводится к двум вещам: ① разработка бинов; ② конфигурирование бинов. Для фреймворка Spring нужно создавать экземпляры бинов в соответствии с конфигурационным файлом и вызывать методы экземпляров бинов для выполнения «внедрения зависимостей» — в этом суть так называемого IoC.
Области видимости бинов в контейнере
При создании экземпляра бина через контейнер Spring можно не только создать экземпляр бина, но и указать для бина определённую область действия. Spring поддерживает следующие пять областей действия:
- singleton: Одиночный режим. Во всём контейнере Spring IoC для бина с областью действия singleton будет создан только один экземпляр.
- prototype: Каждый раз, когда бин с областью действия prototype получается через метод getBean() контейнера, создаётся новый экземпляр бина.
- request: Для одного HTTP-запроса бин с областью действия request создаст только один экземпляр. Это означает, что в рамках одного и того же HTTP-запроса при каждом запросе программы к этому бину она всегда получает один и тот же экземпляр. Эта область действия действительно работает только тогда, когда Spring используется в веб-приложении.
- session: Эта область действия ограничивает определение бина рамками HTTP-сеанса. Она действительна только в контексте веб-ориентированного Spring ApplicationContext.
- global session: Каждому глобальному HTTP-сеансу соответствует один экземпляр бина. В типичных случаях она действует только при использовании контекста портлета и также действительна только в веб-приложениях.
Если область действия бина не указана, Spring по умолчанию использует область действия singleton. Создание и уничтожение бинов с областью действия prototype относительно затратно. Напротив, после создания экземпляра бина с областью действия singleton его можно повторно использовать. Поэтому следует по возможности избегать установки для бинов области действия prototype.
Внедрение взаимодействующих бинов с помощью автосвязывания
Spring может автоматически связывать зависимости между бинами. То есть нет необходимости явно указывать зависимый бин с помощью ref. Вместо этого контейнер Spring анализирует содержимое XML-файла конфигурации и внедряет зависимый бин в вызывающий бин в соответствии с определёнными правилами.
Автосвязывание Spring можно указать с помощью<beans/>элементаdefault-autowireатрибута. Этот атрибут применяется ко всем бинам в файле конфигурации. Его также можно указать с помощью<bean/>элементаautowireатрибута, который влияет только на этот бин.
autowireиdefault-autowireОн может принимать следующие значения:
no: Автосвязывание не используется. Зависимости Bean должны определяться через элемент ref. Это конфигурация по умолчанию. В крупных средах развёртывания изменение этой конфигурации не рекомендуется; явная настройка взаимодействующих компонентов может обеспечить более ясные зависимости.byName: Автосвязывание на основе имён setter-методов. Контейнер Spring ищет все Bean в контейнере, находит Bean, id которого совпадает с именем setter-метода без префикса "set" и с первой буквой в нижнем регистре, и выполняет внедрение. Если подходящий экземпляр Bean не найден, Spring не выполняет внедрение.byType: Автосвязывание на основе типа параметра setter-метода. Контейнер Spring просматривает все Bean в контейнере. Если существует ровно один Bean, тип которого совпадает с типом параметра setter-метода, этот Bean внедряется автоматически; если найдено несколько таких Bean, выбрасывается исключение; если ни один такой Bean не найден, ничего не происходит, и setter-метод не вызывается.constructor: Похоже на byType, за исключением того, что используется для автоматического сопоставления параметров конструктора. Если контейнер не может найти ровно один Bean, соответствующий типу параметра конструктора, будет выброшено исключение.autodetect: Контейнер Spring сам решает, использовать ли стратегию конструктора или byType, основываясь на внутренней структуре Bean. Если найден конструктор по умолчанию, будет применена стратегия byType.
Когда бин использует и автоматически связываемые зависимости, и явно указанные зависимости через ref, явно указанные зависимости переопределяют автоматически связываемые зависимости. В крупных приложениях автоматическое связывание не рекомендуется. Хотя автоматическое связывание может уменьшить объём работы с файлами конфигурации, оно значительно снижает ясность и прозрачность зависимостей. Сборка зависимостей опирается на имена и типы свойств в исходных файлах, из-за чего связанность между бинами сводится к уровню кода, что не способствует высокоуровневому разъединению.
<!--通过设置可以将Bean排除在自动装配之外--> <bean id="" autowire-candidate="false"/> <!--除此之外,还可以在beans元素中指定,支持模式字符串,如下所有以abc结尾的Bean都被排除在自动装配之外--> <beans default-autowire-candidates="*abc"/>
Три способа создания бинов
Создание экземпляров бинов с помощью конструкторов
Использование конструктора для создания экземпляра бина — наиболее распространённый случай. Если внедрение через конструктор не используется, Spring внутри вызывает конструктор без аргументов класса бина для создания экземпляра. Поэтому класс бина должен предоставлять конструктор без аргументов.
При использовании конструктора по умолчанию для создания экземпляра бина Spring выполняет инициализацию по умолчанию всех свойств экземпляра бина, то есть все значения примитивных типов инициализируются как 0 или false, а все значения ссылочных типов инициализируются как null.
Создание бинов с помощью статических фабричных методов
При использовании статического фабричного метода для создания экземпляра бина также необходимо указать атрибут class. Однако в этом случае атрибут class указывает не класс реализации экземпляра бина, а статический фабричный класс. Spring использует этот атрибут, чтобы узнать, какой фабричный класс создаст экземпляр бина.
Кроме того, для указания статического фабричного метода необходимо использовать атрибут factory-method. Spring вызовет статический фабричный метод, чтобы вернуть экземпляр бина. После получения указанного экземпляра бина последующие шаги обработки Spring точно такие же, как при создании экземпляра бина с помощью обычного метода. Если статическому фабричному методу требуются параметры, используйте<constructor-arg.../>элемент для указания параметров статического фабричного метода.
Создание Bean путем вызова методов экземплярной фабрики
Единственное различие между методом экземплярной фабрики и статическим фабричным методом состоит в том, что для вызова статического фабричного метода требуется только фабричный класс, тогда как для вызова метода экземплярной фабрики требуется экземпляр фабрики. При использовании метода экземплярной фабрики<bean.../>элемент не нуждается в атрибуте class. Чтобы настроить метод экземплярной фабрики, используйтеfactory-beanчтобы указать экземпляр фабрики.
Этот<bean.../>элемент для создания Bean с использованием метода экземплярной фабрики требует указания следующих двух атрибутов:
- factory-bean: значение этого атрибута — id фабричного Bean.
- factory-method: этот атрибут задает фабричный метод экземплярной фабрики.
Если при вызове метода экземплярной фабрики нужно передать параметры, используйте<constructor-arg.../>элемент для определения значений параметров.
Согласование Bean с разными областями действия
Когда Bean с областью действия singleton зависит от Bean с областью действия prototype, возникает проблема синхронизации. Это связано с тем, что при инициализации контейнера Spring он предварительно инициализирует всеsingleton Bean, и посколькуsingleton Beanзависит отprototype Bean, когда Spring инициализируетсяsingleton Beanперед этим, он сначала создастprototypeBean— и только затем создастsingleton Bean, а затемprototype Beanвнедритьsingleton Bean。
Существует два способа решения проблемы синхронизации:
- Отказ от внедрения зависимостей: когда Bean с singleton-областью видимости нуждается в Bean с prototype-областью видимости, он активно запрашивает у контейнера новый экземпляр Bean, тем самым гарантируя, что каждый внедрённый
prototype Beanэкземпляр является последним экземпляром. - Использование внедрения методов: внедрение методов обычно использует внедрение метода поиска. Внедрение метода поиска позволяет контейнеру Spring переопределять абстрактные или конкретные методы Bean в контейнере, возвращая результат поиска других Bean в контейнере. Найденный Bean обычно является
non-singleton Bean. Spring достигает вышеуказанного требования, используя динамические прокси JDK или библиотеку cglib для модификации двоичного кода клиента.
Рекомендуется использовать второй метод — внедрение методов. Чтобы использовать внедрение метода поиска, необходимы примерно следующие два шага:
- Определите класс реализации вызывающего Bean как абстрактный класс и определите абстрактный метод для получения зависимого Bean.
- В
<bean.../>Добавьте<lookup-method.../>дочерний элемент к элементу, позволив Spring реализовать указанный абстрактный метод для класса реализации вызывающего бина.
Примечание:
Spring будет использовать динамическое улучшение во время выполнения, чтобы реализовать
<lookup-method.../>абстрактный метод, указанный элементом. Если целевой абстрактный класс реализует интерфейс, Spring будет использовать динамический прокси JDK для реализации абстрактного класса и реализации для него абстрактного метода; если целевой абстрактный класс не реализует интерфейс, Spring будет использовать cglib для реализации абстрактного класса и реализации для него абстрактного метода. Библиотека cglib уже интегрирована в пакет spring-core-xxx.jar в Spring 4.0.
Два типа постпроцессоров
Spring предоставляет два часто используемых постпроцессора:
- Постпроцессор бинов: этот постпроцессор выполняет постобработку бинов в контейнере, обеспечивая дополнительное улучшение бинов.
- Постпроцессор контейнера: этот постпроцессор выполняет постобработку IoC-контейнера для расширения функциональности контейнера.
Постпроцессор бинов
Постпроцессор бинов — это особый вид бина. Этот особый бин не предоставляет сервисы наружу и может даже опустить атрибут id. В основном он отвечает за выполнение постобработки других бинов в контейнере, например за создание прокси для целевых бинов в контейнере. Такой бин называется постпроцессором бинов. После успешного создания экземпляра бина постпроцессор бинов выполняет дальнейшую улучшающую обработку экземпляра бина. Постпроцессор бинов должен реализоватьBeanPostProcessorинтерфейс и должен реализовать оба метода этого интерфейса.
Object postProcessBeforeInitialization(Object bean, String name) throws BeansException: Первый параметр этого метода — экземпляр бина, который система собирается подвергнуть постобработке, а второй параметр — идентификатор конфигурации бина.Object postProcessAfterinitialization(Object bean, String name) throws BeansException: Первый параметр этого метода — экземпляр бина, который система собирается подвергнуть постобработке, а второй параметр — идентификатор конфигурации бина.
После регистрации пост-процессора Bean в контейнере он запускается автоматически и автоматически срабатывает при создании каждого Bean в контейнере. Момент обратного вызова двух методов пост-процессора Bean показан на следующем рисунке:

Обратите внимание, что если вы используетеBeanFactoryв качестве контейнера Spring, необходимо вручную зарегистрировать пост-процессор Bean. Программа должна получить экземпляр пост-процессора Bean, а затем зарегистрировать его вручную.
BeanPostProcessor bp = (BeanPostProcessor)beanFactory.getBean("bp");
beanFactory.addBeanPostProcessor(bp);
Person p = (Person)beanFactory.getBean("person");
Пост-процессор контейнера
Пост-процессор Bean отвечает за обработку всех экземпляров Bean в контейнере, тогда как пост-процессор контейнера отвечает за обработку самого контейнера. Пост-процессор контейнера должен реализоватьBeanFactoryPostProcessorинтерфейс и реализовать метод этого интерфейса.postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)Тело метода, реализующего этот метод, — это обработка, выполняемая над контейнером Spring. Эта обработка может быть пользовательским расширением контейнера Spring или, конечно, может вообще не выполнять никакой обработки контейнера Spring.
аналогичноBeanPostProcessor,ApplicationContextМожет автоматически обнаруживать пост-процессоры контейнера в контейнере и автоматически регистрировать пост-процессоры контейнера. Но если использоватьBeanFactoryв качестве контейнера Spring, необходимо вручную вызвать этот пост-процессор контейнера для обработки.BeanFactoryконтейнера.
Поддержка "нулевой конфигурации" в Spring
Поиск классов Bean
Spring предоставляет следующие аннотации для пометки Bean-компонентов Spring:
@Component: Помечает обычный класс Spring Bean.@Controller: Аннотирует класс компонента-контроллера.@Service: Аннотирует класс компонента бизнес-логики.@Repository: Аннотирует класс компонента DAO.
Выполните следующую настройку в файле конфигурации Spring, чтобы указать пакет для автоматического сканирования:
<context:component-scan base-package="edu.shu.spring.domain"/>
Настройка зависимостей с помощью @Resource
@ResourceНаходится вjavax.annotationНаходится в пакете, это одна из спецификаций JavaEE.Annotation, Spring напрямую позаимствовал этоAnnotation, используя этоAnnotationУкажите взаимодействующие Bean для целевого Bean. Используйте@Resourceи<property.../>Атрибут ref элемента имеет тот же эффект.
@Resourceможет не только аннотировать setter-методы, но и напрямую аннотировать переменные экземпляра. Если используется@ResourceАннотирование переменных экземпляра будет ещё проще; в этом случае Spring будет напрямую использовать внедрение полей стандарта JavaEE, и даже setter-методы не понадобятся.
Настройка поведения жизненного цикла с помощью @PostConstruct и @PreDestroy
@PostConstructи@PreDestroyТакже расположенные в пакете javax.annotation, это две аннотации из спецификации JavaEE. Spring напрямую позаимствовал их, чтобы настраивать поведение жизненного цикла бинов в контейнере Spring. Обе используются для модификации методов и не требуют атрибутов. Метод, изменённый первой, является методом инициализации бина; метод, изменённый второй, — это метод, вызываемый перед уничтожением бина.
Улучшенное автосвязывание и точное связывание в Spring 4.0
Spring предоставляет@Autowiredаннотацию для указания автосвязывания,@Autowiredможно аннотировать setter-методы, обычные методы, переменные экземпляра, конструкторы и так далее. При использовании@AutowiredПри аннотировании setter-метода по умолчанию применяется стратегия автосвязывания byType. При этой стратегии часто имеется несколько экземпляров бинов-кандидатов, соответствующих типу автосвязывания, что может вызывать исключения. Для достижения точного автосвязывания Spring предоставляет@Qualifierаннотацию, используя которую@Qualifier, можно выполнять автоматическое связывание на основе id бина.
AOP в Spring
Зачем нужен AOP
AOP (Aspect-Oriented Programming), также известное как аспектно-ориентированное программирование, как дополнение к объектно-ориентированному программированию, стало относительно зрелым подходом к программированию. На самом деле AOP появилось не так давно. AOP и OOP дополняют друг друга; аспектно-ориентированное программирование разбивает процесс выполнения программы на различные аспекты.
AOP специально используется для обработки сквозных задач, распределённых по различным модулям (разным методам) в системе. В приложениях JavaEE AOP часто применяется для обработки системных сервисов со сквозными характеристиками, таких как управление транзакциями, проверки безопасности, кэширование и управление пулом объектов. AOP стало очень распространённым решением.
Использование AspectJ для реализации AOP
AspectJ — это AOP-фреймворк на основе языка Java, предоставляющий мощные возможности AOP. Многие другие AOP-фреймворки заимствовали или переняли некоторые его идеи. Он в основном состоит из двух частей: одна часть определяет синтаксическую спецификацию для выражения и определения AOP-программирования. С помощью этой синтаксической спецификации AOP может удобно решать сквозные задачи в языке Java; другая часть — инструментальная, включая инструменты компиляции и отладки, и т. д.
Реализации AOP можно разделить на две категории:
- Статическая реализация AOP: AOP-фреймворк модифицирует программу на этапе компиляции, то есть он расширяет целевой класс и генерирует статический AOP-прокси-класс, представителем которого является AspectJ.
- Динамическая реализация AOP: AOP-фреймворк динамически генерирует AOP-прокси во время выполнения для расширения целевых объектов, представителем которой является Spring AOP.
Вообще говоря, статическая реализация AOP обладает лучшей производительностью, но требует специального компилятора. Динамическая реализация AOP — это чистая реализация на Java, поэтому она не требует специального компилятора, но её производительность обычно немного хуже.
Основные концепции AOP
Некоторые термины, относящиеся к аспектно-ориентированному программированию:
- Аспект: Аспект используется для организации нескольких советов, а советы определяются в аспекте.
- Точка соединения: Чёткая точка во время выполнения программы, например вызов метода или выброс исключения. В Spring AOP точка соединения — это всегда вызов метода.
- Совет: Расширенная обработка, выполняемая AOP-фреймворком в определённых точках среза. Обработка имеет типы, такие как "around", "before" и "after".
- Точка среза: Точка соединения, в которую можно вставить расширенную обработку. Короче говоря, когда точка соединения соответствует заданным требованиям, к этой точке соединения добавляется расширенная обработка, и эта точка соединения становится точкой среза.
Поддержка AOP в Spring
AOP-прокси в Spring создаются и управляются IoC-контейнером Spring, а их зависимости также управляются IoC-контейнером.
Чтобы использовать в приложении@AspectJДля его поддержки в Spring необходимо добавить три библиотеки:
aspectjweaver.jaraspectjrt.jaraopalliance.jar
И выполнить следующую настройку в конфигурационном файле Spring:
<!--启动@AspectJ支持-->
<aop:aspectj-autoproxy/>
<!--指定自动搜索Bean组件、自动搜索切面类-->
<context:component-scan base-package="edu.shu.sprint.service">
<context:include-filter type="annotation" expression="org.aspectj.lang.annotation.Aspect"/>
</context:component-scan>
Источник:http://codepub.cn/2015/06/21/Basic-knowledge-summary-of-Spring/