Spring核心之Aop

本文深入探讨了Spring AOP的核心概念,包括Target、Proxy、Aspect、Joinpoint、Pointcut、Advice和Weaving。通过实例解析了如何找到并增强目标对象的方法,以及各种通知(前置、后置、异常、环绕)的处理。此外,还介绍了如何通过注解和XML配置文件实现AOP,包括切入点表达式的使用和通知类型的实践应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

SpringAOP

1 Spring AOP相关概念

SpringAOP实现底层就是对上面的动态代理的代码进行了封装,封装后我们只需要对需要关注的部分进行代码编写,并通过配置的方式完成指定目标的方法增强

2.AOP的相关术语

Target(目标对象)

要被增强的对象,一般是业务逻辑类的对象。

Proxy(代理) 

一个类被 AOP 织入增强后,就产生一个结果代理类。 

 Aspect(切面)

表示增强的功能,就是一些代码完成的某个功能,非业务功能。是切入点和通知的结合。

Joinpoint(连接点) 

所谓连接点是指那些被拦截到的点。在Spring,这些点指的是方法(一般是类中的业务方法)。因为Spring只支持方法类型的连接点。

Pointcut(切入点) 

切入点指声明的一个或多个连接点的集合。通过切入点指定一组方法。
被标记为 final 的方法是不能作为连接点与切入点的。因为最终的是不能被修改的,不能被增强的

Advice(通知/增强) 

所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知。通知定义了增强代码切入到目标代码的时 间点,是目标方法执行之前执行,还是之后执行等。通知类型不同,切入时间不同。
通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。 切入点定义切入的位置,通知定义切入的时间。

Weaving(织入).

是指把增强应用到目标对象来创建新的代理对象的过程。 spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装载期织入。
切面的三个关键因素:
1、切面的功能--切面能干啥
2、切面的执行位置--使用Pointcut表示切面执行的位置
3、切面的执行时间--使用Advice表示时间,在目标方法之前还是之后执行。

具体作用


Spring Aop:Spring中Aop底层基于动态代理实现的,目标对象如果有接口,自动使用jdk动态代理,如果目标对象没有接口,自动使用cglib

问题
1.什么是目标对象?

2.怎么找到目标对象需要增强(修改)的方法?

3.如何对要增强(修改)的方法进行处理?

aop几个概念?(重点)

1.什么是目标对象?

1)Target(目标对象):要被增强(修改)的方法的对象。企业中,一般以业务类的对象作为目标对象。

2.怎么找到目标对象需要增强(修改)的方法?

Joinpoint(连接点) 和Pointcut(切入点)

Joinpoint(连接点):指代需要增强(修改)的目标对象中的特定方法。

Pointcut(切入点):切入点就是表达式:用来找到对应的要增强(修改)的目标对象中的特定方法。

     *  :0或者多个字符
    ..  :在方法的形参列表中,表示参数任意,在包名后面,表示当前包及其子包
    Pointcut表达式格式:
    execution(访问权限 返回值类型 包名.类名.方法名(参数列表)异常类型)
    eg:
    execution(* com.lr.spring.lr.*.*(..));
    表示aop下面所有的类,所有的方法。
    (第一个*表示返回任意类型第二个*表示任意类,第二个*表示任意方法)
    execution(* com.lr.spring..*.*(..));
    表示spring包以及子包中的任意的类中的任意方法

3.如何对要增强(修改)的方法进行处理?

Advice(通知/增强)和Aspect(切面类)
Advice(通知/增强):指定处理方法的位置(Pointcut),具体处理方法
Aspect(切面类):Advice所在的类就是切面类.

代码加入注解

1.准备工作

1.在pom.xml中引入外部文件

<dependency>
//引入的文件
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>5.3.4</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>5.3.4</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
      <version>5.1.15.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.9.6</version>
    </dependency>

//原本的文件
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

2.配置文件spring.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"
       xmlns:context="http://www.springframework.org/schema/context"
       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
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 扫描文件   -->
<context:component-scan base-package="com.lr.spring.service com.lr.spring.aop"></context:component-scan>
    <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
</beans>

2.service

1.UserService接口

package com.lr.spring.service;

public interface UserService {
    void insertUser(String name);
    void updateUser(String name);
    void findUserById(int id);
    void deleteUserBuId(int id);
}

2.UserServiceImpl实现类(目标对象)

package com.lr.spring.service;
//目标对象
@Controller
public class UserServiceImpl implements UserService {
    @Override
    public void insertUser(String name) {
        System.out.println("插入操作");
    }

    @Override
    public void updateUser(String name) {
        System.out.println("修改操作");
    }

    @Override
    public void findUserById(int id) {
        System.out.println("查询操作");

    }

    @Override
    public void deleteUserBuId(int id) {
        System.out.println("删除操作");

    }
}

3.Aop包

AspectJ类 中常用的通知有5种类型:
1. 前置通知
2. 后置通知
3. 环绕通知
4. 异常通知
5. 最终通知

 

 1.前置通知:Aspect代码

package com.lr.spring.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

//切面类(advice,pointcut)
@Component
@Aspect
public class MyAspect {
    @Before("execution(* com.lr.spring.service.*.*(..))")
    public void before(JoinPoint jp){//jp指代要处理的目标对象的方法
        System.out.println("前置通知:在目标对象方法执行之前执行:");
        System.out.println("拦截到要处理的方法的方法名字:");
        String name = jp.getSignature().getName();//要处理的方法名字
        System.out.println("方法名:"+name);
        System.out.println("拦截要处理的方法的参数:");
        Object[] args=jp.getArgs();
        for (Object arg : args) {
            System.out.println("参数:"+arg);
        }

    }
}

2.测试程序

 @Test
    public void test01() {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
        UserService userService = (UserServiceImpl) context.getBean("userServiceImpl");
        userService.insertUser("龙仁");
        System.out.println("...................");
        userService.findUserById(100);
        System.out.println("...................");
        userService.updateUser("龙傲天");
        System.out.println("...................");
        userService.deleteUserBuId(100);

    }

 3.后置通知:修改原码

修改源代码,给查询方法一个返回值

package com.lr.spring.service;

public interface UserService {
    void insertUser(String name);
    void updateUser(String name);
    String findUserById(int id);
    void deleteUserBuId(int id);
}






package com.lr.spring.service;

import org.springframework.stereotype.Controller;

//目标对象
@Controller
public class UserServiceImpl implements UserService {
    @Override
    public void insertUser(String name) {
        
        System.out.println("插入操作");
    }

    @Override
    public void updateUser(String name) {
        
        System.out.println("修改操作");
    }

    @Override
    public String findUserById(int id) {
        System.out.println("查询操作");
        return "张三";
    }

    @Override
    public void deleteUserBuId(int id) {
        System.out.println("删除操作");

    }
}

4.后置通知:Aspect代码

 //后置通知:目标对象方法执行介绍之后,执行后置通知(可以获得当前方法的返回值)
    @AfterReturning(value = "execution(* com.lr.spring.service.*.*(..))",returning = "result")
    public void AfterReturning(Object result){
        System.out.println("后置通知:获得目标对象方法的返回值");
        System.out.println(result);
    }

5.测试程序

@Test
    public void test02() {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
        UserService userService = (UserServiceImpl) context.getBean("userServiceImpl");
        userService.findUserById(100);

    }

 

 6. 异常通知:Aspect代码

//异常通知:只有当前对象方法出现异常,反之没有异常,不执行该通知
    @AfterThrowing(value = "execution(* com.lr.spring.service.*.*(..))",throwing ="ex")
    public void exception(JoinPoint joinPoint,Throwable ex){
        System.out.println(joinPoint.getSignature()+"方法抛出异常");
        System.out.println(ex.getMessage());
    }

7:修改实现类代码:加入空指针异常

@Override
    public String findUserById(int id) {
        System.out.println("查询操作");
        String str =null;
        str.indexOf(101);
        return "张三";
    }

8. 测试程序

    @Test
    public void test02() {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
        UserService userService = (UserServiceImpl) context.getBean("userServiceImpl");
        userService.findUserById(101);
    }

 

9.环绕通知 Aspect代码

    //环绕通知:在目标对象的方法执行前后执行(调用目标对象的方法执行)
    @Around("execution(* com.lr.spring.service.*.*(..))")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
        System.out.println("环绕通知---------目标对象方法执行之前");
        //执行目标对象的方法
        Object result = proceedingJoinPoint.proceed();
        System.out.println("环绕通知---------目标对象方法执行指挥");
        return result;
    }

 

10.最终通知

 //最终通知
    @After(value = "execution(* com.lr.spring.service.*.*(..))")
    public void myAfter(){
        System.out.println("最终被执行");
    }

 

 执行顺序环绕——》前置——》后置——》最终——》环绕

 

注解方式实现AOP

开发阶段:关注核心业务和AOP代码
运行阶段:spring框架会在运行的时候将核心业务和AOP代码通过动态代理的方式编织在一起
代理方式的选择:是否实现了接口:有接口就选择JDK动态代理;没有就选择CGLIB动态代理。

 

 1.注解方法

package com.lr.spring.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.w3c.dom.ls.LSOutput;

//切面类(advice,pointcut)
@Component
@Aspect
public class MyAspect {
    // 标记某一个注解
    @Pointcut("execution(* com.lr.spring.service.*.*(..))")
    private void pointcut(){ }
    //前置通知:在目标对象方法执行之前执行,拦截到要处理的方法的方法名字
    @Before("pointcut()")
    public void before(JoinPoint jp){//jp指代要处理的目标对象的方法
        System.out.println("前置通知:在目标对象方法执行之前执行:");
        System.out.println("拦截到要处理的方法的方法名字:");
        String name = jp.getSignature().getName();//要处理的方法名字
        System.out.println("方法名:"+name);
        System.out.println("拦截要处理的方法的参数:");
        Object[] args=jp.getArgs();
        for (Object arg : args) {
            System.out.println("参数:"+arg);
        }
    }
    //后置通知:目标对象方法执行介绍之后,执行后置通知(可以获得当前方法的返回值)
    @AfterReturning(value = "pointcut()",returning = "result")
    public void AfterReturning(Object result){
        System.out.println("后置通知:获得目标对象方法的返回值");
        System.out.println(result);
    }
    //异常通知:只有当前对象方法出现异常,反之没有异常,不执行该通知
    @AfterThrowing(value = "pointcut()",throwing ="ex")
    public void exception(JoinPoint joinPoint,Throwable ex){
        System.out.println(joinPoint.getSignature()+"方法抛出异常");
        System.out.println(ex.getMessage());
    }
    //环绕通知:在目标对象的方法执行前后执行(调用目标对象的方法执行)
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
        System.out.println("环绕通知---------目标对象方法执行之前");
        //执行目标对象的方法
        Object result = proceedingJoinPoint.proceed();
        System.out.println("环绕通知---------目标对象方法执行指挥");
        return result;
    }

    //最终通知
    @After(value = "execution(* com.lr.spring.service.*.*(..))")
    public void myAfter(){
        System.out.println("最终被执行");
    }
}

2.XML方式实现AOP  

package com.lr.spring.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

//切面类(advice,pointcut)
@Component
@Aspect
public class MyAspect2 {//只要通知了

    public void before(JoinPoint jp){//jp指代要处理的目标对象的方法
        System.out.println("前置通知:在目标对象方法执行之前执行:");
    }

    public void AfterReturning(Object result){
        System.out.println("后置通知:获得目标对象方法的返回值");
        System.out.println(result);
    }

    public void exception(JoinPoint joinPoint,Throwable ex){
        System.out.println(joinPoint.getSignature()+"方法抛出异常");
        System.out.println(ex.getMessage());
    }

    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
        System.out.println("环绕通知---------目标对象方法执行之前");
        //执行目标对象的方法
        Object result = proceedingJoinPoint.proceed();
        System.out.println("环绕通知---------目标对象方法执行指挥");
        return result;
    }

    public void myAfter(){
        System.out.println("最终被执行");
    }
}

 配置文件

<?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"
       xmlns:context="http://www.springframework.org/schema/context"
       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
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 扫描文件   -->
<context:component-scan base-package="com.lr.spring.service com.lr.spring.aop"></context:component-scan>
    <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
    <aop:config>
        <!--  切入点  -->
        <aop:pointcut id="t1" expression="execution(* com.lr.spring.service.*.insert*(..))"/>
        <aop:pointcut id="t2" expression="execution(* com.lr.spring.service.*.update*(..))"/>
        <aop:pointcut id="t3" expression="execution(* com.lr.spring.service.*.delete*(..))"/>
        <aop:pointcut id="t4" expression="execution(* com.lr.spring.service.*.find*(..))"/>
        <!--  切入点跟通知结合  -->
        <aop:aspect ref="myAspect2">
            <aop:before method="before" pointcut-ref="t1"></aop:before>
            <aop:before method="before" pointcut-ref="t4"></aop:before>
            <aop:after-returning method="AfterReturning" returning="result" pointcut-ref="t4"></aop:after-returning>
        </aop:aspect>
    </aop:config>
</beans>

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值