springboot 日志插件-自定义spring boot启动类

springboot 日志插件-自定义spring boot启动类

主要记录在研究过程中遇到的一些坑。

代码地址:
https://gitee.com/mibaowei/springboot-start.git

EnableAutoConfiguration

自动装配的关键在这里插入图片描述

在spring boot中已经加入全部spring boot官网上所有支持的启动类。
在这里插入图片描述
我们以redis 的自动装配来研究
在这里插入图片描述
张的是这个样子

package org.springframework.boot.autoconfigure.data.redis;

import java.net.UnknownHostException;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;

@Configuration 标识是一个配置文件 对应 xml <beans>
@ConditionalOnClass({RedisOperations.class}) 重要注解 表示 只有在项目中存在此class 才加载此配置文件
@EnableConfigurationProperties({RedisProperties.class}) 启用对应的配置类
@Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class}) 引入对应的配置文件对应的xml中 的import 
public class RedisAutoConfiguration {
  public RedisAutoConfiguration() {
  }

  @Bean 
  @ConditionalOnMissingBean(
    name = {"redisTemplreate"}
  ) 重要注解 表示redisTemplreate 不存在才加载这个bean (意思是我们可以复写)
  public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
    RedisTemplate<Object, Object> template = new RedisTemplate();
    template.setConnectionFactory(redisConnectionFactory);
    return template;
  }

  @Bean
  @ConditionalOnMissingBean  重要注解 表示redisTemplreate 不存在才加载这个bean (意思是我们可以复写)
  public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
    StringRedisTemplate template = new StringRedisTemplate();
    template.setConnectionFactory(redisConnectionFactory);
    return template;
  }
}

ConditionalOnClass spring boot 自动装配的关键
不存在的时候返回为空(存在异常不过异常被吃了)

private ConditionOutcome getOutcome(Set<String> candidates) {
			try {
				List<String> missing = getMatches(candidates, MatchType.MISSING,
						this.beanClassLoader);
				if (!missing.isEmpty()) {
					return ConditionOutcome.noMatch(
							ConditionMessage.forCondition(ConditionalOnClass.class)
									.didNotFind("required class", "required classes")
									.items(Style.QUOTE, missing));
				}
			}
			catch (Exception ex) {
				// We'll get another chance later
			}
			return null; //以此来判断是否要装配对应的 template
		}

	}

private enum MatchType {

		PRESENT {

			@Override
			public boolean matches(String className, ClassLoader classLoader) {
				return isPresent(className, classLoader);
			}

		},

		MISSING {

			@Override
			public boolean matches(String className, ClassLoader classLoader) {
				return !isPresent(className, classLoader);
			}

		};

		private static boolean isPresent(String className, ClassLoader classLoader) {
			if (classLoader == null) {
				classLoader = ClassUtils.getDefaultClassLoader();
			}
			try {
				forName(className, classLoader);
				return true;
			}
			catch (Throwable ex) {
				return false;
			}
		}

		private static Class<?> forName(String className, ClassLoader classLoader)
				throws ClassNotFoundException {
			if (classLoader != null) {
				return classLoader.loadClass(className);
			}
			return Class.forName(className);
		}

		public abstract boolean matches(String className, ClassLoader classLoader);

	}	

所以我们也要写一个 自定义的启动类

第一步就是要添加对应的自动装配的类

项目结构如下。项目我是随便找了个spring boot的项目改的

在这里插入图片描述
第二步 添加对应的 配置类

package com.download.wangkeke.config;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;

@Configurable
@ConditionalOnClass(AopLog.class)
@EnableConfigurationProperties(LogConfig.class)
public class AopLogConfig {
  @Autowired
  private LogConfig logConfig;

  @Bean
  @ConditionalOnMissingBean(LogConfig.class)  // 当容器中没有指定Bean的情况下,自动配置
  public AopLog personService(){
    AopLog aopLog = new AopLog(logConfig.getLevel(),logConfig.getType());
    return aopLog;
  }
}



package com.download.wangkeke.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
//引入的配置文件的值 
//坑点一: prefix 请全部使用小写 不支持 驼峰命名和对应的_下划线的形式 
@ConfigurationProperties(prefix = "spring.aoplog",ignoreInvalidFields=true)
public class LogConfig {

  private String level;
  private String type;

  public String getLevel() {
    return level;
  }

  public void setLevel(String level) {
    this.level = level;
  }

  public String getType() {
    return type;
  }

  public void setType(String type) {
    this.type = type;
  }
}
//对应的注解 
@Documented
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MethodLog {


  String methodDesc() default "";
}




package com.download.wangkeke.config;


import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect
public class AopLog {


  private String  level;
  private String type;


  public AopLog() {
    System.out.println("Aop");
  }

  public AopLog(String level, String type) {
    this.level = level;
    this.type = type;
    System.out.println(level+type);
  }

  /**
   * 切点
   */
  @Pointcut("@annotation(com.download.wangkeke.config.MethodLog)")
  public void methodCachePointcut() { }


  /**
   * 切面 日志类 (网上很多例子 自己接着改造就可以用了)
   *
   * @param point
   * @return
   * @throws Throwable
   */
  @Around("methodCachePointcut()")
  public Object around(ProceedingJoinPoint point) throws Throwable {

    SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss E");
    Calendar ca = Calendar.getInstance();
    String operDate = df.format(ca.getTime());
    System.out.println("operDate"+operDate);
    String methodRemark = getMthodRemark(point);
    System.out.println("methodRemark"+methodRemark);
    String methodName = point.getSignature().getName();
    System.out.println("methodName"+methodName);
    String packages = point.getThis().getClass().getName();
    if (packages.indexOf("$$EnhancerByCGLIB$$") > -1) { // 如果是CGLIB动态生成的类
      try {
        packages = packages.substring(0, packages.indexOf("$$"));
        System.out.println(packages);
      } catch (Exception ex) {
        ex.printStackTrace();
      }
    }
    String operatingcontent = "";
    Object[] method_param = null;

    Object object;
    try {
      method_param = point.getArgs(); //获取方法参数
      System.out.println("method_param"+method_param);
      Object param= point.proceed(point.getArgs());
      System.out.println(param);
      System.out.println("param"+param);
      return param;
    } catch (Exception e) {
      // 异常处理记录日志..log.error(e);
      throw e;
    }
    
  }

  /**
   * 方法异常时调用
   *
   * @param ex
   */
  @AfterThrowing
  public void afterThrowing(Exception ex) {
    System.out.println("afterThrowing");
    System.out.println(ex);
  }

  /**
   * 获取方法中的中文备注
   *
   * @param joinPoint
   * @return
   * @throws Exception
   */
  public static String getMthodRemark(ProceedingJoinPoint joinPoint) throws Exception {

    String targetName = joinPoint.getTarget().getClass().getName();
    String methodName = joinPoint.getSignature().getName();
    Object[] arguments = joinPoint.getArgs();

    Class targetClass = Class.forName(targetName);
    Method[] method = targetClass.getMethods();
    String methode = "";
    for (Method m : method) {
      if (m.getName().equals(methodName)) {
        Class[] tmpCs = m.getParameterTypes();
        if (tmpCs.length == arguments.length) {
          MethodLog methodCache = m.getAnnotation(MethodLog.class);
          if (methodCache != null) {
            methode = methodCache.methodDesc();
          }
          break;
        }
      }
    }
    return methode;
  }
}




在这里插入图片描述
在 对应的classpath下面新建对应的META-INF (千万不要改 springboot 会默认加载这个下面的 配置类找到对应的 启动类 文件名字 也不要改。。。)
内容为自己的对应的class 路劲就可
org.springframework.boot.autoconfigure.EnableAutoConfiguration=
com.download.wangkeke.config.AopLogConfig

在这里插入图片描述
坑二。。。。
要加上 maven 打包的插件 。。。。。。。。。。。。别用 spring boot自己提供的 打包插件 不然 你打包不会成功的。。 这样打包 不会把 依赖的包 打进来,采用这个打包方式没问题。

在这里插入图片描述
请找到它
在这里插入图片描述

cmd 这个目录下面。。 执行这个命令 把jar 安装到本地
mvn install:install-file -Dfile=spring-boot-starter-aoplog-0.0.1-SNAPSHOT.jar -DgroupId=com.springwork.aoplog -DartifactId=spring-boot-starter-aoplog -Dversion=1.2.0 -Dpackaging=jar

spring-boot-starter-aoplog-0.0.1-SNAPSHOT.jar 你的jar包的名称
com.springwork.aoplog groupId 随意取
spring-boot-starter-aoplog 随意取
version 随意取
记得这几个 名字

在这里插入图片描述
在我们需要引用的 地方 引入他
在这里插入图片描述
写上对应的注解
此时这个方法就会有了 切面日志的能力

坑点三: 因为我们打的jar 不会把依赖包打进去
所以可以把对应的aop的包也要引进来。

在这里插入图片描述

你也可以尝试自己把依赖包打进来。。。。我没有尝试这样 不知道还有什么坑。
在这里插入图片描述
运行结果

在这里插入图片描述
此处order 要指定哦 有可能 会导致 一些问题 越小 越先执行
看下 这个文章 https://blog.csdn.net/u013147325/article/details/51324340

代码放到 码云上 csdn 老是要分

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值