Spring AOP方法分析
本示例向您展示如何配置Spring AOP方法分析。
我们可以将Spring AOP与任何服务(或者其他)类中的任何方法一起使用,而无需在任何服务类中编写一行分析代码。
面向方面的编程(AOP)使我们能够将(通常是重复的样板代码)分析代码与服务代码分开。
Spring AOP方法分析
我们仅在一个单独的类(SimpleProfiler.java)中编写一次事件探查器代码,仅此而已,剩下的只是在spring.xml中的AOP配置,必须进行工作。
因此,我们能够进行以下方法的性能分析。
- 分析任何(服务)类,
- 无需触摸(服务)类的代码,
- 通过Spring-AOP方法。
Spring AOP方法分析项目结构
Spring AOP Maven依赖项
我们将首先在pom.xml文件中查找所有必需的依赖项及其版本。
在此示例中,我们将使用Spring 4。
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>hu.daniel.hari.learn.spring</groupId>
<artifactId>Tutorial-SpringAop-Profiling</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<properties>
<!-- Generic properties -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.7</java.version>
<!-- SPRING -->
<spring.version>4.0.0.RELEASE</spring.version>
</properties>
<dependencies>
<!-- Spring (includes spring-aop)-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- AspectJ (required spring-aop dependency) -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.5</version>
</dependency>
<!-- LOG -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- This plugin is needed to define the java version in maven project. -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
我们需要spring-context作为Spring依赖项。
spring-aop还具有spring-context作为其依赖库,因此无需添加它。
Aspectjweaver是spring-aop的依赖项,但由于未为spring-aop明确定义,因此我们必须添加它。
Spring Service类(将进行概要分析)
我们首先编写我们的服务类,它具有模拟短流程和长流程的方法,还有一个引发异常的方法。
package hu.daniel.hari.learn.spring.aop.profiling.service;
import org.springframework.stereotype.Component;
/**
* Example service class that we want to profile through AOP.
* (Notice that no need to insert any line of profiling code!)
*
* @author Daniel Hari
*/
@Component
public class FooService {
public void doShortJob() {
sleep(10l);
}
public void doLongJob() {
sleep(300l);
}
public void doSomethingThatThrowsException() {
throw new RuntimeException("Simulated Exception.");
}
private void sleep(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
请注意,我们没有在服务类中添加任何分析代码。
Spring AOP Profiler类
我们的事件探查器类对方法执行的处理时间的毫秒数进行了简单的测量。
package hu.daniel.hari.learn.spring.aop.profiling.core.profiler;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.util.StopWatch;
import org.springframework.util.StopWatch.TaskInfo;
/**
* Our profiler that
* logs process time, and remark if process had exception.
*
* @author Daniel Hari
*/
public class SimpleProfiler {
/**
* Spring AOP 'around' reference method signature is bounded like this, the
* method name "profile" should be same as defined in spring.xml aop:around
* section.
**/
public Object profile(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
StopWatch stopWatch = new StopWatch();
stopWatch.start(proceedingJoinPoint.toShortString());
boolean isExceptionThrown = false;
try {
//execute the profiled method
return proceedingJoinPoint.proceed();
} catch (RuntimeException e) {
isExceptionThrown = true;
throw e;
} finally {
stopWatch.stop();
TaskInfo taskInfo = stopWatch.getLastTaskInfo();
//Log the method's profiling result
String profileMessage = taskInfo.getTaskName() + ": " + taskInfo.getTimeMillis() + " ms" +
(isExceptionThrown ? " (thrown Exception)" : "");
System.out.println(profileMessage);
}
}
}
如您所见,我们的剖析方法使用秒表来衡量方法的执行情况。
该方法在执行方法之前从Spring-AOP接收ProceedingJoinPoint对象,该对象表示方法执行连接点。
该对象还包含有关我们可以获得的执行方法的信息。我们有责任通过调用该方法的progress()来执行该方法。
我们知道在执行的方法中也记录了引发异常的记录,但是我们将其抛出来是透明的,我们不想在此级别处理异常,而只记录它。
("配置文件"方法签名是有界的,因为Spring AOP会调用它,但是方法名称可以是将在spring.xml中设置的任何其他名称。
)
Spring AOP配置XML
现在,我们已经准备好迷你应用程序中所需的每个工作类。
让我们创建Spring的配置文件:
spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans" xmlns:context="https://www.springframework.org/schema/context" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns:aop="https://www.springframework.org/schema/aop"
xsi:schemaLocation="
https://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans-4.0.xsd
https://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context-4.0.xsd
https://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
">
<!-- Scans the classpath for annotated components that will be auto-registered as Spring beans -->
<context:component-scan base-package="hu.daniel.hari.learn.spring.aop.profiling"
<!-- Activates various annotations to be detected in bean classes e.g: @Autowired -->
<context:annotation-config
<!-- AOP Configuration for profiling -->
<!-- Our profiler class that we want to use to measure process time of service methods. -->
<bean id="profiler" class="hu.daniel.hari.learn.spring.aop.profiling.core.profiler.SimpleProfiler"
<!-- Spring AOP -->
<aop:config>
<aop:aspect ref="profiler">
<!-- Catch all method in the service package through AOP. -->
<aop:pointcut id="serviceMethod"
expression="execution(* hu.daniel.hari.learn.spring.aop.profiling.service.*.*(..))"
<!-- For these methods use the profile named method in the profiler class we defined below. -->
<aop:around pointcut-ref="serviceMethod" method="profile"
</aop:aspect>
</aop:config>
</beans>
首先,我们告诉spring我们要对Spring组件使用类路径扫描(而不是在此xml中不方便地一个接一个地定义它们),还要启用Spring注释检测。
我们配置Spring-AOP,以捕获服务包中所有类中的所有方法:我们定义了一个方面,它"切入"了每个服务方法,
并且我们为这些切入点定义了一个"环绕"方法,该方法以我们的SimpleProfiler的"配置文件"命名方法为目标。
Spring AOP方法分析测试类
而已!剩下的只是我们应用程序的测试类。
package hu.daniel.hari.learn.spring.aop.profiling.main;
import hu.daniel.hari.learn.spring.aop.profiling.service.FooService;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Example test class for Spring AOP Profiling.
*
* (remark: Exceptional calls should be put in try catch and handle ctx.close
* properly but we missed to keep simleness of this example.)
*
* @author Daniel Hari
*/
public class Main {
public static void main(String[] args) {
//Create Spring application context that configured by xml.
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:/spring.xml");
//Get our service from the spring context (that we want to be profiled).
FooService fooService = ctx.getBean(FooService.class);
//Test profiling through methods calls.
for (int i = 0; i < 10; i++) {
fooService.doShortJob();
}
fooService.doLongJob();
//Test that profiler also can handle Exceptions in profiled method.
fooService.doSomethingThatThrowsException();
//Close the spring context
ctx.close();
}
}
您会看到我们可以简单地从main方法启动Spring容器,并获得第一个依赖项注入入口点,即服务类实例。
如果运行,将得到以下日志:
execution(FooService.doShortJob()): 38 ms execution(FooService.doShortJob()): 10 ms execution(FooService.doShortJob()): 10 ms execution(FooService.doShortJob()): 10 ms execution(FooService.doShortJob()): 10 ms execution(FooService.doShortJob()): 10 ms execution(FooService.doShortJob()): 10 ms execution(FooService.doShortJob()): 10 ms execution(FooService.doShortJob()): 10 ms execution(FooService.doShortJob()): 10 ms execution(FooService.doLongJob()): 300 ms execution(FooService.doSomethingThatThrowsException()): 0 ms (thrown Exception) Exception in thread "main" java.lang.RuntimeException: Simulated Exception. at hu.daniel.hari.learn.spring.aop.profiling.service.FooService.doSomethingThatThrowsException(FooService.java:23) ...
您可以看到为每个服务方法调用生成的性能分析日志。
您可以在同一包或者以下的包中创建其他服务类,也将对其进行概要分析。
如果您想了解AOP概念的工作原理,请查看下面的参考链接。
如果您使用附件中的log4j.properties文件,并取消第一行的注释,则可以看到当前情况。

