Spring切面
2016-07-05 11:43
337 查看
AOP基本概念
通知(Advice):定义了一个切面该干什么(what)以及什么时候应用(when).spring的通知类型有五种:before:在方法调用之前.
after:在方法调用之后,无论方法是否正常返回.
after-returning:在方法正常返回之后应用.
after-throwing:在方法出现异常时调用.
around:该通知将包住整个方法.
连接点(join point):所有可能应用切面的点,包括方法调用,实例域改变等等.
切点(pointcut):定义了切面的具体应用位置,哪些方法使用了通知之类的.(where)
切面(aspect):是advice与pointcut的结合,它定义了对哪些方法(where)在哪些情况下(when)执行什么操作(what).
引入(INTRODUCTION):用于向已有类中加入新的实例域/方法,使用动态代理实现.
织入(WEAVING):将切面/引入应用到目标对象,并创建代理对象的过程.织入的时机主要有以下3个:
编译期:在目标类编译时将切面织入,需要使用特别的编译器,AspectJ使用该方式.
类加载时期:在类加载器加载字节码时,对字节码进行修改,实现织入.需要特殊的ClassLoader,AspectJ 5支持该织入方式.
运行期:通过创建动态代理(JDK/CGLib),在运行期基于目标对象动态生成代理类,并最终创建代理实例,更多的是复合的意味.这是spring的织入模式.
Spring AOP支持
spring对于AOP的支持主要有以下三种方式:1. 利用xml的aop命名空间,将一个纯pojo转换为切面.
2. 利用@AspectJ驱动的切面.
3. 直接注入AspectJ的切面.
tips:1,2两种方式,Spring是通过动态代理来实现的.
jdk动态代理的应用如下:将Advice对象以及目标对象注入到InvocationHandler实例中,并在invoke方法中实现切面.(该InvocationHandler实例会传递给代理对象).
spring只支持使用方法作为切点,如果需要对构造器/实例域应用切面只能使用3的AspectJ.
Spring AOP
Spring使用AspectJ表达式定义切点,但是只支持AspectJ的子集。Spring支持的AspectJ符号如下表
AspectJ符号 | 含义 |
---|---|
execution(expression) | 用于真正筛选方法切点,其他符号基于该结果进行进一步限定,所以是必须的。 |
this(fullClassName) | 用于选择那些代理是基于fullClassName的bean(切点/代理对象) |
bean(id) | 限定只对特定id的bean应用切面 |
@args(AnnotationTypes) | 用于限定那些参数的类型使用AnnotationTypes进行注解的切点 |
args(name) | 用于传递实际参数到通知 |
within(package.*) | 限定切点的bean的类型在package.*的范围内 |
@within(AT)/@target(AT) | 限定切点所在类必须由@AT进行注解 |
@annotation(AT) | 限定切点对应的方法必须有@AT注解 |
target(targetClass) | 限定切点所在的类必须是targetClass |
基于注解的Spring AOP
必须在配置类中使用@EnableAspectJAutoProxy,允许自动使用切面代理。通过@Aspect声明一个切面并通过@Component/@Bean创建该切面的实例,使其可以使用。
package high.perform; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Aspect @Component public class Audience { //这里声明了切点表达式,用于匹配切点 //*表示匹配任意返回类型 //+表示匹配Perform及其子类 //..表示参数任意 @Pointcut(value = "execution(* high.perform.Perform+.play(..)) && " + // args用于传递来自切点的参数,在这里即为Perform.play的参数, // 需要与playX的参数同名传递(出于多参数考虑) "args(song) && " + // 限定为被@T注解的类作为切点所在的类 "@target(TX) && " + // 选择id为mp的bean,若id不存在则无论bean(id)/!bean(id)过滤结果都为false. "bean(mp) && " + // 用于选择那些被@TX注解的切点(方法) "@annotation(TX)", // 当使用在非debug模式编译,需要使用argNames属性,该属性必须与playX的参数名一致 argNames = "song") public void playX(String song){} //在这里声明一个前置通知,使用前面定义的切点 @Before("playX(song)") public void beforePerform(String song){ System.out.println(song+" is ready to play."); System.out.println("Please sit down."); } } interface Perform { void play(String x); } @Component("mp") @TX //这里是一个切点 class MusicPerform implements Perform{ @Override @TX public void play(@TX String x) { System.out.println("Sing "+x+" out."); } } @Target({ElementType.TYPE,ElementType.PARAMETER,ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @interface TX{}
环绕通知(Aroud)是最强大的通知,其对应的实现方法需要有一个ProceedingJoinPoint参数.
package high.perform; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Aspect @Component public class Audience { @Pointcut("execution(* *.play(..)) && args(song)") public void playX(String song){} @Around("playX(song)") //ProceedingJoinPoint不需要加入到args中 public void aroundPerform(ProceedingJoinPoint pjp,String song){ try{ System.out.println("this is before advice."); Object[] params = {song}; //目标方法调用 pjp.proceed(params); System.out.println("this is after advice."); }catch (Throwable e){ System.out.println("this is after-throwing."); } } } interface Perform { void play(String x); } @Component("mp") @TX //这里是一个切点 class MusicPerform implements Perform{ @Override @TX public void play(@TX String x) { System.out.println("Sing "+x+" out."); } } @Target({ElementType.TYPE,ElementType.PARAMETER,ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @interface TX{}
利用切面实现引入
@Aspect @Component() public class IAIB{ //这里的value必须是一个全限定类名,否则不成功. //对所有B的子类进行代理,引入接口A的功能,具体实现使用AI. //这部分引入的功能基于动态代理很容易实现,只要在创建代理时传进去引入的接口即可. @DeclareParents(value = "high.perform.B+",defaultImpl = AI.class) public static A a; }
注入AspectJ切面
wait for need.相关文章推荐
- 一个jar包里的网站
- 一个jar包里的网站之文件上传
- 一个jar包里的网站之返回对媒体类型
- Spring和ThreadLocal
- Spring Boot 开发微服务
- Spring AOP动态代理-切面
- Spring整合Quartz(JobDetailBean方式)
- Spring整合Quartz(JobDetailBean方式)
- yui3的AOP(面向切面编程)和OOP(面向对象编程)
- JavaScript AOP编程实例
- 使用AOP改善javascript代码
- 初识SmartJS - AOP三剑客
- 模拟Spring的简单实现
- Spring整合WebSocket应用示例(上)
- spring+html5实现安全传输随机数字密码键盘