3. Spring Boot data source configuration and logging configuration

instructions

If you are lucky enough to see it, please read the following;

  • 1. This project is copied from the Guns of ABel533, and his project forks from the Guns of StyleFeng! Open source is a great place to learn.

  • 2, copyright belongs to the original author, I just learn to use. Follow the big guy’s idea, hope oneself also can become big guy. Gogogo…

  • 3. Currently, it is only a background module. I hope that when my skills are enhanced to a certain extent, I can integrate the [Guns] of StyleFeng into it.

  • Note inside is their own learning process, written by the rookie, not written by the big guy. It’s all about the big guy.

I wanted to do it step by step, but the tools are confusing me. So let’s take a look at configuration classes first, that’s where today’s focus is. Start with databases, logs, caches.

SpringBoot has four important features:

  • Bootleg dependencies: Bootleg dependencies are special Maven and Gradle dependencies that use transitive dependency resolution to aggregate common libraries together to form a specific function.
  • Automatic configuration: SpringBoot provides configuration for many of the application functions common to Spring applications (the bottom layer does a lot for us)
  • Command-line interface: No traditional project build,
  • Actuator: Enables you to run the SpringBott app and find out.

The important thing to understand for now is the first two, as long as you see this spring-boot-starter-xxx, it’s a starter dependency. The libraries you want to rely on are automatically imported.

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.3. RELEASE</version>
        <relativePath/>
</parent>
-------------------------------------------------------------------------------
<dependencies>
    <! -- Spring Boot dependency -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>
Copy the code

“SpringBoot Actual Combat” section have the opportunity to be sure to see “Spring Actual Combat” is the same author. It works better with code. Actual practice notes

SpringBoot offers an exciting new way to develop Spring applications. The framework itself provides little resistance, automatic configuration eliminates much of the boilerplate configuration found in traditional Spring applications, and Spring’s starting dependencies let you specify build dependencies by the functionality provided by the library rather than by name and version number.

Database Configuration

2. Let’s go back to the configuration in our project, starting with Ali’s Druid. WebConfig is generally the starting point for configuration. The @Configuration annotation means that this is a Configuration class. There is also the @bean annotation. The bean was previously defined in XMl as

There are two ways to add your own servlets, filters, and Listeners to Spring Boot

  • Register by code:ServletRegistrationBean,FilterRegistrationBean,ServletListenerRegistrationBeanGain control /
  • Annotation Registration: With the @servletcompanentscan annotation on SpringBootApplication, servlets, filters, and listeners can be automatically registered with @webservlet, @webfilter, and @weblistener annotations. No additional code is required.
/** * The web configuration class has many more */
@Configuration
public class WebConfig {

    /** * druidServlet registration */
    @Bean
    public ServletRegistrationBean druidServletRegistration(a) {
        ServletRegistrationBean registration = new ServletRegistrationBean(new StatViewServlet());
        registration.addUrlMappings("/druid/*");
        return registration;
    }

    /** * druid configures URI interception policies */
    @Bean
    public FilterRegistrationBean druidStatFilter(a){
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new WebStatFilter());
        // Add a filter rule.
        filterRegistrationBean.addUrlPatterns("/ *");
        // Add format information that does not need to be ignored.
        filterRegistrationBean.addInitParameter(
                "exclusions"."/static/*,*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid,/druid/*");
        // the username displayed on the session monitoring page needs to be actively injected into the session after login
        filterRegistrationBean.addInitParameter("principalSessionName"."username");
        return filterRegistrationBean;
    }

    /** * druid database connection pool monitoring */
    @Bean
    public DruidStatInterceptor druidStatInterceptor(a) {
        return new DruidStatInterceptor();
    }

    /** * druid database connection pool monitoring */
    @Bean
    public BeanTypeAutoProxyCreator beanTypeAutoProxyCreator(a) {
        BeanTypeAutoProxyCreator beanTypeAutoProxyCreator = new BeanTypeAutoProxyCreator();
        beanTypeAutoProxyCreator.setTargetBeanType(DruidDataSource.class);
        beanTypeAutoProxyCreator.setInterceptorNames("druidStatInterceptor");
        return beanTypeAutoProxyCreator;
    }

    /** * druid adds interception to druidStatPointcut *@return* /
    @Bean
    public Advisor druidStatAdvisor(a) {
        return newDefaultPointcutAdvisor(druidStatPointcut(), druidStatInterceptor()); }}Copy the code

3. Next, let’s take a look at the configuration of the data source. Configure the H2 database and JDBC.

H2 is an open source embedded database engine, written in Java, platform-independent, and provides a very convenient Web console for manipulating and managing database content. H2 also provides compatibility mode, which can be compatible with some mainstream databases, so it is very convenient to use H2 as a development period database. (Data is stored in memory).

Note that the DataSource DataSource is implemented in two main ways:

  • 1, direct database connection, because each time requires three handshakes (remote), all performance is poor.
  • 2, is to use pooling technology, such as the above said Ali Druid (known as the most powerful performance, security measures, but also can monitor), before the most commonly used is C3PO, DBCP, when you need to take directly from the pool, use directly back. DataSource caches connections to improve efficiency and resource reuse.
@Configuration
public class DataConfig {

  @Bean
  public DataSource dataSource(a) {
    return new EmbeddedDatabaseBuilder()
            .setType(EmbeddedDatabaseType.H2)
            .addScript("schema.sql")
            .addScript("my-test-data.sql") .build(); } -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --@Bean
  public JdbcOperations jdbcTemplate(DataSource dataSource) {
    return newJdbcTemplate(dataSource); }}Copy the code

4, need to add a point is: many foreigners are using the underlying JDBC technology, because of native, high efficiency. JdbcTemplate is Spring’s further encapsulation of JDBC. The use of named parameters. Does that make sense?

Spring Date is a Spring Date. Once you inherit the Repository interface, you have 18 methods that you can define yourself if you don’t, plus the JpaRepository recommendation.

private static final String SELECT_SPITTLE = "select sp.id, s.id as spitterId, s.username, s.password, s.fullname, s.email, s.updateByEmail, sp.message, sp.postedTime from Spittle sp, Spitter s where sp.spitter = s.id";
private static final String SELECT_SPITTLE_BY_ID = SELECT_SPITTLE + " and sp.id=?";
private static final String SELECT_SPITTLES_BY_SPITTER_ID = SELECT_SPITTLE + " and s.id=? order by sp.postedTime desc";
private static final String SELECT_RECENT_SPITTLES = SELECT_SPITTLE + " order by sp.postedTime desc limit ?";

public List<Spittle> findBySpitterId(long spitterId) {
  return jdbcTemplate.query(SELECT_SPITTLES_BY_SPITTER_ID, new SpittleRowMapper(), spitterId);
}
public List<Spittle> findBySpitterId(long spitterId) {
  return jdbcTemplate.query(SELECT_SPITTLES_BY_SPITTER_ID, new SpittleRowMapper(), spitterId);
}
Copy the code

5. Next we configure the data source.

I would like to record a Gif, but there is a BUG in my software, what can I recommend? In order not to take up space, just one. About logs, imagine. I really want to share my bookmarks with you, there are so many useful ones.

  • When the @Component Spring initializes, spring will treat all classes annotated with @Component as candidates under the automatic scan injection configuration path while initializing Spring’s @AutoWired
  • The @Controller annotation is a special Component that allows implementation classes to automate injection by scanning the class configuration path. The @Controller annotation is usually used in conjunction with the @RequestMapping annotation.
  • The @ConfigurationProperties annotation is used for externalized configuration, providing the prefix and Locations properties to specify the source of the property file and the prefix of the property
/** * 

Database data source configuration

*

Note: This class contains many default configurations. If these configurations meet your requirements, you can ignore them. If they do not meet your requirements, you are advised not to modify this class

@Component @ConfigurationProperties(prefix = "spring.datasource") public class DruidProperties { private String url = "JDBC: mysql: / / 127.0.0.1:3306 / guns? autoReconnect=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull"; private String username = "root"; private String password = "632364"; private String driverClassName = "com.mysql.jdbc.Driver"; // In order to save space not to post all. public void config(DruidDataSource dataSource) { dataSource.setUrl(url); dataSource.setUsername(username); dataSource.setPassword(password); dataSource.setDriverClassName(driverClassName); dataSource.setInitialSize(initialSize); // Define the initial connection number dataSource.setMinIdle(minIdle); // Minimum free time dataSource.setMaxActive(maxActive); // Define the maximum number of connections dataSource.setMaxWait(maxWait); // Maximum waiting time // Configure how often to detect idle connections that need to be closed, in milliseconds dataSource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis); // Set the minimum time for a connection to live in the pool, in milliseconds dataSource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis); dataSource.setValidationQuery(validationQuery); dataSource.setTestWhileIdle(testWhileIdle); dataSource.setTestOnBorrow(testOnBorrow); dataSource.setTestOnReturn(testOnReturn); // Turn on PSCache and specify the size of PSCache on each connection dataSource.setPoolPreparedStatements(poolPreparedStatements); dataSource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize); try { dataSource.setFilters(filters); } catch(SQLException e) { e.printStackTrace(); }}Copy the code

Review the AOP

6, there is a multi-data source, the use of section weaving. Just take your old notes,

In software development, features scattered across an application are called crosscutting concerns. Crosscutting concerns are typically conceptually separate from the application’s business logic. But they are often coupled together, and separating these crosscutting concerns from business logic is exactly what aspect oriented programming (AOP) is all about.

Dependency injection (DI) manages our application objects, and DI helps decouple them from each other. AOP, on the other hand, enables coupling between crosscutting concerns and the objects they affect.

Crosscutting concerns can be modularized into special classes called aspects. This has two advantages: each concern is concentrated in one place, rather than scattered across multiple code sites; and, second, the service module is simpler because it contains only the code for the primary concern (core functionality). The code of secondary concern is moved to the aspect.

Common terms used to describe cuts are advice, pointcut, join point.

Notice (advice)

The advice defines what the aspect is and when to use it. In addition to describing the work to be done by the aspect, the advice also addresses the issue of when to perform the work. Should it be before a method is called? After? Before and after? Or only when the method throws an exception?

Spring facets can apply notifications of type 5:

  • Before: Call notification Before the target method is called.
  • After notification: The notification is invoked After the target method completes
  • Post-returning Notification: Notification is called After successful execution of the target method
  • After-throwing: Calls notifications After the target method throws an exception
  • Around notification: Performs custom behavior before and after a notification method invocation

The join

Our application may have thousands of times to apply notifications, called join points. A join point is a point that can be inserted during application execution. This point can be when a method is called, when an exception is thrown, or even when a field is modified. Sections can use these points to plug into the normal flow of an application and add new behavior.

Point of tangency

If the advice defines the “what” and “when” of the aspect, then the pointcut defines the “where.” The definition of the pointcut matches one or more join points that the advice is woven into.

section

A section is a combination of notification and pointcut. Notifications and pointcuts define the overall content of an aspect — what it is, when and where it performs its function.

Introduction Introduction allows you to add new methods or attributes to an existing class.

weave

Weaving is the process of applying a facet to a target object and creating a new proxy object. The section is woven into the target object at the specified join point. There are multiple points that can be woven into the life cycle of the target object:

  • Compiler: The aspect is woven in when the target class is compiled. This is how the Aspect weaving compiler weaves into the Aspect.

  • Class loader: The aspect is woven when the target class is loaded into the JVM. A special Classloader is required that enhances the CGlib of the target class before it is introduced.

  • Runtime: Facets are woven in at some point during application runtime. AOP creates a proxy object for the target object. Spring provides four types of AOP support:

  • Classic Proxy-based Spring AOP

  • Pure POJO section

  • @AspectJ annotation-driven facets

  • Injection AspectJ facets

7. With the above concepts in mind, let’s take a look at the configuration of multiple data sources. First, let’s see the test effect:

First, the DataSource is an Aspect annotated with @aspect, and then the pointcut is defined. As long as the @datasource annotation method is used, it is a pointcut. We then define the wrap notification with @around (“cut()”), which executes the data source before and after the call. There is also the use of logging, which I’ll talk about in a minute.

/** * Enumeration of multiple data sources */
public interface DSEnum {

	String DATA_SOURCE_GUNS = "dataSourceGuns";		/ / guns data source

	String DATA_SOURCE_BIZ = "dataSourceBiz";			// Data source for other business} -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --@Override
@DataSource(name = DSEnum.DATA_SOURCE_BIZ)
public void testBiz(a) {
    Test test = testMapper.selectByPrimaryKey(1);
    test.setId(22);
    testMapper.insert(test);
}

@Override
@DataSource(name = DSEnum.DATA_SOURCE_GUNS)
public void testGuns(a) {
    Test test = testMapper.selectByPrimaryKey(1);
    test.setId(33);
    testMapper.insert(test);
}
Copy the code
/** ** multi-data source switch AOP */
@Aspect
@Component
@ConditionalOnProperty(prefix = "guns", name = "muti-datasource-open", havingValue = "true")
public class MultiSourceExAop implements Ordered {

	private Logger log = LoggerFactory.getLogger(this.getClass());


	@Pointcut(value = "@annotation(com.guo.guns.common.annotion.DataSource)")
	private void cut(a) {}@Around("cut()")
	public Object around(ProceedingJoinPoint point) throws Throwable {

		Signature signature = point.getSignature();
        MethodSignature methodSignature = null;
        if(! (signatureinstanceof MethodSignature)) {
            throw new IllegalArgumentException("This annotation can only be used for methods");
        }
        methodSignature = (MethodSignature) signature;

        Object target = point.getTarget();
        Method currentMethod = target.getClass().getMethod(methodSignature.getName(), methodSignature.getParameterTypes());

        DataSource datasource = currentMethod.getAnnotation(DataSource.class);
        if(datasource ! =null){
			DataSourceContextHolder.setDataSourceType(datasource.name());
			log.debug("Set data source to:" + datasource.name());
        }else{
        	DataSourceContextHolder.setDataSourceType(DSEnum.DATA_SOURCE_GUNS);
			log.debug("Set the data source to: dataSourceCurrent");
        }

        try {
        	return point.proceed();
		} finally {
			log.debug("Empty data source information!"); DataSourceContextHolder.clearDataSourceType(); }}}Copy the code

This project uses Mybatis as the persistence layer framework, so see how it is configured. To use it, you have to inject it, and here you use the @AutoWired annotation.

In Spring, objects do not have to find or create other objects associated with them. Instead, the container is responsible for assigning object references to objects that need to cooperate with each other. An order management component requires a credit card authentication component, but instead of creating a credit card authentication component itself, the container will actively give it a human presence in the component. Spirng automatically satisfies dependencies between beans

MapperScan: Automatically scans mappers, associates it with the SqlSessionTemplate, and registers mappers with the Spring container for injection into our Beans.

/** * MybatisPlus configuration */
@Configuration
@EnableTransactionManagement(order = 2)// Because of the introduction of multi-data sources, aop for Spring transactions comes after the multi-data source switch
@MapperScan(basePackages = {"com.guo.guns.modular.*.dao"."com.guo.guns.common.persistence.dao"})
public class MybatisPlusConfig {

    @Autowired
    DruidProperties druidProperties;

    @Autowired
    MutiDataSourceProperties mutiDataSourceProperties;
    /** * another data source */
    private DruidDataSource bizDataSource(a) {
        DruidDataSource dataSource = new DruidDataSource();
        druidProperties.config(dataSource);
        mutiDataSourceProperties.config(dataSource);
        return dataSource;
    }
    // Omit single and guns data sources
    /** * Multi-source connection pool configuration */
    @Bean
    @ConditionalOnProperty(prefix = "guns", name = "muti-datasource-open", havingValue = "true")
    public DynamicDataSource mutiDataSource(a) {

        DruidDataSource dataSourceGuns = dataSourceGuns();
        DruidDataSource bizDataSource = bizDataSource();

        try {
            dataSourceGuns.init();    / / the key
            bizDataSource.init();
        }catch (SQLException sql){
            sql.printStackTrace();
        }

        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        HashMap<Object, Object> hashMap = new HashMap();     // HashMap is used here
        hashMap.put(DSEnum.DATA_SOURCE_GUNS, dataSourceGuns);
        hashMap.put(DSEnum.DATA_SOURCE_BIZ, bizDataSource);
        dynamicDataSource.setTargetDataSources(hashMap);
        dynamicDataSource.setDefaultTargetDataSource(dataSourceGuns);
        returndynamicDataSource; } -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- stay would say -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --/** * Data range mybatis plugin */
    @Bean
    public DataScopeInterceptor dataScopeInterceptor(a) {
        return newDataScopeInterceptor(); }}Copy the code

Looking at the code makes things easier,

One of the uses of interceptors is that we can intercept method calls. We can choose to add logic before and after the execution of the intercepted method, or we can execute our own logic while executing the intercepted method and not execute the intercepted method.

  • @intercepts is used to indicate that the current object is an Interceptor,
  • @signature indicates the interface to be intercepted, the method, and the corresponding parameter type.

Excuse me.


/** * Data range */
public class DataScope {
    /** * The field name of the restricted scope */
    private String scopeName = "deptid";
    /** ** limits the range of */
    private List<Integer> deptIds;
    / /...} -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --/** * Data range interceptor */
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class DataScopeInterceptor implements Interceptor {

    /** * get the real processing object, possibly multiple layers of proxies. */
    public static Object realTarget(Object target) {
        if (Proxy.isProxyClass(target.getClass())) {
            MetaObject metaObject = SystemMetaObject.forObject(target);
            return realTarget(metaObject.getValue("h.target"));
        }
        return target;
    }
  // omit a bunch, come back in line.
}
Copy the code

Logging Configuration

Once the data part is configured, the important logging part comes next. This is important because you can record which users, which services are performed, and which data are modified, and log them asynchronously, also based on JavaConfig.

As usual, look at the factory first

/** * Log object create factory */
public class LogFactory {

    /** * Create operation log */
    public static OperationLog createOperationLog(LogType logType, Integer userId, String bussinessName, String clazzName, String methodName, String msg, LogSucceed succeed) {
        OperationLog operationLog = new OperationLog();
        operationLog.setLogtype(logType.getMessage());
        operationLog.setLogname(bussinessName);
        operationLog.setUserid(userId);
        operationLog.setClassname(clazzName);
        operationLog.setMethod(methodName);
        operationLog.setCreatetime(new Date());
        operationLog.setSucceed(succeed.getMessage());
        operationLog.setMessage(msg);
        return operationLog;
    }
    // The login log is omitted} -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- the Timer Timer is a kind of tool, is used to perform specified tasks in a background thread plan. It can be scheduled to perform a task once or repeatedly. TimerTask An abstract class whose subclasses represent tasks that can be scheduled by a Timer./** * Create factory **@author fengshuonan
 * @dateDecember 6, 2016 9:18:27 PM */
public class LogTaskFactory {

    private static Logger logger             = LoggerFactory.getLogger(LogManager.class);
    private static LoginLogMapper loginLogMapper     = SpringContextHolder.getBean(LoginLogMapper.class);
    private static OperationLogMapper operationLogMapper = SpringContextHolder.getBean(OperationLogMapper.class);

    public static TimerTask loginLog(final Integer userId, final String ip) {
        return new TimerTask() {
            @Override
            public void run(a) {
                try {
                    LoginLog loginLog = LogFactory.createLoginLog(LogType.LOGIN, userId, null, ip);
                    loginLogMapper.insert(loginLog);
                } catch (Exception e) {
                    logger.error("Failed to create login log!, e); }}}; }// Omit a lot, and explore the code slowly.
}


Copy the code

Log manager

public class LogManager {

    // Log operation delay
    private final int OPERATE_DELAY_TIME = 10;

    // The thread pool for logging asynchronous operations
    private ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(10);

    private LogManager(a) {}public static LogManager logManager = new LogManager();

    public static LogManager me(a) {
        return logManager;
    }

    public void executeLog(TimerTask task) { executor.schedule(task, OPERATE_DELAY_TIME, TimeUnit.MILLISECONDS); }} -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --/** * where the modified bean is temporarily stored */
@Component
@Scope(scopeName = WebApplicationContext.SCOPE_SESSION)
public class LogObjectHolder implements Serializable{

    private Object object = null;

    public void set(Object obj) {
        this.object = obj;
    }

    public Object get(a) {
        return object;
    }
    // This method is important.
    public static LogObjectHolder me(a){
        LogObjectHolder bean = SpringContextHolder.getBean(LogObjectHolder.class);
        returnbean; }} -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- notes -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --/** * marks the method that needs to do business logging */
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface BussinessLog {

    /** * Business name, for example :" modify menu "*/
    String value(a) default "";

    /** * The unique identifier of the modified entity, for example, the unique identifier of the menu entity is "ID" */
    String key(a) default "id";

    /** * dictionary (used to find the Chinese names of keys and fields) */
    String dict(a) default "SystemDict";
}


Copy the code

This is a slice,

@Aspect
@Component
public class LogAop {

    private Logger log = LoggerFactory.getLogger(this.getClass());

    @Pointcut(value = "@annotation(com.guo.guns.common.annotion.log.BussinessLog)")
    public void cutService(a) {}@Around("cutService()")
    public Object recordSysLog(ProceedingJoinPoint point) throws Throwable {

        // Execute the service first
        Object result = point.proceed();

        try {
            handle(point);
        } catch (Exception e) {
            log.error("Error logging!", e);
        }

        return result;
    }

    private void handle(ProceedingJoinPoint point) throws Exception {

        // Get the intercepting method name
        Signature sig = point.getSignature();
        MethodSignature msig = null;
        if(! (siginstanceof MethodSignature)) {
            throw new IllegalArgumentException("This annotation can only be used for methods");
        }
        msig = (MethodSignature) sig;
        Object target = point.getTarget();
        Method currentMethod = target.getClass().getMethod(msig.getName(), msig.getParameterTypes());
        String methodName = currentMethod.getName();

        // If the current user is not logged in, no log is generated
        ShiroUser user = ShiroKit.getUser();
        if (null == user) {
            return;
        }

        // Get the intercepting method parameters
        String className = point.getTarget().getClass().getName();
        Object[] params = point.getArgs();

        // Get the operation name
        BussinessLog annotation = currentMethod.getAnnotation(BussinessLog.class);
        String bussinessName = annotation.value();
        String key = annotation.key();
        String dictClass = annotation.dict();

        StringBuilder sb = new StringBuilder();
        for (Object param : params) {
            sb.append(param);
            sb.append("&");
        }

        // Compare changes if modifications are involved
        String msg;
        if (bussinessName.indexOf("Change") != -1 || bussinessName.indexOf("Edit") != -1) {
            Object obj1 = LogObjectHolder.me().get();
            Map<String, String> obj2 = HttpKit.getRequestParameters();
            msg = Contrast.contrastObj(dictClass, key, obj1, obj2);
        } else{ Map<String, String> parameters = HttpKit.getRequestParameters(); AbstractDictMap dictMap = DictMapFactory.createDictMap(dictClass); msg = Contrast.parseMutiKey(dictMap,key,parameters); } LogManager.me().executeLog(LogTaskFactory.bussinessLog(user.getId(), bussinessName, className, methodName, msg)); }}Copy the code

The business logic needs to be studied. Here is just a process, when you have an impression. I really want to post the author’s name, but the place is not allowed. Here’s thanks to abel533 and stylefeng, like big guy learning.

The next one is EhCache. Jd plugin and BEet template engine are left for last. Gogogo.