карта разума
Статья была выбрана Github, добро пожаловать в Star:GitHub.com/yehongqin/lai…
Обзор
Отличный фреймворк должен быть неотделим от использования различных шаблонов проектирования, и фреймворк Spring не исключение. Поскольку многие статьи в Интернете относительно разбросаны, я хотел бы обобщить шаблоны проектирования, используемые в Spring, и надеюсь, что после прочтения вы сможете глубже понять Spring.
заводской узор
Все мы знаем, что фабричный шаблон передает создание объектов фабрике, чтобы уменьшить связь между классами. Фабричный шаблон широко используется в Spring.Примерами здесь являются ApplicationContext и BeanFactory, которые также являются основой контейнера IOC Spring.
Сначала взгляните на BeanFactory, интерфейс самого низкого уровня.
public interface BeanFactory {
Object getBean(String name) throws BeansException;
<T> T getBean(String name, @Nullable Class<T> requiredType) throws BeansException;
Object getBean(String name, Object... args) throws BeansException;
<T> T getBean(Class<T> requiredType) throws BeansException;
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
//省略...
}
ApplicationContext — это класс расширения и интерфейс, его функция — создавать все bean-компоненты одновременно при запуске контейнера.
public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,
MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
@Nullable
String getId();
String getApplicationName();
String getDisplayName();
long getStartupDate();
@Nullable
ApplicationContext getParent();
AutowireCapableBeanFactory getAutowireCapableBeanFactory() throws IllegalStateException;
}
ApplicationContext имеет три класса реализации, а именно:
- ClassPathXmlApplication: найдите указанный файл конфигурации XML из пути к классу ClassPath, найдите и загрузите работу по созданию экземпляра ApplicationContext.
- FileSystemXMLApplicationContext: Найдите указанный файл конфигурации XML по указанному пути к файловой системе, найдите и загрузите работу по созданию экземпляра ApplicationContext.
- XMLWebApplicationContext: загружает информацию, определенную компонентом, из файла XML в веб-систему.Веб-приложение ищет указанный файл конфигурации XML, находит и загружает его для завершения создания экземпляра ApplicationContext.
Поэтому взаимосвязь между этими классами понятна, и диаграмма классов выглядит так:
Где его инициализировать, это немного сложно, поэтому я не буду вдаваться в подробности, просто упомяну об этом. В основном обратите внимание на метод refresh() класса AbstractApplicationContext.
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//省略...
try {
//省略...
//初始化所有的单实例 Bean(没有配置赖加载的)
finishBeanFactoryInitialization(beanFactory);
}catch (BeansException ex) {
//省略...
}finally {
//省略...
}
}
}
одноэлементный шаблон
В системе есть много объектов, нам нужен только один, например пул потоков, объект контекста Spring, объект журнала и так далее. Преимущество шаблона singleton заключается в том, чтоОжидается, что для некоторых объектов тяжелого веса время на создание объектов снизит нагрузку на систему., второй момент - использовать синглтонЭто может уменьшить количество новых операций и уменьшить нагрузку на поток GC для восстановления памяти..
На самом деле область действия bean-компонентов по умолчанию в Springsingleton(синглтон). Как этого добиться?
В основном посмотрите на метод getsingleton () по умолчанию.
public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {
/** 保存单例Objects的缓存集合ConcurrentHashMap,key:beanName --> value:bean实例 */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
synchronized (this.singletonObjects) {
//检查缓存中是否有实例,如果缓存中有实例,直接返回
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
//省略...
try {
//通过singletonFactory获取单例
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
//省略...
if (newSingleton) {
addSingleton(beanName, singletonObject);
}
}
//返回实例
return singletonObject;
}
}
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
}
Как видно из исходников, это через метод ConcurrentHashMap, если он есть в Map, то будет возвращен напрямую, если нет, то будет создан и помещен в Map collection, и вся логика заключен в блок синхронного кода, поэтому он является потокобезопасным.
режим стратегии
Проще говоря, режим стратегии инкапсулирует набор алгоритмов стратегии, и внешние клиенты выбирают разные алгоритмы стратегии для решения задач в зависимости от различных условий. Это шаблон проектирования, который используется во многих фреймворках и повседневной разработке. В примере Spring, который я привел здесь, это класс Resource, который представляет собой интерфейс, реализованный всеми классами доступа к ресурсам.
Для разных способов доступа к ресурсам Spring определяет разные классы реализации классов ресурсов. Давайте посмотрим на диаграмму классов:
Кратко представим класс реализации Resource:
- UrlResource: класс реализации для доступа к сетевым ресурсам.
- ServletContextResource: класс реализации для доступа к ресурсам относительно пути ServletContext.
- ByteArrayResource: классы реализации для доступа к массиву байтовых ресурсов.
- PathResource: класс реализации для доступа к ресурсам пути к файлу.
- ClassPathResource: доступ к классу реализации ресурса в пути загрузки класса.
Напишите псевдокод для демонстрации использования класса Resource:
@RequestMapping(value = "/resource", method = RequestMethod.GET)
public String resource(@RequestParam(name = "type") String type,
@RequestParam(name = "arg") String arg) throws Exception {
Resource resource;
//这里可以优化为通过工厂模式,根据type创建Resource的实现类
if ("classpath".equals(type)) {
//classpath下的资源
resource = new ClassPathResource(arg);
} else if ("file".equals(type)) {
//本地文件系统的资源
resource = new PathResource(arg);
} else if ("url".equals(type)) {
//网络资源
resource = new UrlResource(arg);
} else {
return "fail";
}
InputStream is = resource.getInputStream();
ByteArrayOutputStream os = new ByteArrayOutputStream();
int i;
while ((i = is.read()) != -1) {
os.write(i);
}
String result = new String(os.toByteArray(), StandardCharsets.UTF_8);
is.close();
os.close();
return "type:" + type + ",arg:" + arg + "\r\n" + result;
}
В этом и заключается идея паттерна стратегии, использующего разные алгоритмы для решения задач с внешними условиями. На самом деле это очень просто, потому что метод getInputStream() каждого класса реализации отличается.Давайте посмотрим на исходный код ClassPathResource, который загружает ресурсы через загрузчик классов:
public class ClassPathResource extends AbstractFileResolvingResource {
private final String path;
@Nullable
private ClassLoader classLoader;
@Nullable
private Class<?> clazz;
@Override
public InputStream getInputStream() throws IOException {
InputStream is;
//通过类加载器加载类路径下的资源
if (this.clazz != null) {
is = this.clazz.getResourceAsStream(this.path);
}
else if (this.classLoader != null) {
is = this.classLoader.getResourceAsStream(this.path);
}
else {
is = ClassLoader.getSystemResourceAsStream(this.path);
}
//如果输入流is为null,则报错
if (is == null) {
throw new FileNotFoundException(getDescription() + " cannot be opened because it does not exist");
}
//返回InputStream
return is;
}
}
Глядя на исходный код UrlResource, реализация InputStream — это еще одна стратегия.
public class UrlResource extends AbstractFileResolvingResource {
@Nullable
private final URI uri;
private final URL url;
private final URL cleanedUrl;
@Override
public InputStream getInputStream() throws IOException {
//获取连接
URLConnection con = this.url.openConnection();
ResourceUtils.useCachesIfNecessary(con);
try {
//获取输入流,并返回
return con.getInputStream();
}
catch (IOException ex) {
// Close the HTTP connection (if applicable).
if (con instanceof HttpURLConnection) {
((HttpURLConnection) con).disconnect();
}
throw ex;
}
}
}
прокси-режим
Помимо IOC (инверсия управления), еще одним ядром Spring является АОП (аспектно-ориентированное программирование). АОП может преобразовывать логику (Например, ведение журнала, контроль разрешений и т. д.) инкапсулировать его, уменьшить повторяющийся код системы, уменьшить связь между системами и способствует обслуживанию и расширению системы.
Spring AOP в основном реализуется на основе динамического прокси.Если проксируемый класс реализует интерфейс, используется динамический прокси JDK.Если интерфейс не реализован, используется динамический прокси Cglib.
Давайте посмотрим на метод createAopProxy() класса DefaultAopProxyFactory, с помощью которого Spring создает динамические прокси-классы:
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " + "Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
}
Разница между динамическим прокси JDK и динамическим прокси Cglib:
- Динамические прокси JDK могут генерировать прокси только для классов, реализующих интерфейсы, а классы, которые не реализуют интерфейсы, использовать нельзя.
- Динамический прокси Cglib можно использовать, даже если класс прокси не реализует интерфейс, потому что динамический прокси Cglib расширяется путем наследования класса прокси.
- Динамический прокси Cglib осуществляется через наследование, переопределяя метод прокси-класса на прокси, поэтому, если метод окончательно изменен, он не может проксировать.
Как видно из исходного кода, Spring сначала определит, реализован ли интерфейс.Если интерфейс реализован, он будет использовать динамический прокси JDK.Если интерфейс не реализован, он будет использовать динамический прокси Cglib.Он может также настроить принудительное использование динамического прокси-сервера Cglib.Конфигурация выглядит следующим образом:
<aop:aspectj-autoproxy proxy-target-class="true"/>
Режим шаблона
Шаблон шаблона слишком часто используется в Spring, он определяет скелет алгоритма, откладывая некоторые шаги на подклассы. Как правило, абстрактный класс определяется как скелет, и подклассы переопределяют методы шаблона в абстрактном классе для реализации определенных шагов в скелете алгоритма. Шаблоны шаблонов могут переопределять определенные шаги алгоритма без изменения его структуры.
Менеджер транзакций в Spring использует дизайн шаблона шаблона, сначала взгляните на класс PlatformTransactionManager. Это интерфейс самого низкого уровня, определяющий методы фиксации и отката.
public interface PlatformTransactionManager {
TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;
void commit(TransactionStatus status) throws TransactionException;
void rollback(TransactionStatus status) throws TransactionException;
}
Неудивительно, что абстрактный класс используется в качестве скелета, а затем посмотрите на класс AbstractPlatformtransactionManager.
@Override
public final void commit(TransactionStatus status) throws TransactionException {
//省略...
DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
if (defStatus.isLocalRollbackOnly()) {
//省略...
//调用processRollback()
processRollback(defStatus, false);
return;
}
if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
//省略...
//调用processRollback()
processRollback(defStatus, true);
return;
}
//调用processCommit()
processCommit(defStatus);
}
//这个方法定义了骨架,里面会调用一个doRollback()的模板方法
private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
if (status.hasSavepoint()) {
//省略...
}
else if (status.isNewTransaction()) {
//调用doRollback()模板方法
doRollback(status);
}
else {
//省略...
}
//省略了很多代码...
}
private void processCommit(DefaultTransactionStatus status) throws TransactionException {
//省略...
if (status.hasSavepoint()) {
//省略...
}
else if (status.isNewTransaction()) {
//省略...
//调用doCommit()模板方法
doCommit(status);
}
else if (isFailEarlyOnGlobalRollbackOnly()) {
unexpectedRollback = status.isGlobalRollbackOnly();
}
//省略了很多代码...
}
//模板方法doRollback(),把重要的步骤延迟到子类去实现
protected abstract void doRollback(DefaultTransactionStatus status) throws TransactionException;
//模板方法doCommit(),把重要的步骤延迟到子类去实现
protected abstract void doCommit(DefaultTransactionStatus status) throws TransactionException;
Шаблонный метод реализован классом реализации различных менеджеров транзакций, то есть важный Dorollback() в скелете отложен до подкласса. В общем, Spring — это класс реализации диспетчера транзакций, используемого DataSourceTransactionManager.
//通过继承AbstractPlatformTransactionManager抽象类
public class DataSourceTransactionManager extends AbstractPlatformTransactionManager
implements ResourceTransactionManager, InitializingBean {
//重写doCommit()方法,实现具体commit的逻辑
@Override
protected void doCommit(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
if (status.isDebug()) {
logger.debug("Committing JDBC transaction on Connection [" + con + "]");
}
try {
con.commit();
}
catch (SQLException ex) {
throw new TransactionSystemException("Could not commit JDBC transaction", ex);
}
}
//重写doRollback()方法,实现具体的rollback的逻辑
@Override
protected void doRollback(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
if (status.isDebug()) {
logger.debug("Rolling back JDBC transaction on Connection [" + con + "]");
}
try {
con.rollback();
}
catch (SQLException ex) {
throw new TransactionSystemException("Could not roll back JDBC transaction", ex);
}
}
}
Если вы используете фреймворк Hibernate, у Hibernate также есть собственная реализация, отражающая открытый и закрытый принцип шаблонов проектирования, расширяющаяся за счет наследования или комбинации, а не прямого изменения кода класса. Менеджер транзакций Hibernate — HibernateTransactionManager.
public class HibernateTransactionManager extends AbstractPlatformTransactionManager
implements ResourceTransactionManager, BeanFactoryAware, InitializingBean {
//重写doCommit()方法,实现Hibernate的具体commit的逻辑
@Override
protected void doCommit(DefaultTransactionStatus status) {HibernateTransactionObject txObject = (HibernateTransactionObject) status.getTransaction();
Transaction hibTx = txObject.getSessionHolder().getTransaction();
Assert.state(hibTx != null, "No Hibernate transaction");
if (status.isDebug()) {
logger.debug("Committing Hibernate transaction on Session [" +
txObject.getSessionHolder().getSession() + "]");
}
try {
hibTx.commit();
}
catch (org.hibernate.TransactionException ex) {
throw new TransactionSystemException("Could not commit Hibernate transaction", ex);
}
//省略...
}
//重写doRollback()方法,实现Hibernate的具体rollback的逻辑
@Override
protected void doRollback(DefaultTransactionStatus status) {
HibernateTransactionObject txObject = (HibernateTransactionObject) status.getTransaction();
Transaction hibTx = txObject.getSessionHolder().getTransaction();
Assert.state(hibTx != null, "No Hibernate transaction");
//省略...
try {
hibTx.rollback();
}
catch (org.hibernate.TransactionException ex) {
throw new TransactionSystemException("Could not roll back Hibernate transaction", ex);
}
//省略...
finally {
if (!txObject.isNewSession() && !this.hibernateManagedSession) {
txObject.getSessionHolder().getSession().clear();
}
}
}
}
На самом деле режим шаблона часто используется в повседневной разработке. Например, в методе передний и задний коды одинаковы, а различаются только некоторые операции в середине. Режим шаблона можно использовать для оптимизации кода, который может значительно уменьшить избыточный код и очень практичен.
Шаблон адаптера и шаблон цепочки ответственности
Шаблон адаптера — это структурный шаблон проектирования, которыйПозволяет объектам с несовместимыми интерфейсами взаимодействовать друг с другом, преобразовывая интерфейс одного класса в другой интерфейс, ожидаемый клиентом..
Там есть совет (уведомление) - это очень важная особенность, используемая для повышения функциональности на SpramaoOP в прокси-классе, рекомендациям Основной методик BeaderBeforeadVice, AfterreaturningAdVice, угрозависимо от этих типов. Каждый имеет соответствующий совет перехватчика следующим образом:
Spring необходимо инкапсулировать каждый совет как соответствующий тип перехватчика и вернуть его в контейнер, поэтомуСовет необходимо преобразовать с помощью шаблона адаптера. Есть три соответствующих адаптера, давайте посмотрим на диаграмму классов:
Как адаптер преобразует класс уведомления и класс перехвата в Spring?Давайте сначала посмотрим на интерфейс адаптера. Определены два метода: supportsAdvice() и getInterceptor().
public interface AdvisorAdapter {
//判断通知类是否匹配
boolean supportsAdvice(Advice advice);
//传入通知类,返回对应的拦截类
MethodInterceptor getInterceptor(Advisor advisor);
}
На самом деле все очень просто, видно, что метод конвертации — это getInterceptor(), о чем судит supportsAdvice(). Мы смотрим на класс реализации MethodBeforeAdviceAdapter адаптера предварительного уведомления.
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
//判断是否匹配MethodBeforeAdvice通知类
@Override
public boolean supportsAdvice(Advice advice) {
return (advice instanceof MethodBeforeAdvice);
}
//传入MethodBeforeAdvice,转换为MethodBeforeAdviceInterceptor拦截类
@Override
public MethodInterceptor getInterceptor(Advisor advisor) {
MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
return new MethodBeforeAdviceInterceptor(advice);
}
}
В методе getInterceptor() вызывается конструктор соответствующего класса перехвата для создания соответствующего возвращаемого перехватчика, а совет класса уведомления передается в качестве параметра. Затем смотрим на перехватчик MethodBeforeAdviceInterceptor.
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {
//成员变量,通知类
private MethodBeforeAdvice advice;
//定义了有参构造器,外部通过有参构造器创建MethodBeforeAdviceInterceptor
public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
Assert.notNull(advice, "Advice must not be null");
this.advice = advice;
}
//当调用拦截器的invoke方法时,就调用通知类的before()方法,实现前置通知
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
//调用通知类的before()方法,实现前置通知
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
return mi.proceed();
}
}
Где же тогда инициализировать эти адаптеры мы видим DefaultAdvisorAdapterRegistry().
public class DefaultAdvisorAdapterRegistry implements AdvisorAdapterRegistry, Serializable {
private final List<AdvisorAdapter> adapters = new ArrayList<>(3);
public DefaultAdvisorAdapterRegistry() {
//初始化适配器,添加到adapters集合,也就是注册
registerAdvisorAdapter(new MethodBeforeAdviceAdapter());
registerAdvisorAdapter(new AfterReturningAdviceAdapter());
registerAdvisorAdapter(new ThrowsAdviceAdapter());
}
@Override
public void registerAdvisorAdapter(AdvisorAdapter adapter) {
this.adapters.add(adapter);
}
//获取所有的拦截器
@Override
public MethodInterceptor[] getInterceptors(Advisor advisor) throws UnknownAdviceTypeException {
List<MethodInterceptor> interceptors = new ArrayList<>(3);
Advice advice = advisor.getAdvice();
if (advice instanceof MethodInterceptor) {
interceptors.add((MethodInterceptor) advice);
}
//遍历adapters集合
for (AdvisorAdapter adapter : this.adapters) {
//调用supportsAdvice()方法,判断入参的advisor是否有匹配的适配器
if (adapter.supportsAdvice(advice)) {
//如果匹配,则调用getInterceptor()转换成对应的拦截器,添加到interceptors集合中
interceptors.add(adapter.getInterceptor(advisor));
}
}
if (interceptors.isEmpty()) {
throw new UnknownAdviceTypeException(advisor.getAdvice());
}
//返回拦截器集合
return interceptors.toArray(new MethodInterceptor[0]);
}
}
Режим адаптера здесь заключается в преобразовании класса уведомлений в класс перехвата, а после преобразования в класс перехвата он добавляется в коллекцию перехватчиков. После добавления в коллекцию перехватчиков используется режим цепочки ответственности.При вызове класса ReflectiveMethodInvocation мы смотрим на метод invoke() динамического прокси JDK JdkDynamicAopProxy.
@Override
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
MethodInvocation invocation;
//这里就是获取拦截器集合,最后就会调用到上文说的getInterceptors()
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
if (chain.isEmpty()) {
//省略...
}else {
//创建一个MethodInvocation
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
//调用proceed()方法,底层会通过指针遍历拦截器集合,然后实现前置通知等功能
retVal = invocation.proceed();
}
//省略...
}
Наконец, в ReflectiveMethodInvocation вызывается метод continue(), который представляет собой рекурсивный метод, контролирующий конец рекурсии с помощью указателя. Это типичная схема цепочки ответственности.
public class ReflectiveMethodInvocation implements ProxyMethodInvocation, Cloneable {
protected final List<?> interceptorsAndDynamicMethodMatchers;
//指针
private int currentInterceptorIndex = -1;
protected ReflectiveMethodInvocation(Object proxy, @Nullable Object target, Method method, @Nullable Object[] arguments, @Nullable Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers) {
//省略...
//拦截器的集合
this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers;
}
@Override
@Nullable
public Object proceed() throws Throwable {
// We start with an index of -1 and increment early.
if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
//递归结束
return invokeJoinpoint();
}
//获取拦截器,并且当前的指针+1
Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
}
else {
//匹配失败,跳过,递归下一个
return proceed();
}
}
else {
//匹配拦截器,强转为拦截器,然后执行invoke()方法,然后就会调用拦截器里的成员变量的before(),afterReturning()等等,实现前置通知,后置通知,异常通知
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
}
У студентов, которые не изучили здесь шаблон цепочки ответственности, может немного закружиться голова, но после изучения шаблона цепочки ответственности это должно быть легко понять.На самом деле, это почти то же самое, что и логическая реализация перехватчика SpringMVC.
Шаблон наблюдателя
Паттерн Наблюдатель — это поведенческий паттерн объекта, когда объект изменяется, объекты, от которых он зависит, также реагируют. Модель Spring, управляемая событиями, — это классическое применение шаблона наблюдателя.
роль события
В модели Spring, управляемой событиями, есть роль события ApplicationEvent, которая является абстрактным классом, и четыре класса реализации под абстрактным классом, представляющим четыре типа событий.
- ContextStartedEvent: событие, инициированное после запуска ApplicationContext.
- ContextStoppedEvent: событие запускается после остановки ApplicationContext.
- ContextRefreshedEvent: событие, инициированное после инициализации или обновления ApplicationContext.
- ContextClosedEvent: событие, инициированное после закрытия ApplicationContext.
издатель событий
После события должен быть издатель для публикации события, и соответствующий класс издателя — ApplicationEventPublisher.
@FunctionalInterface
public interface ApplicationEventPublisher {
default void publishEvent(ApplicationEvent event) {
publishEvent((Object) event);
}
void publishEvent(Object event);
}
@FunctionaliNterface, что это функциональный интерфейс, только абстрактный метод функции интерфейса. ApplicationContext Class и наследование
Класс ApplicationEventPublisher, поэтому мы можем использовать ApplicationContext Publish Events.
прослушиватель событий
После публикации события должен быть прослушиватель событий. Прослушиватель событий определяется путем реализации интерфейса ApplicationListener, который является функциональным интерфейсом и имеет общий тип. Параметр E должен быть подклассом ApplicationEvent.
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
void onApplicationEvent(E event);
}
Давайте продемонстрируем, как его использовать.Во-первых, унаследуйте абстрактный класс ApplicationEvent, чтобы определить роль события PayApplicationEvent.
public class PayApplicationEvent extends ApplicationEvent {
private String message;
public PayApplicationEvent(Object source, String message) {
super(source);
this.message = message;
}
public String getMessage() {
return message;
}
}
Затем определите прослушиватель PayApplicationEvent PayListener.
@Component
public class PayListener implements ApplicationListener<PayApplicationEvent> {
@Override
public void onApplicationEvent(PayApplicationEvent event) {
String message = event.getMessage();
System.out.println("监听到PayApplicationEvent事件,消息为:" + message);
}
}
Наконец, мы используем ApplicationContext для публикации событий.
@SpringBootApplication
public class SpringmvcApplication {
public static void main(String[] args) throws Exception {
ApplicationContext applicationContext = SpringApplication.run(SpringmvcApplication.class, args);
applicationContext.publishEvent(new PayApplicationEvent(applicationContext,"成功支付100元!"));
}
}
После начала мы можем увидеть консольную печать:
болтовня
На самом деле шаблоны проектирования, используемые в Spring, можно увидеть повсюду в исходном коде, а не только в тех, которые я перечислил, поэтому исходный код Spring очень достоин прочтения и изучения, и от него много пользы. С другой стороны, если вы не знаете, как проектировать шаблоны, очень сложно читать исходный код, поэтому я предлагаю вам сначала изучить шаблоны проектирования, а затем изучить исходный код.
Я надеюсь, что после прочтения вы сможете глубже понять Spring, поэтому эта статья остановится на этом, спасибо за чтение.
Ставьте лайки, если считаете это полезным, ваши лайки — самая большая мотивация для моего творчества.~
Я программист, который изо всех сил старается запомниться. Увидимся в следующий раз! ! !
Возможности ограничены, если есть какие-то ошибки или неуместности, просьба критиковать и исправлять их, учиться и обмениваться вместе!