谈谈你对Spring AOP理解【面试】

前言

面试中经常会被问到Spring AOP的原理,相信大家都是条件反射的想到JDK动态代理和CGLib动态代理,本文将介绍这两个代理的区别及实现一个Spring AOP实例。

1、为什么会有AOP

AOP的全称是Aspect Oriented Programming,翻译成中文是面向切面编程。当我们在开发日志、权限验证、事务等功能时,如果不使用AOP,只能在每个对象里引用公共行为,这样做不便于维护,而且有大量重复代码,AOP的出现就是解决这些问题。

2、Spring AOP的原理

Spring Aop中使用的代理有两种方式,一种是Jdk的动态代理,另一种是基于CGLIB实现的代理,当我们的bean对象实现了某一接口后,Spring默认将采用Jdk动态代理,当我们的bean对象没有实现接口时,默认将采用CGLIB代理,Spring也支持我们在bean对象实现了接口时也强制的使用CGLIB代理。Spring的Bean容器在初始化bean对象的时候就会判断对应的bean是否需要进行切面编程,即是否需要对其进行代理,如果需要,则初始化的时候就会把它初始化为一个代理对象。

3、Jdk动态代理

  • JDK动态代理基于拦截器和反射来实现。
  • JDK动态代理是不需要第三方库支持的,只需要JDK环境就可以进行代理。

使用的条件:

  1. 必须实现InvocationHandler接口;
  2. 使用Proxy.newProxyInstance产生代理对象;
  3. 被代理的对象必须要实现接口;

4、CGLib动态代理

  • CGLib包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。
  • CGLib是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,并覆盖其中方法实现增强。

使用条件

  1. CGLib是针对类实现代理,采用的是继承,类或方法不要声明成final;
  2. 必须实现MethodInterceptor接口;
  3. 需要使用CGLib包;

5、spring aop 实践

  1. 编写测试类
//user接口
public interface UserDao {
    //登录
    void login();
}
//user实现类,测试jdk代理
public class MyUserDao implements UserDao {
    @Override
    public void login() {
        System.out.println("接口用户正常登录");
    }
}
//无接口实现,测试cglib
public class CglibDao {

    public void cglibLogin(){
        System.out.println("cglib用户正常登录");
    }
}
//自定义切面类
public class MyAspect {

    public void begin() {
        System.out.println("[前置通知]  开启事务..");
    }
    public void commit() {
        System.out.println("[后置通知] 提交事务..");
    }
    public void after() {
        System.out.println("[返回后通知]");
    }
    public void afterThrowing() {
        System.out.println("[异常通知]");
    }
    public void arroud(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("[环绕前:]");
        pjp.proceed(); // 执行目标方法
        System.out.println("[环绕后:]");
    }
}
//测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value = {"classpath:applicationContext.xml"})
public class AopTest {
    @Resource
    UserDao userDao;
    @Resource
    CglibDao cglibDao;
    @Test
    public void testUser(){
        userDao.login();
    }
    @Test
    public void testCglib(){
        cglibDao.cglibLogin();
    }
}

配置文件applicationContext.xml

<?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:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- dao实例加入容器 -->
    <bean id="myUserDao" class="aop.MyUserDao"></bean>

    <!-- dao实例加入容器 -->
    <bean id="cglibDao" class="aop.CglibDao"></bean>

    <!-- 实例化切面类 -->
    <bean id="myAspect" class="aop.MyAspect"></bean>

    <!-- Aop相关配置 -->
    <aop:config>
        <!-- 切入点表达式定义 -->
        <aop:pointcut expression="execution(* aop.*Dao.*(..))" id="transactionPointcut"/>
        <!-- 切面配置 -->
        <aop:aspect ref="myAspect">
            <!-- 【环绕通知】 -->
            <aop:around method="arroud" pointcut-ref="transactionPointcut"/>
            <!-- 【前置通知】 在目标方法之前执行 -->
            <aop:before method="begin" pointcut-ref="transactionPointcut" />
            <!-- 【后置通知】 -->
            <aop:after method="commit" pointcut-ref="transactionPointcut"/>
            <!-- 【返回后通知】 -->
            <aop:after-returning method="after" pointcut-ref="transactionPointcut"/>
            <!-- 异常通知 -->
            <aop:after-throwing method="afterThrowing" pointcut-ref="transactionPointcut"/>
        </aop:aspect>
    </aop:config>
</beans>

输出结果:
在这里插入图片描述
在这里插入图片描述

结束语

本篇详细的介绍了Jdk动态代理和Cglib代理,以及手写了一个spring aop的实例,熟悉spring aop的配置。

原创文章 55 获赞 76 访问量 17万+

猜你喜欢

转载自blog.csdn.net/cool_summer_moon/article/details/105965571