Spring 实战-第四章-4.3使用注解创建切面

时间:2023-01-01 11:39:22

AspectJ面向注解的模型可以非常简便地通过少量注解把任意类转变为切面。

注解类型:

 

注解 功能
@Aspect 将类声明为一个切面
@After 通知方法会在目标方法返回或抛出异常后调用
@AfterReturning 通知方法会在目标方法返回后调用
@AfterThrowing 通知方法会在目标方法抛出异常后调用
@Around 通知方法会将目标方法封装起来
@Before 通知方法会在目标方法调用之前执行

 

 

 

 

 

 

 

定义接口CompactDisc

package main.java.soundsystem;
public interface CompactDisc {
    void play();
    void playTrack(Integer trackNumber);
}

实现BlankDisc:

package main.java.soundsystem;

import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

@Component
public class BlankDisc implements  CompactDisc{

    private  String title;

    private  String artist;

    private List<String> tracks;

    public BlankDisc setTitle(String title) {
        this.title = title;
        return this;
    }

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

    public String getTitle() {
        return title;
    }

    public String getArtist() {
        return artist;
    }

    public void setTracks(List<String> tracks) {
        this.tracks = tracks;
    }

    public void play() {
        System.out.println("Playing " + title + " by " + artist);
        for (String track : tracks) {
            System.out.println("-Track: " + track);
        }
    }

    @Override
    public void playTrack(Integer trackNumber) {
        System.out.println("Playing "+tracks.get(trackNumber-1));
    }


}

定义一个切面,用于计数播放次数,类上的@Aspcet注解表示这是一个切面,在方法上的@Before等表示了方法执行的顺序

其中@Before("execution(* CompactDisc.playTrack(Integer)) && args(trackNumber)")表达式,

表示要在CompactDisc.playTrack方法执行前执行被注解的方法,并且有一个Integer的参数,前面的*表示不限制返回参数。

当对于某个点需要执行多个方法,在每个注解上重复方法声明就有些冗余,所以这里使用

@Pointcut("execution(* CompactDisc.playTrack(Integer)) && args(trackNumber)")
    public void trackPlayed(int trackNumber) {
    }

定义一个切点,其中的方法名字可以为任意名字,只要保证输入输出与代理方法一致就行,这样在声明通知的时候,只需要简短的声明即可。

package main.java.soundsystem;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

import java.util.HashMap;
import java.util.Map;

@Aspect public class TrackCounter {
    private Map<Integer, Integer> trackCounts =
            new HashMap<Integer, Integer>();

    @Pointcut("execution(* CompactDisc.playTrack(Integer)) && args(trackNumber)")
    public void trackPlayed(int trackNumber) {
    }

    //  @Before("execution(* CompactDisc.playTrack(int)) && args(trackNumber)")
    @Before("trackPlayed(trackNumber)")
    public void countTrack(int trackNumber) {
        int currentCount = getPlayCount(trackNumber);
        System.out.println("TrackCoutner:" + trackNumber);
        trackCounts.put(trackNumber, currentCount + 1);
    }

    @Before("execution(* CompactDisc.play())")
    public void test2() {
        System.out.println("this is trackCounter test2");
    }

    @Before("execution(* CompactDisc.play())")
    public void test1() {
        System.out.println("this is trackCounter test1");
    }

    public void PrintMap() {
        trackCounts.forEach((k, v) -> System.out.println("Key:" + k + ",Value:" + v));
    }

    public int getPlayCount(int trackNumber) {
        return trackCounts.containsKey(trackNumber) ? trackCounts.get(trackNumber) : 0;
    }
}

如果使用Javaconfig,可以在配置类的类级别上通过使用@EnableAspectJAutoProxy注解启用自动代理功能。

package main.java.soundsystem;

import com.sun.deploy.util.BlackList;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

import java.util.ArrayList;
import java.util.List;

@Configuration
@EnableAspectJAutoProxy public class TrackCounterConfig {
    @Bean
    public CompactDisc sgtPeppers(){
        BlankDisc cd=new BlankDisc();
        cd.setTitle("Sgt. Pepper's Lonely Hearts Club Band");
        cd.setArtist("The Beatles");
        List<String> tracks=new ArrayList<String>();
        tracks.add("Sgt. Pepper's Lonely Hearts Club Band");
        tracks.add("With a Little Help from My Friends");
        tracks.add("Lucy in the Sky with Diamonds");
        tracks.add("Getting Better");
        tracks.add("Fixing a Hole");

        cd.setTracks(tracks);
        return cd;
    }

    @Bean
    public TrackCounter trackCounter(){
        return new TrackCounter();
    }
}

也可以通过xml配置,使用Spring aop命名空间中的<aop:aspectj-autoproxy>元素,启用自动代理功能,

并且在名空间中使用aop相关配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
            http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="main.java.soundsystem"/>
    <aop:aspectj-autoproxy/>
    <bean class="main.java.soundsystem.TrackCounter"></bean>

    <bean xml:id="sgtPeppers" class="main.java.soundsystem.BlankDisc">
        <property name="title" value="Sgt. Pepper's Lonely Heart Club Band"/>
        <property name="artist" value="the Beatles"/>
        <property name="tracks">
            <list>
                <value>Sgt. Pepper's Lonely Hearts Club Band</value>
                <value>With a Little Help from My Friends</value>
                <value>Lucy in the Sky with Diamonds</value>
                <value>Getting Better</value>
                <value>Fixing a Hole</value>
            </list>
        </property>
    </bean>

</beans>

 

测试代码

package main.java.soundsystem;
import org.springframework.beans.factory.annotation.Autowired;

import static org.junit.Assert.*;

import org.junit.Test;
import org.junit.runner.RunWith;

import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@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(1);
        cd.playTrack(3);
        cd.playTrack(4);
        cd.playTrack(4);
        cd.playTrack(4);

        assertEquals(2,counter.getPlayCount(1));
        assertEquals(1,counter.getPlayCount(3));
        assertEquals(3,counter.getPlayCount(4));
    }
}

结果:

Track:1
Playing Sgt. Pepper's Lonely Hearts Club Band
Track:1
Playing Sgt. Pepper's Lonely Hearts Club Band
Track:3
Playing Lucy in the Sky with Diamonds
Track:4
Playing Getting Better
Track:4
Playing Getting Better
Track:4
Playing Getting Better