spring学习6 —— AOP

从b站学习springcloud,现在进行总结,该总结除去了视频中出现的小错误,对有些易错的地方进行了提醒
b站链接:https://www.bilibili.com/video/BV1Gt411N7HF?p=2
资料链接:
https://pan.baidu.com/s/1o0Aju3IydKA15Vo1pP4z5w
提取码: 21ru

上一节链接:
下一节链接:

下面的内容总结:

AOP:面向切面编程,不同业务 有相同的代码,这个代码重复写 多麻烦!
抽象出来,在一个位置,写1次
不同业务需要这些代码,就在某位置找到代码

AOP 是对面向对象编程的补充,在运行时,动态 将代码切入到类的指定方法、指定位置上 的编程思想就是面向切面编程。
不同方法的 同 1 个位置 抽象成 1 个切面对象,对该切面对象进行编程就是 AOP。

AOP 的优点:

  • 降低模块间 耦合度
  • 系统 更容易扩展
  • 好的代码 复用
  • 非业务代码更集中,不分散,便于统一管理
  • 业务代码 更简洁、存粹,无其他代码的影响

类似 spring学习1 —— IoC,新建工程 springAOP

写一个计算机的功能,先传统方法,后 AOP 方法

1 在 pom 文件中加入代码:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.southwind</groupId>
    <artifactId>springAOP</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>5.1.8.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.1.7.RELEASE</version>
        </dependency>
    </dependencies>

</project>

2 在java 中创包 com.southwind.utils,其内创建接口 Cal,加入代码:

package com.southwind.utils;

public interface Cal {
    public int add(int num1, int num2);
    public int sub(int num1, int num2);
    public int mul(int num1, int num2);
    public int div(int num1, int num2);
}

传统方法

3 在 utils 中,创包Impl,其内 创建实现类 CalImpl,传统代码:

package com.southwind.utils.Impl;

import com.southwind.utils.Cal;

public class CalImpl implements Cal {

    public int add(int num1, int num2) {
        System.out.println("add 方法的参数:" + num1 + "," + num2);
        int result = num1 + num2;
        System.out.println("add 方法的结果 = " + result);
        return result;
    }

    public int sub(int num1, int num2) {
        System.out.println("sub 方法的参数:" + num1 + "," + num2);
        int result = num1 - num2;
        System.out.println("sub 方法的结果 = " + result);
        return result;
    }

    public int mul(int num1, int num2) {
        System.out.println("mul 方法的参数:" + num1 + "," + num2);
        int result = num1 * num2;
        System.out.println("mul 方法的结果 = " + result);
        return result;
    }

    public int div(int num1, int num2) {
        System.out.println("div 方法的参数:" + num1 + "," + num2);
        int result = num1 / num2;
        System.out.println("div 方法的结果 = " + result);
        return result;
    }
}

4 在 southwind 中,创包 test,其内创建 类 Test,加入代码:

package com.southwind.test;

import com.southwind.utils.Cal;
import com.southwind.utils.Impl.CalImpl;

public class Test {
    public static void main(String[] args) {
        Cal cal = new CalImpl();
        cal.add(1,1);
        cal.sub(2,1);
        cal.mul(3,4);
        cal.div(5,1);
    }
}

启动 test/ Test:

AOP - MyInvocationHandler 方法

传统的代码:修改起来很麻烦,加减乘除,均是 输出参数 + 输出结果
用 AOP 优化:

5 在 utils 中,新建 类 MyInvocationHandler,加入代码:

package com.southwind.utils;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;

public class MyInvocationHandler implements InvocationHandler {
    // 接收委托对象
    private Object object = null;

    // 返回代理对象
    public Object bind(Object object){
        this.object = object;
        return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), this);
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(method.getName() + "方法的参数:" + Arrays.toString(args));
        Object result = method.invoke(this.object,args);
        System.out.println(method.getName() + "的结果 = " + result);
        return result;
    }
}

6 utils/ Cal 的 Cal 再复制 1 份,命名为 Cal1,内容不变
Impl/ CarImpl 的 CarImpl 再复制 1 份,命名为 CalImpl1,代码:

package com.southwind.utils.Impl;

import com.southwind.utils.Cal;

public class CalImpl1 implements Cal {

    public int add(int num1, int num2) {
        int result = num1 + num2;
        return result;
    }

    public int sub(int num1, int num2) {
        int result = num1 - num2;
        return result;
    }

    public int mul(int num1, int num2) {
        int result = num1 * num2;
        return result;
    }

    public int div(int num1, int num2) {
        int result = num1 / num2;
        return result;
    }
}

7 在 test 中,新建 类 Test1,加入代码:

package com.southwind.test;

import com.southwind.utils.Cal;
import com.southwind.utils.Impl.CalImpl1;
import com.southwind.utils.MyInvocationHandler;

public class Test1 {
    public static void main(String[] args) {
        Cal cal = new CalImpl1();
        MyInvocationHandler myInvocationHandler = new MyInvocationHandler();
        Cal cal1 = (Cal) myInvocationHandler.bind(cal);
        cal1.add(1,1);
        cal1.sub(2,1);
        cal1.mul(3,4);
        cal1.div(5,1);
    }
}

启动 test/ Test1:

Spring框架 实现 AOP

上面 MyInvocationHandler 用 动态代理实现 AOP,复杂、不好理解

Spring 框架封装了 AOP,不用创建 InvocationHandler,只要创建1 个切面对象
所有 非业务代码 在切面对象中完成就行,Spring 框架底层 会自动根据切面类、目标类 生成 1 个代理对象

1 在 pom 文件中,加入代码:

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.8.RELEASE</version>
        </dependency>

2 在 utils/ Impl/ CalImpl1 中加入 @Component

3 在 southwind 中,创包 aop,其内新建 类 LoggerAspect,加入代码:

package com.southwind.aop;

import jdk.nashorn.internal.scripts.JO;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Aspect
@Component
public class LoggerAspect {

    @Before(value = "execution(public int com.southwind.utils.Impl.CalImpl1.*(..))")
    public  void before(JoinPoint joinPoint){
        // 获取 方法名
        String name = joinPoint.getSignature().getName();

        // 获取参数
        String args = Arrays.toString(joinPoint.getArgs());
        System.out.println(name + "方法的参数:" + args);
    }

    @After(value = "execution(public int com.southwind.utils.Impl.CalImpl1.*(..))")
    public void after(JoinPoint joinPoint){
        // 获取 方法名
        String name = joinPoint.getSignature().getName();
        System.out.println(name + "执行完毕!");
    }

    @AfterReturning(value = "execution(public int com.southwind.utils.Impl.CalImpl1.*(..))", returning = "result")
    public void afterReturning(JoinPoint joinPoint, Object result){
        //获取方法名
        String name = joinPoint.getSignature().getName();
        System.out.println(name + "的结果 = " + result);
    }

    @AfterThrowing(value = "execution(public int com.southwind.utils.Impl.CalImpl1.*(..))", throwing = "exception")
    public  void afterThrowing(JoinPoint joinPoint, Exception exception){
        //获取方法名
        String name = joinPoint.getSignature().getName();
        System.out.println(name + "方法抛出异常" + exception);
    }
}

4 在 resources 中新建 spring-aop.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:context="http://www.springframework.org/schema/context"
       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/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- 自动扫描 -->
    <context:component-scan base-package="com.southwind"></context:component-scan>

    <!-- Aspect注解生效,为目标类 自动生成代理对象 -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

</beans>

context:component-scan:扫描 com.southwind 中 所有类,如果该类有 @Component,该类会被扫描到 IoC 容器中

aop:aspectj-autoproxy:Spring 框架结合切面类、目标类 自动生成动态代理对象

  • 切面:横切关注点 被模块化的抽象对象
  • 通知:切面对象 完成的工作
  • 目标:被通知的对象 = 被横切的对象
  • 代理:切面、通知、目标混合之后的对象
  • 连接点:通知要插入业务代码的具体位置
  • 切点:AOP 通过切点定位到连接点

5 在 test 中,新建 类 Test2,加入代码:

package com.southwind.test;

import com.southwind.utils.Cal;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Test2 {
    public static void main(String[] args) {
        // 配置文件
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-aop.xml");

        // 获取代理对象
        Cal proxy = (Cal) applicationContext.getBean("calImpl1");
        proxy.add(1,1);
        proxy.sub(2,1);
        proxy.mul(3,4);
        proxy.div(5,1);
    }
}

其中 getBean("calImpl1");calImpl1 是默认的,它是 Impl 中的方法的名字,首字母若大写,就变成小写
也可以自己设置名字:

在 Test2 中再改就行了

启动 test/ Test2:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Spring AOPSpring框架中的一个重要模块,它提供了面向切面编程(AOP)的支持。AOP是一种编程思想,它可以在不改变原有代码的情况下,通过在程序运行时动态地将代码“织入”到现有代码中,从而实现对原有代码的增强。 Spring AOP提供了基于注解的AOP实现,使得开发者可以通过注解的方式来定义切面、切点和通知等相关内容,从而简化了AOP的使用。 下面是一个基于注解的AOP实现的例子: 1. 定义切面类 ```java @Aspect @Component public class LogAspect { @Pointcut("@annotation(Log)") public void logPointcut() {} @Before("logPointcut()") public void beforeLog(JoinPoint joinPoint) { // 前置通知 System.out.println("执行方法:" + joinPoint.getSignature().getName()); } @AfterReturning("logPointcut()") public void afterLog(JoinPoint joinPoint) { // 后置通知 System.out.println("方法执行完成:" + joinPoint.getSignature().getName()); } @AfterThrowing(pointcut = "logPointcut()", throwing = "ex") public void afterThrowingLog(JoinPoint joinPoint, Exception ex) { // 异常通知 System.out.println("方法执行异常:" + joinPoint.getSignature().getName() + ",异常信息:" + ex.getMessage()); } } ``` 2. 定义业务逻辑类 ```java @Service public class UserService { @Log public void addUser(User user) { // 添加用户 System.out.println("添加用户:" + user.getName()); } @Log public void deleteUser(String userId) { // 删除用户 System.out.println("删除用户:" + userId); throw new RuntimeException("删除用户异常"); } } ``` 3. 在配置文件中开启AOP ```xml <aop:aspectj-autoproxy/> <context:component-scan base-package="com.example"/> ``` 在这个例子中,我们定义了一个切面类LogAspect,其中通过@Aspect注解定义了一个切面,通过@Pointcut注解定义了一个切点,通过@Before、@AfterReturning和@AfterThrowing注解分别定义了前置通知、后置通知和异常通知。 在业务逻辑类中,我们通过@Log注解标注了需要增强的方法。 最后,在配置文件中,我们通过<aop:aspectj-autoproxy/>开启了AOP功能,并通过<context:component-scan>扫描了指定包下的所有组件。 这样,当我们调用UserService中的方法时,就会触发LogAspect中定义的通知,从而实现对原有代码的增强。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qq_1403034144

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值