Spring Oauth2大多數狀況下仍是用不到的,主要使用的仍是Spring+SpringMVC+Hibernate,有時候加上SpringSecurity,所以,本文及之後的文章的example中都不會包含oauth2的配置,須要的話把前文的applicationContext-security.xml和pom.xml加上就能夠了,本文在「SSH+Spring Security搭建方法及example」一文的基礎上作一些調整,主要內容是:配置Spring AOP而且用log4j來記錄日誌。html
1. 微調了部分配置文件web
前文的pom.xml、spring-dispatcher-servlet.xml、applicationContext.xml等配置文件都是一點一點加的,本文附的代碼作了一些調整。spring
這幾個文件不須要作什麼實質性的修改,只是作了微調,換了換內容的位置,很少作描述。apache
2. Spring AOP編程
面向方面編程 (Aspect Oriented Programming,AOP) 將程序分解成各個方面或者叫關注點。有了AOP,像事務管理這些功能就能夠橫切多個對象的關注點。AOP的主要功能就是記錄日誌、事務管理或者數據校驗,個人例子是爲了記錄日誌。app
Spring AOP的機制我兩年前用SSH的時候就不是很是清楚,因此這裏除了用法以外,多說幾個關鍵概念:ssh
下面就看具體的配置,當年是用XML來配置的,如今用aspectj提供的註解,要更方便和直觀一些。webapp
首先在pom.xml中引入aspectj的包(spring aop的包在前文中做爲依賴已經引入了):ui
<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類:spa
@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下面。