Spring---AOP(面向切面编程)

AOP(Aspect-Oriented Programming: 面向切面编程):将那些与业务无关,却为业务模块所共调用的逻辑(例如事务处理、日志管理、权限控制等)封装抽取成一个可重用的模块,这个模块被命名为“切面”(Aspect),便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可拓展性和可维护性;

一、AOP的核心概念

  1. 切面(Aspect)

    • 切面是一个模块,包含了横切关注点的实现。它可以定义在应用程序的多个地方应用的行为。
  2. 连接点(Join Point)

    • 连接点是程序执行的一个点,例如方法调用、对象实例化等。AOP允许在这些连接点上插入切面。
  3. 通知(Advice)

    • 通知是切面在特定连接点执行的动作。根据执行时机的不同,通知可以分为:
      • 前置通知(Before):在连接点之前执行。
      • 后置通知(After):在连接点之后执行。
      • 环绕通知(Around):在连接点前后执行,可以控制连接点的执行。
      • 异常通知(After Throwing):在连接点抛出异常时执行。
      • 最终通知(After Returning):在连接点正常返回时执行。
  4. 切入点(Pointcut)

    • 切入点定义了哪些连接点会被通知所影响。它通常通过表达式来指定。
  5. 织入(Weaving)

    • 织入是将切面与其他应用程序类型或对象结合的过程。织入可以在编译时、类加载时或运行时进行。
  6. AOP目标对象(Target):

    • 就是挖掉功能的方法对应的类生的对象,这种对象是无法直接完成最终工作的

  7. AOC切面:切点+通知

二、AOP的优点

  • 模块化:将横切关注点与业务逻辑分离,提高代码的可读性和可维护性。
  • 重用性:切面可以在多个地方重用,减少代码重复。
  • 灵活性:可以动态地改变应用程序的行为,而不需要修改业务逻辑代码。

三、AOP的实现

在Java中,AOP通常通过框架实现,如Spring AOP和AspectJ。

1.SpringAOP+AspectJ实现步骤

        1.添加依赖,aop与aspectj表达式的依赖

        2.创建spring的主配置文件,bean内的命名空间要添加aop的

        3.创建业务代码并编写日志记录代码(事务管理代码)

        4.将业务层与日志记录层注入spring容器 5.<aop:config>--aop配置 aop:aspect--aop切              面 aop:before--通知内容与通知类型

 Xml

<!--注入业务层-->
业务层 Bean 注入:这行代码将业务层的实现类 AccountServiceImp 注入到 Spring 容器中
    <bean id="accountServiceImp" class="com.xn.service.AccountServiceImp"></bean>


<!--注入日志记录层(通知)-->
日志记录层 Bean 注入:这行代码将日志记录器 Logger 注入到 Spring 容器中。
    <bean id="logger" class="com.xn.util.Logger"></bean>
<!--配置AOP-->
AOP 配置:这部分用于配置 AOP
    <aop:config>



<!--配置切面-->
切面配置:里定义了一个切面,引用了日志记录器 logger。
        <aop:aspect id="aopAspect" ref="logger">

            <!--切点-->切点 dian 定义了所有在 com.xn.service 包下的任意方法的执行。
            <aop:pointcut id="dian" expression="execution(* com.xn.service.*.*(..))"/>


            <!--通知-->这里使用了环绕通知 arroundMethod,它将在切点方法执行前后执行。
<!--        <aop:before method="beforeMethod" pointcut-ref="dian"></aop:before>-->
<!--        <aop:after-returning method="returnMethod" pointcut-ref="dian"></aop:after-returning>-->
<!--        <aop:after-throwing method="throwMethod" pointcut-ref="dian"></aop:after-throwing>-->
<!--        <aop:after method="afterMethod" pointcut-ref="dian"></aop:after>-->
            <aop:around method="arroundMethod" pointcut-ref="dian"></aop:around>
        </aop:aspect>
    </aop:config>

public class Logger {
    public void beforeMethod() {
        System.out.println("日志类logger中调用beforeMethod方法进行日志记录");
    }

    public void returnMethod() {
        System.out.println("日志类logger中调用returnMethod方法进行日志记录");
    }

    public void throwMethod() {
        System.out.println("日志类logger中调用thowMethod方法进行日志记录");
    }

    public void afterMethod() {
        System.out.println("日志类logger中调用afterMethod方法进行日志记录");
    }


    public Object arroundMethod(ProceedingJoinPoint pjp) {

        Object obj = null;
        try {
            System.out.println("环绕通知===前置通知");
            //切点方法
            Object[] ars = pjp.getArgs();
            obj = pjp.proceed(ars);
            System.out.println("环绕通知===返回通知");
        } catch (Throwable e) {
            e.printStackTrace();
            System.out.println("环绕通知===异常通知");
        } finally {
            System.out.println("环绕通知===后置通知");
            return obj;
        }


    }
}

注解

<!--扫描注解-->
    <context:component-scan base-package="com.xn"></context:component-scan>
<!--开启spring注解的aop动态代理-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

 

@Component
@Aspect
public class Logger {
    @Pointcut("execution(* com.xn.service.AccountServiceImp.update())")
    public void dian(){

    }
    @Before("dian()")
    public void beforeLogger() {
        System.out.println("前置通知===>日志类logger中调用beforeMethod方法进行日志记录");
    }
    @AfterReturning("dian()")
    public void returnLogger()
    {
        System.out.println("返回通知===>日志类logger中调用returnMethod方法进行日志记录");
    }
    @AfterThrowing("dian()")
    public void throwLogger() {

        System.out.println("异常通知===>日志类logger中调用throwMethod方法进行日志记录");
    }
    @After("dian()")
    public void afterLogger()
    {
        System.out.println("后置通知===>日志类logger中调用afterMethod方法进行日志记录");
    }
    //环绕通知
    @Around("dian()")
    public Object arroundLogger(ProceedingJoinPoint pjp) {

        Object returnobj=null;//保存主业务方法的返回值
        try {
            //前置通知
            System.out.println("环绕通知===前置通知");
            //切点方法
            Object[] objs = pjp.getArgs();//主业务方法的参数
            returnobj = pjp.proceed(objs);//调用主业务方法

            System.out.println("环绕通知===>后置通知");
        } catch (Throwable e) {
            //异常通知
            e.printStackTrace();
            System.out.println("环绕通知===异常通知");
        } finally {
            //最终通知
            System.out.println("环绕通知===最终通知");
            return returnobj;
        }


    }
}

 

 

2.切点表达式配置语法:

execution(修饰符 返回值 包名称.类名称.方法名称(参数列表))
    eg: execution(public void com.apesource.service.ServiceImp.findAll())
    1.修饰符可以省略代表任意
        execution(返回值 包名称.类名称.方法名称(参数列表))
    2.返回值可以使用“*”代表任意
        execution(* 包名称.类名称.方法名称(参数列表))
    3.包名可以使用“*”代表任意名称
        execution(* *.*.*.类名称.方法名称(参数列表))
    4.包名可以使用“..”代表任意个数
        execution(* *...类名称.方法名称(参数列表))
    5.类名与方法名可以使用“*”代表任意
        execution(* *...*.*(参数列表))
    6.参数列表可以使用".."代表任意个数任意类型
        execution(* *...*.*(..))
        如果有参数
            int======>int
            String===>java.lang.String

 

  • 12
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值