使用SpringAop与自定义注解实现日志记录
背景:使用spring有蛮久了,一直都觉得spring的优点是,通过spring容器替我们管理bean,降低程序的耦合性,也就是IOC,但是spring还有另一个优点,那就AOP,Aop叫做面向切面编程,最开始很是纠结,学习面向对象编程思维之后,竟然又冒出来一个面向切面,一直没没怎么关注,直到项目中后来使用了spring的日志记录功能之后,才大概的体会了面向切面的思想,这里简单记录一下,使用spr
背景:使用spring有蛮久了,一直都觉得spring的优点是,通过spring容器替我们管理bean,降低程序的耦合性,也就是IOC,但是spring还有另一个优点,那就AOP,Aop叫做面向切面编程,最开始很是纠结,学习面向对象编程思维之后,竟然又冒出来一个面向切面,一直没没怎么关注,直到项目中后来使用了spring的日志记录功能之后,才大概的体会了面向切面的思想,这里简单记录一下,使用springAop记录用户操作日志过程,作为学习笔记。
简单回顾一下aop:
AOP称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等待,Struts2的拦截器设计就是基于AOP的思想,是个比较经典的例子。
一 AOP的基本概念
(1)Aspect(切面):通常是一个类,里面可以定义切入点和通知
(2)JointPoint(连接点):程序执行过程中明确的点,一般是方法的调用
(3)Advice(通知):AOP在特定的切入点上执行的增强处理,有before,after,afterReturning,afterThrowing,around
(4)Pointcut(切入点):就是带有通知的连接点,在程序中主要体现为书写切入点表达式
(5)AOP代理:AOP框架创建的对象,代理就是目标对象的加强。Spring中的AOP代理可以使JDK动态代理,也可以是CGLIB代理,前者基于接口,后者基于子类
二 Spring AOP
Spring中的AOP代理还是离不开Spring的IOC容器,代理的生成,管理及其依赖关系都是由IOC容器负责,Spring默认使用JDK动态代理,在需要代理类而不是代理接口的时候,Spring会自动切换为使用CGLIB代理,不过现在的项目都是面向接口编程,所以JDK动态代理相对来说用的还是多一些。
ok,就到这里,我这里不主要介绍使用aop,主要是记录一下自己使用aop的一个过程,并没有使用到aop的全部功能,只是实现了我自己想要的效果,达到目的就好了,接下来贴代码。
1.首先pom依赖包(可根据你项目自选版本也可直接到maven仓库搜索)
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.12</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.6.12</version>
</dependency>
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
2.使用aop需要的配置文件
<!-- 事务 aop 配置 -->
<aop:config>
<aop:pointcut id="serviceMethods" expression="execution(* cn.hejd.web.service..*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethods"/>
</aop:config>
<!-- 配置使Spring采用CGLIB代理 -->
<aop:aspectj-autoproxy proxy-target-class="true"/>
<!-- 启用对事务注解的支持 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
3.自定义一个注解
import java.lang.annotation.*;
/**
* @author HeJD
* @date 2018/8/7 12:39
* @Description:自定义日志注解
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface BussAnnotation {
//模块名
String moduleName() default "";
//操作内容
String option() default "";
}
关于自定义注解及介绍参考:https://my.oschina.net/yangzg/blog/343945
4.声明一个切面类
import org.apache.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @Auther: HeJD
* @Date: 2018/8/4 17:46
* @Description:日志切面类
*/
@Component
@Aspect
public class LogInterceptor {
private Logger logger =Logger.getLogger(LogInterceptor.class);
@Autowired
private LogService LogService;
@Pointcut("execution(public * cn.hejd.web.serviceimpl..*.*(..))")
public void serviceLogAspect() {
}
@Around(value = "serviceLogAspect() && @annotation(annotation) &&args(..) ", argNames = "pj,annotation")
public ServerResponse interceptorApplogic(ProceedingJoinPoint pj, BussAnnotation annotation){
logger.info("=====================切面日志启动==========================");
LogService.printConsoleLog(pj,annotation); //输出日志到控制台
ServerResponse serverResponse=null;
try {
serverResponse=(ServerResponse)pj.proceed();
if (serverResponse.getStatus()!=0){
logger.info("当前用户执行结果:执行失败");
}else{
logger.info("当前用户执行结果:执行成功");
}
}catch (Throwable e){
logger.info("当前用户执行结果:执行出现异常");
}
logger.info("=====================切面日志结束==========================");
return serverResponse;
}
}
这里解释一下其中出现的类:
一:ServerResponse 是我自定义的服务器返回接口,因为我需要切面得到我执行方法后的放回值,所以我的切面返回值也是ServerResponse
二:LogService:日志的服务类,主要实现打印切面日志到控制台,以及可存储日志内容到数据库
切面切点表达式规则参考:https://blog.csdn.net/lang_niu/article/details/51559994
5.这里是LogService及其实现类,一块贴上来
/**
* @Auther: HeJD
* @Date: 2018/8/6 09:43
* @Description:日志服务
*/
public interface LogService {
/**
* @author HeJD
* @date 2018/8/6 9:47
* @param [pj, annotation]
* @return void
* @Description:打印日志到控制台显示
*/
void printConsoleLog(ProceedingJoinPoint pj, BussAnnotation annotation);
//如果需要将添加到数据库,可在此添加
}
/**
* @Auther: HeJD
* @Date: 2018/8/6 09:44
* @Description:日志服务
*/
@Service("logService")
public class LogServiceImpl implements LogService {
private Logger logger=Logger.getLogger(LogServiceImpl .class);
@Override
public void printConsoleLog(ProceedingJoinPoint pj, BussAnnotation annotation) {
//获取当前执行用户
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
Person Person = (Person) request.getSession().getAttribute(Const.CURRENT_USER);
//获取当前执行操作的用户对象
logger.info("当前用户:"+Person.getName());
logger.info("当前用户执行模块:" + annotation.moduleName());
logger.info("当前用户执行操作:" + annotation.option());
//获取方法名
logger.info("当前用户执行方法:"+pj.getSignature().getName());
Method method=((MethodSignature)pj.getSignature()).getMethod();
logger.info("当前方法的放回类型"+method.getReturnType());
//获取系统时间
String time = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss").format(new Date());
logger.info("当前用户操作时间:"+time);
for(Object obj : pj.getArgs()){
logger.info("当前用户执行的方法名参数:"+obj.toString());
}
}
}
接下来如何使用我们的自定义注解及切面的效果展示
@BussAnnotation(moduleName = "商城管理",option = "更新-商家")
@Override
public ServerResponse updateFactory(Factory factory) {
int result= FactoryMapper.updateByPrimaryKeySelective(factory);
if (result>0){
return ServerResponse.createBySuccessMessage("编辑更新成功");
}
return ServerResponse.createByErrorMessage("编辑更新失败");
}
直接在需要的方法上面添加我们自定义的注解,然后当这个方法被执行时就会记录日志,记录日志如下
我这里没有存入数据库,如果需要存入数据库,可以建一个表,再定义一个实体类,打印日志接收之后,将日志实体类对应存储日志的相关记录,再通过LogService即可添加到数据库,效果图如下,下图是从其它博客拿过来的,做演示使用:
更多推荐
所有评论(0)