spring学习日记-day6-AOP

一、学习目标

        面向切面的编程(AOP)通过提供另一种思考程序结构的方式来补充面向对象的编程(OOP)。OOP中模块化的关键单位是类,而AOP中模块化的单位是切面。切面使跨越多种类型和对象的关注点(如事务管理)模块化。

        spring的关键组件之一是AOP框架。虽然Spring IoC容器并不依赖于AOP(意味着如果你不想使用AOP,就不需要使用),但AOP补充了Spring IoC,提供了一个非常有能力的中间件解决方案。  

        AOP(Aspect-Oriented Programming,面向切面编程)是一种编程范式,它旨在提高模块化程度。在AOP中,程序被划分为不同的关注点(或称为切面),这些关注点可以独立地开发、修改和重用。AOP主要用于处理横切关注点(cross-cutting concerns),这些关注点通常跨越多个类或方法,如日志记录、事务管理、安全控制、性能监控等。

二、AOP

        AOP的核心概念

  1. 切面(Aspect):切面是跨多个类和方法的横切关注点的模块化,如事务管理。切面可以定义切入点、通知(增强)等。

  2. 连接点(Joinpoint):连接点是程序执行中的一个点,如方法的执行或异常的处理。在Spring AOP中,连接点通常是方法的执行点。

  3. 切入点(Pointcut):切入点是一组连接点的集合,这些连接点通常与特定的类或方法相关。切入点定义了哪些连接点将被增强(即应用通知)。

  4. 通知(Advice):通知是切面在特定连接点执行的动作。通知有多种类型,包括前置通知(在方法执行前执行)、后置通知(在方法执行后执行)、环绕通知(在方法执行前后执行,并可以决定方法是否继续执行)、异常通知(在方法抛出异常时执行)和最终通知(无论方法是否成功执行,都会执行)。

  5. 目标对象(Target):目标对象是被一个或多个切面增强的对象。

  6. 代理(Proxy):代理是由AOP框架创建的对象,用于实现切面功能的织入。代理对象在目标对象的基础上增加了额外的功能。

  7. 织入(Weaving):织入是将切面应用到目标对象并创建代理对象的过程。织入可以在编译时、类加载时或运行时进行。

三、三种实现方式

1.搭建测试环境

项目结构:

导入依赖

 <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>6.0.2</version>
        </dependency>
        <dependency>
            <groupId>jakarta.annotation</groupId>
            <artifactId>jakarta.annotation-api</artifactId>
            <version>2.1.1</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>compile</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.4</version>
        </dependency>
    </dependencies>

    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

接口类

public interface  blogService {
    public void addBlog();
    public void deletBlog();
    public void  updateBlog();
    public void  query();
}

接口实现类

public class blogServiceImpl implements blogService{
    public void addBlog() {
        System.out.println("增加了一篇博客");
    }

    public void deletBlog() {

    }

    public void updateBlog() {

    }

    public void query() {

    }
}

xml文件中注册bean

<?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">
    <!--注册bean-->
    <bean id="blog" class="com.lzh.service.blogServiceImpl"></bean>
    
</beans>

测试

public class myTest {
    @Test
    public void  test01(){
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        blogService blog = (blogService) context.getBean("blog");
        blog.addBlog();
    }
}

2.springAPI实现

        通过Spring的AOP功能,实现了在不修改blogServiceImpl类代码的情况下,为其所有方法执行前后添加额外的逻辑。这是通过定义切入点来指定哪些方法会被拦截,并通过定义顾问来指定在这些方法执行前后执行哪些逻辑(即BeforeAfter类中的方法)来实现的。

/**  
 * 实现了Spring AOP的AfterReturningAdvice接口的类,用于在目标方法正常执行完成后执行一些逻辑。  
 * 这个类将作为AOP通知(Advice)使用,在Spring配置中需要被注册为一个Bean,并与切点(Pointcut)关联。  
 */  
public class After implements AfterReturningAdvice {  
  
    /**  
     * 当目标方法正常执行完成后,此方法将被调用。  
     *   
     * @param returnValue 目标方法的返回值。如果目标方法没有返回值(即void类型),则此参数为null。  
     * @param method 被调用的目标方法对象,包含了方法的所有信息(如名称、参数类型等)。  
     * @param args 传递给目标方法的参数数组。如果目标方法没有参数,则此数组为空。  
     * @param target 目标对象实例,即被代理的对象。  
     * @throws Throwable 如果在通知执行过程中发生异常,则此异常将被抛出。  
     *                   注意:如果此通知中抛出了异常,它可能会阻止后续通知(如果有的话)的执行,  
     *                   并且可能会影响到目标方法的返回值(如果目标方法返回void,则不会有影响)。  
     */  
    @Override  
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {  
        // 输出目标方法执行后的相关信息  
        System.out.println("执行了" + target.getClass().getName() // 目标对象的类名  
                + "的" + method.getName() + "方法," // 目标方法的名称  
                + "返回值:" + returnValue); // 目标方法的返回值  
    }  
}

/**  
 * Before类实现了Spring AOP的MethodBeforeAdvice接口,  
 * 用于在目标方法执行之前执行一些前置逻辑。  
 *   
 * 这个类可以被Spring容器管理,并通过AOP配置应用到指定的目标方法上,  
 * 以实现无侵入式的日志记录、安全检查、性能监控等功能。  
 */  
public class Before implements MethodBeforeAdvice {  
  
    /**  
     * before方法会在目标方法执行之前被调用。  
     *   
     * @param method  被调用的目标方法的Method对象,包含方法名、参数类型等信息。  
     * @param args    目标方法执行时传入的参数数组,如果方法没有参数,则为空数组。  
     * @param target  目标对象,即被代理的对象,实际执行方法的那个对象。  
     * @throws Throwable 如果前置逻辑执行中抛出异常,则此异常会被传递给调用者,  
     *                   可能导致目标方法不会被执行。  
     *   
     * 此方法的主要用途是执行一些前置逻辑,如日志记录、权限检查等,  
     * 而不改变目标方法的参数或返回值。  
     */  
    @Override  
    public void before(Method method, Object[] args, Object target) throws Throwable {  
        // 输出目标对象的类名、被调用的方法名以及一个简单的执行通知  
        System.out.println(target.getClass().getName() + "的" + method.getName() + "方法被执行了");  
    }  
}

    <!--注册bean-->
    <bean id="blog" class="com.lzh.service.blogServiceImpl"></bean>
    <bean id="before" class="com.lzh.service.Before"></bean>
    <bean id="after" class="com.lzh.service.After"></bean>
    <!--aop的配置-->
    <!--切入点  expression:表达式匹配要执行的方法-->
     <!--执行环绕; advice-ref执行方法 . pointcut-ref切入点-->
    <aop:config>
        <aop:pointcut id="pointcut" expression="execution(* com.lzh.service.blogServiceImpl.*(..))"/>
        <aop:advisor advice-ref="before" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="after" pointcut-ref="pointcut"/>
    </aop:config>

3.自定义类实现

public class MyPointcut {
    public void before(){
        System.out.println("--before--");
    }
    public void after(){
        System.out.println("--after--");
    }
}

修改xml文件:

    <!--注册bean-->
    <bean id="blog" class="com.lzh.service.blogServiceImpl"></bean>
    <bean id="before" class="com.lzh.service.Before"></bean>
    <bean id="after" class="com.lzh.service.After"></bean>
    <bean id="second" class="com.lzh.config.MyPointcut"></bean>
    <!--aop的配置-->
    <!--aop:config>
        <aop:pointcut id="pointcut" expression="execution(* com.lzh.service.blogServiceImpl.*(..))"/>
        <aop:advisor advice-ref="before" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="after" pointcut-ref="pointcut"/>
    </aop:config-->
    <!--aop的配置-->
    <aop:config>
        <aop:aspect ref="second">
            <aop:pointcut id="myPonitcut" expression="execution(* com.lzh.service.blogServiceImpl.*(..))"/>
            <aop:before pointcut-ref="myPonitcut" method="before"/>
            <aop:after pointcut-ref="myPonitcut" method="after"/>
        </aop:aspect>
    </aop:config>

测试结果:

4.注解实现

// 使用@Aspect注解定义一个切面类,用于在指定的方法执行前后或周围进行增强处理  
@Aspect  
public class AnnotationPointcut {  
  
    // 使用@Before注解定义一个前置增强,该增强会在指定方法执行前执行  
    // "execution(* com.lzh.service.blogServiceImpl.*(..))"是切点表达式,表示匹配com.lzh.service.blogServiceImpl包下所有类的所有方法  
    @Before("execution(* com.lzh.service.blogServiceImpl.*(..))")  
    public void before(){  
        System.out.println("---before---"); // 在匹配的方法执行前打印信息  
    }  
  
    // 使用@After注解定义一个后置增强,该增强会在指定方法执行后执行(无论方法是否抛出异常)  
    @After("execution(* com.lzh.service.blogServiceImpl.*(..))")  
    public void after(){  
        System.out.println("---after---"); // 在匹配的方法执行后打印信息  
    }  
  
    // 使用@Around注解定义一个环绕增强,该增强可以在方法执行前后进行自定义处理  
    // 环绕增强需要接收一个ProceedingJoinPoint类型的参数,用于控制目标方法的执行  
    @Around("execution(* com.lzh.service.blogServiceImpl.*(..))")  
    public Object around(ProceedingJoinPoint jp) throws Throwable {  
        System.out.println("环绕前"); // 在目标方法执行前打印信息  
        System.out.println("签名:"+jp.getSignature()); // 打印目标方法的签名信息  
  
        // 通过jp.proceed()执行目标方法,并获取其返回值  
        // 注意:如果目标方法抛出异常,这里也会抛出相同的异常  
        Object proceed = jp.proceed();  
  
        System.out.println("环绕后"); // 在目标方法执行后打印信息  
        System.out.println(proceed); // 打印目标方法的返回值  
  
        // 返回目标方法的执行结果  
        return proceed;  
    }  
}

    <!--注册bean-->
    <bean id="blog" class="com.lzh.service.blogServiceImpl"></bean>
    <bean id="before" class="com.lzh.service.Before"></bean>
    <bean id="after" class="com.lzh.service.After"></bean>
    <bean id="second" class="com.lzh.config.MyPointcut"></bean>
    <!--aop的配置-->
    <!--aop:config>
        <aop:pointcut id="pointcut" expression="execution(* com.lzh.service.blogServiceImpl.*(..))"/>
        <aop:advisor advice-ref="before" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="after" pointcut-ref="pointcut"/>
    </aop:config-->
    <!--aop的配置-->
    <!--aop:config>
        <aop:aspect ref="second">
            <aop:pointcut id="myPonitcut" expression="execution(* com.lzh.service.blogServiceImpl.*(..))"/>
            <aop:before pointcut-ref="myPonitcut" method="before"/>
            <aop:after pointcut-ref="myPonitcut" method="after"/>
        </aop:aspect>
    </aop:config-->
    <bean id="annotationPointcut" class="com.lzh.config.AnnotationPointcut"/>
    <aop:aspectj-autoproxy/>

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值