Spring實戰(第4版)Spring In Action - 第4章 面向切面的Spring

第4章 面向切面的Spring

4.1 什麼是面向切面編程

4.1.4 定義AOP術語

  • 橫切關注點(cross-cutting concern)
  • 通知(advice)

  1. 前置通知(Before)
  2. 後置通知(After)
  3. 返回通知(After-returning)
  4. 異常通知(After-throwing)
  5. 環繞通知(Around)
  • 切點(pointcut)
  • 連接點(joinpoint)

  • 切面(Aspcet)

  • 引入(Introduction)

  • 織入(Weaving)

  1. 編譯期織入
  2. 類加載期織入
  3. 運行期織入

4.1.2 Spring對AOP的支持

Spring提供的4中類型的AOP支持

  • 基於代理的經典Spring AOP(這種不介紹,因爲非常笨重和過於複雜,太老舊)
  • 純POJO切面
  • @AspectJ註解驅動的切面
  • 注入式AspectJ切面(適用於Spring各版本)

介紹的AOP方式

  • 聲明式AOP
  • 基於註解的AOP

Spring通知是Java編寫的

AspectJ是以JAVA語言擴展的方式實現的

Spring在運行時通知對象

SpringAOP實現的實質是代理類

Spring只支持方法級別的連接點

4.2 通過切點來選擇連接點

SpringAOP中,使用AspectJ的切點表達式語言來定義切點。 Spring僅支持AspectJ的切點指示器(pointcut designator)的一個子集。 總結:SpringAOP 與 AspectJ相比弱爆了。

4.2.1 編寫切點

public interface Performance {

	void perform();
}

4.2.2 在切點中選擇bean

execution(* concert.Performance.perform(..)) and bean('woodstock')

排除!

execution(* concert.Performance.perform(..)) and !bean('woodstock')

4.3 使用註解創建切面

4.3.1 定義切面

@Aspect
public class Audience {
	
	@Before("execution(** concert.Performance.perform(..))")
	public void silenceCellPhones() {
		System.out.println("Silencing cell phones");
	}
	
	@Before("execution(** concert.Performance.perform(..))")
	public void takeSeats() {
		System.out.println("Taking seats");
	}
	
	@After("execution(** concert.Performance.perform(..))")
	public void applause() {
		System.out.println("CLAP CLAP CLAP!!!");
	}
	
	@AfterThrowing("execution(** concert.Performance.perform(..))")
	public void demandRefund() {
		System.out.println("Demanding a refund");
	}
	
}

定義切點,添加@Aspect註解

@Aspect
public class Audience {
	
	@Pointcut("execution(** concert.Performance.perform(..))")
	public void performance() {}
	
	@Before("performance()")
	public void silenceCellPhones() {
		System.out.println("Silencing cell phones");
	}
	
	@Before("performance()")
	public void takeSeats() {
		System.out.println("Taking seats");
	}
	
	@Before("performance()")
	public void applause() {
		System.out.println("CLAP CLAP CLAP!!!");
	}
	
	@AfterThrowing("performance()")
	public void demandRefund() {
		System.out.println("Demanding a refund");
	}
	
}

啓用自動代理

@Configuration
@EnableAspectJAutoProxy
@ComponentScan
public class ConcertConfig {
	
	@Bean
	public Audience audience() {
		return new Audience();
	}
}

XML啓動

<context:conponent-scan base-package="concert" />
<aop:aspectj-autoproxy />
<bean class="concert.Audience" />

4.3.2 創建環繞通知

@Aspect
public class Audience {
	
	@Pointcut("execution(** concert.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 e) {
			System.out.println("Demanding a refund");
		}
	}
}

4.3.3 處理通知中的參數

public interface CompactDisc {
	void play();
	
	void playTrack(int trackNumber);
}


public class BlackDisc implements CompactDisc {
	
	private String title = "";
	private String artist = "";
	
	public void setTitle(String title) {
		this.title = title;
	}

	public void setArtist(String artist) {
		this.artist = artist;
	}

	@Override
	public void playTrack(int trackNumber) {
		System.out.println("track " + trackNumber + " play.");
	}
	
	@Override
	public void play() {
		System.out.println("Playing " + title + " by " + artist);
	}
}

切面

@Aspect
public class TrackCounter {
	private Map<Integer, Integer> trackCounts = new HashMap<Integer, Integer>();
	
	@Pointcut("execution(* soundsystem.CompactDisc.playTrack(int)) " 
			+ "&& args(trackNumber)")
	public void trackPlayed(int trackNumber) {}
	
	@Before("trackPlayed(trackNumber)")
	public void countTrack(int trackNumber) {
		int currentCount = getPlayCount(trackNumber);
		trackCounts.put(trackNumber, currentCount+1);
	}
	
	@Before("execution(* soundsystem.CompactDisc.play(..))")
	public void countPlay() {
		System.out.println("countPlay");
	}
	
	public int getPlayCount(int trackNumber) {
		return trackCounts.containsKey(trackNumber) ? trackCounts.get(trackNumber) : 0;
	}
}

配置

@Configuration
@EnableAspectJAutoProxy
public class TrackCounterConfig {
	
	@Bean
	public CompactDisc sgtPeppers() {
		BlackDisc cd = new BlackDisc();
		cd.setTitle("Sgt. pepper's Lonely Hearts Club Band");
		cd.setArtist("The Beatles");
		return cd;
	}
	
	@Bean
	public TrackCounter trackCounter() {
		return new TrackCounter();
	}
}

測試

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=TrackCounterConfig.class)
public class TrackCounterTest {

	@Autowired
	private CompactDisc cd;
	
	@Autowired
	private TrackCounter counter;
	
	@Test
	public void testTrackCounter() {
		cd.playTrack(1);
		cd.playTrack(2);
		cd.playTrack(3);
		cd.playTrack(3);
		cd.playTrack(3);
		cd.playTrack(3);
		cd.playTrack(7);
		cd.playTrack(7);
		
		cd.play();
		cd.play();
		
		
		for (int i=1; i<=7; ++i) {
			int count = counter.getPlayCount(i);
			System.out.println(i + " : " + count);
		}
	}
}

4.3.4 通過註解引入新功能

新接口

public interface Encoreable {
	void performEncore();
}

切面

@Aspect
public class EncoreableIntroducer {

	@DeclareParents(value="concert.Performance+",
			defaultImpl=DefaultEncoreable.class)
	public static Encoreable encoreable;

}

4.4 在XML中聲明切面

public class Audience {
	
	public void silenceCellPhones() {
		System.out.println("Silencing cell phones");
	}
	
	public void takeSeats() {
		System.out.println("Taking seats");
	}
	
	public void applause() {
		System.out.println("CLAP CLAP CLAP!!!");
	}
	
	public void demandRefund() {
		System.out.println("Demanding a refund");
	}
}

4.4.1 聲明前置和後置通知

<aop:config>
		<aop:aspect ref="audience">
			<aop:before 
				pointcut="execution(** concert.Performance.perform(..))" 
				method="silenceCellPhones" />
			<aop:before 
				pointcut="execution(** concert.Performance.perform(..))" 
				method="takeSeats" />
			<aop:after-returning
				pointcut="execution(** concert.Performance.perform(..))" 
				method="applause" />
			<aop:after-throwing
				pointcut="execution(** concert.Performance.perform(..))" 
				method="demandRefund" />
		</aop:aspect>
	</aop:config>

定義切點

<aop:config>
		<aop:aspect ref="audience">
			<aop:pointcut 
				expression="execution(** concert.Performance.perform(..))" 
				id="performance"/>
			<aop:before 
				pointcut-ref="performance" 
				method="silenceCellPhones" />
			<aop:before 
				pointcut-ref="performance" 
				method="takeSeats" />
			<aop:after-returning
				pointcut-ref="performance" 
				method="applause" />
			<aop:after-throwing
				pointcut-ref="performance" 
				method="demandRefund" />
		</aop:aspect>
	</aop:config>

4.4.2 聲明環繞通知

public class Audience {
	
	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 e) {
			System.out.println("Demanding a refund");
		}
	}
}
    <aop:config>
		<aop:aspect ref="audience">
			<aop:pointcut 
				expression="execution(** concert.Performance.perform(..))" 
				id="performance"/>
			<aop:around 
				pointcut-ref="performance" 
				method="watchPerformance" />
		</aop:aspect>
	</aop:config>

4.4.3 爲通知傳遞參數

public interface CompactDisc {
	void playTrack(int trackNumber);
}

public class BlackDisc implements CompactDisc {
	
	private String title = "";
	private String artist = "";
	
	@Override
	public void playTrack(int trackNumber) {
		System.out.println("track " + trackNumber + " play.");
	}
	
	public void setTitle(String title) {
		this.title = title;
	}

	public void setArtist(String artist) {
		this.artist = artist;
	}
	
	public String getTitle() {
		return title;
	}

	public String getArtist() {
		return artist;
	}
}

TrackCounter

public class TrackCounter {
	private Map<Integer, Integer> trackCounts = new HashMap<Integer, Integer>();
	
	public void countTrack(int trackNumber) {
		int currentCount = getPlayCount(trackNumber);
		trackCounts.put(trackNumber, currentCount+1);
	}
	
	public int getPlayCount(int trackNumber) {
		return trackCounts.containsKey(trackNumber) ? trackCounts.get(trackNumber) : 0;
	}
}

XML

	<bean id="trackCounter" class="soundsystem4_4_3.TrackCounter" />
	
	<bean id="cd" class="soundsystem4_4_3.BlackDisc">
		<property name="title" value="Sgt. pepper's Lonely Hearts Club Band" />
		<property name="artist" value="The Beatles" />
	</bean>

	<aop:config>
		<aop:aspect ref="trackCounter">
			<aop:pointcut 
			expression="execution(* soundsystem4_4_3.CompactDisc.playTrack(int)) and args(trackNumber)" 
			id="trackPlayed"/>
			
			<aop:before pointcut-ref="trackPlayed" method="countTrack" />
		</aop:aspect>
	</aop:config>

test

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations= {"classpath:soundsystem4_4_3/trackconfig.xml"})
public class TrackCounterTest {

	@Autowired
	private CompactDisc cd;
	
	@Autowired
	private TrackCounter counter;
	
	@Test
	public void testTrackCounter() {
		cd.playTrack(1);
		cd.playTrack(2);
		cd.playTrack(3);
		cd.playTrack(3);
		cd.playTrack(3);
		cd.playTrack(3);
		cd.playTrack(7);
		cd.playTrack(7);

		for (int i=1; i<=7; ++i) {
			int count = counter.getPlayCount(i);
			System.out.println(i + " : " + count);
		}
	}
}

4.4 通過切面引入新的功能

    <aop:config>
		<aop:aspect>
			<aop:declare-parents 
				types-matching="concert.Performance+"
				implement-interface="concert.Encoreable" 
				default-impl="concert.DefaultEncoreable" />
		</aop:aspect>
	</aop:config>

方法2:delegate-ref

<bean id="encoreableDelegate" class="concert.DefaultEncoreable" />
	
	<aop:config>
		<aop:aspect>
			<aop:declare-parents 
				types-matching="concert.Performance+"
				implement-interface="concert.Encoreable" 
				delegate-ref="encoreableDelegate" />
		</aop:aspect>
	</aop:config>

4.5 注入AspectJ切面

public aspect CriticAspect {
	public CriticAspect() {}
	
	pointcut performance() : execution(* perform(..));
	
	before():performance() {
		System.out.println(criticismEngine.getCriticism());
	}
	
	private CriticismEngine criticismEngine;
	
	public void setCriticismEngine(CriticismEngine criticismEngine) {
		this.criticismEngine = criticismEngine;
	}
}
public interface CriticismEngine {
	String getCriticism();
}

public class CriticismengineImpl implements CriticismEngine {

	@Override
	public String getCriticism() {
		int i = (int)(Math.random() * criticismPool.length);
		return criticismPool[i];
	}

	private String[] criticismPool;
	public void setCriticismPool(String[] criticismPool) {
		this.criticismPool = criticismPool;
	}
}

XML 注意 factory-method="aspectOf"

<bean id="criticismEngine" class="concert.CriticismengineImpl">
		<property name="criticismPool">
			<list>
				<value>Wrost perforamance ever.</value>
				<value>Wrost perforamance ever.</value>
				<value>Wrost perforamance ever.</value>
			</list>
		</property>
	</bean>
	
	<bean class="concert.CriticAspect" factory-method="aspectOf">
		<property name="criticismEngine" ref="criticismEngine"></property>
	</bean>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章