面向切面的Spring

面向切面的Spring

  • 面向切面编程基本原理
  • 通过POJO创建切面
  • 使用@AspectJ注解
  • 为AspectJ切面注入依赖

一.什么是面向切面编程?

切面能够帮助我们模块化横切关注点,简而言之,横切关注点可以被描述为影响应用多处的功能。

在这里插入图片描述

实现通用功能的方法常见有继承和委托,继承会导致一个脆弱的对象体系,委托需要对委托对象进行复杂的调用。而切面提供了取代继承和委托的另一种方案,在很多场景下更加清晰简洁。横切关注点被模块化为一种特殊的类即切面。

1.定义AOP术语

AOP也形成了自己的术语:通知切点连接点

在这里插入图片描述

在AOP中切面的工作被称为通知,通知定义了切面是什么以及何时使用。

Spring切面可以应用五种类型的通知:

  • 前置通知(Before):在目标方法调用之前通知功能。
  • 后置通知(After):在目标方法调用之后通知功能,此时不用关心输出什么。
  • 返回通知(After-returning):目标方法成功执行后调用通知。
  • 异常通知(After-throwing):目标方法抛出异常后调用通知。
  • 环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。

应用可能有数以千计的时机应用通知,而这些时机被称为连接点切点的定义会匹配通知所要织入的一个或多个连接点。切面是通知和切点的结合。通知和切点共同定义了切面的全部内容——它是什么,在何时完成其功能。引入允许我们向现有的类添加新方法或属性。

织入

在这里插入图片描述

2.Spring对AOP的支持

​ 并不是所有AOP框架都是相同的,Spring和AspectJ项目之间有大量的合作,Spring对AOP的支持在很多方面借鉴了AspectJ项目。

​ Spring提供了4种AOP支持:

  • 基于代理的经典Spring AOP;
  • 纯POJO切面;
  • @AspectJ注解驱动的切面;
  • 注入式AspectJ切面(适用于Spring各版本)

二.通过切点来选择连接点

Spring AOP所支持的AspectJ指示器

在这里插入图片描述

注意,主有execution是真正执行匹配的,使用其它指示器来限制所匹配的切点。

1.编写切点

为阐述Spring中的切面,我们需要一个主题来定义切面的切点,为此,我们定义Performance接口:

package com.huang;

public interface Performance {
    void perform();
}

切点表达式,能够设置当perform()执行时触发通知的调用。

execution(* com.huang.Performance.perform(..))

*表示任意返回类型,…表示任意的perform()方法,不论里面参数如何。

要配置的切点仅匹配com.huang包,可使用within()指示器来限制匹配。

execution(* com.huang.Performance.perform(..))&&within(com.huang.*)
2.在切点中选择bean

Spring引入一种新的bean()指示器,它允许我们在切点表达式中使用bean 的ID标识bean。根据bean的ID或bean名称限制切点只匹配特定的bean

execution(* com.huang.Performance.perform(..)) and bean("woodstock")

使用!bean()在某些情况下变得很有意义,可为除此bean外的其它bean应用通知。

三.使用注解创建切面

使用注解来创建切面是AspectJ5所引入的关键特性,其面向注解的模型可以非常方便的通过少量注解将任意类转变为切面。要在Spring项目中添加额外的aspectjweaver-1.9.5.jar和aspectjrt-1.9.5.jar包。

1.定义切面

从演出的角度将,观众的作用是非常重要的,但对于演出本身来讲,观众并不是核心,这是一个单独的关注点,因此,将观众定义为一个切面,将其应用到演出上就是一个明智的选择。

Audience类:观看演出的切面:

package com.huang.concert;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class Audience {

    @Before("execution(* com.huang.concert.Performance.perform(..))")
    public void silenceCellPhone(){
        System.out.println("观众将手机静音!");
    }
    @Before("execution(* com.huang.concert.Performance.perform(..))")
    public void seat(){
        System.out.println("观众找位置坐下");
    }

    @AfterReturning("execution(* com.huang.concert.Performance.perform(..))")
    public void admire(){
        System.out.println("掌声响起来!");
    }

    @AfterThrowing("execution(* com.huang.concert.Performance.perform(..))")
    public void demandrefund(){
        System.out.println("你到底行不行啊?不行就退款");
    }
}

Audience不仅是一个POJO,也是一个切面,Audience类中的方法都使用注解来定义切面的具体行为。可知该切面中定义了四个方法,这些方法都使用注解来表明它们应该在什么时候调用,AspectJ提供了五个注解来定义通知:

在这里插入图片描述

但上述例子中对同一切点定义了四次,如果我们只定义一次,需要的时候引用它就行了。可使用@Pointcut,具体改写如下:

package com.huang.concert;
import org.aspectj.lang.annotation.*;

@Aspect
public class Audience {
    //2.使用@Pointcut
    //定义切点
    @Pointcut("execution(* com.huang.concert.Performance.perform(..))")
    public void performance(){}
    @Before("performance()")
    public void silenceCellPhone(){
        System.out.println("观众将手机静音!");
    }
    @Before("performance()")
    public void seat(){
        System.out.println("观众找位置坐下");
    }

    @AfterReturning("performance()")
    public void admire(){
        System.out.println("掌声响起来!");
    }

    @AfterThrowing("performance()")
    public void demandrefund(){
        System.out.println("你到底行不行啊?不行就退款");
    }
}

值得注意的是Audience像其它java类一样可以装配为Spring的bean。若使用Java配置,那么默认所定义的切面是失效的,若要使其有效可使用@EnableAspectJAutoProxy注解。

package com.huang.concert;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@ComponentScan
@EnableAspectJAutoProxy  //使定义的切面生效
public class Concertconfing {
    @Bean
    public Audience audience(){
        return new Audience();
    }
}
2.创建环绕通知

​ 作为最强大的通知类型,将目标方法包裹起来,就相当于同时编写了前置通知和后置通知。我们重写Audience切面。

package com.huang.concert;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class AudienceRound {
    @Pointcut("execution(* com.huang.concert.Performance.perform(..))")
    public void performance(){}

    @Around("performance()")
    public void WatchPerformance(ProceedingJoinPoint jp){
        try {
            System.out.println("观众将手机静音!");
            System.out.println("观众找位置坐下");
            jp.proceed();//若不调用proceed方法,那么本通知将会阻塞被通知方法的执行。
            System.out.println("掌声响起来!");
        }
        catch (Throwable R){
            System.out.println("你到底行不行啊?不行就退款");
        }
3.处理通知中的参数

​ 之前,切面通知的perform()方法中没有任何参数,要进行参数的传递可使用args(参数名),在所要通知的方法中添加参数类型即可,具体以前面的BlankDisk为例:

//CompactDisk.java
package com.huang.soundsystem;
public interface CompactDisc {
  void play();
  void playTrack(int trackNumber);

}

//BlankDisk.java
package com.huang.soundsystem;
import java.util.List;
public class BlankDisc implements CompactDisc {
  private String title;
  private String artist;
  private List<String> tracks;
  public BlankDisc(String title, String artist, List<String> tracks) {
    this.title = title;
    this.artist = artist;
    this.tracks = tracks;
  }

  public String getTitle() {
    return title;
  }

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

  public String getArtist() {
    return artist;
  }

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

  public List<String> getTracks() {
    return tracks;
  }

  public void setTracks(List<String> tracks) {
    this.tracks = tracks;
  }
  public BlankDisc() {
  }
  public void play() {
    System.out.println("Playing " + title + " by " + artist);
    for (String track : tracks) {
      System.out.println("-Track: " + track);
    }
  }
  @Override
  public void playTrack(int trackNumber) {
    System.out.println("Track: " + tracks.get(i));
  }
}

//TrackCounter.java
package com.huang.soundsystem;

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(* com.huang.soundsystem.CompactDisc.playTrack(int)) && args(trackNumber)")  //传递参数到通知方法中
    public void trackplayed(int trackNumber){}
    @Before("trackplayed(trackNumber)")
    public void countTrack(int trackNumber){
        int count=getPlayCount(trackNumber);
        //记录加一
        trackCounts.put(trackNumber,count+1);
    }

    //查询记录图中有没有相应track的记录,没有就取0
    public int  getPlayCount(int trackNumber){
        return trackCounts.containsKey(trackNumber) ? trackCounts.get(trackNumber) : 0;
    }
}


//Diskconfing.java
package com.huang.soundsystem;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import java.util.ArrayList;
@Configuration
@EnableAspectJAutoProxy
public class Diskconfing {
    @Bean
    public CompactDisc sgtPapers(){
        BlankDisc bd=new BlankDisc();
        bd.setTitle("Sgt. People'lonely Hearts Club Brand");
        bd.setArtist("The Beatles");
        ArrayList<String> tracks=new ArrayList<>();
        tracks.add("Sgt. People'lonely Hearts Club Brand");
        tracks.add("With a Little Help From My Friends");
        tracks.add("Getting Better");
        tracks.add("Fixing a Hole");
        bd.setTracks(tracks);
         return bd;
    }
    @Bean
    public TrackCounter trackCounter(){
        return new TrackCounter();
    }
}

使用JavaConfing配置Bean并使Aspect生效后,我们如何测试呢?

package com.huang.soundsystem;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = Diskconfing.class)
public class TrackCountTest {
    @Autowired
    private CompactDisc cd;
    @Autowired
    private TrackCounter tc;

    @Test
    public void testcount(){
        cd.playTrack(1);
        cd.playTrack(1);
        cd.playTrack(2);
        cd.playTrack(3);
        cd.playTrack(3);
        cd.playTrack(3);
        assertNotNull(tc);//测试
        assertEquals(1,tc.getPlayCount(2));
        assertEquals(3,tc.getPlayCount(3));
        assertEquals(2,tc.getPlayCount(1));
        System.out.println("track1:"+tc.getPlayCount(1));
        System.out.println("track2:"+tc.getPlayCount(2));
        System.out.println("track3:"+tc.getPlayCount(3));
        
    }
}

输出没问题:

Track: With a Little Help From My Friends
Track: With a Little Help From My Friends
Track: Getting Better
Track: Fixing a Hole
Track: Fixing a Hole
Track: Fixing a Hole
track1:2
track2:1
track3:3

通过切点表达式传递参数要严格要求参数类型和参数名与切点方法名中一致且切点定义方法名中也与切点方法名一致

4.通过注解引入新功能

利用被称为引入的AOP概念,切面可为Spring bean添加新方法。

在这里插入图片描述

​ 当引入接口的方法被调用时,代理会把此调用委托给实现了新接口的某个其它对象,实际上,一个bean的实现被拆分到了多个类上。

​ 以下给出代理接口和实现类:

package com.huang.concert;
//Performance的代理接口
public interface Encoreable {
    void performEncore();
}

package com.huang.concert;
//Performance的新增方法的代理实现类
public class DefaultEncoreable implements Encoreable {
    @Override
    public void performEncore() {
        System.out.println("这就是代理添加的新方法");
    }
}

在切面中使用@DeclareParent注解将Encoreable接口引入到Performance bean中。

package com.huang.concert;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.DeclareParents;

@Aspect
public class EncoreableIntroducer {
    @DeclareParents(value = "com.huang.concert.Performance", defaultImpl =DefaultEncoreable.class)
    public static Encoreable encoreable;

}

最后通过Java配置或者XML配置bean和激活AspectJ即可。

四.在XML中声明切片

​ 喜好的配置方式因人而异,个人不是很喜欢,更偏向于一体化的配置。或者说你不想将注解放置到你的代码中可使用XML配置。

​ 在Spring的aop命名空间中,提供了多个元素在XML中声明切面。

在这里插入图片描述

1.声明前置和后置通知
//无任何切面相关注解的pojo
package com.huang.xmlConfing;
public class Audience {
    public void silenceCellPhone(){
        System.out.println("观众将手机静音!");
    }
    public void seat(){
        System.out.println("观众找位置坐下");
    }
    public void admire(){
        System.out.println("掌声响起来!");
    }
    public void demandrefund(){
        System.out.println("你到底行不行啊?不行就退款");
    }
}

具体的如下:

<?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:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--配置bean-->
    <bean id="audience" class="com.huang.xmlConfing.Audience">
    </bean>
    <bean id="performance" class="com.huang.xmlConfing.PerformanceImpl">
    </bean>
<aop:config>
    <!-- 配置一个切面-->
    <aop:aspect ref="audience">
        <aop:before method="silenceCellPhone"
                  pointcut="execution(* com.huang.xmlConfing.Performance.perform(..))" />
        <aop:before method="seat"
                  pointcut="execution(* com.huang.xmlConfing.Performance.perform(..))" />
        <aop:after-returning method="admire"
                  pointcut="execution(* com.huang.xmlConfing.Performance.perform(..))" />
       <aop:after-throwing method="demandrefund"
                  pointcut="execution(* com.huang.xmlConfing.Performance.perform(..))" />
    </aop:aspect>
</aop:config>
</beans>

通过aop:pointcut来定义命名切点。

<?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:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--配置bean-->
    <bean id="audience" class="com.huang.xmlConfing.Audience">
    </bean>
    <bean id="performance" class="com.huang.xmlConfing.PerformanceImpl">
    </bean>
<aop:config>
    <!-- 配置一个切面-->
    <aop:aspect ref="audience">
        
        <aop:pointcut id="perform()" expression="execution(* com.huang.xmlConfing.Performance.perform(..))"/>
        <aop:before method="silenceCellPhone"
                    pointcut-ref="perform()" />
        <aop:before method="seat"
                    pointcut-ref="perform()" />
        <aop:after-returning method="admire"
                             pointcut-ref="perform()" />
        <aop:after-throwing method="demandrefund"
                            pointcut-ref="perform()" />

    </aop:aspect>
</aop:config>
</beans>
2.声明环绕通知
package com.huang.xmlConfing;
import org.aspectj.lang.ProceedingJoinPoint;
public class AudienceRound {
    public void WatchPerformance(ProceedingJoinPoint jp){
        try {
            System.out.println("观众将手机静音!");
            System.out.println("观众找位置坐下");
            jp.proceed();//若不调用proceed方法,那么本通知将会阻塞原方法的执行。
            System.out.println("掌声响起来!");
        }
        catch (Throwable R){
            System.out.println("你到底行不行啊?不行就退款");
        }
    }
}

使用aop:around详情如下:

<?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:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--配置bean-->
    <bean id="audienceRound" class="com.huang.xmlConfing.AudienceRound">
    </bean>
    <bean id="performance" class="com.huang.xmlConfing.PerformanceImpl">
    </bean>
<aop:config>
    <aop:aspect ref="audienceRound">
        <aop:pointcut id="perform()" expression="execution(* com.huang.xmlConfing.Performance.perform(..))"/>
        <!--声明环绕通知-->
        <aop:around method="WatchPerformance" pointcut-ref="perform()" />
    </aop:aspect>
</aop:config>
</beans>
3.为通知传递参数

与java配置中不同,xml会将&符号解析成实体的开始,故使用and,其它配置都基本相同。

<?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:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="xcd" class="com.huang.soundsystemxmlConfing.XBlankDiscX">
        <property name="artist" value="The Beatles"/>
        <property name="title" value="Sgt. People'lonely Hearts Club Brand"/>
        <property name="tracks" >
            <list>
                <value>Sgt. People'lonely Hearts Club Brand</value>
                <value>With a Little Help From My Friends</value>
                <value>Getting Better</value>
                <value>Fixing a Hole</value>
            </list>
        </property>
    </bean>
    <bean id="xtrackCounter" class="com.huang.soundsystemxmlConfing.XTrackCounter">
    </bean>
<aop:config>
    <aop:aspect ref="xtrackCounter">
        <aop:pointcut id="play" expression="execution(* com.huang.soundsystemxmlConfing.XCompactDisc.playTrack(int)) and args(trackNumber)"/>
        <aop:before method="countTrack" pointcut-ref="play"/>
    </aop:aspect>
</aop:config>
</beans>

添加上述xml配置应用上下文。

具体的类和测试与前面差不多,改了名好区分,以及去除了AspectJ的切面注解,测试时要注意是从xml加载应用上下文,而不是java配置类了,具体如下:

package com.huang.soundsystemxmlConfing;
import java.util.List;
public class XBlankDiscX implements XCompactDisc {
  private String title;
  private String artist;
  private List<String> tracks;
  public XBlankDiscX(String title, String artist, List<String> tracks) {
    this.title = title;
    this.artist = artist;
    this.tracks = tracks;
  }
  public String getTitle() {
    return title;
  }
  public void setTitle(String title) {
    this.title = title;
  }
  public String getArtist() {
    return artist;
  }
  public void setArtist(String artist) {
    this.artist = artist;
  }
  public List<String> getTracks() {
    return tracks;
  }
  public void setTracks(List<String> tracks) {
    this.tracks = tracks;
  }
  public XBlankDiscX() {
  }
  public void play() {
    System.out.println("Playing " + title + " by " + artist);
    for (String track : tracks) {
      System.out.println("-Track: " + track);
    }
  }
  @Override
  public void playTrack(int trackNumber) {
    System.out.println("xTrack: " + tracks.get(trackNumber));
  }

}

package com.huang.soundsystemxmlConfing;

public interface XCompactDisc {

  void play();
  void playTrack(int trackNumber);

}

package com.huang.soundsystemxmlConfing;



import org.aspectj.lang.annotation.Pointcut;

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


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

    public void countTrack(int trackNumber){
        int count=getPlayCount(trackNumber);
        //记录加一
        trackCounts.put(trackNumber,count+1);
    }
    //查询记录图中有没有相应track的记录,没有就取0
    public int  getPlayCount(int trackNumber){

        return trackCounts.getOrDefault(trackNumber, 0);
    }
}


测试代码如下:

package com.huang.soundsystemxmlConfing;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:./com/huang/soundsystemxmlConfing/trackConfing.xml")
public class XTrackCountTest {
    @Autowired
    private XCompactDisc cd;
    @Autowired
    private XTrackCounter tc;

    @Test
    public void testcount(){
        cd.playTrack(1);
        cd.playTrack(1);
        cd.playTrack(2);
        cd.playTrack(3);
        cd.playTrack(3);
        cd.playTrack(3);
        assertNotNull(tc);
        assertEquals(1,tc.getPlayCount(2));
        assertEquals(3,tc.getPlayCount(3));
        assertEquals(2,tc.getPlayCount(1));
        System.out.println("xtrack1:"+tc.getPlayCount(1));
        System.out.println("xtrack2:"+tc.getPlayCount(2));
        System.out.println("xtrack3:"+tc.getPlayCount(3));
    }
}

与之前Java配置的输出一致,只不过改了输出内容便于区分控制台输出如下:

xTrack: With a Little Help From My Friends
xTrack: With a Little Help From My Friends
xTrack: Getting Better
xTrack: Fixing a Hole
xTrack: Fixing a Hole
xTrack: Fixing a Hole
xtrack1:2
xtrack2:1
xtrack3:3
4.通过切面引入新功能

借助AspectJ的@DeclareParent注解向bean引入新功能,但AOP引入并不是AspectJ特有的,借助Spring aop命名空间中的aop:declare-parents来引入。

代理接口和代理实现类:

package com.huang.soundsystemxmlConfing;
public interface ExtraFunction {
   void displayLyric();
}

package com.huang.soundsystemxmlConfing;
public class Lyric implements ExtraFunction {
    @Override
    public void displayLyric() {
        System.out.println("这就是歌词!");
    }
}

引入配置:

 <aop:aspect >
        <aop:declare-parents types-matching="com.huang.soundsystemxmlConfing.XCompactDisc+" implement-interface="com.huang.soundsystemxmlConfing.ExtraFunction" delegate-ref="lyric"/>
    </aop:aspect>

注意使用default-impl来直接标识委托和间接使用delegate-ref的区别在于后者是Spring bean,它本身可以被注入、通知或使用其它的Spring配置。

5.注入AspectJ切面

​ 与AspectJ相比Spring AOP是一个比较弱的方案,Spring基于代理的AOP无法把通知应用于对象的创建过程。精心设计的切面可能依赖其它类来完成它们的工作,如果执行通知时,切面依赖于一个或多个类,我们可以在切面内部实例化这些协作对象,但更好的方法是我们利用Spring的依赖注入将bean装进AspectJ切面中。

在这里插入图片描述

​ 创建演出评论员切面:

package com.huang.AspectJ;

//AspectJ方式
public aspect CriticAspect {
    public CriticAspect(){}
    private CriticismEngine criticismEngine;
    //定义切点
    pointcut performance(): execution(* com.huang.AspectJ.Performance.perform(..));
    //后置返回成功执行通知
    after()returning :performance(){
        System.out.println("---------评论---------");
        System.out.println(criticismEngine.getCriticism());
    }
    //注入
    public void setCriticismEngine(CriticismEngine criticismEngine) {
        this.criticismEngine = criticismEngine;
    }
}

要注入切面的代理接口和实现bean

package com.huang.AspectJ;

public interface CriticismEngine {
    String getCriticism();
}



package com.huang.AspectJ;
public class CriticismEngineImpl implements CriticismEngine{
    @Override
    public String getCriticism() {
        return criticismPool[(int) (Math.random()*criticismPool.length)];
    }

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

基于xml的上下文配置:


```xml
<?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:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean id="criticismEngine" class="com.huang.AspectJ.CriticismEngineImpl">
        <property name="criticismPool">
            <list>
                <value>五星好评</value>
                <value>三星中评</value>
                <value>一星差评</value>
            </list>
        </property>
    </bean>

    <bean class="com.huang.AspectJ.CriticAspect" factory-method="aspectOf">
        <property name="criticismEngine" ref="criticismEngine"/>
    </bean>

    <bean id="performanceImpl" class="com.huang.AspectJ.PerformanceImpl">

    </bean>
    <aop:aspectj-autoproxy/>
</beans>

以上就是利用Spring给AspectJ切面注入依赖的过程。

由 Spring In action 4th edition 整理

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值