中文字幕在线观看,亚洲а∨天堂久久精品9966,亚洲成a人片在线观看你懂的,亚洲av成人片无码网站,亚洲国产精品无码久久久五月天

Java AOP 實(shí)例踩坑記

2018-07-20    來(lái)源:編程學(xué)習(xí)網(wǎng)

容器云強(qiáng)勢(shì)上線!快速搭建集群,上萬(wàn)Linux鏡像隨意使用

其實(shí)這篇文章是存了很久的草稿,寫了一半沒有繼續(xù)完稿。終于得空繼續(xù)完善它,之后還會(huì)抽時(shí)間繼續(xù)研究Spring AOP 的使用和實(shí)現(xiàn)( ̄? ̄),絕不放過任何絆腳石。

嘗試Spring AOP未果

項(xiàng)目中遇到一個(gè)性能監(jiān)控需求:在線搜集統(tǒng)計(jì)服務(wù)方法的調(diào)用次數(shù)和平均開銷時(shí)間,用于服務(wù)性能調(diào)優(yōu)和服務(wù)死活監(jiān)控。考慮到涉及到的統(tǒng)計(jì)點(diǎn)很多,一個(gè)個(gè)手寫采集點(diǎn)代碼非常傻,而且代碼侵入性很大。

想起之前為了重構(gòu)代碼中的手工auto-retry(見下面的代碼庫(kù) Orz),曾經(jīng)找到過jcabi這樣的庫(kù),其中是采用了Java中的一大“神器”,面相切面編程(AOP)。于是性能點(diǎn)采集邏輯也打算采用AOP的方式來(lái)實(shí)現(xiàn)。

// 坑爹的手工 for retry
for (int i=0; i<MAX_TRY; i++){
    try{
        doSomething();
        break;
    }catch(Exception ignore){
    }
}

考慮到項(xiàng)目中使用了一部分spring功能(依賴注入),于是網(wǎng)上找資料,很多都是關(guān)于Spring AOP+AspectJ來(lái)實(shí)現(xiàn)的例子。比如參考的[1]、[2],里面詳細(xì)的講解了關(guān)于AOP的概念,如何使用Spring AOP和AspectJ風(fēng)格的注釋來(lái)實(shí)現(xiàn)動(dòng)態(tài)代理方式的AOP編程。然而卻走上了Spring AOP的踩坑之路。大名鼎鼎的Spring,自動(dòng)裝配bean,Spring AOP號(hào)稱還能自動(dòng)運(yùn)行時(shí)織入切點(diǎn)。但是卻怎么嘗試都 “不work!” 。參考了一下 [2] 里面的坑,還是不行。(百分百被大眾/官網(wǎng)實(shí)例坑的設(shè)定)

暫時(shí)放棄Spring AOP,老老實(shí)實(shí)的學(xué)習(xí)一下AspectJ吧。

完工的代碼(去掉了公司業(yè)務(wù)的代碼框架)

來(lái)一段AspectJ風(fēng)格的代碼

@Aspect
public class Monitor {
    private static final Logger logger = LoggerFactory.getLogger(Monitor.class);

    @Pointcut("execution(public * *(..)) && @annotation(demo.APM)")
    private void apmHandler(){}

    @Around("apmHandler()")
    public Object apmRecordTime(ProceedingJoinPoint pjp) throws Throwable{
        Object ret = null;
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();
        APM apmAnnotation = method.getAnnotation(APM.class);
        String commandName = apmAnnotation.value();
        try {
            long startTime = System.nanoTime();
            ret = pjp.proceed();
            long processTime = (System.nanoTime() - startTime); // avg_time unit: nano seconds
            logger.trace("command[{}] spend avg_time:{} ms", commandName, processTime/1000000d);
        } finally{
        }
        return ret;
    }

    @AfterThrowing(pointcut="apmHandler()", throwing= "error")
    public void apmRecordException(JoinPoint jp, Throwable error){
        MethodSignature signature = (MethodSignature) jp.getSignature();
        Method method = signature.getMethod();
        APM apmAnnotation = method.getAnnotation(APM.class);
        String commandName = apmAnnotation.value();
        logger.trace("command[{}] throw exception: {}", commandName, error);
    }
}

為了方便設(shè)定切點(diǎn),我使用了Java annotation的方式來(lái)做標(biāo)記:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface APM {
    String value() default "";
}

凡是標(biāo)記了@APM 的方法,都會(huì)被AOP切入。例如代碼里定義的 pointcut 可以捕捉所有 public 并且?guī)в蠤APM("xxxx") 的函數(shù)調(diào)用,通過Java Reflection 可以拿到APM的value(作為command名字,其實(shí)也可以考慮用method name)

實(shí)例化的坑

當(dāng)然實(shí)際中實(shí)現(xiàn)的AOP類沒有那么簡(jiǎn)單,還需要加入統(tǒng)計(jì)的工具,尤其是當(dāng)需要注入外部的對(duì)象時(shí),就不得不通過Spring bean的方式來(lái)配置管理它。例如上面的Monitor類,在Spring 的 Java Config里:

@configure
public AppConfig{
    @Bean
    public Monitor monitor(){
        Monitor monitor = new Monitor();
        // monitor.config(xxx);
        // monitor.register(xxxx);
        return monitor;
    }
}

乍眼一看感覺上面的代碼沒問題是吧?查看日志的時(shí)候發(fā)現(xiàn)Monitor實(shí)例化了兩次!翻看AspectJwen dang發(fā)現(xiàn)一段說(shuō)Aspect類的實(shí)例化是由AspectJ接管的!

Like classes, aspects may be instantiated, but AspectJ controls how that instantiation happens -- so you can't use Java's new form to build new aspect instances. By default, each aspect is a singleton , so one aspect instance is created. This means that advice may use non-static fields of the aspect, if it needs to keep state around

The aspect is a singleton object and is created outside the Spring container. A solution with XML configuration is to use Spring's factory method to retrieve the aspect.

<bean id="syncLoggingAspect" class="uk.co.demo.SyncLoggingAspect" 
      factory-method="aspectOf" />

如何讓spring來(lái)控制這個(gè)spring之外實(shí)例化的東西呢?

參考 Configuring AspectJ aspects using Spring IoC with JavaConfig?

With this configuration the aspect will be treated as any other Spring bean and the autowiring will work as normal.

You have to use the factory-method also on Enum objects and other objects without a constructor or objects that are created outside the Spring container.

通過如此如下方式是可以成功得到AOP的bean

import org.aspectj.lang.Aspects;

@configure
public AppConfig{
    @Bean
    public Monitor monitor(){
        Monitor monitor = Aspects.aspectOf(Monitor.class);
        // monitor.config(xxx);
        // monitor.register(xxxx);
        return monitor;
    }
}

實(shí)例化的坑(續(xù)):默認(rèn)構(gòu)造函數(shù)

AspectJ會(huì)使用默認(rèn)構(gòu)造函數(shù)來(lái)實(shí)例化Aspect的類,當(dāng)你無(wú)意中實(shí)現(xiàn)了一個(gè)非默認(rèn)構(gòu)造函數(shù)又沒有默認(rèn)構(gòu)造函數(shù)時(shí),他會(huì)報(bào)下面的錯(cuò)誤:

aspectj method <init>()V not found

所以請(qǐng)使用默認(rèn)構(gòu)造函數(shù)來(lái)實(shí)例化Aspect類。ńK于明白我要拿到AOP bean的苦衷了吧)

maven配置AspectJ weaving

未完待續(xù)( ̄? ̄)

參考:

  1. Spring實(shí)戰(zhàn)4—面向切面編程
  2. Spring AOP 注解方式實(shí)現(xiàn)的一些“坑”
  3. How to get a method's annotation value from a ProceedingJoinPoint?
  4. Aspectj @Around pointcut all methods in Java
  5. http://aspects.jcabi.com/
  6. Strategies for using AspectJ in a Maven multi-module reactor

 

來(lái)自:http://www.jianshu.com/p/62f22d821333

 

標(biāo)簽: 代碼

版權(quán)申明:本站文章部分自網(wǎng)絡(luò),如有侵權(quán),請(qǐng)聯(lián)系:west999com@outlook.com
特別注意:本站所有轉(zhuǎn)載文章言論不代表本站觀點(diǎn)!
本站所提供的圖片等素材,版權(quán)歸原作者所有,如需使用,請(qǐng)與原作者聯(lián)系。

上一篇:iOS:如何捕獲異常?

下一篇:JAVA 常用集合內(nèi)部機(jī)制原理