Spring-Aop

常见面试问题

  • AOP是什么?解决了什么问题?应用场景?
  • AOP编程中核心对象以及应用关系(代理对象,切面对象,通知,切入点)
  • AOP思想在spring中实现原理,代理方式有哪些,有什么区别
  • AOP应用掌握了哪些切入点表达式
  • AOP应用各个通知类型以及执行时间
  • AOP事务控制注解@Transactional都有什么属性?
  • Aop中切面可以有多个吗?可以作用于同一个切入点方法吗?

AOP概念

AOP(Aspect Oriented Programming,面向切面编程),是OOP(Object Oriented Programming,面向对象编程)的补充和完善。OOP定义纵向的关系,AOP定义横向的关系。AOP将那些与业务无关,却被业务模块所共同调用的逻辑(如日志记录,事务处理,数据权限等)封装起来,降低业务逻辑代码的耦合度,提高程序的可重用性,提高开发效率,通俗的讲就是:在不修改原始代码的基础上在主干功能上添加新的功能。

AOP核心体系

在这里插入图片描述

Aspect(切面)

通常是一个类,里面可以定义切入点和通知,使用 @Aspect 注解。把通知应用到切入点的过程称作是切面,切面是过程

PointCut(切入点)

对连接点进行拦截的定义,在程序中主要体现在书写切入点表达式。
切入点表达式有两种常见形式:

  1. @execution(……):根据方法的签名来匹配

    execution(访问修饰符?  返回值  包名.类名.?方法名(方法参数) throws 异常?)
    
  • 其中带?的表示可以省略的部分
  • 访问修饰符:可省略(比如: public、protected)
  • 包名.类名: 可省略
  • throws 异常:可省略(注意是方法上声明抛出的异常,不是实际抛出的异常)
  1. @annotation(……) :根据注解匹配

    @Pointcut(value = "@annotation(注解类名)")
    

Advice(通知)

实现切面的逻辑部分称作是通知,共分为以下几种类型:
在这里插入图片描述

Before Advice:连接点前面执行,不能终止后续流程,除非抛异常。
Around Advice:围绕连接点前后执行,也能捕获异常处理。
After Advice:连接点退出时执行,无论是正常退出还是异常退出。
After Returning Advice:连接点正常返回时执行,有异常不执行。
After Throwing Advice:连接点方法抛出异常时执行。

Target(目标对象)

代理的目标对象

JoinPoint(连接点)

因为Spring只支持方法类型的连接点,所以在Spring中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器。

Weaving(织入)

把通知的动作融入到对象中,生成代理对象的过程就叫做织入。

底层原理

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

Spring AOP 采用了两种混合的实现方式:

JDK动态代理( 有接口)

Spring AOP的首选方法。 每当目标对象实现一个接口时,就会使用JDK动态代理。目标对象必须实现接口,创建动态代理实现类对象,增强类方法.

CGLIB动态代理(没有接口)

如果目标对象没有实现接口,则可以使用CGLIB代理。

AOP三种使用方式

AOP编程其实是很简单的事情,纵观AOP编程,程序员只需要参与三个部分:

  1. 定义普通业务组件
  2. 定义切入点,一个切入点可能横切多个业务组件
  3. 定义增强处理,增强处理就是在AOP框架为普通业务组件织入的处理动作
    所以进行AOP编程的关键就是定义切入点和定义增强处理,一旦定义了合适的切入点和增强处理,AOP框架将自动生成AOP代理,即:代理对象的方法=增强处理+被代理对象的方法。

使用Spring自带的AOP

创建UserService 接口:

public interface UserService {
    void add() ;
}

创建UserServiceImpl 实现:

@Service
public class UserServiceImpl implements UserService {
    @Override
    public void add() {
        System.out.println("这是一个UserServiceImpl的新增方法");
    }
}

创建LogAdvice 增强:

public class LogAdvice implements MethodBeforeAdvice, AfterReturningAdvice, MethodInterceptor {
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        // 前置通知
        System.out.println("前置通知");
    }
    @Override
    public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
        //后置通知
        System.out.println("后置通知");
    }
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        // 环绕通知
        //目标方法之前执行
        Object resultVal =methodInvocation.proceed();// 目标方法
        //目标方法之后执行
        System.out.println("环绕通知");
        return resultVal;
    }
}

配置bean文件:

<!--Aop demo-->
    <!--创建被代理对象-->
    <bean id="userServiceImpl" class="main.java.com.aop.UserServiceImpl"/>
    <!--通知(Advice)-->
    <bean id="logAdvice" class="main.java.com.aop.LogAdvice"/>
    <!--切入点(Pointcut):通过正则表达式描述指定切入点(某些 指定方法)-->
    <bean id="addMethodPointcutBean"   class="org.springframework.aop.support.JdkRegexpMethodPointcut">
        <!--注入正则表达式:描述那些方法为切入点-->
        <property name="pattern" value=".add.*"/>
    </bean>
    <!--    创建自动代理-->
    <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
        <!--Bean名称规则(数组):指定那些bean创建自动代理-->
        <property name="beanNames">
            <list>
                <value>*ServiceImpl</value>
            </list>
        </property>
        <!--通知列表:需要执行那些通知-->
        <property name="interceptorNames">
            <list>
                <value>logAdvice</value>
            </list>
        </property>
    </bean>

主程序调用:

// 获取IOC容器
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("main/resources/applicationContext.xml");
UserService userService= (UserService) ctx.getBean("userServiceImpl");
userService.add();

执行结果:
在这里插入图片描述

使用Aspectj实现切面(普通POJO的实现方式)

创建LogAspectj 类:

public class LogAspectj {
    //前置通知
    public void beforeAdvice(JoinPoint joinPoint){
        System.out.println("========== 【Aspectj前置通知】 ==========");
    }

    //后置通知:方法正常执行后,有返回值,执行该后置通知:如果该方法执行出现异常,则不执行该后置通知
    public void afterReturningAdvice(JoinPoint joinPoint,Object returnVal){
        System.out.println("========== 【Aspectj后置通知】 ==========");
    }
    public void afterAdvice(JoinPoint joinPoint){
        System.out.println("========== 【Aspectj后置通知】 ==========");
    }

    //环绕通知
    public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("##########【环绕通知中的前置通知】##########");
        Object returnVale = joinPoint.proceed();
        System.out.println("##########【环绕通知中的后置通知】##########");
        return returnVale;
    }

    /**
     * 异常通知:方法出现异常时,执行该通知
     */
    public void throwAdvice(JoinPoint joinPoint, Exception ex){
        System.out.println("出现异常:" + ex.getMessage());
    }
}

配置bean文件:

<!--Aop demo-->
    <!--    使用Aspectj实现切面-->
    <!--业务组件bean-->
    <bean id="userServiceImpl" class="main.java.com.aop.UserServiceImpl"/>
    <!--日志Aspect切面-->
    <bean id="logAspectj" class="main.java.com.aop.LogAspectj"/>
    <!--使用Aspectj实现切面,使用Spring AOP进行配置-->
    <aop:config>
        <!--配置切面-->
        <!--注入切面bean-->
        <aop:aspect ref="logAspectj">
            <!--定义Pointcut:通过expression表达式,来查找 特定的方法(pointcut)-->
            <aop:pointcut id="pointcut"
                          expression="execution(* main.java.com.aop.*.add*(..))"/>
            <!--配置"前置通知"-->
            <!--在pointcut切入点(serviceMethodPointcut)查找到 的方法执行"前",
                来执行当前logAspectBean的doBefore-->
            <aop:before method="beforeAdvice" pointcut-ref="pointcut"/>

            <!--配置“后置通知”-->
            <!--returning属性:配置当前方法中用来接收返回值的参数名-->
            <aop:after-returning returning="returnVal"
                                 method="afterReturningAdvice" pointcut-ref="pointcut"/>
            <aop:after method="afterAdvice" pointcut-ref="pointcut"/>

            <!--配置"环绕通知"-->
            <aop:around method="aroundAdvice" pointcut-ref="pointcut"/>

            <!--配置“异常通知”-->
            <!--throwing属性:配置当前方法中用来接收当前异常的参数名-->
            <aop:after-throwing throwing="ex" method="throwAdvice" pointcut-ref="pointcut"/>
        </aop:aspect>
    </aop:config>

主程序调用:

  // 获取IOC容器
    ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("main/resources/applicationContext.xml");
    UserService userService= (UserService) ctx.getBean("userServiceImpl");
    userService.add();

执行结果:
在这里插入图片描述

使用Aspectj实现切面(基于注解的实现方式)

创建LogAnnotationAspectj 类:

//声明当前类为Aspect切面,并交给Spring容器管理
@Component
@Aspect
public class LogAnnotationAspectj {
    private final static String EXPRESSION =
            "execution(* main.java.com.aop.*.add*(..))";

    //前置通知
    @Before(EXPRESSION)
    public void beforeAdvice(JoinPoint joinPoint){
        System.out.println("========== 【Aspectj前置通知】 ==========");
    }


    //后置通知:方法正常执行后,有返回值,执行该后置通知:如果该方法执行出现异常,则不执行该后置通知
    @AfterReturning(value = EXPRESSION,returning = "returnVal")
    public void afterReturningAdvice(JoinPoint joinPoint,Object returnVal){
        System.out.println("========== 【Aspectj后置通知】 ==========");
    }

    //后置通知
    @After(EXPRESSION)
    public void afterAdvice(JoinPoint joinPoint){
        System.out.println("========== 【Aspectj后置通知】 ==========");
    }

    //环绕通知
    @Around(EXPRESSION)
    public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("##########【环绕通知中的前置通知】##########");
        Object returnVale = joinPoint.proceed();
        System.out.println("##########【环绕通知中的后置通知】##########");
        return returnVale;
    }

    // 异常通知:方法出现异常时,执行该通知
    @AfterThrowing(value = EXPRESSION,throwing = "ex")
    public void throwAdvice(JoinPoint joinPoint, Exception ex){
        System.out.println("********** 【Aspectj异常通知】执行开始 **********");
        System.out.println("出现异常:" + ex.getMessage());
        System.out.println("********** 【Aspectj异常通知】执行结束 **********");
    }

}

主程序调用:

  // 获取IOC容器
    ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("main/resources/applicationContext.xml");
    UserService userService= (UserService) ctx.getBean("userServiceImpl");
    userService.add();

执行结果:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值