# 精通Spring+4.x++企业开发与实践之基于@AspectJ和Schema的AOPjava
1.保证是java5以上的版本(须要使用注解,而java5及以上才使用注解)spring
2.须要将Spring的asm(轻量级的字节码处理框架)的模块添加到类路径中,由于java的反射没法获取入参的名字,因此Spring就是要asm处理@AspectJ中描述的方法入参名。app
3.Spring使用AspectJ提供的@AspectJ注解类库及相应的解析类库,须要在pom.xml文件添加aspectjweaver和aspectj的工具类aspectjrt框架
例子:函数
PreGreetingAspect.java工具
@Aspect//使用该注解定义一个切面 public class PreGreetingAspect { /** * 这段代码包含了横切的逻辑 * @Before 加强的类型 * "execution(* greetTo(..))"目标切点的表达式 */ @Before("execution(* greetTo(..))") public void beforeGreeting(){ System.out.println("How are you"); } }
测试代码:测试
//建立被代理的对象 Waiter waiter = new PoliteWaiter(); //AspectJ代理工厂 AspectJProxyFactory factory = new AspectJProxyFactory(); //提娜佳目标类 factory.setTarget(waiter); //添加切面类 factory.addAspect(PreGreetingAspect.class); //生成切入的代理对象 Waiter proxy = factory.getProxy(); proxy.greetTo("张三");
执行结果: How are youflex
greet to 张三....this
使用schema的配置方式进行配置spa
<?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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!--基于asjectj的切面驱动器--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> <bean id="waiter" class="com.flexible.aspectj.PoliteWaiter"></bean> <bean class="com.flexible.aspectj.PreGreetingAspect"></bean> </beans>
测试代码:
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:beans.xml"); Waiter waiter = (Waiter) context.getBean("waiter"); waiter.greetTo("zhangsan");
@AspectJ使用Java5.0注解和正规的AspectJ的切点表达式语言描述切面,因为Spring只支持方法的连接,因此Spring仅支持部分AspectJ的切点语言。
组成:1.关键字和操做参数execution(* greetTo(..)),execution是关键字,"* greetTo(..)"位操做参数。execution表明目标类执行某一方法,"* greetTo(..)"描述目标方法的匹配模式串,两者联合起来表示目标类的greetTo()方法的链接点。execution()称为函数,"* greet(..)"称为函数入参。
Spring支持9个@AdpectJ切点表达式函数,它用不一样的方式藐视目标类的链接点。根据描述的不一样能够分为4种: 1.方法切点函数:经过描述目标类方法的信息定义链接点。 2.方法入参切点函数:经过描述目标类的方法入参的信息定义链接点。 3.目标类切点函数:经过描述目标类类型的信心定义链接点。 4.代理切点函数:经过描述目标类的代理类的信息定义链接点。
*:匹配任意字符,但它只能匹配上下文中的一个元素。
..:匹配任意字符,能够匹配上下文中的多个元素,但在表示类时,必须和*联合使用,而在表示入参时则单独使用。
+:表示按类型匹配指定的类的全部类,必须跟在类名后面,如com.flexible.Car+表示继承或者拓展了制定类的全部类,同时还包括指定类自己。
@AaspectJ函数按其是否支持通配符及支持的程度,能够分为三类:
1.支持通配符:execution()和within(),如 within(com.flexible.),within(com.flexible.service...*.Service).
2.仅支持"+"通配符:args(),this()和target(),如args(com.flexible.Waiter+),target(java.util.List+),可是其实这几个函数是否是有这个通配符都时同样的。
3.不支持通配符的:@args,@within(),@target()和@annotation().
1.@Before 前置加强,至关于BeforeAdvice。Before注解类用于两个成员
2.@AfterReturning 后置加强,至关于AfterReturningAdvice。AfterReturning注解类拥有4个成员。
3.@Around 环绕加强,至关于MethodInterceptor。Around注释类用于两个成员。
4.@AfterThrowing 抛出加强,至关于ThrowsAdvice。AfterThrowing注解有4个成员。
value:指定切点
pointcut:表示切点的信息,若是显示指定pointcut值,那么它将覆盖value值得设置值,能够将pointcut成员看做value得同义词。
throwing:将抛出得异常绑定到加强方法中。
argNames:如上所述。
5.@After Final加强,无论时抛出异常仍是正常退出,该加强都会执行,该加强没有对应得加强接口,能够把它当作ThrowsAdvice和AfterReturningAdvice得混合物,议案用于释放资源,至关于try{}finally{}的控制流程。它有两个成员;
value:该成员用于定义切点。
argNames:如上所述。
6.@DeclareParents 引介加强,至关于IntroductionInterceptor.DeclareParents注解类拥有两个成员
value:定义切点,表示在那个木堡垒上添加引介加强。
defaultImpl:默认接口实现类。
经过引介加强将waiter也具备seller的功能
EnableSellerAspect.java
@Aspect public class EnableSellerAspect { //1.为PoliteWaiter太你家接口实现 2.默认接口实现类 3.要是西安的目标接口。 @DeclareParents(value = "com.flexible.inroductionofdeclareparent.PoliteWaiter",defaultImpl = SmartSeller.class) public Seller seller; }
beans.xml
<!--基于asjectj的切面驱动器--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> <bean id="waiter_2" class="com.flexible.inroductionofdeclareparent.PoliteWaiter"></bean> <bean class="com.flexible.inroductionofdeclareparent.EnableSellerAspect"></bean>
测试代码:
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:beans.xml"); Waiter waiter = (Waiter) context.getBean("waiter_2"); waiter.greetTo("zhangsan"); Seller seller = (Seller) waiter; seller.sell("apple");
执行结果:
直接将切点声明在加强的方式是匿名加强,而若是但愿在其余地方重用一个切点,能够经过@Pointcut注解及切面类方法对切点进行命名。
例子:
import org.aspectj.lang.annotation.Pointcut; public class NamedPointcut { //经过注解方法inpackage()对该切点进行命名,方法可视域修饰符为private //代表该命名切点只能在本切面类中使用 @Pointcut("within(com.flexible.*)") private void inpackage(){} //经过注解方法greetTo()对该切点进行命名,方法能够视域修饰符为protected //代表该命名切点能够在当前包中的切面类,自切脉你类只要。 @Pointcut("execution(* greetTo(..))") protected void greetTo(){} //引用命名切点定义的切点,本切点也是命名切点,它对应的可视域为public @Pointcut("inpackage() && greetTo()") public void inPkgGreetTo(){} }
命名切点的使用类方法做为切点的名称,此方法的访问修饰符还控制了切点的可引用性。命名切点定义好以后咱们就能够在定义切面的时经过名称引用切点。
例子:
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; @Aspect public class NamedAspect { @Before("NamedPointcut.inPkgGreetTo()") public void pkgGreetTo(){ System.out.println("----------pkgGreetTo() executed!---"); } @Before("!target(com.flexible.pointcutbreakdown.annotation.PoliteWaiter) && NamedPointcut.inPkgGreetTo()") public void pkgGreetToNotNaivewaiter(){ System.out.println("----------pkgGreetToNotNaivewaiter() executed!---"); } }
一个链接点可u哦同时匹配多个其欸但,切点对应的加强在链接点上的植入顺序有三种状况须要讨论
1.若是加强在同一个切面类中声明,则依照加强在切面类中定义的顺序织入
2.若是加强位于不一样的切面类中,且这些切面类都实现了org.springframework.core.Ordered接口,则由接口方法的顺序好决定(顺序号小的先织入)
3.若是加强位于不一样的切面类中,且这些切面类没有实现org.springframework.core.Ordered接口,则织入顺序是不肯定的。
例子: 若是有切面A和切面B,并且这两个切面都实现了org.springframework.core.Ordered接口,A的顺序是1,而得顺序是2,并且A定义了三个切点,B定义两个切点。那么访问顺序以下图所示:
AspectJ使用org.aspectj.lang.JoinPoint接口表示目标类链接点对象。若是是环绕加强,则使用org.aspectj.lang.ProceedingJoinPoint表示连系欸但对象,该类是JoinPoint得子接口。任何加强方法均可以经过将第一个入参声明为JoinPoint访问连系欸但上下文信息。 1.JoinPoint
2.ProceedingJoinPoint
ProceedingJoinPoint继承于JoinPoint子接口,它新增了两个用于执行链接点得方法。
例子:
TestAspect.java
@Aspect public class TestAspect { @Around("execution(* com.flexible.obtainproceedingpointinfo..*(..))") public void joinPointAccess(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.out.println("------joinPointAccess----"); System.out.println("args[0]:" + proceedingJoinPoint.getArgs()[0]); System.out.println("signature:" + proceedingJoinPoint.getTarget().getClass()); proceedingJoinPoint.proceed(); System.out.println("------joinPointAccess----"); } }
beans.xml
<!--基于asjectj的切面驱动器--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> <!--获取链接点的信息--> <bean id="waiter_4" class="com.flexible.obtainproceedingpointinfo.PoliteWaiter"></bean> <bean class="com.flexible.obtainproceedingpointinfo.TestAspect"></bean>
测试代码:
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:beans.xml"); Waiter waiter = (Waiter) context.getBean("waiter_4"); waiter.greetTo("zhangsan");
执行结果: