今天玩了下Java重写toString()方法:
Object是所有类的父类:
Object类中存在toString方法是所有类重写该方法的根本:
Integer.toHexString(hashCode()) 以对象的哈希码为参数,以16进制无符号整数形式返回此哈希码的字符串表示形式;
可以简单理解为根据对象的的Hash值返回字符代表对象的地址值
在重写该方法时脑袋蹦出一个想法:在我以往的代码开发其中 经常出先对象tostring报错:空指针异常的情况
解决方法:在企业生产中 所有实体的父类重写toString()方法:判读当前对象是否为null 如果为null
则返回”null 提示 “ 如下:
运行测试方法 发现仍会报错空指针异常:
查阅资料知道:
当一个对象为null时 不能调用该对象的非静态方法 静态方法会存在静态池中 被该对象的所有实例使用:
null对象自身的存储空间内是没有任何方法和属性的,所以任何空对象.xxx调用非静态方法或属性都会出现空指针异常;
Static 方法是存储在静态池当中的,一个类的所有对象共享静态池的方法;
tostring方法无法修改为静态:
看来Java这些基础的东西已经很完整了!
=======分割线=============================================================
是否可以通过切面的形式实现呢?
* 1、execution(public * *(..)) 任意的公共方法
我们可以针对所有的toString方法切面 根据point 获取当前对象 如果为null 不报错呢?
成功使用切面前 解决maven依赖版本问题:
大事化简!
终于解决aop切面日志注解未生效的问题!!!!
总结:抛弃无用依赖 回归原始基础依赖 缺什么再补!
2.Spring Aop 默认不代理测试类中的方法 所以之前在测试类方法添加注解 不会生效!
3.AfterReturning()的切面顺序高于After!
4.Around()环绕通知未生效! 原因是未添加注解:
发现添加环绕通知后 before 和 after 等切点均为生效 如下:
后来发现 环绕通知后 并未让程序继续运行!
可以看出执行顺序:
环绕通知Around -》Before-》 AfterReturning-》After
生产环境中 貌似before 以及 after 等 不常使用 :
注:@Slf4j == private Logger logger = LoggerFactory.getLogger(LogAop.class);
不信请看:
所以使用该注解 在代码中可直接使用log.xxxx
二、日志具体优化
1.简化类名:
logger.info("执行方法:"+ joinPoint.getTarget().getClass().getName()+"."+name);
logger.info("执行方法优化后:"+ joinPoint.getTarget().getClass().getSimpleName() + "." + joinPoint.getSignature()
.getName());
.getSimpleName()可以获取简单的类名 getName()获取全路径下的类名 生产环境中 包层级复杂 建议使用.getSimpleName()
2.参数优化
优化之前分别输出方法和参数值 减少日志条数 采用StringBuilder拼接字符串 提高性能的同时以程序员习惯的方式输出方法调用 关于字符串拼接的性能问题 可以参考文档:
关于 Java 字符串拼接的几种方式以及性能比较 - 简书
总结就是
三、输出方法执行时间+返回值
以上就是简单的日志格式优化以及内容丰富
下一步要实现怎么弄到数据库中
附源码:
package xbh.aop.impl;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import java.net.InetAddress;
import java.net.UnknownHostException;
@Component
@Aspect
@Slf4j
public class LogAop {
/**
* 定义切入点:对要拦截的方法进行定义与限制,如包、类Z
*
* 1、execution(public * *(..)) 任意的公共方法 //注 使用不加限定的切点 程序启动会报错!
* 2、execution(* set*(..)) 以set开头的所有的方法
* 3、execution(* com.lingyejun.annotation.LoggerApply.*(..))com.lingyejun.annotation.LoggerApply这个类里的所有的方法
* 4、execution(* com.lingyejun.annotation.*.*(..))com.lingyejun.annotation包下的所有的类的所有的方法
* 5、execution(* com.lingyejun.annotation..*.*(..))com.lingyejun.annotation包及子包下所有的类的所有的方法
* 6、execution(* com.lingyejun.annotation..*.*(String,?,Long)) com.lingyejun.annotation包及子包下所有的类的有三个参数,第一个参数为String类型,第二个参数为任意类型,第三个参数为Long类型的方法
* 7、execution(@annotation(com.lingyejun.annotation.Lingyejun))
*/
private Logger logger = LoggerFactory.getLogger(LogAop.class);
private static String IP = null;
static {
try {
IP = InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
log.error("ip get fail");
}
}
/**
* 拦截的方法进行定义与限制
* xbh.aop.impl包下任意的公共方法
*/
@Pointcut("execution(public * xbh.aop.impl..*(..))")
public void getLogByExe(){
}
/**
* 自定义注解
*/
@Pointcut("@annotation(xbh.aop.impl.LogAnno)")
public void getLogByAnno(){
}
// @Before("getLogByExe()")
// public void beforeMethod(){
// logger.info("调用函数前!");
// }
//
// @After("getLogByExe()")
// public void afterMethod(){
// logger.info("调用函数后!");
// }
//
// @AfterReturning("getLogByExe()")
// public void afterReturn(){
// logger.info(" @AfterReturning");
// }
@AfterThrowing(pointcut = "execution(public * xbh.aop.impl..*(..))", throwing = "error")
public void afterThrowing(JoinPoint jp, Throwable error){
logger.error("@AfterThrowing:"+error.getMessage());
}
@Around("getLogByExe()")
public void logAspect(ProceedingJoinPoint joinPoint) throws Throwable {
logger.info("host ip : "+IP);
// 获取目标方法的名称
String name = joinPoint.getSignature().getName();
//getSimpleName 类名简化形式
String method = joinPoint.getTarget().getClass().getSimpleName() + "." + joinPoint.getSignature()
.getName();
// 获取方法传入参数
Object[] args = joinPoint.getArgs();
if (args.length==0){
logger.info("执行方法 : "+ method+"()");
}else{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < args.length; i++) {
//getSimpleName 参数类型简化形式
sb.append(args[i].getClass().getSimpleName());
sb.append(" ");
sb.append(args[i]);
if (i!=args.length-1){
sb.append(",");
}
}
logger.info("执行方法 : "+ method+"("+sb+")");
}
long start = System.currentTimeMillis();
long cost;
//获取方法返回值:
Object proceed = joinPoint.proceed();
if (proceed != null){
logger.info("方法返回:"+proceed.getClass().getSimpleName()+" "+proceed);
}
//方法调用花费时间
cost = System.currentTimeMillis() - start;
logger.info("方法执行时间:"+cost);
}
}