Spring AOP在函數接口調用性能分析及其日志處理方面的應用


面向切面編程可以實現在不修改原來代碼的情況下,增加我們所需的業務處理邏輯,比如:添加日志。本文AOP實例是基於Aspect Around注解實現的,我們需要在調用API函數的時候,統計函數調用的具體信息,包括:函數簽名,傳入參數,函數處理時間,異常信息攔截等, @Around是可以同時在所攔截方法的前后執行一段邏輯,可以滿足我們的需求。

目標對象

目標對象是一個客戶管理服務,下面分別是其服務接口定義和具體業務邏輯實現。

API

public interface CustomerManagerService {
    void addCustomer(String customer) throws CustomeExistException;
}

Implementation

public class customerManager implements CustomerManagerService {

    private List<String> list = Lists.newArrayList();

    public void addCustomer(String customer) throws CustomeExistException {
        if (list.contains(customer)) {
            throw new CustomeExistException("Customer exists, repeat operation");
         }
        list.add(customer);
    }
    @Override
    public String toString() {
        return "customerManager{" +
            "list=" + list +
            '}';
    }
}

切面類代理

切面代理可以實現對客戶管理服務的控制,在切面類中定義Advice函數,設定切面要增加的業務邏輯。APIProxy定義了一個簡單的切面類around advice,作用范圍為com.mj.spring.aop.api包下面所有的函數, 當作用域內的函數被調用時會執行aroundAdvice中的業務邏輯。

@Aspect
public class APIProxy {

    private final static Log LOGGER = LogFactory.getLog(APIProxy.class);

    //切面應用范圍是在com.mj.spring.aop.api下面所有的接口函數
    @Around("execution(* com.mj.spring.aop.api..*.*(..))")
    public void aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {

        Signature signature = proceedingJoinPoint.getSignature();
        String args = Arrays.toString(proceedingJoinPoint.getArgs());

        long start = System.currentTimeMillis();
        try {
            proceedingJoinPoint.proceed();
        } catch (Exception e) {
            if (e instanceof CustomeExistException) {
                LOGGER.warn(e.getMessage());
            }
            LOGGER.error(String.format("Method:%s call failed  parameter input:%s",
                    signature,
                    args), e);
        } finally {
            LOGGER.info(String.format("method:%s  parameter input:%s carry_out_time:%s ms",
                    signature, args, System.currentTimeMillis() - start));
        }
    }
}

ProceedingJoinPoint接口

ProceedingJoinPoint接口提供了很多實用的函數,便於用戶獲取應用切面點函數具體的信息。下面四個接口是我們用的比較多的:

  1. Object proceed() throws Throwable; 調用要攔截的方法
  2. Object proceed(Object[] var1) throws Throwable;調用要攔截的方法,可以自定義傳入參數
  3. Object[] getArgs();獲取攔截方法的傳入參數
  4. Signature getSignature();獲取攔截方法的方法簽名

XML配置

<?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:aop="http://www.springframework.org/schema/aop"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans
			http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
			http://www.springframework.org/schema/aop
			http://www.springframework.org/schema/aop/spring-aop-3.1.xsd">
    <!-- 使能AOP-->
    <aop:aspectj-autoproxy/>


    <!--聲明切面類 -->
    <bean id="apiProxy" class="com.mj.spring.aop.aspect.APIProxy"></bean>
    <!-- 聲明customerManager bean-->
    <bean id="customerManagerService" class="com.mj.spring.aop.impl.customerManager"></bean>
	
</beans>

測試類

public class customerManagerTest {
    private ApplicationContext applicationContext = null;

    @Before
    public void setUp() throws Exception {
        applicationContext = new ClassPathXmlApplicationContext("ApplicationContext.xml");
    }

    @Test
    public void should_print_normal_log_info_when_add_a_customer() throws Exception {
        CustomerManagerService customerManagerService = (CustomerManagerService) applicationContext.getBean("customerManagerService");
        customerManagerService.addCustomer("Mengya");
    }

    @Test
    public void should_print_warn_log_info_when_add_a_same_customer_twice() throws Exception {
        CustomerManagerService customerManagerService = (CustomerManagerService) applicationContext.getBean("customerManagerService");
        customerManagerService.addCustomer("Mengya");
        customerManagerService.addCustomer("Mengya");
    }
}

運行第一個Test,接口調用信息:

2015-09-24 18:25:42,000 INFO [com.mj.spring.aop.aspect.APIProxy] - method:void com.mj.spring.aop.api.CustomerManagerService.addCustomer(String)  parameter input:[Mengya] carry_out_time:0 ms

運行第二個Test,異常報錯信息會被攔截下來:

2015-09-24 18:26:58,885 INFO [com.mj.spring.aop.aspect.APIProxy] - method:void com.mj.spring.aop.api.CustomerManagerService.addCustomer(String)  parameter input:[Mengya] carry_out_time:0 ms
2015-09-24 18:26:58,886 WARN [com.mj.spring.aop.aspect.APIProxy] - Customer exists, repeat operation
2015-09-24 18:26:58,888 ERROR [com.mj.spring.aop.aspect.APIProxy] - Method:void com.mj.spring.aop.api.CustomerManagerService.addCustomer(String) call failed  parameter input:[Mengya]
com.mj.spring.aop.impl.CustomeExistException: Customer exists, repeat operation
	at com.mj.spring.aop.impl.customerManager.addCustomer(customerManager.java:16)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:497)
	at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:302)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:190)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
	at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:85)
	at com.mj.spring.aop.aspect.APIProxy.aroundAdvice(APIProxy.java:30)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:497)
	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:621)
	at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:610)
	at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:68)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
	at com.sun.proxy.$Proxy12.addCustomer(Unknown Source)
	at com.mj.spring.aop.impl.customerManagerTest.should_print_normal_log_info_when_add_a_same_customer_twice(customerManagerTest.java:30)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:497)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:157)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:78)
	at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:212)
	at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:68)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:497)
	at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)
2015-09-24 18:26:58,905 INFO [com.mj.spring.aop.aspect.APIProxy] - method:void com.mj.spring.aop.api.CustomerManagerService.addCustomer(String)  parameter input:[Mengya] carry_out_time:20 ms

Console窗口日志無法打印問題

使用log4j有時候我們會遇到日志無法在concole窗口打印的問題,console窗口提示日志配置異常。

log4j:WARN No appenders could be found for logger (org.springframework.core.env.StandardEnvironment).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.

解決這個問題很簡單,和src平級目錄創建log4j.properties文件,輸入下面內容,就可以解決這個問題了

log4j.rootLogger=INFO,stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n

Conslusion

本文講解了Spring AOP在函數接口調用性能分析及其日志處理方面的應用,希望能夠給大家帶來一些幫助和啟發。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM