携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第21天,点击查看活动详情
以前都是一个项目的开发,在多项目下的发现自己学的就很差
aop
众所周知,aop是面向切面编程,并且是通过反向代理实现的,那我们到底需要怎么样的业务场景,需要我们操作呢,比如说,很简单的,我们想要的就是log的日志输出,但是呢,我们有很多类,而且里面还有很多方法,我们不想要所有方法都log输出,只是想要一些特定的类的特定方法,并且这些类不在一个包下面,怎么实现?
注解!!通过注解作用的类才进行日志打印。
好动手实操,我们一个项目很大,还是分布式的,所以一些业务场景需要调用我们基础包里面的代码,好那我们就把我们的基础包(暂且称为core项目)中实现我们aop
定义一个注解@SysLog
/**
* 系统日志注解
*
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SysLog {
String value() default "";
}
意思是只要是我们这个注解作用的方法才可以通过aop反向代理生成的类执行日志操作。
好来实现aop
@Aspect
@Component
public class LogAop {
private Logger log = LoggerFactory.getLogger(LogAop.class);
@Pointcut("@annotation(com.he.teamilk.core.common.annotation.SysLog)")
public void logPointCut() {
}
@Around("logPointCut()")
public Object recordSysLog(ProceedingJoinPoint point) throws Throwable {
//先执行业务
Object result = point.proceed();
try {
handle(point, result);
} catch (Exception e) {
log.error("日志记录出错!", e);
}
return result;
}
private void handle(ProceedingJoinPoint point, Object result) {
//获取拦截的方法名
Signature sig = point.getSignature();
if (!(sig instanceof MethodSignature)) {
throw new IllegalArgumentException("该注解只能用于方法");
}
// 获取方法
Method method = ((MethodSignature) sig).getMethod();
// 获取方法上的注解
SysLog sysLog = method.getAnnotation(SysLog.class);
// 获取请求的方法名
String className = point.getTarget().getClass().getName();
// 获取 请求的参数
Object[] params = point.getArgs();
log.info("执行了:" + className + "方法, 结果为:" + result + " 参数为:" + Arrays.toString(params));
}
}
可以看到:
- 我们通过
@Aspect定义切面实现aop并通过@Component交给我们的spring容器来管理 - 再我们通过
@PointCut注解来获取切面点 @Around("logPointCut()")实现环绕通知来增强,里面的参数就是切入点ProceedingJoinPoint point是我们增强方法的形参,point.proceed()用于执行我们原本方法里面的业务逻辑- 这里我定义了
handle方法来执行日志操作,你也可以直接写在里面,这看情况 - handle方法里面的一些操作已经有注释了,你甚至可以在里面进行日志的存储到数据库中,非常方便!
你以为到这里就结束了?
这里我遇到了个坑,比较考验基础,估计厉害的大佬在开头就已经看出来了。此时如果我导入common项目依赖的项目使用了该注解的话,我们发现,输出没有任何改变?什么情况呢?
我们这个定义的非常完美啊,怎么会没效果呢?
有没有可能是我们的spring容器压根没有这个bean。
我在我们idea的Spring的ToolWindow下找我们项目的bean,发现确实没有。那是什么原因,我们使用了@Component注解了。在仔细思索和实践过后,我想明白了,我这个项目里面凭什么找别的jar包里面的注解呢?他没这个义务。
所以我们要扫描这个common项目对应的包下的这个aop类,使用@ComponentScan扫描对应的包名,而且这个注解要作用在能够被spring容器实例化的类上,比如:
@Component
@ComponentScan(basePackages = "com.he.xxx")
public class AopConfig {
}
这样就可以了。来测试一下,果然,有结果了。
为什么呢?我们想想我们的springboot相比spring到底高级在哪里:不需要配置xml文件。如何实现的,就是自动装配,在我们使用@SpringBootApplication注解的时候就已经说明我们是自动装配了,去找spring.factories文件里面需要自动装配的类了,这个也是基础。那我们上面定义的类显得臃肿,也不高级,怎么改更优雅,那我们就在common项目里面的resources目录下新建META-INF文件夹,再新建spring.factories文件,里面写上
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.he.teamilk.core.common.aop.LogAop
就可以了,之前的AopConfig删掉,以后导入common项目的依赖都可以直接使用@SysLog注解了