背景:使用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即可添加到数据库,效果图如下,下图是从其它博客拿过来的,做演示使用:

 

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐