独立使用 JFinal Aop

添加 jfinal-aop 依赖

jfinal-aop 可以独立使用,

  • 你可以在其他项目中使用 jfinal-aop
  • 你也可以在 Android 工程中使用 jfinal-aop
    <dependency>
      <groupId>com.litongjava</groupId>
      <artifactId>jfinal-aop</artifactId>
      <version>${jfinal-aop.version}</version>
    </dependency>

使用拦截器

如何在 Java 应用程序中使用 JFinal AOP (面向切面编程)库来实现拦截器功能。 这个演示主要涉及三个部分:目标类(Cat),拦截器类(Aspect1),以及主测试类(CatMainTest)。

1. 目标类(Cat)

Cat 类代表了一个简单的实体,包含一个方法 eat(),该方法通过 @Before 注解与 Aspect1 类关联,表示在调用 eat() 方法前,应先执行 Aspect1 拦截器的逻辑。

import com.litongjava.jfinal.aop.Before;

public class Cat {
  @Before(Aspect1.class)
  public String eat() {
    return "eat chat";
  }
}

2. 拦截器类(Aspect1)

Aspect1 实现了 Interceptor 接口,提供了 intercept 方法。该方法在与之关联的方法(本例中为 Cat.eat)执行前后被调用。

intercept 方法内部,可以访问被调用方法的详细信息(如方法名称、参数等),并在方法执行前后执行自定义逻辑。本例中,intercept 方法的实现做了以下几件事:

  • 打印日志,显示拦截器被调用前后的信息("Before Aspect1 invoking" 和 "After Aspect1 invoking")。
  • 使用 Invocation 对象获取被拦截方法的相关信息(方法本身、方法名、参数、目标对象)并打印。
  • 执行实际的方法调用(invocation.invoke())。
  • 修改返回值为 "set new value"
import com.litongjava.jfinal.aop.Interceptor;
import com.litongjava.jfinal.aop.Invocation;
import lombok.extern.slf4j.Slf4j;

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


@Slf4j
public class Aspect1 implements Interceptor {

  @Override
  public void intercept(Invocation invocation) {
    System.out.println("Before Aspect1 invoking");
    Method method = invocation.getMethod();
    String methodName = invocation.getMethodName();
    Object[] args = invocation.getArgs();
    Object target = invocation.getTarget();
    log.info("method:{}", method);
    log.info("methodName:{}", methodName);
    log.info("args:{}", Arrays.toString(args));
    log.info("target:{}", target);
    Object invoke = invocation.invoke();
    invocation.setReturnValue("set new value");
    log.info("invoke:{}", invoke);

    System.out.println("After Aspect1 invoking");
  }
}

3. 主测试类(CatMainTest)

CatMainTest 类中,通过 Aop.get(Cat.class) 获取 Cat 类的代理实例。当调用 eat 方法时,会先执行 Aspect1 中的拦截器逻辑,然后执行 eat 方法本身。

最终,由于拦截器中修改了返回值(将其设置为 "set new value"),因此即使原始 eat 方法返回的是 "eat chat",打印出的结果也是 "set new value"

package com.issues01;

import com.litongjava.jfinal.aop.Aop;


public class CatMainTest {

  public static void main(String[] args) {
    new CatMainTest().index();
  }

  public void index() {
    String javaVersion = System.getProperty("java.version");
    System.out.println("java-version:" + javaVersion);
    Cat cat = Aop.get(Cat.class);
    String eat = cat.eat();
    System.out.println("result:" + eat);
  }
}

日志信息

java-version:1.8.0_121
Before Aspect1 invoking
2024-01-30 21:30:43.615 [main] INFO  c.i.Aspect1.intercept:21 - method:public java.lang.String com.issues01.Cat.eat()
2024-01-30 21:30:43.617 [main] INFO  c.i.Aspect1.intercept:22 - methodName:eat
2024-01-30 21:30:43.618 [main] INFO  c.i.Aspect1.intercept:23 - args:[]
2024-01-30 21:30:43.618 [main] INFO  c.i.Aspect1.intercept:24 - target:com.issues01.Cat$$EnhancerByJFinal@661972b0
2024-01-30 21:30:43.618 [main] INFO  c.i.Aspect1.intercept:27 - invoke:eat chat
After Aspect1 invoking
result:set new value

日志信息展示了日志信息展示了整个拦截过程的细节和执行流程:

  1. 输出 Java 版本:显示正在使用的 Java 版本。

    java-version:1.8.0_121
    
  2. 拦截器前的打印信息:显示拦截器开始工作之前的日志。

    Before Aspect1 invoking
    
  3. 拦截器内部的日志信息:显示拦截器内部获取的方法信息和目标对象信息。这部分信息由拦截器 Aspect1 中的代码产生:

    • 方法名、方法参数、目标对象实例和调用的结果("eat chat")被打印。
    • 这些信息主要用于调试和记录目标方法的调用情况。
    2024-01-30 21:30:43.615 [main] INFO  c.i.Aspect1.intercept:21 - method:public java.lang.String com.issues01.Cat.eat()
    2024-01-30 21:30:43.617 [main] INFO  c.i.Aspect1.intercept:22 - methodName:eat
    2024-01-30 21:30:43.618 [main] INFO  c.i.Aspect1.intercept:23 - args:[]
    2024-01-30 21:30:43.618 [main] INFO  c.i.Aspect1.intercept:24 - target:com.issues01.Cat$$EnhancerByJFinal@661972b0
    2024-01-30 21:30:43.618 [main] INFO  c.i.Aspect1.intercept:27 - invoke:eat chat
    
  4. 拦截器后的打印信息:显示拦截器完成工作之后的日志。

    After Aspect1 invoking
    
  5. 最终结果:尽管原始 Cat.eat() 方法返回的是 "eat chat",但由于在拦截器 Aspect1 中修改了返回值,所以最终打印的结果是 "set new value"

    result:set new value
    

这个过程体现了 AOP(面向切面编程)的典型用例,即在不修改原有业务逻辑的情况下,能够对方法调用进行拦截,并执行额外的处理逻辑,比如日志记录、性能统计、安全控制等。在这个例子中,拦截器 Aspect1 被用来修改方法的返回值,展示了 AOP 的强大能力。

类扫描和自动注入

使用 JFinal AOP 库来实现类的扫描、自动注入(依赖注入),以及基于注解的服务定义和消费。

1. 接口定义(DemoService)

首先定义了一个接口 DemoService,它声明了一个方法 Hello

public interface DemoService {
  public String Hello();
}

2. 服务实现(DemoServiceImpl)

然后,实现了上述接口 DemoService。使用 @AService 注解标记该类,让 AOP 容器能够识别和管理它。这种方式通常用于在应用程序中定义服务组件。

import com.litongjava.jfinal.aop.annotation.AService;

@AService
public class DemoServiceImpl implements DemoService {
  public String Hello(){
    return "Hello";
  }
}

3. 控制器定义(DemoController)

接下来定义了一个控制器类 DemoController。使用 @AController 注解标记该类,表明它是一个 MVC 架构中的控制器组件。在该类中,通过 @AAutowired 注解自动注入了 DemoService 的实例 demoService。然后定义了一个方法 hello(),调用 demoService.Hello() 来返回结果。

import com.litongjava.jfinal.aop.annotation.AAutowired;
import com.litongjava.jfinal.aop.annotation.AController;

@AController
public class DemoController {
  @AAutowired
  private DemoService demoService;

  public String hello() {
    return demoService.Hello();
  }
}

4. 应用入口(DemoApp)

最后,在应用入口 DemoApp 中,使用 JFinal AOPAop 工具类来扫描、初始化注解,并获取 DemoController 的实例。然后调用 demoController.hello() 方法,打印其返回值。

import java.util.List;
import com.litongjava.jfinal.aop.Aop;
import com.litongjava.jfinal.aop.annotation.AComponentScan;

@AComponentScan
public class DemoApp {
  public static void main(String[] args) throws Exception {
    List<Class<?>> scannedClasses = Aop.scan(DemoApp.class);
    Aop.initAnnotation(scannedClasses);

    DemoController demoController = Aop.get(DemoController.class);
    String hello = demoController.hello();
    System.out.println(hello);
    //关闭容器
    Aop.close();
  }
}

main 方法中,Aop.scan(DemoApp.class) 扫描带有 @AComponentScan 注解的类和其包下的所有类。然后 Aop.initAnnotation(scannedClasses) 初始化扫描到的类的注解。通过 Aop.get(DemoController.class) 获取 DemoController 的代理实例,并调用其 hello() 方法。最后,通过 Aop.close() 关闭 AOP 容器。

输出(output)

Hello

这是调用 demoController.hello() 方法的结果,显示服务正常工作,依赖注入和类扫描也都按预期工作。

这段代码通过使用 JFinal AOP` 库,演示了如何在 Java 应用程序中实现类扫描、自动注入和基于注解的服务定义与消费。