Spring在2.0以后对AOP功能进行了重要的增强,主要表现在一下几个方面:
1.新增了基于Schema的配置支持,为AOP专门提供了aop命名空间;
2.新增了对AspectJ切点表达式语言支持,@AspectJ是AspectJ 1.5新增的功能,他功过JDK 5.0的注解技术,允许开发者在POJO中定义切面,Spring使用和@AspectJ相同风格的注解,并通过AspectJ提供的注解库和解析库处理切点;
3.可以无缝的继承AspectJ,因为Spring支持的@AspectJ本身是基于JDK 5.0的注解技术,所以我们先看一个JDK5.0的注解例子
一、了解注解
在JDK 5.0中,我们可以自定义自己的注解,并通过java反射机制获取类中标注的注解,完成特定的功能,注解是代码的附属信息,它遵循一个原则:注解不能直接干扰程序代码的运行;
1.一个简单的注解类
package spring.UseAspectj.demo1; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME)// 声明注解的保留期限 @Target(ElementType.METHOD)// 声明可以使用该注解的目标类型 public @interface NeedTest { // 定义注解 boolean value() default true; // 声明注解成员 }
其中成员类型是受限的,合法的类型包括原始类型及封装类型、String、Class、enums、注解类型;
RetentionPolicy.RUNTIME表示这个注解可以在运行期被JVM读取,注解的保留期限类型在java.lang.annotation.RetentionPolicy定义有:
public static final RetentionPolicy SOURCE; public static final RetentionPolicy CLASS; public static final RetentionPolicy RUNTIME;
SOURCE:注解信息仅保留在目标类代码的源文件中,但对应的字节码文件将不再保留;
CLASS:注解信息将进入目标类的字节码文件中,但类加载器加载字节码文件时不会讲注解加载到JVM中,即运行期不能获取注解信息;
ElementType.METHOD表示注解只能应用到目标类的方法上,注解的应用目标在java.lang.annotation.ElementType定义有:
public static final ElementType TYPE; public static final ElementType FIELD; public static final ElementType METHOD; public static final ElementType PARAMETER; public static final ElementType CONSTRUCTOR; public static final ElementType LOCAL_VARIABLE; public static final ElementType ANNOTATION_TYPE; public static final ElementType PACKAGE;
如果注解只有一个成员,则成员名必须取名为value(),在使用注解时可以忽略成员名和赋值号;
2.使用注解
package spring.UseAspectj.demo1; //使用注解 public class UserService { @NeedTest(value = true) public void deleteUser(String userId) { System.out.println("删除用户:" + userId); } @NeedTest(value = false) public void addUser(String userId) { System.out.println("添加用户:" + userId); } }
3.访问注解:
package spring.UseAspectj.demo1; import java.lang.reflect.Method; public class TestTool { public static void main(String[] args) { Class<UserService> clazz = UserService.class; Method[] methods = clazz.getMethods(); System.out.println("方法个数:" + methods.length); for (Method method : methods) { // 获取方法上所标注的注解对象 NeedTest nt = method.getAnnotation(NeedTest.class); if (nt != null) { if (nt.value()) { System.out.println(method.getName() + "需要测试。"); } else { System.out.println(method.getName() + "不需要测试。"); } } } } }
输出:
方法个数:11
deleteUser需要测试。
addUser不需要测试。
二、使用@AspectJ
之前我们使用Pointcut和Advice接口来描述切点和增强,并用Advisor整合来描述切面,现在@AspectJ则采用注解来描述切点、增强,两者只是表达方式不同,描述内容的本质是完全相同的。Spring在使用@Aspect注解表达式时,需要将Spring的asm模块添加到类路径中,asm是轻量级的字节码处理框架,因为java的反射机制无法获取入参名,Spring就利用asm处理@AspectJ中所描述的方法入参名,下面看一个简单的例子:
package spring.UseAspectj.demo2; public interface Waiter { public void deleteWaiter(String clientName); public void addWaiter(String clientName); }
package spring.UseAspectj.demo2; public class NaiveWaiter implements Waiter { @Override public void deleteWaiter(String clientName) { System.out.println("NaiveWaiter:delete: " + clientName); } @Override public void addWaiter(String clientName) { System.out.println("NaiveWaiter:add: " + clientName); } }
使用@Aspect定义一个切面:
package spring.UseAspectj.demo2; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; @Aspect //通过该注解将此类标示为一个切面 public class PreGreetingAspect { @Before("execution(* spring.UseAspectj.demo2.NaiveWaiter.deleteWaiter(..))") //定义切点和增强类型 public void beforeGreeting(){ //增强的横切逻辑 System.out.println("How r u!"); } }
这个切面没有实现任何特殊的接口,它只是一个普通的POJO。@Before注解表示该增强是前置增强,而成员值是一个@AspectJ切点表达式,它的意思是在目标类的deleteWaiter方法上织入增强,deleteWaiter方法可以带任何的入参和任意的返回值;
package spring.UseAspectj.demo2; import org.springframework.aop.aspectj.annotation.AspectJProxyFactory; public class AspectJProxyTest { public static void main(String[] args) { Waiter target = new NaiveWaiter(); AspectJProxyFactory factory = new AspectJProxyFactory(); //factory.setInterfaces(target.getClass().getInterfaces()); //factory.setOptimize(true); //设置目标对象 factory.setTarget(target); //添加类切面 factory.addAspect(PreGreetingAspect.class); //生成织入切面的代理对象 Waiter proxyWaiter = factory.getProxy(); System.out.println(proxyWaiter); proxyWaiter.deleteWaiter("lee"); proxyWaiter.addWaiter("nicholas"); } }
输出:
spring.UseAspectj.demo2.NaiveWaiter@16fd0b7
How r u!
NaiveWaiter:delete: lee
NaiveWaiter:add: nicholas
我们也可以通过在Spring配置文件中配置:
<!-- 配置基于@AspectJ的切面 --> <!--<aop:aspectj-autoproxy proxy-target-class="true" /> --> <bean id="useAspectWaiter" class="spring.UseAspectj.demo2.NaiveWaiter" /> <bean id="useAspectWaiter2" class="spring.UseAspectj.demo2.NaiveWaiter2" /> <bean class="spring.UseAspectj.demo2.PreGreetingAspect" /> <bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator" p:proxy-target-class="false" />
public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext( "classpath:springAOP.xml"); System.out.println(ctx.getBean("useAspectWaiter").getClass().getName()); //使用JDK代理的时候 直接使用接口Waiter 而不是使用NaiveWaiter 不然会报错 Waiter waiter = (Waiter)ctx.getBean("useAspectWaiter"); waiter.deleteWaiter("lee"); waiter.addWaiter("nicholas"); System.out.println("------------------"); NaiveWaiter2 waiter2 = (NaiveWaiter2)ctx.getBean("useAspectWaiter2"); waiter2.deleteWaiter("lee"); waiter2.addWaiter("nicholas"); }
输出:
$Proxy7
How r u!
NaiveWaiter:delete: lee
NaiveWaiter:add: nicholas
------------------
NaiveWaiter:delete: lee
NaiveWaiter:add: nicholas
因为我们之定义了NaiveWaiter.java中的增强逻辑,所以NaiveWaiter2不会织入增强;
proxy-target-class="true表示使用JDK代理,否则使用CGLib代理;
下面看一个引介增强的用法:
package spring.UseAspectj.demo2; public interface Seller { public void sell(String goods); }
package spring.UseAspectj.demo2; public class SmartSeller implements Seller { @Override public void sell(String goods) { System.out.println("sell:" + goods); } }
因为引介增强是基于类级别的,我们希望NaiveWaiter能够同事充当售货员的角色,即通过切面技术为NaiveWaiter新增Seller接口的实现:
package spring.UseAspectj.demo2; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.DeclareParents; @Aspect public class EnableSellerAspect { @DeclareParents(value="spring.UseAspectj.demo2.NaiveWaiter",defaultImpl=spring.UseAspectj.demo2.SmartSeller.class) public Seller seller; }
在这个切面中,我们通过@DeclareParents为NaiveWaiter添加了一个需要实现的Seller接口,并制定默认的实现类为SmartSeller;
<!-- 配置基于@AspectJ的切面 --> <bean id="useAspectWaiter" class="spring.UseAspectj.demo2.NaiveWaiter" /> <bean class="spring.UseAspectj.demo2.PreGreetingAspect" /> <bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator" p:proxy-target-class="false" /> <bean class="spring.UseAspectj.demo2.EnableSellerAspect" />
public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext( "classpath:springAOP.xml"); System.out.println(ctx.getBean("useAspectWaiter").getClass().getName()); //使用JDK代理的时候 直接使用接口Waiter 而不是使用NaiveWaiter 不然会报错 Waiter waiter = (Waiter)ctx.getBean("useAspectWaiter"); waiter.deleteWaiter("lee"); waiter.addWaiter("nicholas"); System.out.println("------------------"); Seller seller = (Seller)waiter; seller.sell("spring 3.x"); }
$Proxy7
How r u!
NaiveWaiter:delete: lee
NaiveWaiter:add: nicholas
------------------
sell:spring 3.x
可见,NaiveWaiter已经成功的新增了Seller接口的实现;
三、切点函数详解
1. @annotation
@annotation表示标注了某个注解的所有方法,看一个实例:
package spring.UseAspectj.atAnnotation; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Aspect; @Aspect public class TestAspect { //@annotation表示标注了某个注解的所有方法 @AfterReturning("@annotation(spring.UseAspectj.demo1.NeedTest)") public void needTestFun() { System.out.println("执行needTestFun()"); } }
<!-- 配置基于@AspectJ的切面 --> <bean id="useAspectWaiter" class="spring.UseAspectj.demo2.NaiveWaiter" /> <bean id="naughtyWaiter" class="spring.UseAspectj.atAnnotation.NaughtyWaiter" /> <bean class="spring.UseAspectj.demo2.PreGreetingAspect" /> <bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator" p:proxy-target-class="false" /> <bean class="spring.UseAspectj.atAnnotation.TestAspect" />
package spring.UseAspectj.atAnnotation; import spring.UseAspectj.demo1.NeedTest; import spring.UseAspectj.demo2.Waiter; public class NaughtyWaiter implements Waiter { @Override @NeedTest public void deleteWaiter(String clientName) { System.out.println("删除:" + clientName); } @Override public void addWaiter(String clientName) { System.out.println("添加:" + clientName); } }
package spring.UseAspectj.atAnnotation; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import spring.UseAspectj.demo2.Waiter; public class test { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext( "classpath:springAOP.xml"); Waiter waiter = ctx.getBean("naughtyWaiter",Waiter.class); waiter.deleteWaiter("a"); waiter.addWaiter("b"); } }
输出:
删除:a
执行needTestFun()
添加:b
可以看到,切面被织入到NaughtyWaiter#deleteWaiter方法中;
2. execution()
a.通过方法签名定义切点
execution(public * *(..))
- 匹配所有目标类的public方法,第一个*代表返回类型;第二个*代表方法名;而..代表任意入参的方法;
execution(* *To(..))
-匹配目标类所有以To为后缀的方法,第一个*代表返回类型;
b.通过类定义切点
execution(* spring.UseAspectj.demo2.Waiter.*(..))
-匹配Waiter接口的所有方法,它匹配它所有实现类里的方法,第一个*代表任意返回类型;
execution(* spring.UseAspectj.demo2.Waiter+.*(..))
-匹配Waiter接口极其所有实现类中的其他的方法,即使其他的方法没有在Waiter接口中定义;
c.通过类包定义切点
execution(* spring.UseAspectj.demo2.*(..))
-匹配spring.UseAspectj.demo2包下所有类的所有方法;
execution(* spring.UseAspectj.demo2..*(..))
-匹配spring.UseAspectj.demo2包极其子孙包下所有类的所有方法;
execution(* spring..*.*Dao.find*(..))
-匹配包名前缀为spring的任何包下类名后缀为Dao的方法,该方法名必须以find为前缀;
d.通过方法入参定义切点
execution(* joke(String,int))
-匹配joke方法,方法的第一个入参类型为String,第二个入参类型为int;
execution(* joke(String,*)
-匹配目标类中的joke方法,第一个类型为String,第二个为任意类型,但参数个输不能超过两个;
execution(* joke(String,..))
-匹配目标类中的joke方法,第一个参数类型为String,后面可以有人一个入参且取餐类型不限;
3.target() 和 this()
target()切点函数通过判断目标类是否按类型匹配指定类决定连接点是否匹配,而this()则通过判断代理类是否按类型匹配指定类决定连接点是否匹配。两者都仅接受类名的入参,虽然类名可以带“+”通配符,但对于这二者来说加和不加都一样。
target(spring.UseAspectj.demo2.Waiter)
-接口极其实现类中的所有方法都匹配切点
package spring.UseAspectj.demo2; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Aspect; @Aspect public class TestAspect { @AfterReturning("this(spring.UseAspectj.demo2.Seller)") public void thisTest() { System.out.println("thisTest()..."); } }
<!-- 配置基于@AspectJ的切面 --> <bean id="useAspectWaiter" class="spring.UseAspectj.demo2.NaiveWaiter" /> <bean class="org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator" p:proxy-target-class="false" /> <bean class="spring.UseAspectj.demo2.EnableSellerAspect" /> <bean class="spring.UseAspectj.demo2.TestAspect" />
public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext( "classpath:springAOP.xml"); //System.out.println(ctx.getBean("useAspectWaiter").getClass().getName()); //使用JDK代理的时候 直接使用接口Waiter 而不是使用NaiveWaiter 不然会报错 Waiter waiter = (Waiter)ctx.getBean("useAspectWaiter"); waiter.deleteWaiter("lee"); waiter.addWaiter("nicholas"); System.out.println("------------------"); Seller seller = (Seller)waiter; seller.sell("spring 3.x"); }
输出:
NaiveWaiter:delete: lee
thisTest()...
NaiveWaiter:add: nicholas
thisTest()...
------------------
sell:spring 3.x
thisTest()...
说明二者的区别在于通过引介切面产生代理对象时的具体表现,this匹配NaiveWaiter代理对象的所有方法,包括本身的方法以及通过Seller接口引入的sell方法。
切点函数之间也可以有逻辑运算符:
&&: 与操作符,相当于切点的交集运算;如果在Spring的XML文档中使用,要使用转义字符&&表示,类文件中可以只用and;
||: 或操作符,相当于切点的并集运算;类文件中使用||;
!:非操作符,相当于切点的反集运算,类文件中使用!;
四、@AspectJ进阶
1.切点符合运算
切点直接生命在增强方发出的切点是匿名切点,匿名切点只能在声明处使用。如果希望在其他地方重用一个切点,我们可以通过@Pointcut注解以及切面类方法对切点进行命名,下面是一个例子:
package spring.AspectjMore.demo1; import org.aspectj.lang.annotation.Pointcut; public class UseNamePointcut { @Pointcut("within(spring.UseAspectj.demo2.*)") private void inPackage() { } @Pointcut("execution(* deleteWaiter(..))") protected void deleteWaiter() { } @Pointcut("inPackage() and deleteWaiter()") public void inPrgDeleteWaiter() { } }
package spring.AspectjMore.demo1; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; @Aspect public class FuheAspect { /*@After("within(spring.UseAspectj.demo2.*)" + " && execution(* deleteWaiter(..))")*/ @After("UseNamePointcut.inPrgDeleteWaiter()") // 与运算 public void deleteWaiterFun() { System.out.println("--deleteWaiterFun().."); } @Before(" target(spring.UseAspectj.demo2.NaiveWaiter)" + " && !execution(* deleteWaiter(..))") // 非运算 public void notDeleteWaiterFun() { System.out.println("--notDeleteWaiterFun().."); } @AfterReturning("target(spring.UseAspectj.demo2.Waiter)" + " || target(spring.UseAspectj.demo2.Seller)") // 或运算 public void waiterOrSeller() { System.out.println("--waiterOrSeller().."); } }
ApplicationContext ctx = new ClassPathXmlApplicationContext( "classpath:springAOP.xml"); Waiter waiter = (Waiter)ctx.getBean("useAspectWaiter"); waiter.addWaiter("b"); waiter.deleteWaiter("a");
<bean class="spring.AspectjMore.demo1.FuheAspect" />
输出:
--notDeleteWaiterFun()..
NaiveWaiter:add: b
--deleteWaiterFun()..
--waiterOrSeller()..
织入增强的顺序:
a.如果增强在同一个切面类中,则依照增强在切面类中定义的顺序进行织入;
b.如果增强位于不同的切面类中,且这些切面都实现了org.springframework.core.Ordered接口,则由接口方法的顺序号决定,小的先织入;
c.如果增强位于不同的切面类中,且这些切面类没有实现那个接口,那么织入的顺序是不确定的;
2.访问连接点信息
AspectJ使用org.aspectj.lang.JoinPoint接口表示目标类连接点对象,如果是环绕增强,使用ProceedingJoinPoint表示连接点对象,该类是JoinPoint的子接口,任何一个增强方法都可以通过降低一个入参声明为JoinPoint访问到连接点上下文的信息。
package spring.AspectjMore.demo1; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; @Aspect public class VisitPointcut { @Around("target(spring.UseAspectj.demo2.NaiveWaiter)" + " && !execution(* deleteWaiter(..))") public void joinPointAccess(ProceedingJoinPoint pjp) throws Throwable { System.out.println("------joinPointAccess------"); System.out.println("arg[0]:" + pjp.getArgs()[0]); System.out.println("signature:" + pjp.getClass().getName()); pjp.proceed(); System.out.println("------joinPointAccess------"); } }
Waiter waiter = (Waiter)ctx.getBean("useAspectWaiter");
waiter.addWaiter("b");
<bean class="spring.AspectjMore.demo1.VisitPointcut" />
输出:
------joinPointAccess------
arg[0]:b
signature:org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint
NaiveWaiter:add: b
------joinPointAccess------
3.绑定连接点方法入参
package spring.AspectjMore.demo1; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; @Aspect public class BindArgsAspect { @Before("target(spring.UseAspectj.demo2.NaiveWaiter)" + " && !execution(* deleteWaiter(..))" + " && args(clientName)") public void bindPointParams(String clientName) { System.out.println("------------bindPointParams------------"); System.out.println("clientName:" + clientName); System.out.println("------------bindPointParams------------"); } }
ApplicationContext ctx = new ClassPathXmlApplicationContext( "classpath:springAOP.xml"); Waiter waiter = (Waiter)ctx.getBean("useAspectWaiter"); waiter.addWaiter("b"); waiter.deleteWaiter("a");
<bean class="spring.AspectjMore.demo1.BindArgsAspect" />
输出:
------------bindPointParams------------
clientName:b
------------bindPointParams------------
NaiveWaiter:add: b
NaiveWaiter:delete: a
4.绑定代理对象
使用this()或target()可绑定被代理对象实例,在通过类实例名绑定对象时,还依然具有原来连接点匹配的功能,只不过类名是通过增强方法中同名入参的类型间接决定罢了。
package spring.AspectjMore.demo1; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import spring.UseAspectj.demo2.Waiter; @Aspect public class BindProxyObj { @Before("this(waiter)") public void bindObj(Waiter waiter) { System.out.println("--------bindObj-------"); System.out.println("waiter:" + waiter.getClass().getName()); System.out.println("--------bindObj-------"); } }
ApplicationContext ctx = new ClassPathXmlApplicationContext( "classpath:springAOP.xml"); Waiter waiter = (Waiter)ctx.getBean("useAspectWaiter"); waiter.addWaiter("b");
<bean class="spring.AspectjMore.demo1.BindProxyObj" />
输出:
--------bindObj-------
waiter:$Proxy6
--------bindObj-------
NaiveWaiter:add: b
5.绑定类注解对象
@within()@target函数可以将目标类的注解对象绑定到增强方法中:
package spring.AspectjMore.demo1; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; @Aspect public class BindAspect { @Before("@within(needLog)") public void bindNeedTest(NeedLog needLog) { System.out.println("------------ bindNeedLog ------------"); System.out.println("NeedTest:" + needLog.getClass().getName()); System.out.println("------------ bindNeedLog ------------"); } }
package spring.AspectjMore.demo1; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME)// 声明注解的保留期限 @Target(ElementType.TYPE)// 声明可以使用该注解的目标类型 public @interface NeedLog { boolean value() default true; // 声明注解成员 }
package spring.UseAspectj.atAnnotation; import spring.AspectjMore.demo1.NeedLog; import spring.UseAspectj.demo1.NeedTest; import spring.UseAspectj.demo2.Waiter; @NeedLog public class NaughtyWaiter implements Waiter { @Override @NeedTest public void deleteWaiter(String clientName) { System.out.println("删除:" + clientName); } @Override public void addWaiter(String clientName) { System.out.println("添加:" + clientName); } }
Waiter naughtyWaiter = (Waiter)ctx.getBean("naughtyWaiter");
naughtyWaiter.deleteWaiter("aaa");
<bean class="spring.AspectjMore.demo1.BindAspect" />
输出:
------------ bindNeedLog ------------
NeedTest:$Proxy3
------------ bindNeedLog ------------
删除:aaa
6.绑定返回值
package spring.AspectjMore.demo1; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Aspect; @Aspect public class BindArgsReturn { @AfterReturning(value = "target(spring.UseAspectj.demo2.SmartSeller)", returning = "retVal") public void bindReturnValue(int retVal) { System.out.println("----------------bindReturnValue"); System.out.println("returnValue:" + retVal); System.out.println("----------------bindReturnValue"); } }
Seller seller = (Seller)ctx.getBean("smartSeller");
((SmartSeller)seller).sell2("yy");
package spring.UseAspectj.demo2; public class SmartSeller implements Seller { @Override public void sell(String goods) { System.out.println("sell:" + goods); if(goods.equals("yy")) throw new IllegalArgumentException("iae Exception"); } public int sell2(String goods) { System.out.println("sell:" + goods); return 1000; } }
<bean class="spring.AspectjMore.demo1.BindArgsReturn" />
输出:
sell:yy
----------------bindReturnValue
returnValue:1000
----------------bindReturnValue
7.绑定抛出的异常
package spring.AspectjMore.demo1; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Aspect; @Aspect public class BindExpAspect { @AfterThrowing(value = "target(spring.UseAspectj.demo2.SmartSeller)", throwing = "iae") //@After("target(spring.UseAspectj.demo2.SmartSeller)") public void bindException(IllegalArgumentException iae) { //public void bindException() { System.out.println("-------------------bindException"); System.out.println("exception:" + iae.getMessage()); System.out.println("-------------------bindException"); } }
Seller seller = (Seller)ctx.getBean("smartSeller"); ((SmartSeller)seller).sell2("yy"); seller.sell("yy");
输出:
sell:yy
sell:yy
-------------------bindException
exception:iae Exception
-------------------bindException
Exception in thread "main" java.lang.IllegalArgumentException: iae Exception
at spring.UseAspectj.demo2.SmartSeller.sell(SmartSeller.java:9)
at spring.UseAspectj.demo2.SmartSeller$$FastClassByCGLIB$$808c8687.invoke(<generated>)
at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:191)
at org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation.invokeJoinpoint(Cglib2AopProxy.java:688)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:55)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:161)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:89)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:621)
at spring.UseAspectj.demo2.SmartSeller$$EnhancerByCGLIB$$159bc07b.sell(<generated>)
at spring.AspectjMore.demo1.test.main(test.java:26)
五、基于Schema配置切面
基于@AspectJ注解的切面,本质上是将切点,增强类型的信息使用注解进行描述,现在把这两个信息移到Schema的XML配置文件中。
package spring.AspectjMore.UseSchema; public class AdviceMethods { public void preGreeting(){ System.out.println("how!"); } }
<!-- 基于Schema的aop --> <aop:config proxy-target-class="false"> <aop:aspect ref="adviceMethods"> <aop:before pointcut="execution(* spring.UseAspectj.demo2.NaiveWaiter.deleteWaiter(..))" method="preGreeting"/> </aop:aspect> </aop:config> <bean id="adviceMethods" class="spring.AspectjMore.UseSchema.AdviceMethods" />
ApplicationContext ctx = new ClassPathXmlApplicationContext( "classpath:springAOP.xml"); Waiter waiter = (Waiter)ctx.getBean("useAspectWaiter"); waiter.addWaiter("b"); waiter.deleteWaiter("a");
输出:
NaiveWaiter:add: b
how!
NaiveWaiter:delete: a