1. 程式人生 > >通過切面為Spring bean新增新的方法

通過切面為Spring bean新增新的方法

背景:

Performance代表任何型別的現場表演,如舞臺劇、電影或音樂會。

Audience有四個方法,定義了一個觀眾在觀看演出時可能會做的事情。在演出之前,觀眾要就坐takeSeats()並將手機調至靜音狀態silenceCellPhones()。如果演出很精彩的話,觀眾應該會鼓掌喝彩applause()。不過,如果演出沒有達到觀眾預期的話,觀眾會要求退款demandRefund()。

加入Encoreable介面,代表此表演有返場演出performEncore()方法。此演出並不是常規的Performance中存在的,屬於新的方法。如何在執行perform()之後執行新的方法?

Java並不是動態語言。一旦類編譯完成了,我們就很難再為該類新增新的功能了。如果切面不僅僅實現了所包裝bean相同的介面,而且還實現了新的介面,那麼切面所通知的bean看起來似乎也像是實現了新的介面。其實底層實現類並沒有真的實現新的介面,當新的介面的方法被呼叫時,代理會把此呼叫委託給實現了新介面的某個實現物件,實際上,一個bean的實現被拆分到了多個類中。

Performance介面與實現類PerformanceImpl

public interface Performance {

    void perform();
}

@Component("performance")
public class PerformanceImpl implements Performance {
    @Override
    public void perform() {
        System.out.println("perform...");

    }
}

常規的切面類:Audience類

@Component
@Aspect
public class Audience {

    @Pointcut("execution(* chapter04.performance.Performance.perform(..))")
    public void performance(){}

    @Around("performance()")
    public void watchPerformance(ProceedingJoinPoint jp){
        try {
            System.out.println("Silencing cell phones");
            System.out.println("Taking seats");
            jp.proceed();
            System.out.println("CLAP CLAP CLAP");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            System.out.println("Demanding a refund");
        }
    }
}

引入新的介面:Encoreable介面與實現類DefaultEncoreable

public interface Encoreable {

    void performEncore();
}

public class DefaultEncoreable implements Encoreable {
    @Override
    public void performEncore() {
        System.out.println("perform encore...");
    }
}

新引入介面需要的切面類EncoreableIntroducer

/**
 * 通過@DeclareParents註解,將Encoreable介面引入到Performance bean中
 * Created by Administrator on 2017/12/1.
 */
@Component
@Aspect
public class EncoreableIntroducer {
    /**
     * value屬性指定了哪種型別的bean要引入該介面,在本例中,也就是所有實現Performance的型別。
     * defaultImpl屬性指定了為引入功能提供實現的類
     * @DeclareParents 註解所標註的靜態屬性指明瞭要引入了介面。在這裡,我們所引入的是Encoreable介面。
     */
    @DeclareParents(value="chapter04.performance.Performance+",defaultImpl = DefaultEncoreable.class)
    public static Encoreable encoreable;

}

applicationContext.xml配置

    <context:component-scan base-package="aop_test,chapter04"/>
	
	<!-- 開啟aop註解 -->
    <aop:aspectj-autoproxy/>

測試

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class testPerformance {
    @Autowired
    Performance performance;

    @Test
    public void test(){
        performance.perform();
        Encoreable encoreable = (Encoreable) performance;
        encoreable.performEncore();
    }
}

測試結果

Silencing cell phones
Taking seats
perform...
CLAP CLAP CLAP
perform encore...