1. 程式人生 > >Spring AOP小記

Spring AOP小記

在一起 factor XML ble wing ssi spec 匹配 oid

Spring AOP vs AspectJ

AOP keywords

  • Aspect, 橫切面,對象
  • Jointpoint, 連接點,在Spring裏是方法,還可以是其他(AspectJ中的表達式within等)
  • Pointcut, 切入點,連接點攔截的定義
  • Advice, 通知,攔截到攔截點之後要做的動作(Before, After, Around, AfterReturning, AfterThrowing)
  • Weaving, 織入(編譯期,類加載期(AspectJ5 LTW),運行期(Spring AOP基於此,動態創建代理))

Spring中對AOP的支持

  • 基於代理的Spring AOP及其變種(ProxyFactoryBean等)
  • AspectJ切面

AspectJ切點表達式

arg() 限制連接點匹配參數為指定類型的執行方法
@args() 限制連接點匹配參數為指定註解標註的執行方法
execution() 用於匹配是連接點的執行方法
within() 限制連接點匹配指定的類型
@within() 限制連接點匹配指定註解所標註的類型(使用Spring AOP時,方法定義在由指定註解標註的類裏)
@annotation 限定匹配帶有指定註解的連接點(比如限定被xx註解標註的方法)

組合表達式:&& || !

ProxyFactoryBean

    <bean id="helloProxy" class=
"org.springframework.aop.framework.ProxyFactoryBean"> <property name="interfaces"> <list> <value>io.dirac.aop.HelloService</value> </list> </property> <property name="target" ref="hello"/> <property
name="interceptorNames"> <list> <value>advisor</value> </list> </property> </bean> <!-- proxy target --> <bean id="hello" class="io.dirac.aop.HelloServiceImpl"/> <!-- AdvisorLogger implements MethodInterceptor 在這裏面實現前後通知 --> <bean id="advisor" class="io.dirac.aop.AdvisorLogger"/>

<aop:config>

    <bean id="logger" class="io.dirac.aop.LoggerHandler"/>

    <!-- 對於log這個切面,會攔截HelloService中say方法這個切點id=sayMethod,say之前調用log切面的preHandle和postHandle方法 -->
    <aop:config>
        <aop:aspect id="log" ref="logger">
            <aop:pointcut id="sayMethod" expression="execution(* io.dirac.aop.HelloService.say(..))"/>
            <aop:before method="preHandle" pointcut-ref="sayMethod"/>
            <aop:after method="postHandle" pointcut-ref="sayMethod"/>
        </aop:aspect>
    </aop:config>

AspectJ

如果是XML配置,需要配置如下開啟<aop:aspectj-autoproxy/>, 如果是JavaConfig,需要在配置類上@EnableAspectJAutoProxy開啟。

@Aspect
@Component
public class ConcurrencyLimitAspect {

    // 值就是連接點的具體形式Pointcut,只不過此處現在一起了
    // 對於AspectJ而言,連接點可以不止是方法
    // 這個切點的意思是:對於註解了Component類中註解了ConcurrencyLimit方法起作用,寫切面在方法上做並發控制
    @Around("@annotation(limit) && @within(org.springframework.stereotype.Component)")
    public Object limit(ProceedingJoinPoint point, ConcurrencyLimit limit) {
        int lmt = limit.limit();
        if (lmt < 0) {
            throw new RuntimeException("limit!");
        }

        try {
            Object ret = point.proceed();
            return ret;
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return null;
    }

    // 在createService方法處連接點的切點
    @Pointcut("execution(public * io.dirax.api.OrderService.createService(..))")
    public void pointcut() {
    }
    
    // pointcut切點前置通知
    @Before("pointcut()")
    public void beforeAction(JoinPoint jp) {
        // TODO
    }

    // 指定參數,可以在通知中把參數傳入(如果對參數request修改,會影響後續)
    @Pointcut("execution(public * io.dirax.api.OrderService.createService(Object)) && args(request)")
    public void pointcutWithArgs(Object request) {
    }

    @After(value = "pointcut(request)", argNames = "request")
    public void after(Object request){
        //TODO
    }
}

對環繞(@Around)通知,參數ProceedingJoinPoint可以在實際方法前後做環繞處理;其他的可以使用JoinPoint
如上實例,對於註解了Component的對象中註解了ConcurrencyLimit的方法,會使用切面ConcurrencyLimitAspect中的limit做增強。

Spring AOP小記