首页 > 编程语言 > 详细

Spring - AOP

时间:2019-12-24 01:18:58      阅读:111      评论:0      收藏:0      [点我收藏+]

Spring AOP 简介

如果说 IoC 是 Spring 的核心,那么面向切面编程就是 Spring 最为重要的功能之一了,在数据库事务中切面编程被广泛使用。

AOP 即 Aspect Oriented Program 面向切面编程

首先,在面向切面编程的思想里面,把功能分为核心业务功能,和周边功能。

● 所谓的核心业务,比如登陆,增加数据,删除数据都叫核心业务
● 所谓的周边功能,比如性能统计,日志,事务管理等等

周边功能在 Spring 的面向切面编程AOP思想里,即被定义为切面。

在面向切面编程AOP的思想里面,核心业务功能和切面功能分别独立进行开发,然后把切面功能和核心业务功能 "编织" 在一起,这就叫AOP。

AOP 的目的

AOP能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性。

aop概念介绍(面向切面的编程)

AOP行话:

● 连接点(JointPoint):目标对象中,所有可以增强的方法
● 切入点(Pointcut):目标对象中,已经增强的方法
● 通知(Advice):增强的代码
● 目标对象(Target Object):被代理的对象
● 织入(Weaving):将通知应用到切入点的过程
● 代理(AOP Proxy):将通知织入到目标对象之后,形成的对象
● 切面(Aspect):切入点+通知

技术分享图片

AOP所需的包:

aop核心包:spring-aspects 和 spring-aop
aop联盟包:springsource.org.aopalliance
aop织入包:springsource.org.aspectj.weaver

AOP配置使用

XML配置AOP:

① 准备目标对象(被代理对象,被通知的对象,被增强的类对象)

技术分享图片
package com.sikiedu.service;

public interface UserService {

    //
    void save();

    //
    void delete();

    //
    void update();

    //
    void find();

}
UserService.java
技术分享图片
package com.sikiedu.service;

public class UserServiceImpl implements UserService {

    @Override
    public void save() {
        System.out.println("save...run");
    }

    @Override
    public void delete() {
        System.out.println("delete...run");
    }

    @Override
    public void update() {
        System.out.println("update...run");
    }

    @Override
    public void find() {
        System.out.println("find...run");
    }

}
UserServiceImpl.java

② 准备通知(被增强方法的代码,想要实现功能的方法代码)

技术分享图片
package com.sikiedu.aop;

import org.aspectj.lang.ProceedingJoinPoint;

/**
 * 自定义通知类
 * 
 * @author ASUS
 *
 */
public class MyAdvice {

    // before 前置通知 - 在目标方法前调用
    public void befor() {
        System.out.println("前置通知");
    }

    // afterReturning 成功通知(后置通知)- 在目标方法执行后,并且执行成功,如果方法出现异常则不调用
    public void afterReturning() {
        System.out.println("成功通知");
    }

    // around 环绕通知 - 需要我们手动调用目标方法,并且可以设置通知
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("环绕通知 - 前");
        Object proceed = pjp.proceed();
        System.out.println("环绕通知 - 后");
        return proceed;
    }

    // afterThrowing 异常通知(后置通知)- 在目标方法执行出现异常的时候才会调用
    public void afterThrowing() {
        System.out.println("异常通知");
    }

    // after 最终通知(后置通知)- 在目标方法后调用,无论是否出现异常都会执行,finally
    public void after() {
        System.out.println("最终通知");
    }
}
MyAdvice.java

③ 配置 applicationContext.xml

1 - 导入aop(约束)命名空间
2 - 配置目标对象
3 - 配置通知对象
4 - 配置将通知织入目标对象
技术分享图片
<?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-4.3.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">

    <!-- 配置目标对象 -->
    <bean name="userService" class="com.sikiedu.service.UserServiceImpl">
    </bean>

    <!-- 配置通知对象 -->
    <bean name="myAdvice" class="com.sikiedu.aop.MyAdvice"></bean>
    
    <!-- 织入 - 将通知织入到目标对象中 -->
    <aop:config>
        <!-- 切入点 
            1、expression:切入点表达式,可以配置要增强的方法
                ① public void com.sikiedu.service.UserServiceImpl.save()
                  - 增强单一方法save,必须为无参、public类型、返回值为void
                ② * com.sikiedu.service.*.*(..)
                  - 增强service包下所有类的所有方法
            2、id:唯一标识        -->
        <aop:pointcut expression="execution(* com.sikiedu.service.*.*(..))" id="servicePc" />
        
        <!-- 切面:通知+切入点-->
        <aop:aspect ref="myAdvice">
        <!-- 通知类型 -->
            <!-- 前置通知 -->
            <aop:before method="befor" pointcut-ref="servicePc"/>
            <!-- 成功通知 -->
            <aop:after-returning method="afterReturning" pointcut-ref="servicePc"/>
            <!-- 环绕通知 -->
            <aop:around method="around" pointcut-ref="servicePc"/>
            <!-- 异常通知 -->
            <aop:after-throwing method="afterThrowing" pointcut-ref="servicePc"/>
            <!-- 最终通知 -->
            <aop:after method="after" pointcut-ref="servicePc"/>
        </aop:aspect>
        
    </aop:config>

</beans>
applicationContext.xml

④ 测试

技术分享图片
package com.sikiedu.test;

import javax.annotation.Resource;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.sikiedu.service.UserService;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AopTest {

    @Resource(name = "userService")
    UserService us;

    @Test
    public void test1() {

        us.delete();

    }
}
AopTest

运行结果:

 - 非异常:技术分享图片,异常:技术分享图片

注解配置AOP

① 配置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-4.3.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">

    <!-- 配置目标对象 -->
    <bean name="userService" class="com.sikiedu.service.UserServiceImpl">
    </bean>

    <!-- 配置通知对象 -->
    <bean name="myAdvice" class="com.sikiedu.aop.MyAdvice"></bean>

    <!-- 开启使用注解完成织入 -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

</beans>
applicationContext.xml

② 书写通知类

@Aspect:指定切面类
@JoinPoint:作为函数的参数传入切面方法,可以得到目标方法的相关信息
声明一个切入点:@Pointcut(
"execution(返回值 全类名.方法名(参数))")
技术分享图片
package com.sikiedu.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

/**
 * 自定义通知类
 * 
 * @author ASUS
 *
 */
@Aspect // 通知类 - 使用Aspect表示是一个通知类
public class MyAdvice {

    // 声明一个切入点 - servicePc为切入点名称
    @Pointcut("execution(* com.sikiedu.service.*.*(..))")
    public void servicePc() {
    };

    @Before("MyAdvice.servicePc()") // before 前置通知 - 在目标方法前调用
    public void befor() {
        System.out.println("前置通知");
    }

    @AfterReturning("MyAdvice.servicePc()") // afterReturning 成功通知(后置通知)- 在目标方法执行后,并且执行成功,如果方法出现异常则不调用
    public void afterReturning() {
        System.out.println("成功通知");
    }

    @Around("MyAdvice.servicePc()") // around 环绕通知 - 需要我们手动调用目标方法,并且可以设置通知。
    public void around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("环绕通知 - 前");
        pjp.proceed();
        System.out.println("环绕通知 - 后");
    }

    @AfterThrowing("MyAdvice.servicePc()") // afterThrowing 异常通知(后置通知)- 在目标方法执行出现异常的时候才会调用。
    public void afterThrowing() {
        System.out.println("异常通知");
    }

    @After("MyAdvice.servicePc()") // after 最终通知(后置通知)- 在目标方法后调用,无论是否出现异常都会执行。
    public void after() {
        System.out.println("最终通知");
    }
}
Test

● 注意环绕通知:需要我们手动调用目标方法

 proceed()表示对拦截的方法进行放行,若注释proceed()则不会执行被AOP匹配的方法。

public void around(ProceedingJoinPoint pjp) throws Throwable {
    System.out.println("环绕通知 - 前")
    pjp.proceed();
    System.out.println("环绕通知 - 后")
}

③ 测试:

技术分享图片
package com.sikiedu.test;

import javax.annotation.Resource;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.sikiedu.service.UserService;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext2.xml")
public class AopTest {

    @Resource(name = "userService")
    UserService us;

    @Test
    public void test1() {

        us.delete();

    }
}
Test

运行结果:

 - 非异常:技术分享图片,异常:技术分享图片

通知类型介绍

① 前置通知(前置通知):目标方法运行之前调用
  @Before:在目标方法被调用之前做增强处理,@Before只需要指定切入点表达式即可

② 成功通知(后置通知):目标方法运行之后调用(如果出现异常不调用)  
@AfterReturning:在目标方法正常完成后做增强,@AfterReturning除了指定切入点表达式后,还可以指定一个返回值形参名returning,代表目标方法的返回值

④ 异常拦截通知(后置通知):如果出现异常,就会调用  
@AfterThrowing:主要用来处理程序中未处理的异常,@AfterThrowing除了指定切入点表达式后,还可以指定一个throwing的返回值形参名,可以通过该形参名来访问目标方法中所抛出的异常对象

④ 最终通知(后置通知):目标方法运行之后调用(无论是否出现异常都会调用)  
@After:在目标方法完成之后做增强,无论目标方法是否成功完成。@After可以指定一个切入点表达式

③ 环绕通知:目标方法之前和之后都调用 - 需要我们手动调用目标方法,并且可以设置通知  
@Around:环绕通知,在目标方法完成前后做增强处理,环绕通知是最重要的通知类型,像事务,日志等都是环绕通知,注意编程中核心是一个ProceedingJoinPoint

Spring - AOP

原文:https://www.cnblogs.com/Dm920/p/12076090.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!