background
Because of the need for performance testing method performed on the item, if the method were coupled before and after each following code
long beginTime = System.currentTimeMillis();
long endTime = System.currentTimeMillis();
long timeConsume = endTime - beginTime;
复制代码
Apparently the code will become very jumbled, and where it is needed statistical performance may also have a lot of, if you can simply add a comment on the method, we can achieve recording method for performing time-consuming, so will be able to reduce the number of simple and complicated the code, more convenient extension
Technology Selection
Like this scene is a typical scenario AOP, search SpringAOP can find a lot of code samples, but the project does not depend on Spring, there is no need to introduce Spring. Usually to achieve this function, there are two options, one is like SpringAOP as dynamic proxy or by CGLib JDK dynamic proxy, when you create an object, the object is dynamically generated proxy class, the other is AspectJ, compiled logic when executed the code will need woven into the bytecode.
For dynamic proxy, you need to create an object proxy class before they can enhance, but the project, there are a lot of static methods, when used by the object is not to call, but even if the method is invoked by the object, there is no convenient Spring IOC mechanisms, we have to modify the code to handle all the new objects before they can use the enhanced proxy object, a little trouble. And if it is frequently create object because another step to create a proxy object of the operation, there will be some loss of performance.
For AspectJ this manner, it is possible to place the cut point satisfies the expression, are woven into the enhanced logic, but the need to rely on assistance weaving tools, to enhance the compiled bytecode. Fortunately, there has been a corresponding aspectj compiled plug-in on maven, can easily treated weaving
Under the comprehensive consideration, we decided to adopt the custom annotation (target) + ApsectJ (Aop Enhanced) + aspectj the Maven build plug-ins to achieve
Technical realization
1, custom annotation
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface TimeConsumeLogAnnotation {
}
复制代码
2, dependent on the introduction Aspectj
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.9</version>
</dependency>
复制代码
3, Aspectj section
@Aspect
public class TimeConsumeLogAspectJ {
//通过ThreadLocal隔离不同线程的变量
ThreadLocal<Long> timeRecord = new ThreadLocal<>();
@Pointcut("execution(* *(..)) && @annotation(cn.freekiddo.annotation.TimeConsumeLogAnnotation)")
public void jointPoint(){}
@Before("jointPoint()")
public void doBefore(JoinPoint joinPoint){
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
System.out.println("方法" + method.getName() + "开始");
timeRecord.set(System.currentTimeMillis());
}
@After("jointPoint()")
public void doAfter(JoinPoint joinPoint){
long beginTime = timeRecord.get();
System.out.println("方法" +joinPoint.getSignature().getName()+ "结束,耗时"+(System.currentTimeMillis()-beginTime) +"ms");
}
}
复制代码
4, the introduction maven compiler plugin
Longer work after the maven-compiler-plugin processed
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.10</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<complianceLevel>1.8</complianceLevel>
</configuration>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
复制代码
5. Add @TimeConsumeLogAnnotation notes compiled to run on the target method
@TimeConsumeLogAnnotation()
public static void sayHelloWorld(String name) {
System.out.println("Hello " + name);
}
复制代码
Compiled bytecode
@TimeConsumeLogAnnotation
public static void sayHelloWorld(String name) {
JoinPoint var1 = Factory.makeJP(ajc$tjp_0, (Object)null, (Object)null, name);
try {
TimeConsumeLogAspectJ.aspectOf().doBefore(var1);
System.out.println("Hello " + name);
} catch (Throwable var4) {
TimeConsumeLogAspectJ.aspectOf().doAfter(var1);
throw var4;
}
TimeConsumeLogAspectJ.aspectOf().doAfter(var1);
}
复制代码
effect
方法sayHelloWorld开始
Hello world
方法sayHelloWorld结束,耗时1ms
复制代码
6, stepped pit
(1) section executed twice
Section at the beginning of the expression is
@Pointcut("@annotation(cn.freekiddo.annotation.TimeConsumeLogAnnotation)")
复制代码
The compiler will recognize aspectj of the method calls and method for performing an entry point in two stages, as will perform in two stages
Expression is modified by section
@Pointcut("execution(* *(..)) && @annotation(cn.freekiddo.annotation.TimeConsumeLogAnnotation)")
复制代码
It may be defined as only the recognition method performed at this stage
(2) multi-module project fails to compile aspectj
If when the multi-module program, in a specific sub-class module declaration section, defined cut point expression, but the point of attachment of each cut dispersed in other modules, to the tangent point with the scanning AJC expression, present only in the corresponding scanning module connection point, the connection point of the other modules are cut into the cut surface for Sounds of no way, ajc is not going to go when scanning unravel some other module has no connection with the current cut-off point expression match point
By adding custom annotations and are cut in each module, the compiler can solve problems
More operations
Since the custom annotation support assignment, Aspectj section and can intercept method, and the method parameters to get through reflection, so you can do more customized optimization on this basis
Reference links
Spring AOP implementation principles and application CGLIB
AspectJ about things you might not know
AspectJ aspect performed twice Analysis
Multi-module maven project using the Eclipse AspectJ weaving in for Sounds