虽然Spring AOP能够满足许多应用的切面需求,但是与AspectJ相比, Spring AOP 是一个功能比较弱的AOP解决方案。AspectJ提供了Spring AOP所不能支持的许多类型的切点。
但是精心设计且有意义的切面很可能依赖其他类来完成它们的工作。 如果在执行通知时,切面依赖于一个或多个类,我们可以在切面内部 实例化这些协作的对象。但更好的方式是,我们可以借助Spring的依 赖注入把bean装配进AspectJ切面中。
我们为演出创建一个新切面。具体来讲,我们以切面的方式创建一个评论员的角色,他会观看演出并且会在演出之后提 供一些批评意见。
在此我们给出完整的代码。首先必须为myeclipse安装AspectJ插件,可以参考:https://blog.csdn.net/qq_35592011/article/details/71602026,安装完成后创建一个AspectJ项目。
1.表演接口Performance.java
1 package concert4; 2 3 public interface Performance { 4 public void perform(); 5 }
2.表演实现类Classcial.java
1 package concert4; 2 3 public class Classcial implements Performance { 4 5 @Override 6 public void perform() { 7 // TODO Auto-generated method stub 8 System.out.println("我是古典音乐会!"); 9 } 10 11 }
3.观众类(切面)Audience.java
1 package concert4; 2 3 import org.aspectj.lang.ProceedingJoinPoint; 4 5 public class Audience { 6 7 // ProceedingJoinPoint作为参数。 8 // 这个对象是必须要有的,因为 你要在通知中通过它来调用被通知的方法。 9 // 通知方法中可以做任何的 事情,当要将控制权交给被通知的方法时,它需要调 用ProceedingJoinPoint的proceed()方法。 10 public void watchPerformance(ProceedingJoinPoint jp) { 11 try { 12 System.out.println("Silencing cell phone"); 13 System.out.println("Taking seats"); 14 jp.proceed(); 15 System.out.println("CLAP CLAP CLAP"); 16 } catch (Throwable e) { 17 System.out.println("Demanding a refund"); 18 } 19 } 20 }
4.附加表演接口Encoreable.java
1 package concert4; 2 3 public interface Encoreable { 4 void performEncore(); 5 }
5.附加表演实现类DefaultEncoreable.java
1 package concert4; 2 3 public class DefaultEncoreable implements Encoreable { 4 5 @Override 6 public void performEncore() { 7 // TODO Auto-generated method stub 8 System.out.println("川剧变脸"); 9 } 10 11 }
6.创建评论切面CriticAspect.java
CriticAspect的主要职责是在表演结束后为表演发表评论。其中的performance()切点匹配perform()方法。当它 与after()returning通知一起配合使用时,我们可以让该切面在 表演结束时起作用。
需要注意的是整个过程并不是评论员自己发表评论,实际 上,CriticAspect与一个CriticismEngine对象相协作,在表 演结束时,调用该对象的getCriticism()方法来发表一个苛刻的 评论。为了避免CriticAspect和CriticismEngine之间产生不 必要的耦合,我们通过Setter依赖注入为CriticAspect设 置CriticismEngine。下图展示了此关系:
1 package concert4; 2 3 public aspect CriticAspect { 4 public CriticAspect(){} 5 pointcut performance():execution(* perform(..)); 6 after()returning :performance(){ 7 System.out.println("--------------评论----------------"); 8 System.out.println(criticismEngine.getCriticism()); 9 System.out.println("----------------------------------"); 10 } 11 12 private CriticismEngine criticismEngine; 13 14 public void setCriticismEngine(CriticismEngine criticismEngine){ 15 this.criticismEngine=criticismEngine; 16 } 17 }
7.创建评论员接口CriticismEngine.java
1 package concert4; 2 3 public interface CriticismEngine { 4 public String getCriticism(); 5 }
8.实现评论员接口CriticismEngineTmpl.java(要注入到CriticAspect中的CriticismEngine实现)
1 package concert4; 2 3 public class CriticismEngineTmpl implements CriticismEngine { 4 5 private String[] criticismPool; 6 7 public void setCriticismPool(String[] criticismPool) { 8 this.criticismPool = criticismPool; 9 } 10 11 public CriticismEngineTmpl() { 12 } 13 14 @Override 15 public String getCriticism() { 16 // TODO Auto-generated method stub 17 int i = (int) (Math.random() * criticismPool.length); 18 return criticismPool[i]; 19 } 20 21 }
CriticismEngineImpl实现了CriticismEngine接口,通过从 注入的评论池中随机选择一个苛刻的评论。要将这个类在XML文档中声明为一个bean。
9.XML配置文件
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:aop="http://www.springframework.org/schema/aop" 5 xmlns:tx="http://www.springframework.org/schema/tx" 6 xmlns:context="http://www.springframework.org/schema/context" 7 xmlns:c="http://www.springframework.org/schema/c" 8 xsi:schemaLocation=" 9 http://www.springframework.org/schema/beans 10 http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 11 http://www.springframework.org/schema/aop 12 http://www.springframework.org/schema/aop/spring-aop-3.0.xsd 13 http://www.springframework.org/schema/tx 14 http://www.springframework.org/schema/tx/spring-tx-3.0.xsd 15 http://www.springframework.org/schema/context 16 http://www.springframework.org/schema/context/spring-context-3.0.xsd"> 17 18 <bean class="concert4.Classcial"></bean> 19 <bean id="audience" class="concert4.Audience"></bean> 20 <bean id="criticismEngine" class="concert4.CriticismEngineTmpl"> 21 <property name="criticismPool"> 22 <list> 23 <value>不好</value> 24 <value>很不好</value> 25 <value>非常不好</value> 26 </list> 27 </property> 28 </bean> 29 <bean class="concert4.CriticAspect" factory-method="aspectOf"> 30 <property name="criticismEngine" ref="criticismEngine"></property> 31 </bean> 32 <aop:config> 33 <aop:aspect ref="audience"> 34 <aop:pointcut expression="execution(* concert4.Performance.perform(..))" id="performance"/> 35 <aop:around method="watchPerformance" pointcut-ref="performance"/> 36 <aop:declare-parents types-matching="concert4.Performance+" 37 implement-interface="concert4.Encoreable" 38 default-impl="concert4.DefaultEncoreable"/> 39 </aop:aspect> 40 </aop:config> 41 </beans>
需要注意的是,在为CriticAspect装配CriticismEngineImple之前,我们必须清楚AspectJ切面根本不需要 Spring就可以织入到我们的应用中。如果想使用Spring的依赖注入为 AspectJ切面注入协作者,那我们就需要在Spring配置中把切面声明为 一个Spring配置中的<bean>。
但是在配置过程中使用了特殊的factorymethod属性。通常情况下,Spring bean由Spring容器初始化,但是 AspectJ切面是由AspectJ在运行期创建的。等到Spring有机会 为CriticAspect注入CriticismEngine时,CriticAspect已 经被实例化了。 因为Spring不能负责创建CriticAspect,那就不能在 Spring中简单 地把CriticAspect声明为一个bean。我们需要一种方式为 Spring获得已经由AspectJ创建的CriticAspect实例的句柄,从而可 以注入CriticismEngine。的AspectJ切面都提供了一 个静态的aspectOf()方法,该方法返回切面的一个单例。所以为了 获得切面的实例,我们必须使用factory-method来调 用asepctOf()方法而不是调用CriticAspect的构造器方法。
简而言之,Spring不能像之前那样使用<bean>声明来创建一 个CriticAspect实例——它已经在运行时由AspectJ创建完成了。 Spring需要通过aspectOf()工厂方法获得切面的引用,然后像 <bean>元素规定的那样在该对象上执行依赖注入。
10.测试类ConcertTest.java
1 package concert4; 2 3 import org.junit.Test; 4 import org.junit.runner.RunWith; 5 import org.springframework.beans.factory.annotation.Autowired; 6 import org.springframework.test.context.ContextConfiguration; 7 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 8 9 @RunWith(SpringJUnit4ClassRunner.class) 10 // @ContextConfiguration(classes = concert3.ConcertConfig.class) 11 @ContextConfiguration("classpath:ConcertConfig6.xml") 12 public class ConcertTest { 13 @Autowired 14 public Performance p; 15 @Autowired 16 public Encoreable en; 17 18 @Test 19 public void test() { 20 21 p.perform(); 22 System.out.println("-----------------------------"); 23 System.out.println("自己创建对象调用"); 24 en.performEncore(); 25 System.out.println("-----------------------------"); 26 System.out.println("通过Performance对象调用“新方法”"); 27 Encoreable e = (Encoreable) p; 28 e.performEncore(); 29 } 30 }
11.结果(有疑问!!!!!!!!!!)