重温Spring in action3篇二AOP

/ Java技术 / 没有评论 / 201浏览

alt

面向切面的Spring

AOP术语

通知(Advice):简单理解为是一段增强代码,由切面添加到特定连接点的功能代码

连接点(Joinpoint)

切点(Pointcut)

连接点(Joinpoint)和切点(Pointcut)关系

织入

Spring对AOP的支持

常见的AOP框架

  1. AspectJ
  2. JBoss AOP
  3. Srping AOP

Spring 提供了4种AOP支持

  1. 基于代理的经典AOP
  2. @AspectJ注解驱动的切面
  3. POJO切面
  4. 注入式AspectJ切面

Spring代理的几个特点

  1. Spring通知是Java编写的,AspectJ与之相反。
  2. Spring在运行期间通知对象,Spring在运行期间将切面织入到Spring管理的Bean中,因为运行时才床架代理对象,所以我们不需要特殊的编译器来织入AOP的切面。
  3. Spring只支持方法连接点,因为Spring是基于动态代理。AspectJJboosAOP框架还支持更细粒度的拦截,比如:字段和构造器的拦截。如果方法拦截不能满足我们的要求时可以通过AspectJ配合来满足我们的要求。

使用切点选择连接点

编写切点

Spring的Bean()指示器

execution(* com.zealzhangz.common.play(..)) and bean(piano)
execution(* com.zealzhangz.common.play(..)) and !bean(piano)  

在XML中声明切面

声明前置和后置通知

   <aop:config>
       <aop:aspect ref="audience">
           <!--前置通知,表演之前观众就坐-->
           <aop:before method="takeSeats" pointcut="execution(* com.zealzhangz.common.Instrument.play(..))"/>
           <!--前置通知,表演之前观众关闭手机-->
           <aop:before method="takeOffCellphones" pointcut="execution(* com.zealzhangz.common.Instrument.play(..))"/>
           <!--成功表演观众鼓掌-->
           <aop:after-returning method="applaud" pointcut="execution(* com.zealzhangz.common.Instrument.play())"/>
           <!--表演失败,观众要求退钱-->
           <aop:after-throwing method="demandRefund" pointcut="execution(* com.zealzhangz.common.Instrument.play()))"/>
       </aop:aspect>
   </aop:config>
   <aop:config>
       <aop:aspect ref="audience">
           <aop:pointcut id="player" expression="execution(* com.zealzhangz.common.Instrument.play(..))"/>
           <!--前置通知,表演之前观众就坐-->
           <aop:before method="takeSeats"  pointcut-ref="player"/>
           <!--前置通知,表演之前观众关闭手机-->
           <aop:before method="takeOffCellphones" pointcut-ref="player"/>
           <!--成功表演观众鼓掌-->
           <aop:after-returning method="applaud" pointcut-ref="player"/>
           <!--表演失败,观众要求退钱-->
           <aop:after-throwing method="demandRefund" pointcut-ref="player"/>
       </aop:aspect>
   </aop:config>

声明环绕通知

   <aop:config>
       <aop:aspect ref="audience">
           <aop:pointcut id="player" expression="execution(* com.zealzhangz.common.Instrument.play(..))"/>
           <!--环绕通知-->
           <aop:around method="watchPerformance" pointcut-ref="player"/>
       </aop:aspect>
   </aop:config>
    public void watchPerformance(ProceedingJoinPoint joinPoint){
        try{
            System.out.println("The audience taking their seat.");
            System.out.println("The audience take off their cellphones");
            long start = System.currentTimeMillis();

            joinPoint.proceed();

            long end = System.currentTimeMillis();
            System.out.println("CLAP CLAP CLAP CLAP");
            System.out.println("The performance took " + (end - start) + " milliseconds");

        } catch (Throwable t){
            System.out.println("Boo! We want our money back");
        }
    }

注意: 以上环绕通知方法包含了入参ProceedingJoinPoint这里是必须的,joinPoint.proceed();这行代码是必须的,

为通知传递参数

public interface MindReader {
    /**
     * 截听志愿者的思想
     * @param thoughts
     */
    void interceptThoughts(String thoughts);

    /**
     * 获取志愿者的思想
     * @return
     */
    String getThoughts();
}
//预言家`Magician`是MindReader的具体实现
public class Magician implements MindReader {

    private String thoughts;

    @Override
    public void interceptThoughts(String thoughts){
        System.out.println("Intercepting volunteer's thoughts");
        this.thoughts = thoughts;
    }

    @Override
    public String  getThoughts(){
        return this.thoughts;
    }
}
//现在为读心者定义一个读心对象Thinker
public interface Thinker {
    /**
     * 思考者
     * @param thoughts
     */
    void thinkOfSomething(String thoughts);
}

//一个思考着具体实现志愿者
public class Volunteer implements Thinker {
    private String thoughts;
    @Override
    public void thinkOfSomething(String thoughts){
        this.thoughts = thoughts;
    }

    public String getThoughts(){
        return thoughts;
    }
}
    <bean id="magician" class="com.zealzhangz.pojo.Magician"/>
    <bean id="volunteer" class="com.zealzhangz.pojo.Volunteer"/>

        <aop:config>
        <aop:aspect ref="magician">
            <aop:pointcut id="thinking"
                          expression="execution(* com.zealzhangz.common.Thinker.thinkOfSomething(String)) and args(thoughts)"/>
            <aop:before pointcut-ref="thinking"
                        method="interceptThoughts"
                        arg-names="thoughts"/>
        </aop:aspect>
    </aop:config>

通过切面引入新功能(为Bean添加新方法)

    <aop:config>
        <aop:aspect>
            <aop:declare-parents types-matching="com.zealzhangz.common.Instrument+"
                                 implement-interface="com.zealzhangz.common.Contestant"
                                 default-impl="com.zealzhangz.pojo.GraciousContestant"/>
        </aop:aspect>
    </aop:config>

    <!--或者可以把实现直接引用默认的bean-->
    <bean id="graciousContestant" class="com.zealzhangz.pojo.GraciousContestant"/>
    <aop:config>
        <aop:aspect>
            <aop:declare-parents types-matching="com.zealzhangz.common.Instrument+"
                                 implement-interface="com.zealzhangz.common.Contestant"
                                 delegate-ref="graciousContestant"/>
        </aop:aspect>
    </aop:config>

注解切面

public interface Athlete {
    /**
     * 具体运动
     */
    void running();
}
public class Swimmer implements Athlete {
    @Override
    public void running(){
        System.out.println("I'm swimming");
    }
}
@Aspect
@Component
public class AthleteAop {
    /**
     * 定义切点
     */
    @Pointcut(
            "execution(* com.zealzhangz.common.Athlete.running(..))")
    public void sports(){

    }
    @Before("sports()")
    public void readySports(){
        System.out.println("I'm going to ready to sports");
    }
    @Before("sports()")
    public void haveReady(){
        System.out.println("Prepare to end");
    }
    @AfterReturning("sports()")
    public void calculationResult(){
        System.out.println("Good job.");
    }
    @AfterThrowing("sports()")
    public void failed(){
        System.out.println("I screwed up");
    }
}
<aop:aspectj-autoproxy/>

注意事项:切面类AthleteAop一定要注解@Component再注解@Aspect,@Component是必须的且顺序不能变,否则切面不会生效。

注解环绕通知

@Aspect
@Component
public class AthleteAop {
    /**
     * 定义切点
     */
    @Pointcut(
            "execution(* com.zealzhangz.common.Athlete.running(..))")
    public void sports(){

    }
    @Around("sports()")
    public void watchingAthlete(ProceedingJoinPoint proceedingJoinPoint){
        try{
            System.out.println("I'm going to ready to sports");
            System.out.println("Prepare to end");

            proceedingJoinPoint.proceed();

            System.out.println("Good job.");
        }catch (Throwable t){
            System.out.println("I screwed up");
        }
    }
}

传递参数给标注的通知

public interface Athlete {
    /**
     * 具体运动
     * @param myDream
     */
    void running(String myDream);
}

public class Swimmer implements Athlete {
    @Override
    public void running(String myDream){
        System.out.println("I'm swimming");
        System.out.println(myDream);
    }
}

@Aspect
@Component
public class AthleteAop {
    @Pointcut(
            "execution(* com.zealzhangz.common.Athlete.running(String)) && args(myDream)")
    public void sports(String myDream){

    }

    @Before("sports(myDream)")
    public void readySports(String myDream){
        System.out.println("I'm going to ready to sports");
        System.out.println("Swimmer dream is " + myDream);
    }
}

    @Test
    public void TestSwim(){
        Athlete swimmer = (Athlete)this.context.getBean("swimmer");
        swimmer.running("I want to win.");
    }

标注引入(使用注解为Bean添加新方法)

@Aspect
@Component
public class ContestantIntroducer {
    @DeclareParents(value = "com.zealzhangz.common.Instrument+",
    defaultImpl = GraciousContestant.class)
    public static Contestant contestant;
}

注入AspectJ切面

public aspect JudgeAspect {
    public JudgeAspect(){}

    //定义切点
    pointcut sports():execution(* com.zealzhangz.common.Athlete.running(..));
    //后置通知
    after() returning():sports(){
        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{
    private String[] criticismPool;
    @Override
    public String getCriticism(){
        int i = (int)(Math.random()*this.criticismPool.length);
        return this.criticismPool[i];
    }
    /**
     * by spring inject
     * @param criticismPool
     */
    public void setCriticismPool(String[] criticismPool) {
        this.criticismPool = criticismPool;
    }
}
    <bean id="criticismEngine" class="com.zealzhangz.pojo.CriticismEngineImpl">
        <property name="criticismPool">
            <list>
                <value>I'm not rude.</value>
                <value>You are great.</value>
                <value>I'm sorry.Too bad.</value>
                <value>Are you serious.</value>
                <value>Bye!Bye!</value>
            </list>
        </property>
    </bean>
    <bean class="com.zealzhangz.pojo.JudgeAspect"
          factory-method="aspectOf">
        <property name="criticismEngine" ref="criticismEngine"/>
    </bean>
    <properties>
        <spring.version>3.2.8.RELEASE</spring.version>
        <aspectj.version>1.8.9</aspectj.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.source-target.version>1.7</java.source-target.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>${aspectj.version}</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>${aspectj.version}</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>aspectj-maven-plugin</artifactId>
                <version>1.7</version>
                <configuration>
                    <showWeaveInfo>true</showWeaveInfo>
                    <source>${java.source-target.version}</source>
                    <target>${java.source-target.version}</target>
                    <Xlint>ignore</Xlint>
                    <complianceLevel>${java.source-target.version}</complianceLevel>
                    <encoding>UTF-8</encoding>
                    <verbose>true</verbose>
                </configuration>
                <executions>
                    <execution>
                        <!-- IMPORTANT -->
                        <phase>process-sources</phase>
                        <goals>
                            <goal>compile</goal>
                            <goal>test-compile</goal>
                        </goals>
                    </execution>
                </executions>
                <dependencies>
                    <dependency>
                        <groupId>org.aspectj</groupId>
                        <artifactId>aspectjtools</artifactId>
                        <version>${aspectj.version}</version>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
    </build>

注意: 这里有个特别要注意的地方,编译aspectj语法的代码需要需要使用Ajc编译器,而不能使用javac编译器。否则会出现找不到类的异常提示Caused by: java.lang.ClassNotFoundException: com.zealzhangz.pojo.JudgeAspect,如上POM文件中的<build>节点就定义了相关信息。