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下面。