AOP

AOP

AOP 即面向切面编程,常用的场景是实现业务代码和非业务代码的解耦合。

简单例子

  • 创建一个计算器接口 Cal,定义4个方法:

    1
    2
    3
    4
    5
    6
    7
    8
    package com.kk.utils;

    public interface Cal {
    int add(int num1, int num2);
    int sub(int num1, int num2);
    int mul(int num1, int num2);
    int div(int num1, int num2);
    }
  • 实现接口:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    package com.kk.utils;

    import org.springframework.stereotype.Component;

    @Component
    public class CalImpl implements Cal {
    @Override
    public int add(int num1, int num2) {
    System.out.println("add方法的参数是["+num1+","+num2+"]");
    int result = num1+num2;
    System.out.println("add方法的结果是"+result);
    return result;
    }

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

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

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

    改代码中,日志信息和业务代码的耦合性很高,不利于系统的维护,使用 AOP 可以进行优化,将日志代码全部抽象出去统一进行处理,计算器方法中只保留核心的业务代码,做到核心业务和非业务代码的解耦合。

  • 引入依赖

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>5.3.16</version>
    </dependency>
    <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.3.16</version>
    </dependency>
  • 切面类:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    package com.kk.entity;

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

    import java.util.Arrays;

    @Aspect //使其成为切面对象
    @Component //让IoC管理该对象
    @EnableAspectJAutoProxy // 为目标类自动生成代理对象
    public class LoggerAspect {
    @Before("execution(public int com.kk.utils.CalImpl.*(..))")
    public void before(JoinPoint joinPoint){
    //获取方法名
    String name = joinPoint.getSignature().getName();
    //获取参数
    String args = Arrays.toString(joinPoint.getArgs());
    System.out.println(name + "方法的参数是:" + args);
    }

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

    @AfterReturning(value = "execution(public int com.kk.utils.CalImpl.*(..))",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.kk.utils.CalImpl.*(..))",throwing = "exception")
    public void afterThrowing(JoinPoint joinPoint,Exception exception){
    //获取方法名
    String name = joinPoint.getSignature().getName();
    System.out.println(name+"方法抛出异常"+exception);
    }
    }
  • CalImpl 也要交给 IoC 容器来管理,添加 @Component

    1
    2
    3
    4
    @Component
    public class CalImpl implements Cal {
    ...
    }
  • 配置文件中配置 AOP

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    <?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-3.2.xsd
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-4.3.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!-- 注入 -->
    <bean class="com.kk.utils.CalImpl" id="calImpl"/>

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

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

    </beans>
  • 测试

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class Test {
    public static void main(String[] args) {
    // 加载IoC
    ApplicationContext applicationContext =
    new ClassPathXmlApplicationContext("spring.xml");
    Cal cal = (Cal) applicationContext.getBean("calImpl"); // 参数为配置类中的方法名
    cal.add(10, 3);
    cal.sub(10, 3);
    cal.mul(10, 3);
    cal.div(10, 3);
    }
    }
  • 或者不使用配置文件,在切面类上添加注解即可

    1
    2
    3
    4
    5
    6
    @Aspect // 使其成为切面对象
    @Component // 让IoC管理该对象
    @EnableAspectJAutoProxy // 为目标类自动生成代理对象
    public class LoggerAspect {
    ...
    }
  • 测试

    1
    2
    3
    4
    5
    6
    7
    8
    public class Test {
    public static void main(String[] args) {
    // 加载IoC
    ApplicationContext applicationContext =
    new AnnotationConfigApplicationContext("com.kk"); // 同时扫到CalImpl、LoggerAspect
    ...
    }
    }

动态代理

动态代理是 Spring AOP 的底层核心原理;首先需要清楚代理的意思:

代理:Java 常见的设计模式;两个主体:委托方、代理方;委托方原本要自己做的事情,现在交给代理方来代替委托方去完成。

Java 中实现代理机制,需要委托方和代理方都具备完成需求的能力,即委托方和代理方需要实现同一个接口。

静态代理:预先写好代理类的代码,如果要修改需要重新编译,灵活性较差:

1
2
3
4
5
package com.kk.reflection.proxy;

public interface Phone {
String sellPhone();
}

委托类:

1
2
3
4
5
6
public class Apple implements Phone{
@Override
public String sellPhone() {
return "销售iPhone";
}
}
1
2
3
4
5
6
public class HuaWei implements Phone{
@Override
public String sellPhone() {
return "销售HuaWei手机";
}
}

代理类:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class PhoneProxy implements Phone {
private Phone phone;

public PhoneProxy(Phone phone) {
this.phone = phone;
}

@Override
public String sellPhone() {
System.out.println("开启代理模式");
return phone.sellPhone();
}
}
1
2
3
4
5
6
7
8
public class Test {
public static void main(String[] args) {
PhoneProxy phoneProxy = new PhoneProxy(new Apple());
System.out.println(phoneProxy.sellPhone());
phoneProxy = new PhoneProxy(new HuaWei());
System.out.println(phoneProxy.sellPhone());
}
}

动态代理:不需要预先写好代理类的代码,程序运行期间动态生成,灵活性很好;必须提前创建一个模板,由这个模板规定动态代理类的执行逻辑,并动态生成代理类

创建动态代理对象的方法:

1
Proxy.newProxyInstance( , , )

参数 1:类加载器 ClassLoader,将类(由参数 2 创建的类)加载到内存中

参数 2:Class<?>[]

参数 3:InvocationHandler 实例对象(完成创建类、并加载到内存的工作)

newProxyInstance 方法是创建代理对象的,创建委托对象的代理接口的,委托类和代理类实现同一个接口,所以只需要知道委托类实现了哪些接口(即参数 2),就可以动态创建代理类(对象)了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.kk.reflection.proxy;

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

public class MyInvocationHandler implements InvocationHandler {

// 接收委托方
private Object object;

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

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("开启代理模式(动态代理)");
Object invoke = method.invoke(object, args);
return invoke;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
package com.kk.reflection.proxy;

public class InvocationTest {
public static void main(String[] args) {
MyInvocationHandler handler = new MyInvocationHandler();
Phone proxy1 = (Phone) handler.bind(new Apple());
System.out.println(proxy1.sellPhone());
// 无需手动创建代理类,灵活性增强
// Car proxy2 = (Car) handler.bind(new BMW());
// System.out.println(proxy2.sellCar());
}

}

AOP
http://example.com/2022/09/04/AOP/
作者
fkxia
发布于
2022年9月4日
许可协议