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 老是要分