Spring AOP

一、SpringAOP是什么?

        Spring的核心是控制反转(IoC)和面向切面(AOP)。AOP (Aspect Orient Programming),直译过来就是 面向切面编程,AOP 是一种编程思想,是面向对象编程(OOP)的一种补充。AOP可以拦截指定的方法并且对方法增强,而且无需侵入到业务代码中,使业务与非业务处理逻辑分离,比如Spring的事务,通过事务的注解配置,Spring会自动在业务方法中开启、提交业务,并且在业务处理失败时,执行相应的回滚策略。

二、AOP术语

连接点(Joinpoint) 程序执行的某个特定位置,如某个方法调用前,调用后,方法抛出异常后,这些代码中的特定点称为连接点。简单来说,就是在哪加入你的逻辑增强
连接点表示具体要拦截的方法,上面切点是定义一个范围,而连接点是具体到某个方法
切点(PointCut) 每个程序的连接点有多个,如何定位到某个感兴趣的连接点,就需要通过切点来定位。比如,连接点--数据库的记录,切点--查询条件
切点用于来限定Spring-AOP启动的范围,通常我们采用表达式的方式来设置,所以关键词是范围
增强(Advice) 增强是织入到目标类连接点上的一段程序代码。在Spring中,像BeforeAdvice等还带有方位信息
通知是直译过来的结果,我个人感觉叫做“业务增强”更合适 对照代码就是拦截器定义的相关方法,通知分为如下几种:
前置通知(before):在执行业务代码前做些操作,比如获取连接对象
后置通知(after):在执行业务代码后做些操作,无论是否发生异常,它都会执行,比如关闭连接对象
异常通知(afterThrowing):在执行业务代码后出现异常,需要做的操作,比如回滚事务
返回通知(afterReturning),在执行业务代码后无异常,会执行的操作
环绕通知(around)
目标对象(Target) 需要被加强的业务对象
织入(Weaving) 织入就是将增强添加到对目标类具体连接点上的过程。
织入是一个形象的说法,具体来说,就是生成代理对象并将切面内容融入到业务流程的过程。
代理类(Proxy) 一个类被AOP织入增强后,就产生了一个代理类。
切面(Aspect) 切面由切点和增强组成,它既包括了横切逻辑的定义,也包括了连接点的定义,SpringAOP就是将切面所定义的横切逻辑织入到切面所制定的连接点中。

三. 常见用法

使用AOP最常见的用法,就是定义个@Aspect注解类,在该类里面写上大量的切入点和增强方法,当然也可以以一种组合的方式,直接把pointcut的定义写在增强方法注解上。

@Aspect
@Component
public class MyAspect {

    /**
     * 以注解方式找到切入点
     * 此处是找到被 MyAnnotation 注解标记的 methodA()方法,把这些方法作为切入点
     */
    @Pointcut("@annotation(com.zhanfu.springtest.MyAnnotation)")
    public void someService1(){}

    /**
     * 在指定的范围找切入点
     * 此处是 找到定义在springtest包里的任意方法
     */
    @Pointcut("execution(* com.zhanfu.springtest.*.*(..)) ")
    public void someService2(){}

    /**
     * 增强方法,本增强方法作用于上面定义的切入点someService1(),
     * 增强方式为Around
     */
    @Around("someService1()")
    public Object doSomething (ProceedingJoinPoint joinPoint){
        System.out.println("执行前");
        try {
            Object result = joinPoint.proceed();
            System.out.println("执行后");
            return result;
        } catch (Throwable e) {
            System.out.println("发生异常");
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 增强方法,组合使用,这种方式不需要另外写个pointcut,而是直接把 pointcut 写在@before注解里面
     * 增强方式为Before
     */
    @Before("execution(* com.zhanfu.springtest.*.main(..))")
    public void before(ProceedingJoinPoint joinPoint){
        String name = joinPoint.getSignature().getName();
        System.out.println("前置通知:在" + name + "执行前");
    }
}

四、Spring AOP的实现原理


Spring的AOP实现原理其实很简单,就是通过动态代理实现的。

Spring AOP 采用了两种混合的实现方式:JDK 动态代理和 CGLib 动态代理。

JDK动态代理:Spring AOP的首选方法。 每当目标对象实现一个接口时,就会使用JDK动态代理。目标对象必须实现接口
CGLIB代理:如果目标对象没有实现接口,则可以使用CGLIB代理。
JDK动态代理
Spring默认使用JDK的动态代理实现AOP,类如果实现了接口,Spring就会使用这种方式实现动态代理。

JDK实现动态代理需要两个组件,首先第一个就是InvocationHandler接口。

我们在使用JDK的动态代理时,需要编写一个类,去实现这个接口,然后重写invoke方法,这个方法其实就是我们提供的代理方法。

如下源码所示:

/**
 * 动态代理
 *
 * @author mikechen
 */
public class JdkProxySubject implements InvocationHandler {
 
    private Subject subject;
 
    public JdkProxySubject(Subject subject) {
        this.subject = subject;
    }
 
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
 
        System.out.println("before 前置通知");
        Object result = null;
 
        try {
            result = method.invoke(subject, args);
        }catch (Exception ex) {
            System.out.println("ex: " + ex.getMessage());
            throw ex;
        }finally {
            System.out.println("after 后置通知");
        }
        return result;
    }
}

然后JDK动态代理需要使用的第二个组件就是Proxy这个类,我们可以通过这个类的newProxyInstance方法,返回一个代理对象。

生成的代理类实现了原来那个类的所有接口,并对接口的方法进行了代理,我们通过代理对象调用这些方法时,底层将通过反射,调用我们实现的invoke方法。

public class Main { public static void main(String[] args) { 
   //获取InvocationHandler对象 在构造方法中注入目标对象 
   InvocationHandler handler = new JdkProxySubject(new RealSubject()); 
 
   //获取代理类对象 
  Subject proxySubject = (Subject)Proxy.newProxyInstance(Main.class.getClassLoader(), new Class[]{Subject.class}, handler); 
 
   //调用目标方法
   proxySubject.request(); proxySubject.response();
 


}
运行结果:

before 前置通知
执行目标对象的request方法......
after 后置通知
before 前置通知
执行目标对象的response方法......
after 后置通知


五、JDK动态代理优缺


优点
JDK动态代理是JDK原生的,不需要任何依赖即可使用;

通过反射机制生成代理类的速度要比CGLib操作字节码生成代理类的速度更快;

缺点
如果要使用JDK动态代理,被代理的类必须实现了接口,否则无法代理;

JDK动态代理无法为没有在接口中定义的方法实现代理,假设我们有一个实现了接口的类,我们为它的一个不属于接口中的方法配置了切面,Spring仍然会使用JDK的动态代理,但是由于配置了切面的方法不属于接口,为这个方法配置的切面将不会被织入。

JDK动态代理执行代理方法时,需要通过反射机制进行回调,此时方法执行的效率比较低;

六、CGLib代理

CGLIB组成结构
Cglib是一个强大的、高性能的代码生成包,它广泛被许多AOP框架使用,为他们提供方法的拦截,如下图所示Cglib与Spring等应用的关系:

最底层的是字节码 Bytecode ,字节码是Java为了保证“一次编译、到处运行”而产生的一种虚拟指令格式,例如iload_0、iconst_1、if_icmpne、dup等
位于字节码之上的是 ASM ,这是一种直接操作字节码的框架,应用ASM需要对Java字节码、Class结构比较熟悉
位于 ASM 之上的是 CGLIB 、 Groovy 、 BeanShell ,后两种并不是Java体系中的内容而是脚本语言,它们通过ASM框架生成字节码变相执行Java代码,这说明 在JVM中执行程序并不一定非要写Java代码----只要你能生成Java字节码,JVM并不关心字节码的来源 ,当然通过Java代码生成的JVM字节码是通过编译器直接生成的,算是最“正统”的JVM字节码
位于 CGLIB 、 Groovy 、 BeanShell 之上的就是 Hibernate 、 Spring AOP 这些框架了,这一层大家都比较熟悉
最上层的是Applications,即具体应用,一般都是一个Web项目或者本地跑一个程序
所以,Cglib的实现是在字节码的基础上的,并且使用了开源的ASM读取字节码,对类实现增强功能的。

 

1.SpringAOP不是一种新的AOP实现,使用JDK动态代理和CGLIB动态代理实现
2.SpringAOP配置方式核心是Advisor,可以自定义Advisor,也可以通过AspectJ间接定义Advisor
3.SpringAOP的实现遵循了AOP联盟规范,AOP联盟顶级API接口贯穿了整个AOP过程

 

  • 17
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值