版权声明:本文为博主原创文章, 转载请注明出处。 https://blog.csdn.net/u012523359/article/details/72851479
写在开头:
本文参考资料为Spring In Action(第4版),文中理解不对之处均为本人原因,与书籍无关。
Spring In Action中提到:“Spring AOP构建在动态代理之上,因此,Spring对AOP的支持局限于方法拦截”。
那么,当我们需要创建更细粒度的通知或想监测bean的创建时,Spring所支持的AOP就比较弱了,这时,可以选择使用AspectJ提供的构造器切点,并且可以借助Spring的依赖注入把bean装配进AspectJ切面中。下面就来举个栗子:
//定义表演接口
package concert;
public interface Performance {
void perform();
void finishPerform(String performer, String title);
}
//定义钢琴表演
package concert;
public class PianoPerform implements Performance {
public PianoPerform(){
System.out.println("有请钢琴表演");
}
@Override
public void perform() {
System.out.println("钢琴表演开始");
}
@Override
public void finishPerform(String performer, String title) {
System.out.println(performer + "演奏钢琴曲:" + title);
}
}
//定义小提琴表演
package concert;
public class ViolinPerform implements Performance {
public ViolinPerform(){
System.out.println("有请小提琴表演");
}
@Override
public void perform() {
System.out.println("小提琴表演开始");
}
@Override
public void finishPerform(String performer, String title){
System.out.println(performer + "演奏了小提琴曲:" + title);
}
}
//定义工作人员,将作为切面的协作bean
package concert;
public class Worker {
public void take(){
System.out.println("观众已全部交出手机");
}
public void sendMsg(String name){
System.out.println(name + "表演即将开始,请各位观众交出手机");
}
public void broadcast(String performer, String title){
System.out.println(performer + "演奏完毕,刚才演奏的曲子叫:" + title);
}
}
//定义切面
package concert;
public aspect Audience {
private Worker worker;
public Audience(){}
//通过setter方法注入
public void setWorker(Worker worker){
this.worker = worker;
System.out.println("工作人员已入场");
}
//定义piano构造器切点和后置通知
pointcut piano():execution(concert.PianoPerform.new());
after():piano(){
worker.sendMsg("钢琴");
}
//定义violin构造器切点和后置通知
pointcut violin():execution(concert.ViolinPerform.new());
after():violin(){
worker.sendMsg("小提琴");
}
//定义不带参数方法切点和前置通知
pointcut perform():execution(* concert.Performance.perform());
before():perform(){
worker.take();
}
//定义带两个参数的切点和后置通知
pointcut finishPerform(String performer, String title):execution(* concert.Performance.finishPerform(String, String)) && args(performer, title);
after(String performer, String title):finishPerform(performer, title){
worker.broadcast(performer, title);
}
}
XML配置文件:spring.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--<bean id="piano" class="concert.PianoPerform" lazy-init="true"/>-->
<bean id="worker" class="concert.Worker"/>
<!--Spring需要通过静态方法aspectOf获得audience实例,Audience切面编译后的class文件附在文末-->
<bean class="concert.Audience" factory-method="aspectOf">
<property name="worker" ref="worker" /><!--通过Spring把协作的bean注入到切面中-->
</bean>
<!--这里注意一下bean的顺序,因为在构造器切点后置通知时调用了worker的sendMsg(String)方法,所以避免出现空指针异常,咱们先把worker声明在前-->
<!--如果要将piano或者violin声明在前,可以设置lazy-init="true"-->
<!--所以spring是从上到下解析并实例化bean?还是解析完整个文件再实例化呢?欢迎评论区留言交流-->
<bean id="piano" class="concert.PianoPerform"/>
<bean id="violin" class="concert.ViolinPerform"/>
</beans>
//主程序
package concert;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainClass {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring.xml");
Performance piano = context.getBean("piano", Performance.class);
piano.perform();
piano.finishPerform("亚莎·海菲兹", "致爱丽斯");
Performance violin = context.getBean("violin", Performance.class);
violin.perform();
violin.finishPerform("霍洛维茨", "爱之喜悦");
}
}
用到的依赖:pom.xml
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.3.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.7</version>
</dependency>
</dependencies>
<build>
<plugins>
<!--编译aspect的插件,必须要将Audience.aj编译为class文件,
不然spring创建audience bean的时候找不到类-->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.8</version>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
<configuration>
<complianceLevel>1.8</complianceLevel>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
运行结果:
工作人员已入场
有请钢琴表演
钢琴表演即将开始,请各位观众交出手机
有请小提琴表演
小提琴表演即将开始,请各位观众交出手机
观众已全部交出手机
钢琴表演开始
亚莎·海菲兹演奏钢琴曲:致爱丽斯
亚莎·海菲兹演奏完毕,刚才演奏的曲子叫:致爱丽斯
观众已全部交出手机
小提琴表演开始
霍洛维茨演奏了小提琴曲:爱之喜悦
霍洛维茨演奏完毕,刚才演奏的曲子叫:爱之喜悦
Audience.class文件
package concert;
import concert.Worker;
import org.aspectj.lang.NoAspectBoundException;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class Audience {
private Worker worker;
static {
try {
ajc$postClinit();
} catch (Throwable var1) {
ajc$initFailureCause = var1;
}
}
public void setWorker(Worker worker) {
this.worker = worker;
System.out.println("工作人员已入场");
}
public Audience() {
}
@After(
value = "piano()",
argNames = ""
)
public void ajc$after$concert_Audience$1$dd71540a() {
this.worker.sendMsg("钢琴");
}
@After(
value = "violin()",
argNames = ""
)
public void ajc$after$concert_Audience$2$57c630b6() {
this.worker.sendMsg("小提琴");
}
@Before(
value = "perform()",
argNames = ""
)
public void ajc$before$concert_Audience$3$1cad9822() {
this.worker.take();
}
@After(
value = "finishPerform(performer, title)",
argNames = "performer,title"
)
public void ajc$after$concert_Audience$4$1840cdb9(String performer, String title) {
this.worker.broadcast(performer, title);
}
//Aspect提供的静态方法,返回Audience切面的一个实例,Spring即可通过factory-method属性获得该实例
//<bean class="concert.Audience" factory-method="aspectOf">
public static Audience aspectOf() {
if(ajc$perSingletonInstance == null) {
throw new NoAspectBoundException("concert_Audience", ajc$initFailureCause);
} else {
return ajc$perSingletonInstance;
}
}
public static boolean hasAspect() {
return ajc$perSingletonInstance != null;
}
}
写在最后:
Spring所支持的AOP已经可以满足很多需求,如果要求更高,可以使用AspectJ提供的更丰富的切点类型,当然需要熟悉AspectJ语法。
本文只是简单的举出了部分切点类型和通知类型,更多的类型读者可以自行尝试。欢迎留言指正,感谢您的阅读!