AOP——日志

AOP技术——日志功能

 

一.AOP技术本质

          AOPAspect-Oriented Programming,面向方面编程),可以说是OOPObject-Oriented Programing,面向对象编程)的补充和完善。OOP引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。当我们需要为分散的对象引入公共行为的时候,OOP则显得无能为力。也就是说,OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。对于其他类型的代码,如安全性、异常处理和透明的持续性也是如此。这种散布在各处的无关的代码被称为横切(cross-cutting)代码,在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。

            而AOP技术则恰恰相反,它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即方面。所谓“方面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。AOP代表的是一个横向的关系,如果说“对象”是一个空心的圆柱体,其中封装的是对象的属性和行为;那么面向方面编程的方法,就仿佛一把利刃,将这些空心圆柱体剖开,以获得其内部的消息。而剖开的切面,也就是所谓的“方面”了。然后它又以巧夺天功的妙手将这些剖开的切面复原,不留痕迹。

       如果将整个模块比喻为一个圆柱体,那么关注点识别过程可以用三棱镜法则来形容,穿越三棱镜的光束(指需求),照射到圆柱体各处,获得不同颜色的光束,最后识别出不同的关注点。


        AOP的目的,就是要将诸如Logging之类的横切关注点从BusinessLogic类中分离出来。利用AOP技术,可以对相关的横切关注点封装,形成单独的“aspect”。这就保证了横切关注点的复用。由于BusinessLogic类中不再包含横切关注点的逻辑代码,为达到调用横切关注点的目的,可以利用横切技术,截取BusinessLogic类中相关方法的消息,然后将这些“aspect”织入到该方法中。

 

 

 

.日志实现

        为了建立松散耦合的、可扩展的企业系统,AOP应用到的横切技术,通常分为两种类型:动态横切和静态横切,此处用的是静态织入的方式,大致可分为如下步骤:

1.定义切面

1)OperationLogAspect.java

@Aspect  

public class OperationLogAspect {

private static final Logger logger = LoggerFactory.getLogger(OperationLogAspect.class.getName());

@Autowired

OperationLogService logService;

@Pointcut("@annotation(com.vastio.aop.OperationLog)")

     public void methodCachePointcut() {  

     }

@Around("methodCachePointcut()")

     public Object methodCachePointcut(ProceedingJoinPoint joinPoint)  throws Throwable {  

Map<String, String> operationRecord = getMethodRemark(joinPoint);

System.out.println("1-----------------------------------------");

         Object result = null;  

         try {  

             // 记录操作日志.....在什么时间..做了什么事情..

             result = joinPoint.proceed();

             String ref = operationRecord.get("ref");

             String refId = operationRecord.get("refId");

             if (refId.isEmpty()){

               if (ref.equals("true")){

              refId = result.toString();

                 } else{

             String methodName = coventArgName(ref);

Method tmpM = result.getClass().getMethod(methodName);

refId = tmpM.invoke(result).toString();

             }

             operationRecord.put("refId", refId);

             }

             append(operationRecord);

         } catch (Exception e) {  

         logger.error("Error while save log, Message: {}", e.toString());

             throw e;  

       }  

         return result;  

     }  

 

 

private boolean append(Map<String, String> operationRecord) {

String operation = operationRecord.get("remark");

String description = operationRecord.get("des");

String refId = operationRecord.get("refId");

logService.create(operation, description, refId);

System.out.println("2-----------------------------------------");

return true;

     }

 

/**

  * 获取方法名

 *

 */

private String coventArgName(String argName){

System.out.println("3-----------------------------------------");

if (!argName.isEmpty()){

String str  = argName.substring(0,1).toUpperCase()+argName.substring(1);

return "get" + str;

}

return "";

}

    

/**

  * 此方法描述的是:获得参数中的refId

* @param m

  * @param arguments

  * @return

  * @throws Exception

  */

private String getRefIdName(Method m, Object[] arguments) {

System.out.println("4-----------------------------------------");

  Annotation[][]parameterAnnotations = m.getParameterAnnotations();    //获得所有参数

  String argName = "";

 int argIndex = -1;

          for(int i=0; i<parameterAnnotations.length; i++) {

              for (Annotation an:parameterAnnotations[i]) {

             // 获得指定的注释

               if (an instanceof RefId) {

                      argName = ((RefId)an).value();   //获得参数描述

                       argIndex = i;

                      break;

               }

              }

          }

          if (argIndex != -1){

          if (!argName.isEmpty()){

try {

String methodName = coventArgName(argName);

Method tmpM = arguments[argIndex].getClass().getMethod(methodName);

return tmpM.invoke(arguments[argIndex]).toString();

}  catch (Exception e) {

logger.error("Error while get:{}, Message: {}", argName, e);

}

          }else{

          return arguments[argIndex].toString();

           }

          }

return "";

}

/**

         * 此方法描述的是:获得注解的内容和参数的值

 * @throws Exception

         */

private Map<String, String> getMethodRemark(ProceedingJoinPoint joinPoint) {  

MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();

         Method targetMethod = methodSignature.getMethod();

         OperationLog methodCache = targetMethod.getAnnotation(OperationLog.class);

        OperationType type = methodCache.remark();

        Object[] arguments = joinPoint.getArgs();

System.out.println("5-----------------------------------------");

         Map <String, String> operationRecord = new HashMap <String, String>();

        

     operationRecord.put("remark", type.getName());

                operationRecord.put("ref", methodCache.ref());

                operationRecord.put("refId", getRefIdName(targetMethod, arguments));

 

String description = methodCache.des();

     if (description == null || description.equals("")) {

     description = type.getDescription();

      }

     operationRecord.put("des", description);

  return operationRecord;

     }  

}

 

2)OperationgLog.java 

/**

 * 此类描述的是:操作日志注解

 */

@Target({ElementType.METHOD})  

@Retention(RetentionPolicy.RUNTIME)  

@Documented  

public @interface OperationLog {

//操作功能说明

public OperationType remark();

//操描述

public String des() default "";

//返回值作是否做为ref_id

public String ref() default "";

}

 

3)OperationType.java

 

/**

* 定义所有需要跟踪的操作行为

*/

public enum OperationType {

DOC_CREATE("DOC_CREATE","新建报表"),

DOC_UPDATE("DOC_UPDATE","修改报表"),

DOC_DELETE("DOC_DELETE","删除报表"),

SCREEN_CREATE("SCREEN_CREATE","添加屏幕"),

SCREEN_DELETE_DOC_ID("SCREEN_DELETE","删除屏幕"),

CHART_CREATE("CHART_CREATE","添加图表"),

CHART_DELETE("CHART_DELETE","删除图表"),

USER_MODIFY("USER_MODIFY","修改用户"),

PWD_MODIFY("PWD_MODIFY","修改密码"),

USER_CREATE("USER_CREATE","添加用户");

 

private String _name;

private String _description;

 

private OperationType(String name, String description) {

_name = name;

_description = description;

}

 

public String getDescription() {

         return _description;

     }

 

     public String getName() {

         return _name;

     }

}

 

4)RefId.java

@Target({ElementType.PARAMETER})

@Retention(RetentionPolicy.RUNTIME)

@Documented

public @interface RefId {

String value() default "";

}

 

5)OperationLogService.java

@Service

public class OperationLogService {

private static final Logger logger = LoggerFactory.getLogger(OperationLogService.class.getName());

@Autowired

private IDBI dbi;

 

public long create(String operation, String description, String refId) {

OperationLogDao operationLogDao = dbi.onDemand(OperationLogDao.class);

OperationLog record = new OperationLog();

Integer id = operationLogDao.getSequence();

Date realDate = new Date();

Timestamp timestamp  = new Timestamp(realDate.getTime());

Subject currentUser = SecurityUtils.getSubject();

String userId = (String)currentUser.getPrincipal();

if (userId == null) {

userId = "0";

l ogger.error("Cannot get User info. Use id=0 instead.");

}

record.setId(id);

record.setOperationRecord(operation, timestamp, userId, description, refId);

try{

return operationLogDao.insert(record);

}catch(Exception e){

logger.error("Error save db, Message: {}", e.getMessage());

return -1;

}

}

public long count(StatCriteria criteria) {

OperationLogDao operationLogDao = dbi.onDemand(OperationLogDao.class);

Date startTime = DateTimeUtil.getStartOfDay(criteria.startDate);

Date endTime = DateTimeUtil.getEndOfDay(criteria.endDate);

return operationLogDao.count(

new Timestamp(startTime.getTime()),

new Timestamp(endTime.getTime()));

}

 

public List<OperationLog> findLogs(int from , int size, SearchBean searchBean) {

OperationLogDao operationLogDao = dbi.onDemand(OperationLogDao.class);

if(searchBean.getUserId() != null && !("".equals(searchBean.getUserId().trim()))){

searchBean.setUserId("%"+searchBean.getUserId().trim()+"%");

}

return operationLogDao.findLogs(from,size,searchBean);

}

 

public int total(SearchBean searchBean){

OperationLogDao operationLogDao = dbi.onDemand(OperationLogDao.class);

return operationLogDao.total(searchBean);

}

 

public void clearLog(long day){

OperationLogDao operationLogDao = dbi.onDemand(OperationLogDao.class);

Date d1=new Date();

Long t1=d1.getTime() - (day*1000L*3600L*24L);

Timestamp time=new Timestamp(t1);

operationLogDao.clearLog(time);

}

}

 

还有BeanDao之类的这里不说明了。

 

2.通过@Bean注解注入容器

在配置文件(此处以我自己的为例AppConfig.java)中,将其放入容器当中。

@Bean(name = "operationLogAspect")

public OperationLogAspect getOperationLogAspect() {

return new OperationLogAspect();

}

 

3.找到目标类service

//插入一个Doc

@OperationLog(remark = OperationType.DOC_CREATE)

public void insert(@RefId ("id") Doc doc) {

DocDao docDao = dbi.onDemand(DocDao.class);

Timestamp createTime = new Timestamp(new Date().getTime());

doc.setCreateTime(createTime);

Timestamp modifyTime = new Timestamp(0);

doc.setModifyTime(modifyTime);

docDao.insert(doc);

}

 

//根据id删除Doc

@OperationLog(remark = OperationType.DOC_DELETE)

public void deleteById(@RefId String id) {

DocDao docDao = dbi.onDemand(DocDao.class);

docDao.deleteById(id);

}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值