Spring AOP(一)Spring AOP实现原理简介

我们知道,使用面向对象编程(OOP)有一些弊端,当需要为多个不具有继承关系的对象引入同一个公共行为时,例如日志、安全检测等,我们只有在每个对象里引用公共行为,这样程序中就产生了大量重复代码,所以就有了一个对面向对象编程的补充:面向切面编程(AOP),AOP所关注的方向是横面的,不同于OOP的纵面。

Spring AOP的实现原理是 动态代理,实现方式有两种: JDK动态代理和CgLib动态代理。

JDK动态代理

Jdk动态代理是基于 InvocationHandlerProxy 来实现,由Java内部的 反射 机制来实例化代理对象,并调用委托类方法。

一个JDK实现动态代理的Demo:

  • 目标接口
public interface TestAop {

    void process();
}
  • 目标实现类
@Component
public class TestAopImpl implements TestAop {
    @Override
    public void process() {
        System.out.println("testAop.process()");
    }
}
  • 切面类
@Component
public class AopAspect {

    public void before() {
        System.out.println("前置通知");
    }

    public void after() {
        System.out.println("后置通知");
    }

}
  • 测试类
public class MainTest {
    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("spring/applicationContext.xml");
        final TestAop testAop = ac.getBean(TestAop.class);
        final AopAspect aopAspect = ac.getBean(AopAspect.class);
        //创建代理
        TestAop testAopProxy = (TestAop) Proxy.newProxyInstance(
                ac.getClassLoader(),
                testAop.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //前执行
                        aopAspect.before();
                        //执行目标类方法
                        Object obj = method.invoke(testAop, args);
                        //后执行
                        aopAspect.after();
                        return obj;
                    }
                }
        );
        testAopProxy.process();
    }

}

输出结果:

前置通知
testAop.process()
后置通知

CgLib动态代理

基于CGlib 动态代理模式是基于 继承 被代理类生成代理子类,不用实现接口。只需要被代理类是非final 类即可,cglib动态代理底层是借助asm字节码技术。

一个CgLib实现动态代理的Demo:

  • 目标类、切面类都同上
  • 测试类
public class MainTest {


    public static void main(final String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("spring/applicationContext.xml");
        final AopAspect aopAspect = ac.getBean(AopAspect.class);
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(TestAopImpl.class);
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                aopAspect.before();
                Object result = methodProxy.invokeSuper(o, objects);
                aopAspect.after();
                return result;
            }
        });
        TestAopImpl testAop = (TestAopImpl) enhancer.create();
        testAop.process();
    }
}

输出结果:

前置通知
testAop.process()
后置通知

Spring AOP

与上述两种编码方式对比:

  • Spring AOP代理是 基于IOC容器 负责生成与管理:切面Bean的创建、通知的应用类、应用时间点等关系也都由IOC容器管理;
  • Spring根据策略判断使用JDK和还是CGLIB实现动态代理,策略规则为:
    1. Spring AOP默认是使用JDK动态代理,如果代理的类没有接口则会使用CGLib代理。
    2. CGLib代理其生成的动态代理对象是目标类的子类,也可以强制使用CGLib模式。

Spring中AOP相关概念

  • 通知(advice):在方法执行之前或之后采取的实际操作。 这是在Spring AOP框架的程序执行期间调用的实际代码片段
  • 连接点(JointPoint):应用程序中使用Spring AOP框架采取操作的实际位置
  • 切点(PointCut):在切点应该执行Advice
  • 切面(Aspect)
  • 引入(Introduction):引用允许我们向现有的类添加新的方法或者属性
  • 织入(Weaving):创建一个被增强对象的过程

Spring中提供了3种类型的AOP支持:

  1. 基于代理的经典Spring AOP,低版本的spring配置方式;

  2. 使用XML配置方式,纯POJO切面,aop命名空间

  3. @AspectJ注解方式:与xml形式的效果相同,是最简洁和最方便的;

有关这三种方式的使用方式及详细介绍:Spring AOP 使用介绍,从前世到今生

我们重点分析基于注解方式的Spring AOP实现。首先是一个Demo:

  • 配置文件:
<context:component-scan base-package="example.aop"/>
<aop:aspectj-autoproxy/>
  • 目标接口
public interface TestAop {

    void process();
}
  • 目标实现类
@Component
public class TestAopImpl implements TestAop {
    @Override
    public void process() {
        System.out.println("testAop.process()");
    }
}
  • 切面类
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class AopAspect {

    //定义切点
    @Pointcut("execution(* example.aop.*.*(..))")
    public void aopPointcut() {
    }

    @Before("aopPointcut()")
    public void before() {
        System.out.println("前置通知");
    }

    //后置通知
    @After("aopPointcut()")
    public void after() {
        System.out.println("后置通知");
    }

    //环绕通知
    @Around("aopPointcut()")
    public void around(ProceedingJoinPoint pjp) throws Throwable{
        System.out.println("环绕通知:环绕前");
        pjp.proceed();//执行方法
        System.out.println("环绕通知:环绕后");
    }
}
  • 测试类
public class MainTest {
    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("spring/applicationContext.xml");
        TestAop testAop = ac.getBean(TestAop.class);
        testAop.process();
    }
}

输出结果:

环绕通知:环绕前
前置通知
testAop.process()
环绕通知:环绕后
后置通知
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值