Spring AOP方法分析
此示例显示如何配置Spring AOP方法概要分析。我们可以在任何服务(或其他)类中使用Spring AOP和任何方法,而无需在任何服务类中编写任何一行分析代码。面向方面编程(AOP)允许我们将(通常是重复的和样板)分析代码与服务代码分开。
目录[ 隐藏 ]
Spring AOP方法分析
我们只在一个单独的类(SimpleProfiler.java)中编写我们的探查器代码一次,这就是全部,其余的只有AOP配置spring.xml
才能完成工作。
因此,我们可以对以下方法进行方法分析。
- 分析任何(服务)类,
- 没有触及(服务)类的代码,
- 通过Spring-AOP方法。
Spring AOP方法分析项目结构
Spring AOP Maven依赖项
我们将首先查看我们的pom.xml文件,了解所有必需的依赖项及其版本。我们在这个例子中使用Spring 4。
<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>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对象,该对象在执行该方法之前表示此情况下的方法执行关节点。该对象还有关于我们可以获得的要执行的方法的信息。
- 我们负责通过调用它的proceed()来执行该方法。
- 我们也知道在执行的日志记录方法中抛出的异常,但是我们将其推向透明,我们不希望在此级别处理异常,只记录它。
(“profile”方法签名受限于Spring AOP将调用它,但方法名称可以是将在spring.xml中设置的任何其他内容。)
Spring AOP配置XML
现在我们准备好了迷你应用程序中所需的每个工作类。让我们创建Spring的配置文件:
spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" 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-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop
http://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,以捕获服务包中所有类的所有方法:
- 我们定义了一个“切入点”的方面 - 每个服务方法,
- 我们为这些切入点定义了一个“around”方法,该方法以SimpleProfiler的“profile”命名方法为目标。
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
来自附加源的文件,并取消注释第一行,您可以看到幕后发生了什么。
参考文献:
使用Spring进行面向方面编程
您可以从下面的链接下载完整的maven项目源。