Heim > Java > javaLernprogramm > So implementiert SpringBoot die Modulprotokollspeicherung

So implementiert SpringBoot die Modulprotokollspeicherung

WBOY
Freigeben: 2023-05-11 09:37:05
nach vorne
1009 Leute haben es durchsucht

1. Kurzbeschreibung

Es gibt ungefähr drei Möglichkeiten, Modulprotokolle zu implementieren:

  • AOP + benutzerdefinierte Anmerkungsimplementierung#🎜🎜 Nr Rufen Sie dazu die Methode zum Protokollieren auf.

  • Hier diskutieren wir hauptsächlich die dritte Implementierungsmethode.

  • Angenommen, wir müssen einen Vorgang implementieren, um das Anmeldeprotokoll aufzuzeichnen, nachdem sich der Benutzer angemeldet hat.
  • Die Aufrufbeziehung ist wie folgt:

Der Kerncode hier wird in der Methode LoginService.login() unter festgelegt das Ende der Transaktion Nach der Ausführung:

// 指定事务提交后执行
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
    // 不需要事务提交前的操作,可以不用重写这个方法
    @Override
    public void beforeCommit(boolean readOnly) {
        System.out.println("事务提交前执行");
    }
    @Override
    public void afterCommit() {
        System.out.println("事务提交后执行");
    }
});
Nach dem Login kopieren

Hier kapseln wir diesen Code in eine Toolklasse, Referenz: 4.TransactionUtils.

Wenn eine Transaktion in der Methode LoginService.login() aktiviert ist und nach dem Absenden der Transaktion nicht angegeben wird, kommt es zu Problemen mit der asynchronen Protokollverarbeitungsmethode und neuen Transaktionen:

# 🎜🎜 #So implementiert SpringBoot die Modulprotokollspeicherung

Asynchrone Ausführung: Da die Haupttransaktion möglicherweise nicht abgeschlossen wird, werden die neu hinzugefügten oder geänderten Dateninformationen in der Haupttransaktion möglicherweise nicht gelesen ## 🎜🎜#Neue Dinge tun: Sie können neue Transaktionen über das Transaktionsweitergabeverhalten Propagation.REQUIRES_NEW erstellen. Das Ausführen von Protokollierungsvorgängen in der neuen Transaktion kann die folgenden Probleme verursachen:

#🎜 🎜#

Da die Standardtransaktionsisolationsstufe der Datenbank wiederholbares Lesen ist, bedeutet dies, dass nicht festgeschriebene Inhalte nicht zwischen Dingen gelesen werden können, sodass die neuen oder geänderten Dateninformationen in der Haupttransaktion nicht gelesen werden können. 🎜🎜#

Wenn die neu geöffnete Transaktion dieselbe Tabelle wie die vorherige Transaktion bedient, führt dies dazu, dass die Tabelle gesperrt wird.
  • Nichts tun, direkt synchron anrufen: das problematischste Problem, das zu folgenden Problemen führen kann:
  • #🎜 🎜 #

  • Fängt keine Ausnahmen ab, was direkt zum Rollback aller Vorgänge auf der Schnittstelle führt;
    • Ausnahmen werden abgefangen, z Wie bei PostgreSQL schlagen in derselben Transaktion alle verbleibenden Datenbankvorgänge fehl und lösen Ausnahmen aus, solange ein Ausführungsfehler auftritt.
    • 2.LoginController
    @RestController
    public class LoginController {
        @Autowired
        private LoginService loginService;
        @RequestMapping("/login")
        public String login(String username, String pwd) {
            loginService.login(username, pwd);
            return "succeed";
        }
    }
    Nach dem Login kopieren
  • 3.Action

    /**
     * <p> @Title Action
     * <p> @Description 自定义动作函数式接口
     *
     * @author ACGkaka
     * @date 2023/4/26 13:55
     */
    public interface Action {
            /**
            * 执行动作
            */
            void doSomething();
    }
    Nach dem Login kopieren

    4.TransactionUtils#🎜. 🎜 #
    import org.springframework.transaction.support.TransactionSynchronization;
    import org.springframework.transaction.support.TransactionSynchronizationManager;
    /**
     * <p> @Title TransactionUtils
     * <p> @Description 事务同步工具类
     *
     * @author ACGkaka
     * @date 2023/4/26 13:45
     */
    public class TransactionUtils {
        /**
         * 提交事务前执行
         */
        public static void beforeTransactionCommit(Action action) {
            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
                @Override
                public void beforeCommit(boolean readOnly) {
                    // 异步执行
                    action.doSomething();
                }
            });
        }
        /**
         * 提交事务后异步执行
         */
        public static void afterTransactionCommit(Action action) {
            TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
                @Override
                public void afterCommit() {
                    // 异步执行
                    action.doSomething();
                }
            });
        }
    }
    Nach dem Login kopieren
  • 5.LoginService
      @Service
      public class LoginService {
          @Autowired
          private LoginLogService loginLogService;
          /** 登录 */
          @Transactional(rollbackFor = Exception.class)
          public void login(String username, String pwd) {
              // 用户登录
              // TODO: 实现登录逻辑..
              // 事务提交后执行
              TransactionUtil.afterTransactionCommit(() -> {
                  // 异步执行
                  taskExecutor.execute(() -> {
                      // 记录日志
                      loginLogService.recordLog(username);
                  });
              });
          }
      }
      Nach dem Login kopieren
    • 6.LoginLogService

      6.1 @Async implementiert asynchron
    • @Service
      public class LoginLogService {
          /** 记录日志 */
          @Async
          @Transactional(rollbackFor = Exception.class)
          public void recordLog(String username) {
              // TODO: 实现记录日志逻辑...
          }
      }
      Nach dem Login kopieren
    • Hinweis: @Async muss kooperieren @EnableAsync wird verwendet und @EnableAsync kann zur Startklasse, Konfigurationsklasse oder benutzerdefinierten Thread-Pool-Klasse hinzugefügt werden.

      Ergänzung: Da die @Async-Annotation dynamisch eine geerbte Klasse erstellt, um die Implementierung der Methode zu erweitern, kann dies dazu führen, dass die aktuelle Klasse nicht in den Bean-Container eingefügt wird. BeanCurrentlyInCreationException Sie können die verwenden Folgende Methode: Benutzerdefinierter Thread-Pool + @Autowired
    • 6.2 Benutzerdefinierter Thread-Pool zur Implementierung von asynchronem

      1) Benutzerdefinierter Thread-Pool
    AsyncTaskExecutorConfig.java#🎜 🎜#
    import com.demo.async.ContextCopyingDecorator;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.core.task.TaskExecutor;
    import org.springframework.scheduling.annotation.EnableAsync;
    import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
    import java.util.concurrent.ThreadPoolExecutor;
    /**
     * <p> @Title AsyncTaskExecutorConfig
     * <p> @Description 异步线程池配置
     *
     * @author ACGkaka
     * @date 2023/4/24 19:48
     */
    @EnableAsync
    @Configuration
    public class AsyncTaskExecutorConfig {
        /**
         * 核心线程数(线程池维护线程的最小数量)
         */
        private int corePoolSize = 10;
        /**
         * 最大线程数(线程池维护线程的最大数量)
         */
        private int maxPoolSize = 200;
        /**
         * 队列最大长度
         */
        private int queueCapacity = 10;
        @Bean
        public TaskExecutor taskExecutor() {
            ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
            executor.setCorePoolSize(corePoolSize);
            executor.setMaxPoolSize(maxPoolSize);
            executor.setQueueCapacity(queueCapacity);
            executor.setThreadNamePrefix("MyExecutor-");
            // for passing in request scope context 转换请求范围的上下文
            executor.setTaskDecorator(new ContextCopyingDecorator());
            // rejection-policy:当pool已经达到max size的时候,如何处理新任务
            // CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
            executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
            executor.setWaitForTasksToCompleteOnShutdown(true);
            executor.initialize();
            return executor;
        }
    }
    Nach dem Login kopieren
2 )Kontextanforderung kopieren

ContextCopyingDecorator.java

import org.slf4j.MDC;
import org.springframework.core.task.TaskDecorator;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import java.util.Map;
/**
 * <p> @Title ContextCopyingDecorator
 * <p> @Description 上下文拷贝装饰者模式
 *
 * @author ACGkaka
 * @date 2023/4/24 20:20
 */
public class ContextCopyingDecorator implements TaskDecorator {
    @Override
    public Runnable decorate(Runnable runnable) {
        try {
            // 从父线程中获取上下文,然后应用到子线程中
            RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
            Map<String, String> previous = MDC.getCopyOfContextMap();
            SecurityContext securityContext = SecurityContextHolder.getContext();
            return () -> {
                try {
                    if (previous == null) {
                        MDC.clear();
                    } else {
                        MDC.setContextMap(previous);
                    }
                    RequestContextHolder.setRequestAttributes(requestAttributes);
                    SecurityContextHolder.setContext(securityContext);
                    runnable.run();
                } finally {
                    // 清除请求数据
                    MDC.clear();
                    RequestContextHolder.resetRequestAttributes();
                    SecurityContextHolder.clearContext();
                }
            };
        } catch (IllegalStateException e) {
            return runnable;
        }
    }
}
Nach dem Login kopieren

3) Benutzerdefinierter Thread-Pool zur Implementierung des asynchronen LoginService

import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
@Service
public class LoginService {
    @Autowired
    private LoginLogService loginLogService;
    @Qualifier("taskExecutor")
    @Autowired
    private TaskExecutor taskExecutor;
    /** 登录 */
    @Transactional(rollbackFor = Exception.class)
    public void login(String username, String pwd) {
        // 用户登录
        // TODO: 实现登录逻辑..
        // 事务提交后执行
        TransactionUtil.afterTransactionCommit(() -> {
            // 异步执行
            taskExecutor.execute(() -> {
                // 记录日志
                loginLogService.recordLog(username);
            });
        });
    }
}
Nach dem Login kopieren
7. Andere Lösungen#🎜🎜 #

7.1 Verwenden Sie programmgesteuerte Transaktionen, um @Transactional zu ersetzen

Wir können auch TransactionTemplate verwenden, um @Transactional zu ersetzen.

# 🎜🎜#Nachdem diese Implementierung eine Ausnahme auslöst, kann die Transaktion auch normal zurückgesetzt werden

Nach der normalen Ausführung kann der Inhalt nach der Transaktionsausführung auch gelesen werden, was machbar ist.

Unabhängig davon, wie einfach es ist, Protokollaufzeichnungen zu implementieren, gibt es wirklich viele Fallstricke. Hier werden nur die bisher aufgetretenen Probleme aufgezeichnet.

Das obige ist der detaillierte Inhalt vonSo implementiert SpringBoot die Modulprotokollspeicherung. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Verwandte Etiketten:
Quelle:yisu.com
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage