`
luoyu-ds
  • 浏览: 137346 次
  • 性别: Icon_minigender_1
  • 来自: 成都
社区版块
存档分类
最新评论

基于Spring aop 和JAVA注解方式添加日志

 
阅读更多

前几天做项目时,在做系统日志这一块,都是在每个方法里手写代码来添加,觉得很繁琐,考虑到spring有aop的功能,便寻思着用AOP来做这个日志功能。

首先需要传入日志记录的具体操作名称,我们可以用java的注解功能来带入参数,代码如下:

 

/**
 * 类的方法描述注解
 * @author LuoYu
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface Log {

    /** 要执行的操作类型比如:add操作 **/
    public String operationType() default "";
   
    /** 要执行的具体操作比如:【添加仓库】 **/
    public String operationName() default "";
   
}

 

 注解类编写好之后,就要考虑spring我切面的问题目了,首先我们要创建一个切点,也就是需要插入的代码块,代码如下:

/**
 * 通过Spring AOP来添加系统日志
 * @author LuoYu
 */
public class LogAspect extends BaseAction{

    private static final long serialVersionUID = -5063868902693772455L;

    private Log logger = LogFactory.getLog(LogAspect.class);
   
    @SuppressWarnings( { "rawtypes", "unchecked" } )
    public void doSystemLog(JoinPoint point) throws Throwable { 
        Object[] param = point.getArgs();
        Method method = null;
        String methodName = point.getSignature().getName(); 
        if (!(methodName.startsWith("set") || methodName.startsWith("get")||methodName.startsWith("query"))){
            Class targetClass = point.getTarget().getClass(); 
            method = targetClass.getMethod(methodName, param[0].getClass());
            if (method != null) {
                boolean hasAnnotation = method.isAnnotationPresent(com.tlj.pcxt.common.logaop.Log.class); 
                if (hasAnnotation) {
                    com.tlj.pcxt.common.logaop.Log annotation = method.getAnnotation(com.tlj.pcxt.common.logaop.Log.class); 
                    String methodDescp = annotation.operationType()+annotation.operationName();
                    if (logger.isDebugEnabled()) { 
                        logger.debug("Action method:" + method.getName() + " Description:" + methodDescp); 
                    } 
                    User appUser=(User) this.getHttpServletRequest().getSession().getAttribute("user");
                    if(appUser!=null){ 
                        try{ 
                            com.tlj.pcxt.entity.admin.Log logInfo=new com.tlj.pcxt.entity.admin.Log(); 
                            logInfo.setIp(this.getHttpServletRequest().getRemoteAddr());
                            logInfo.setSubmitUser(appUser);
                            logInfo.setContent(annotation.operationType()+","+appUser.getUserName()+
                                    "执行【"+annotation.operationName()+"】操作,影响数据的ID集合为["+getID(param[0])+"]");
                            this.logService.save(logInfo); 
                        }catch(Exception ex){ 
                            logger.error(ex.getMessage()); 
                        } 
                    } 
                } 
            } 
        } 
    }
   
    /**
     * 通过java反射来从传入的参数object里取出我们需要记录的id,name等属性,
     * 此处我取出的是id
     *@author 罗宇
     *@date 2013-4-11
     *@param obj
     *@return
     *@return String
     */
    public String getID(Object obj){
        if(obj instanceof String){
            return obj.toString();
        }
        PropertyDescriptor pd = null;
        Method method = null;
        String v = "";
        try{
            pd = new PropertyDescriptor("id", obj.getClass());
            method = pd.getReadMethod(); 
            v = String.valueOf(method.invoke(obj));
        }catch (Exception e) {
            e.printStackTrace();
        }
        return v;
    }
}

 

 切入代码编写好之后,需要在applicatioContext.xml里配置切入规则,也就是说要在哪些方法执行的时候来切入上面编写的代码:配置如 下:

<aop:aspectj-autoproxy/>
    <bean id="logAspect" class="com.tlj.pcxt.common.logaop.LogAspect"/>   
     <aop:config> 
        <aop:aspect ref="logAspect"> 
            <aop:pointcut id="logPointCut" expression="
                   (execution(* com.tlj.pcxt.service.*.*Impl.add*(..)))
                or (execution(* com.tlj.pcxt.service.*.*Impl.update*(..)))
                or (execution(* com.tlj.pcxt.service.*.*Impl.delete*(..)))
            "/> 
            <aop:after pointcut-ref="logPointCut" method="doSystemLog"/> 
        </aop:aspect> 
    </aop:config> 

 

在此我配置的时在方法执行之后插入代码块

 

<aop:after pointcut-ref="logPointCut" method="doSystemLog"/>

 

 

并且是在所有以add,update,delete开头的方法才执行,其余的方法将不再匹配。

调用方法如下,

 

 

  @Log(operationType="add操作:",operationName="添加仓库房间")
    public void addWareHouseRoom(WareHouseRoom wareHouseRoom) throws ServiceException {
        try{
            this.getWareHouseRoomDao().save(wareHouseRoom);
        }catch (Exception e) {
            throw new ServiceException(e);
        }
    }

 

 

 

是在方法头前添加上面自定义的@Log注解,传入相关日志信息

另外,在LogAspect的doSystemLog方法里的

Object[] param = point.getArgs();

 

 就是取出所匹配方法传入的参数,我们记录日志所需要的相关参数就是从这个对象里取出来的,并且在该方法下面的代码会检查所匹配的方法是否有注解@log,如果没有,会直接跳出该方法,不做任何处理.

6
3
分享到:
评论
10 楼 zhaoljin 2014-11-11  
采用了  
9 楼 luoyu-ds 2014-06-08  
zhaoljin 写道
这些代码可以实现不

可以实现,对于是基本的添加,修改,删除的操作有效,前提是你传入的参数是一个对象
比如,你要保存一个用户,service层的接口方法应该是这样的
public void addUser(User user);
具体为什么请细看代码。
8 楼 zhaoljin 2014-05-31  
请问我该怎么调用和测试,框架不熟
7 楼 zhaoljin 2014-05-31  
这些代码可以实现不
6 楼 zwyancc 2013-06-09  
你上传个jar 不行呀 搞死了这个东西问题多多
5 楼 zwyancc 2013-06-09  
你上传个jar包不行呀 擦问题多多
4 楼 luoyu-ds 2013-04-16  
cbbaaa1989 写道
你的切面为什么要继承BaseAction?

this.logService.save(logInfo); 为了取最后存日志的logService,我是注入到BaseAction里面的
3 楼 cbbaaa1989 2013-04-16  
你的切面为什么要继承BaseAction?
2 楼 luoyu-ds 2013-04-16  
leh627 写道
性能上是个问题,呵呵

对于一般的企业级系统,访问量本身就不是很大,如果在切入规则上限制较小的话,性能的问题应该是没问题的
1 楼 leh627 2013-04-15  
性能上是个问题,呵呵

相关推荐

Global site tag (gtag.js) - Google Analytics