AOP底层原理与注解配置详解
注解開發(fā)AOP制作步驟:
在XML格式基礎(chǔ)上
注解開發(fā)AOP注意事項(xiàng):
AOP注解詳解:
@Aspect
-
名稱:@Aspect
-
類型:注解
-
位置:類定義上方
-
作用:設(shè)置當(dāng)前類為切面類
-
格式:
- 說明:一個(gè)beans標(biāo)簽中可以配置多個(gè)aop:config標(biāo)簽
@Pointcut
-
名稱:@Pointcut
-
類型:注解
-
位置:方法定義上方
-
作用:使用當(dāng)前方法名作為切入點(diǎn)引用名稱
-
格式:
- 說明:被修飾的方法忽略其業(yè)務(wù)功能,格式設(shè)定為無參無返回值的方法,方法體內(nèi)空實(shí)現(xiàn)(非抽象)
@Before
-
名稱:@Before
-
類型:注解
-
位置:方法定義上方
-
作用:標(biāo)注當(dāng)前方法作為前置通知
-
格式:
-
特殊參數(shù):
- 無
@After
-
名稱:@After
-
類型:注解
-
位置:方法定義上方
-
作用:標(biāo)注當(dāng)前方法作為后置通知
-
格式:
-
特殊參數(shù):
- 無
@AfterReturning
-
名稱:@AfterReturning
-
類型:注解
-
位置:方法定義上方
-
作用:標(biāo)注當(dāng)前方法作為返回后通知
-
格式:
-
特殊參數(shù):
- returning :設(shè)定使用通知方法參數(shù)接收返回值的變量名
@AfterThrowing
-
名稱:@AfterThrowing
-
類型:注解
-
位置:方法定義上方
-
作用:標(biāo)注當(dāng)前方法作為異常后通知
-
格式:
-
特殊參數(shù):
- throwing :設(shè)定使用通知方法參數(shù)接收原始方法中拋出的異常對象名
@Around
-
名稱:@Around
-
類型:注解
-
位置:方法定義上方
-
作用:標(biāo)注當(dāng)前方法作為環(huán)繞通知
-
格式:
配置文件加載注解:
public class AopPointcut {@Pointcut("execution(* *..ABC(..))")public void pt(){} } @Before("AopPointcut.pt()")public void before(){}AOP注解開發(fā)通知執(zhí)行順序控制
1.AOP使用XML配置情況下,通知的執(zhí)行順序由配置順序決定,在注解情況下由于不存在配置順序的概念的概念,參照通知所配置的方法名字符串對應(yīng)的編碼值順序,可以簡單理解為字母排序
-
同一個(gè)通知類中,相同通知類型以方法名排序?yàn)闇?zhǔn)
-
不同通知類中,以類名排序?yàn)闇?zhǔn)
-
使用@Order注解通過變更bean的加載順序改變通知的加載順序
2.企業(yè)開發(fā)經(jīng)驗(yàn)
-
通知方法名由3部分組成,分別是前綴、順序編碼、功能描述
-
前綴為固定字符串,例如baidu、itzhuzhu等,無實(shí)際意義
-
順序編碼為6位以內(nèi)的整數(shù),通常3位即可,不足位補(bǔ)0
-
功能描述為該方法對應(yīng)的實(shí)際通知功能,例如exception、strLenCheck
-
制通知執(zhí)行順序使用順序編碼控制,使用時(shí)做一定空間預(yù)留
-
003使用,006使用,預(yù)留001、002、004、005、007、008
-
使用時(shí)從中段開始使用,方便后期做前置追加或后置追加
-
最終順序以運(yùn)行順序?yàn)闇?zhǔn),以測試結(jié)果為準(zhǔn),不以設(shè)定規(guī)則為準(zhǔn)
-
AOP注解驅(qū)動(dòng)
-
名稱:@EnableAspectJAutoProxy
-
類型:注解
-
位置:Spring注解配置類定義上方
-
作用:設(shè)置當(dāng)前類開啟AOP注解驅(qū)動(dòng)的支持,加載AOP注解
-
格式:
AOP底層原理:
- 靜態(tài)代理
- 動(dòng)態(tài)代理——Proxy
- 動(dòng)態(tài)代理——CGLIB
- 織入形式
靜態(tài)代理:
裝飾者模式(Decorator Pattern):在不驚動(dòng)原始設(shè)計(jì)的基礎(chǔ)上,為其添加功能
public class UserServiceDecorator implements UserService{private UserService userService;public UserServiceDecorator(UserService userService) {this.userService = userService;}public void save() {//原始調(diào)用userService.save();//增強(qiáng)功能(后置)System.out.println("刮大白");} }動(dòng)態(tài)代理——JDK Proxy:
JDKProxy動(dòng)態(tài)代理是針對對象做代理,要求原始對象具有接口實(shí)現(xiàn),并對接口方法進(jìn)行增強(qiáng)
public class UserServiceJDKProxy {public UserService createUserServiceJDKProxy(final UserService userService){//獲取被代理對象的類加載器ClassLoader classLoader = userService.getClass().getClassLoader();//獲取被代理對象實(shí)現(xiàn)的接口Class[] classes = userService.getClass().getInterfaces();//對原始方法執(zhí)行進(jìn)行攔截并增強(qiáng)InvocationHandler ih = new InvocationHandler() {public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//前置增強(qiáng)內(nèi)容Object ret = method.invoke(userService, args);//后置增強(qiáng)內(nèi)容System.out.println("刮大白2");return ret;}};//使用原始被代理對象創(chuàng)建新的代理對象UserService proxy = (UserService) Proxy.newProxyInstance(classLoader,classes,ih);return proxy;} }動(dòng)態(tài)代理——CGLIB:
- CGLIB(Code Generation Library),Code生成類庫
- CGLIB動(dòng)態(tài)代理不限定是否具有接口,可以對任意操作進(jìn)行增強(qiáng)
- CGLIB動(dòng)態(tài)代理無需要原始被代理對象,動(dòng)態(tài)創(chuàng)建出新的代理對象
- 可以動(dòng)態(tài)生成字節(jié)碼文件
代理模式的選擇:
Spirng可以通過配置的形式控制使用的代理形式,默認(rèn)使用jdkproxy,通過配置可以修改為使用cglib
- XML配置
- XML注解支持
- 注解驅(qū)動(dòng)
綜合案例:
對項(xiàng)目進(jìn)行業(yè)務(wù)層接口執(zhí)行監(jiān)控,測量業(yè)務(wù)層接口的執(zhí)行效率
public interface AccountService {void save(Account account);void delete(Integer id);void update(Account account);List<Account> findAll();Account findById(Integer id); }案例分析:
-
測量接口執(zhí)行效率:接口方法執(zhí)行前后獲取執(zhí)行時(shí)間,求出執(zhí)行時(shí)長
- System.currentTimeMillis( )
-
對項(xiàng)目進(jìn)行監(jiān)控:項(xiàng)目中所有接口方法,AOP思想,執(zhí)行期動(dòng)態(tài)織入代碼
-
環(huán)繞通知
-
proceed()方法執(zhí)行前后獲取系統(tǒng)時(shí)間
-
案例制作步驟:
-
定義切入點(diǎn)(務(wù)必要綁定到接口上,而不是接口實(shí)現(xiàn)類上)
-
制作AOP環(huán)繞通知,完成測量功能
-
注解配置AOP
-
開啟注解驅(qū)動(dòng)支持
案例制作核代碼:
@Component @Aspect public class RunTimeMonitorAdvice {//切入點(diǎn),監(jiān)控業(yè)務(wù)層接口@Pointcut("execution(* com.itzhuzhu.service.*Service.find*(..))")public void pt() {}@Around("pt()")public Object runtimeAround(ProceedingJoinPoint pjp) throws Throwable {//獲取執(zhí)行簽名信息Signature signature = pjp.getSignature();//通過簽名獲取執(zhí)行類型(接口名)String className = signature.getDeclaringTypeName();//通過簽名獲取執(zhí)行操作名稱(方法名)String methodName = signature.getName();//執(zhí)行時(shí)長累計(jì)值long sum = 0L;for (int i = 0; i < 10000; i++) {//獲取操作前系統(tǒng)時(shí)間beginTimelong startTime = System.currentTimeMillis();//原始操作調(diào)用pjp.proceed(pjp.getArgs());//獲取操作后系統(tǒng)時(shí)間endTimelong endTime = System.currentTimeMillis();sum += endTime - startTime;}//打印信息System.out.println(className + ":" + methodName + " (萬次)run:" + sum + "ms");return null;} }案例后續(xù)思考與設(shè)計(jì):
-
測量真實(shí)性
-
開發(fā)測量是隔離性反復(fù)執(zhí)行某個(gè)操作,是理想情況,上線測量差異過大
-
上線測量服務(wù)器性能略低于單機(jī)開發(fā)測量
-
上線測量基于緩存的性能查詢要優(yōu)于數(shù)據(jù)庫查詢測量
-
上線測量接口的性能與最終對外提供的服務(wù)性能差異過大
-
當(dāng)外部條件發(fā)生變化(硬件),需要進(jìn)行回歸測試,例如數(shù)據(jù)庫遷移
-
-
測量結(jié)果展示
-
測量結(jié)果無需每一個(gè)都展示,需要設(shè)定檢測閾值
-
閾值設(shè)定要根據(jù)業(yè)務(wù)進(jìn)行區(qū)分,一個(gè)復(fù)雜的查詢與簡單的查詢差異化很大
-
閾值設(shè)定需要做獨(dú)立的配置文件或通過圖形工具配置(工具級別的開發(fā))
-
配合圖形界面展示測量結(jié)果
-
總結(jié)
以上是生活随笔為你收集整理的AOP底层原理与注解配置详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: oracle 数据库字段html显示正常
- 下一篇: java手游 《剑心》_java