AOP
實現(xiàn)aop的四種方式
- AspectJ
- AspectWerkz
- SpringFramework
- JBoss
AOP核心概念
- 1、橫切關(guān)注點
對哪些方法進(jìn)行攔截,攔截后怎么處理呢岗,這些關(guān)注點稱之為橫切關(guān)注點 - 2、切面(aspect)
類是對物體特征的抽象,切面就是對橫切關(guān)注點的抽象 - 3汉形、連接點(joinpoint)
被攔截到的點纸镊,因為Spring只支持方法類型的連接點,所以在Spring中連接點指的就是被攔截到的方法获雕,實際上連接點還可以是字段或者構(gòu)造 - 4薄腻、切入點(pointcut)
對連接點進(jìn)行攔截的定義 - 5、通知(advice)
所謂通知指的就是指攔截到連接點之后要執(zhí)行的代碼届案,通知分為前置庵楷、后置、異常楣颠、最終尽纽、環(huán)繞通知五類 - 6、目標(biāo)對象
代理的目標(biāo)對象 - 7童漩、織入(weave)
將切面應(yīng)用到目標(biāo)對象并導(dǎo)致代理對象創(chuàng)建的過程 - 8弄贿、引入(introduction)
在不修改代碼的前提下,引入可以在運行期為類動態(tài)地添加一些方法或字段
切入點語法
- 1.@Pointcut("下面的東西")
任何類的任何返回值的任何方法
- 1. execution(public * *(..))
任何類的set開頭的方法
- 2.execution(* set*(..))
任何返回值的規(guī)定類里面的方法
- 3.execution(* com.example.demo.AccountService.*(..))
任何返回值的矫膨,規(guī)定包或者規(guī)定包子包的任何類任何方法
- 4.execution(* com.example.demo.service..*.*(..))
- 2.@Pointcut("@annotation(自定義注解)")
環(huán)繞通知(前置通知+執(zhí)行方法+后置通知)語法
- @Around("logPointCut()") //@Pointcut打在哪個方法,就填哪個方法
- @Around(execution(* com.example.demo.service...(..)))
注意:執(zhí)行方法:method(ProceedingJoinPoint joinPoint),要帶joinpoint這個參數(shù)
AspectJ
- AspectJ是目前最完善的AOP語言
- AspectJ是對Java編程語言的擴(kuò)展差凹,通過增加了一些新的構(gòu)造塊支持對橫切關(guān)注點的模塊化封裝,通過對源代碼級別的代碼混合實現(xiàn)織入侧馅,是一種典型的使用靜態(tài)織入的AOP實現(xiàn)機(jī)制危尿。
- AspectJ提供了兩種橫切實現(xiàn)機(jī)制
- 動態(tài)橫切(DynamicCrosscutting)
- 另一種稱為靜態(tài)橫切(StaticCrosscutting)。
簡要api
Joinpoint
- java.lang.Object[] getArgs()/*獲取連接點方法運行時的入?yún)⒘斜?/
- Signature getSignature() /*獲取連接點的方法簽名對象*/
- java.lang.Object getTarget() //獲取連接點所在的目標(biāo)對象
- java.lang.Object getThis() //獲取代理對象本身
ProceedingJoinPoint
ProceedingJoinPoint繼承JoinPoint子接口馁痴,它新增了兩個用于執(zhí)行連接點方法的方法
//通過反射執(zhí)行目標(biāo)對象的連接點處的方法
- java.lang.Object proceed() throws Throwable
//通過反射執(zhí)行目標(biāo)對象連接點處的方法谊娇,不過使用新的入?yún)⑻鎿Q原來的入?yún)ⅰ?- java.lang.Object proceed(java.lang.Object[] args) throws Throwable
示例代碼
使用aop進(jìn)行日志管理(自定義注解方式)
自定義注解
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Log{
String value() default ""; //用于描述方法作用
/**
* 是否忽略返回值,僅方法上有效
* @return
*/
boolean ignoreReturn() default false;
}
切面類
/**
* 日志切面
*/
@Aspect
//切面優(yōu)先級
@Order(100)
@Component
public class LogAspect {
private static final Logger log = LoggerFactory.getLogger(LogAspect.class);
private static final String dateFormat = "yyyy-MM-dd HH:mm:ss";
private static final String STRING_START = "\n--------------------------->\n";
private static final String STRING_END = "\n<----------------------------\n";
/**
* 切點
*/
/**
* 不使用注解方式打切點
* //@Pointcut("execution (* com.example.demo.web..*(..))")")
* 表示在使用Log注解的地方切入
*/
@Pointcut("@annotation(Log)")
public void logPointCut() {
}
//@Around("logPointCut()")---> 用于controller層
public Object controllerAround(ProceedingJoinPoint joinPoint) {
try {
return printLog(joinPoint);
} catch (Throwable throwable) {
log.error(throwable.getMessage(), throwable);
return true;
}
}
//通知:攔截到連接點之后要執(zhí)行的代碼
@Around("logPointCut()")
private Object printLog(ProceedingJoinPoint joinPoint) throws Throwable {
//獲取連接點的方法簽名
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
//獲取方法簽名里的方法:方法簽名里有兩個方法:getReturnType getMethod
Method method = signature.getMethod();
//獲取類
Class<?> targetClass = method.getDeclaringClass();
StringBuffer classAndMethod = new StringBuffer();
// 獲取目標(biāo)方法上的Log注解
Log methodAnnotation = method.getAnnotation(Log.class);
// 判斷是否有LOG注解以及是否帶有ignore參數(shù)
if (methodAnnotation != null) {
classAndMethod.append(methodAnnotation.value());
}
//拼接目標(biāo)切入的類名稱和方法名稱
String target = targetClass.getName() + "#" + method.getName();
// 請求參數(shù)轉(zhuǎn)JSON罗晕,對日期進(jìn)行格式轉(zhuǎn)換并打印出所有為null的參數(shù)
String params = JSONObject.toJSONStringWithDateFormat(joinPoint.getArgs(), dateFormat, SerializerFeature.WriteMapNullValue);
//日志打印拼接的調(diào)用信息
log.info(STRING_START + "{} 開始調(diào)用--> {} 參數(shù):{}", classAndMethod.toString(), target, params);
long start = System.currentTimeMillis();
//proceed()通過反射執(zhí)行目標(biāo)對象的連接點處的方法济欢;
Object result = joinPoint.proceed();
long timeConsuming = System.currentTimeMillis() - start;
if (methodAnnotation != null && methodAnnotation.ignoreReturn()) {
log.info("\n{} 調(diào)用結(jié)束<-- {} 耗時:{}ms" + STRING_END, classAndMethod.toString(), target, timeConsuming);
return result;
}
// 響應(yīng)參數(shù)轉(zhuǎn)JSON,對日期進(jìn)行格式轉(zhuǎn)換并打印出所有為null的參數(shù)
log.info("\n{} 調(diào)用結(jié)束<-- {} 返回值:{} 耗時:{}ms" + STRING_END, classAndMethod.toString(), target, JSONObject.toJSONStringWithDateFormat(result, dateFormat, SerializerFeature.WriteMapNullValue), timeConsuming);
return result;
}
}
實現(xiàn)類
@Service
@Slf4j
public class UserServiceImpl implements UserService{
@Autowired
private UserMapper usermapper;
@Override
@Log
public User getUser(Integer id) {
User user=usermapper.getById(id);
log.info("message: {}",user);
return usermapper.getById(id);
}
}
控制臺輸出
--------------------------->
開始調(diào)用--> com.example.demo.service.serviceimpl.UserServiceImpl#getUser 參數(shù):[2]
2019-01-19 21:24:57.027 [http-nio-9091-exec-1] INFO com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2019-01-19 21:24:57.406 [http-nio-9091-exec-1] INFO com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
2019-01-19 21:24:57.413 [http-nio-9091-exec-1] DEBUG c.e.demo.dao.UserMapper.getById : ==> Preparing: SELECT * FROM user u WHERE u.id = ?
2019-01-19 21:24:57.436 [http-nio-9091-exec-1] DEBUG c.e.demo.dao.UserMapper.getById : ==> Parameters: 2(Integer)
2019-01-19 21:24:57.468 [http-nio-9091-exec-1] DEBUG c.e.demo.dao.UserMapper.getById : <== Total: 1
2019-01-19 21:24:57.470 [http-nio-9091-exec-1] INFO c.e.d.s.serviceimpl.UserServiceImpl : message: User(id=2, name=321, birthday=2019-01-17, address=4)
2019-01-19 21:24:57.471 [http-nio-9091-exec-1] DEBUG c.e.demo.dao.UserMapper.getById : ==> Preparing: SELECT * FROM user u WHERE u.id = ?
2019-01-19 21:24:57.471 [http-nio-9091-exec-1] DEBUG c.e.demo.dao.UserMapper.getById : ==> Parameters: 2(Integer)
2019-01-19 21:24:57.484 [http-nio-9091-exec-1] DEBUG c.e.demo.dao.UserMapper.getById : <== Total: 1
2019-01-19 21:24:57.516 [http-nio-9091-exec-1] INFO c.example.demo.config.log.LogAspect :
調(diào)用結(jié)束<-- com.example.demo.service.serviceimpl.UserServiceImpl#getUser 返回值:{"address":"4","birthday":"2019-01-17","id":2,"name":"321"} 耗時:478ms
<----------------------------