【JavaEE】SSH+Spring Security基礎上配置AOP+log4j


Spring Oauth2大多數情況下還是用不到的,主要使用的還是Spring+SpringMVC+Hibernate,有時候加上SpringSecurity,因此,本文及以后的文章的example中都不會包含oauth2的配置,需要的話把前文的applicationContext-security.xml和pom.xml加上就可以了,本文在“SSH+Spring Security搭建方法及example”一文的基礎上做一些調整,主要內容是:配置Spring AOP並且用log4j來記錄日志。

1. 微調了部分配置文件

前文的pom.xml、spring-dispatcher-servlet.xml、applicationContext.xml等配置文件都是一點一點加的,本文附的代碼做了一些調整。

這幾個文件不需要做什么實質性的修改,只是做了微調,換了換內容的位置,不多做描述。

2. Spring AOP

面向方面編程 (Aspect Oriented Programming,AOP) 將程序分解成各個方面或者叫關注點。有了AOP,像事務管理這些功能就可以橫切多個對象的關注點。AOP的主要功能就是記錄日志、事務管理或者數據校驗,我的例子是為了記錄日志。

Spring AOP的機制我兩年前用SSH的時候就不是非常清楚,所以這里除了用法之外,多說幾個關鍵概念:

  • Aspect:Aspect就是一個類,它的方法要插入到其他類執行過程中,這個類可以通過XML配置,也可以通過@Aspect注解與Spring的AspectJ整合,比如說我要在日志里為每一個DAO的查詢方法記錄執行時間,那么記錄時間的這些切面方法所在的類就是Aspect;
  • Join Point:連接點,就是要插入方面代碼的點,上面的例子中,每一個要被記錄時間的dao方法就是一個Joinpoint;
  • Advice:通知,某一個JoinPoint可以有多個通知,比如方法執行前“before”,方法執行前后“around”等,主要就是before、after、after returning、after throwing、arount這5種;
  • Pointcut:是匹配Join Point的一個表達式,來決定Advice是否要觸發。

下面就看具體的配置,當年是用XML來配置的,現在用aspectj提供的注解,要更方便和直觀一些。

首先在pom.xml中引入aspectj的包(spring aop的包在前文中作為依賴已經引入了):

<properties>
    ……
    <aspectj.version>1.8.2</aspectj.version>
</properties>
<dependencies>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjtools</artifactId>
        <version>${aspectj.version}</version>
    </dependency>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjrt</artifactId>
        <version>${aspectj.version}</version>
    </dependency>
</dependencies>

下面創建Aspect類,創建一個叫aspect的package(我的項目中是org.zhangfc.demo4ssh.aspect),並創建Aspect類:

@Component
@Aspect
public class LogAspect {

}

@Aspect是一個aspectj提供的注解,spring-aop可以根據這個注解以及PointCut等注解來解析aop配置,這兒需要注意的是@Component注解,我一開始怎么也配不出來,后來發現Spring容器里面根本沒有生成logAspect這個bean,因為@Aspect這個注解並不是Spring自己的,IoC模塊並不認識這個注解,所以沒有創建管理這個bean。加上@Component這個注解就是為了讓Spring來管理它。

下面加一個最簡單的通知:

@Before("execution(* org.zhangfc.demo4ssh.service..*.*(..))")
public void loggingServiceAdvice() {
    System.out.println("Executing Service...");
}

這個通知是說,在service包及service子包下的任何類的具有任意參數和返回值的任何方法,在執行之前(@Before)都要執行這個方法。

因為要讓Spring來管理這個bean,自然要配置一下讓Spring去自動掃描Aspect所在的package,打開applicationContext.xml,加入:

<context:component-scan base-package="org.zhangfc.demo4ssh.aspect"/>

因為我現在配置aop主要是為了記錄日志,所以aop的配置放在hibernate的配置文件里,打開infrastructure.xml,在最后加入一句讓aop自動代理aspectj的配置:

<aop:aspectj-autoproxy />

然后運行程序,在執行Service方法的時候,就會打印出Executing Service...的語句。這只是一個簡單的demo,下面來配置我們真正需要的,對dao訪問的日志,首先來把程序中拋異常的情況都記下來,在LogAspect類中添加下面的方法:

@AfterThrowing(value="within(org.zhangfc.demo4ssh..*)", throwing = "ex")
public void loggingExceptions(JoinPoint joinPoint, Exception ex) {
    System.err.println("Exception thrown in Method = "
            + joinPoint.toString() + " " + ex.getClass().getSimpleName() + " = " + ex.getMessage());
}

@AfterThrowing是在匹配的方法拋出異常的情況下執行,我這個配置就是指明在我的項目包下面任意package的任意類中,拋出異常之后都會讓loggingExceptions來記錄,現在我在某個Dao方法中寫了一行會拋異常的代碼:

Integer.parseInt("asd");

運行過程中就會打印如下的內容:

Exception thrown in Method = execution(List org.zhangfc.demo4ssh.service.UserService.getAllUsers()) NumberFormatException = For input string: "asd"

最后配置dao的訪問記錄,我要在每一個dao方法執行前后各創建一個切面,那么就要用到@Around通知了,首先來創建一個Pointcut:

@Pointcut("execution(* org.zhangfc.demo4ssh.repo..*.*(..))")
public void daoPointCut() {
}

這個Pointcut匹配了repo package及子package下的任意類的任意方法,之后就可以根據這個Pointcut來創建Around通知:

@Around("daoPointCut()")
public Object loggingAround(ProceedingJoinPoint joinpoint) throws Throwable {
    long start = System.currentTimeMillis();
    System.out.println("method starts...."
            + joinpoint.getSignature().getDeclaringTypeName() + "_"
            + joinpoint.getSignature().getName() + " with "
            + arrayToString(joinpoint.getArgs()));
    Object result = joinpoint.proceed();
    long diff = System.currentTimeMillis() - start;
    System.out.println("method ends...."
            + joinpoint.getSignature().getDeclaringTypeName() + "_"
            + joinpoint.getSignature().getName() + " with " + diff + "ms");
    return result;
}

private String arrayToString(Object[] traces) {
    StringBuilder trace = new StringBuilder();
    for (Object s : traces) {
        trace.append(s == null ? "" : s.toString() + "\t");
    }
    if (trace.length() == 0) {
        trace.append("no parameter");
    }
    return trace.toString();
}

運行輸出如下:

method starts....org.zhangfc.demo4ssh.repo.UserDao_findAll with no parameter
method ends....org.zhangfc.demo4ssh.repo.UserDao_findAll with 537ms

3. Log4j

AOP就說到這兒,下面來配置log4j,首先導入log4j的jar包:

<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

log4j默認的配置文件需要放在classpath下,而且不能是子目錄下,配置文件多了這就非常不方便,如果覺得放在classpath下沒有問題,那么把配置文件放過去就可以直接運行了,我現在要改這個配置文件的目錄,正好,spring的一個類可以幫我們處理這個事情,不過要在web.xml里面配置一下:

<context-param>
    <param-name>log4jConfigLocation</param-name>
    <param-value>classpath:/META-INF/properties/log4j.properties</param-value>
</context-param>
<context-param>
    <param-name>log4jRefreshInterval</param-name>
    <param-value>60000</param-value>
</context-param>
<listener>
    <listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>

這個配置要放在spring listener的前面,指定配置文件位置並且每60秒重新讀取配置文件,這樣如果變更了配置也不需要重啟應用。配置文件的配置方法我就不詳述了,這里貼出來我的配置,我把配置往Console和文件里各輸出了一份:

log4j.rootLogger=INFO,CONSOLE,ROLLING_FILE
#INFO,CONSOLE,ROLLING_FILE
#ERROR,ROLLING_FILE

###################
# Console Appender
###################
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.Threshold=INFO
log4j.appender.CONSOLE.Target=System.out
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern= [%p] %d %c - %m%n

########################
# Rolling File
########################
log4j.appender.ROLLING_FILE=org.apache.log4j.RollingFileAppender
log4j.appender.ROLLING_FILE.Threshold=INFO
log4j.appender.ROLLING_FILE.File=${webapp.root}/WEB-INF/webapp.log
log4j.appender.ROLLING_FILE.Append=true
log4j.appender.ROLLING_FILE.MaxFileSize=5000KB
log4j.appender.ROLLING_FILE.MaxBackupIndex=2
log4j.appender.ROLLING_FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.ROLLING_FILE.layout.ConversionPattern=[%p] %d %c - %m%n

輸出的格式demo如下:

[INFO] 2014-11-22 11:30:07,944 LogAspect - method ends....org.zhangfc.demo4ssh.repo.UserDao_findAll with 385ms

日志文件按配置放在了項目的WEB-INF下面。

源碼下載

 


免責聲明!

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



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