AOP简介
AOP(Aspect Orient Programming)是一种设计思维,是软件设计畛域中的面向切面编程,它是面向对象编程(OOP)的一种补充和欠缺。理论我的项目中咱们通常将面向对象了解
为一个动态过程(例如一个零碎有多少个模块,一个模块有哪些对象,对象有哪些属性),面向切面了解为一个动静过程(在对象运行时动静织入一些扩大性能或管制对象执行)。
AOP 与 OOP 字面意思相近,但其实两者齐全是面向不同畛域的设计思维。理论我的项目中咱们通常将面向对象了解为一个动态过程(例如一个零碎有多少个模块,一个模块有哪些
对象,对象有哪些属性),面向切面的运行期代理形式,了解为一个动静过程,能够在对象运行时动静织入一些扩大性能或管制对象执行。
实现原理
AOP 能够在系统启动时为指标类型创立子类或兄弟类型对象,这样的对象咱们通常会称之为动静代理对象.如图所示:
其中,为指标类型(XxxServiceImpl)创立其代理对象形式有两种:
第一种形式:借助 JDK 官网 API 为指标对象类型创立其兄弟类型对象,然而指标对象类型须要实现相应接口.
第二种形式:借助 CGLIB 库为指标对象类型创立其子类类型对象,然而指标对象类型不能应用 final 润饰.
相干术语剖析
切面(aspect):横切面对象,个别为一个具体类对象
切入点(pointcut):定义了切入扩大业务逻辑的地位(哪些办法运行时切入扩大业务),个别会通过表达式进行相干定义,一个切面中能够定义多个切入点。
告诉(Advice):外部封装扩大业务逻辑的具体方法对象,一个切面中能够有多个告诉(在切面的某个地位上执行的动作(扩大性能))
连接点(joinpoint):程序执行过程中,封装了某个正在执行的指标办法信息的对象,能够通过此对象获取具体的指标办法信息,甚至去调用指标办法。连接点与切入点定义如图:
案例
增加依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
业务切面对象设计
通过设计切面对象,为指标业务办法做性能加强,关键步骤如下:
第一步:创立注解类型,利用于切入点表达式的定义,要害代码如下:
@Target 用于形容定义的注解可能润饰的对象,@Retention 用于形容定义的注解何时无效。
package com.cy.pj.sys.commen.annotation;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)public @interface RequiredLog{ String operation();//在指标业务上应用@RequiredLog时须要为operation赋值// String operation() default "";//在指标业务上应用@RequiredLog时不须要为operation赋值}
第二步:创立切面对象,用于做日志业务加强,要害代码如下:
package com.cy.pj.sys.service.aspect;import com.cy.pj.sys.commen.annotation.RequiredLog;import com.cy.pj.sys.dao.SysLogDao;import com.cy.pj.sys.pojo.SysLog;import com.cy.pj.sys.service.SysLogService;import com.fasterxml.jackson.core.JsonProcessingException;import com.fasterxml.jackson.databind.ObjectMapper;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.Signature;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Pointcut;import org.aspectj.lang.reflect.MethodSignature;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import java.lang.reflect.Method;import java.util.Date;import java.util.List;/*在spring aop利用中,基于@Aspect注解形容的类型为一个切面类型,此类中要封装切入点及告诉办法的定义1.切入点:要切入扩大业务逻辑的一些指标办法汇合2.告诉:封装了扩大业务逻辑的一个办法* 创立切面对象,用于做日志业务加强* */@Aspect@Componentpublic class SysLogAspect { private static final Logger log= LoggerFactory.getLogger(SysLogAspect.class); /*定义切入点,基于@pointcut注解定义,这里的@annotation为一种切入点表达式 * 示意由RequiredLog注解形容的办法为一个切入点办法,咱们在这样的办法上增加业务扩大 * */ @Pointcut("@annotation(com.cy.pj.sys.commen.annotation.RequiredLog)") public void doLog(){}//此办法只负责承载切入点的定义 /* * @Around注解形容的办法,能够在切入点执行之前和之后进行业务拓展 * @param jp连接点对象,在此对象封装了要执行的指标办法信息 * ProceedingJoinPoint此类型的注解只能定义在@Around中 * 能够通过连接点对象调用指标办法 * @return指标办法的执行后果 * @throws Throwable * */ @Around("doLog()") //@Around("@annotation(com.cy.pj.sys.commen.annotation.RequiredLog)") public Object doAround(ProceedingJoinPoint jp) throws Throwable { log.info("method start {}", System.currentTimeMillis()); long t1=System.currentTimeMillis(); try { Object result=jp.proceed();//调用指标办法 System.out.println(jp.getTarget().getClass().getName()); log.info("method after {}", System.currentTimeMillis()); long t2=System.currentTimeMillis(); doLogInfo(jp,(t2-t1),null); return result; }catch (Throwable e){ log.error("exception {}", e.getMessage()); long t3=System.currentTimeMillis(); doLogInfo(jp,(t3-t1),e); throw e; } }//记录用户行为日志 @Autowired private SysLogService sysLogService; private void doLogInfo(ProceedingJoinPoint jp,long time,Throwable e) throws NoSuchMethodException, JsonProcessingException { //获取行为日志 String username="tony";//登录用户 String ip="192.168.100.11";//登录用户的IP借助三方工具类 //获取用户操作 //获取办法所在类的字节码对象(指标对象对应的字节码对象) Class<?>cls=jp.getTarget().getClass(); //获取注解形容的办法对象(字节码对象,办法名,参数列表) //System.out.println("cls="+cls); Signature signature=jp.getSignature(); //System.out.println("signature="+signature.getClass().getName());//MethodSignatureImpl MethodSignature methodSignature= (MethodSignature) signature;// Method targetMethod=methodSignature.getMethod();//cglib Method targetMethod=//cglib,jdk cls.getDeclaredMethod(methodSignature.getName(), methodSignature.getParameterTypes());// System.out.println("targetMethod="+targetMethod); //获取RequiredLog注解 RequiredLog requiredLog=targetMethod.getAnnotation(RequiredLog.class); String operation= requiredLog.operation(); //String operation=null; //获取办法申明信息// System.out.println(cls.getName());//com.cy.pj.sys.service.serviceImpl.SysLogServiceImpl// System.out.println(targetMethod.getName());//findLogs String method=cls.getName()+"."+targetMethod.getName(); //获取办法执行时传入的理论参数 String params=new ObjectMapper().writeValueAsString(jp.getArgs()); //获取状态信息 Integer status=e==null?1:0; String error=e==null?null:e.getMessage(); //封装用户行为日志 SysLog sysLog=new SysLog(); sysLog.setUsername(username); sysLog.setIp(ip); sysLog.setCreatedTime(new Date()); sysLog.setOperation(operation); sysLog.setParams(params); sysLog.setStatus(status); sysLog.setError(error); sysLog.setTime(time); //打印日志 log.info("user log {}", new ObjectMapper().writeValueAsString(sysLog)); //将日志写入到数据库 sysLogService.saveLog(sysLog); }}
第三步:通过注解 RequiredLog 注解形容日志查问或删除业务相干办法,此时这个办法为日志切入点办法,例如:
@RequiredLog(operation = "布告查问") @Override public List<SysLog> findLogs(SysLog sysLog) { return sysLogDao.selectLogs(sysLog); } @RequiredLog(operation = "删除日志")// @RequiredLog @Override public int deleteById(long... id) { return sysLogDao.deleteLogs(id); }