使用Spring AOP前置通知,在访问Controller中每个方法前,记录用户的操作日志。
Spring AOP使用步骤:
实现此案例需要按照如下步骤进行。
步骤一:创建Controller
复制项目SpringUnit04,创建新项目SpringUnit05。
创建员工业务控制器EmpController,并实现员工查询,代码如下:
package com.tarena.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller @RequestMapping("/emp") public class EmpController { /** * 查询员工 */ @RequestMapping("/findEmp.do") public String find() { // 模拟查询员工数据 System.out.println("查询员工数据,发送至列表页面."); return "emp/emp_list.jsp"; } }
步骤二:创建方面组件
创建方面组件OperateLogger,并在该类中创建记录用户操作日志的方法,代码如下:
package com.tarena.aspect; import java.text.SimpleDateFormat; import java.util.Date; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; /** * 用于记录日志的方面组件,演示Spring AOP的各种通知类型。 */ public class OperateLogger { /** * 前置通知、后置通知、最终通知使用的方法 */ public void log1() { // 记录日志 System.out.println("-->记录用户操作信息"); } }
步骤三:声明方面组件
在applicationContext.xml中,声明该方面组件,关键代码如下:
#cold_bold <!-- 声明方面组件 --> #cold_bold <bean id="operateLogger" class="com.tarena.aspect.OperateLogger"/>
步骤四:将方面组件作用到目标组件上
在applicationContext.xml中,将声明的方面组件作用到com.tarena.controller包下所有类的所有方法上,关键代码如下:
<!-- 声明方面组件 --> <bean id="operateLogger" class="com.tarena.aspect.OperateLogger"/> #cold_bold <!-- 配置AOP --> #cold_bold <aop:config> #cold_bold <aop:aspect ref="operateLogger"> #cold_bold <aop:before method="log1" #cold_bold pointcut="within(com.tarena.controller..*)"/> #cold_bold </aop:aspect> #cold_bold </aop:config>
步骤五:测试
创建Junit测试类TestEmpController,并增加测试查询员工的方法,代码如下:
package com.tarena.test; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.tarena.controller.EmpController; public class TestEmpController { /** * 测试查询员工 */ @Test public void test1() { ApplicationContext ctx = new ClassPathXmlApplicationContext( "applicationContext.xml"); EmpController ctl = ctx.getBean(EmpController.class); ctl.find(); } }
执行该测试方法,控制台输出效果如下图:
图-1
可见,在执行EmpController.find()方法之前,执行了方面组件的记录日志的方法,由于该方法采用AOP面向对象的思想实现的,因此不需要对Controller类做任何改动。
步骤六:扩展
后置通知、最终通知的用法与前置通知完全一致,只需要在配置AOP时将aop:before改为aop: after-returning和aop:after。请自己尝试将前置通知类型改为后置通知、最终通知,并执行测试方法,观察控制台的输出情况。
本案例的完整代码如下所示:
EmpController完整代码如下:
package com.tarena.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller @RequestMapping("/emp") public class EmpController { /** * 查询员工 */ @RequestMapping("/findEmp.do") public String find() { // 模拟查询员工数据 System.out.println("查询员工数据,发送至列表页面."); return "emp/emp_list.jsp"; } }
OperateLogger完整代码如下:
package com.tarena.aspect; import java.text.SimpleDateFormat; import java.util.Date; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; /** * 用于记录日志的方面组件,演示Spring AOP的各种通知类型。 */ public class OperateLogger { /** * 前置通知、后置通知、最终通知使用的方法 */ public void log1() { // 记录日志 System.out.println("-->记录用户操作信息"); } }
applicationContext.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:context="http://www.springframework.org/schema/context" xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:jpa="http://www.springframework.org/schema/data/jpa" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd"> <!-- 配置数据源 --> <bean id="ds" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="url" value="jdbc:oracle:thin:@localhost:1521:xe"/> <property name="driverClassName" value="oracle.jdbc.OracleDriver"/> <property name="username" value="lhh"/> <property name="password" value="123456"/> </bean> <!-- 配置SqlSessionFactory --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="ds" /> <property name="mapperLocations" value="classpath:com/tarena/entity/*.xml"/> </bean> <!-- 配置MyBatis注解 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.tarena.dao" /> <property name="annotationClass" value="com.tarena.annotation.MyBatisRepository"/> </bean> <!-- 开启注解扫描 --> <context:component-scan base-package="com.tarena" /> <!-- 支持@RequestMapping请求和Controller映射 --> <mvc:annotation-driven /> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/"/> <property name="suffix" value=".jsp"/> </bean> <!-- 声明方面组件 --> <bean id="operateLogger" class="com.tarena.aspect.OperateLogger"/> <!-- 配置AOP --> <aop:config> <aop:aspect ref="operateLogger"> <aop:before method="log1" pointcut="within(com.tarena.controller..*)"/> </aop:aspect> </aop:config> </beans>
TestEmpController完整代码如下:
package com.tarena.test; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.tarena.controller.EmpController; public class TestEmpController { /** * 测试查询员工 */ @Test public void test1() { ApplicationContext ctx = new ClassPathXmlApplicationContext( "applicationContext.xml"); EmpController ctl = ctx.getBean(EmpController.class); ctl.find(); } }
使用Spring AOP环绕通知,在访问Controller中每个方法前,记录用户的操作日志。
Spring AOP使用步骤:
实现此案例需要按照如下步骤进行。
步骤一:创建方面组件
复用方面组件OperateLogger,在该类中创建新的记录日志的方法log2,代码如下:
package com.tarena.aspect; import java.text.SimpleDateFormat; import java.util.Date; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; /** * 用于记录日志的方面组件,演示Spring AOP的各种通知类型。 */ public class OperateLogger { //其他方法略 #cold_bold /** #cold_bold * 环绕通知使用的方法 #cold_bold */ #cold_bold public Object log2(ProceedingJoinPoint p) throws Throwable { #cold_bold // 目标组件的类名 #cold_bold String className = p.getTarget().getClass().getName(); #cold_bold // 调用的方法名 #cold_bold String method = p.getSignature().getName(); #cold_bold // 当前系统时间 #cold_bold String date = new SimpleDateFormat( #cold_bold "yyyy-MM-dd hh:mm:ss").format(new Date()); #cold_bold // 拼日志信息 #cold_bold String msg = "-->用户在" + date + ",执行了" #cold_bold + className + "." + method + "()"; #cold_bold // 记录日志 #cold_bold System.out.println(msg); #cold_bold #cold_bold // 执行目标组件的方法 #cold_bold Object obj = p.proceed(); #cold_bold #cold_bold // 在调用目标组件业务方法后也可以做一些业务处理 #cold_bold System.out.println("-->调用目标组件业务方法后..."); #cold_bold #cold_bold return obj; #cold_bold } }
步骤二:声明方面组件
由于复用的方面组件已经声明,因此该步骤可以省略。
步骤三:将方面组件作用到目标组件上
在applicationContext.xml中,声明方面组件的log2方法,关键代码如下:
<!-- 声明方面组件 --> <bean id="operateLogger" class="com.tarena.aspect.OperateLogger"/> <!-- 配置AOP --> <aop:config> <aop:aspect ref="operateLogger"> <aop:before method="log1" pointcut="within(com.tarena.controller..*)"/> </aop:aspect> #cold_bold <aop:aspect ref="operateLogger"> #cold_bold <aop:around method="log2" #cold_bold pointcut="within(com.tarena.controller..*)"/> #cold_bold </aop:aspect> </aop:config>
步骤四:测试
执行测试方法TestEmpController.test1(),控制台输出效果如下图:
图-2
本案例的完整代码如下所示:
OperateLogger完整代码如下:
package com.tarena.aspect; import java.text.SimpleDateFormat; import java.util.Date; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; /** * 用于记录日志的方面组件,演示Spring AOP的各种通知类型。 */ public class OperateLogger { /** * 前置通知、后置通知、最终通知使用的方法 */ public void log1() { // 记录日志 System.out.println("-->记录用户操作信息"); } /** * 环绕通知使用的方法 */ public Object log2(ProceedingJoinPoint p) throws Throwable { // 目标组件的类名 String className = p.getTarget().getClass().getName(); // 调用的方法名 String method = p.getSignature().getName(); // 当前系统时间 String date = new SimpleDateFormat( "yyyy-MM-dd hh:mm:ss").format(new Date()); // 拼日志信息 String msg = "-->用户在" + date + ",执行了" + className + "." + method + "()"; // 记录日志 System.out.println(msg); // 执行目标组件的方法 Object obj = p.proceed(); // 在调用目标组件业务方法后也可以做一些业务处理 System.out.println("-->调用目标组件业务方法后..."); return obj; } }
applicationContext.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:context="http://www.springframework.org/schema/context" xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:jpa="http://www.springframework.org/schema/data/jpa" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd"> <!-- 配置数据源 --> <bean id="ds" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="url" value="jdbc:oracle:thin:@localhost:1521:xe"/> <property name="driverClassName" value="oracle.jdbc.OracleDriver"/> <property name="username" value="lhh"/> <property name="password" value="123456"/> </bean> <!-- 配置SqlSessionFactory --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="ds" /> <property name="mapperLocations" value="classpath:com/tarena/entity/*.xml"/> </bean> <!-- 配置MyBatis注解 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.tarena.dao" /> <property name="annotationClass" value="com.tarena.annotation.MyBatisRepository"/> </bean> <!-- 开启注解扫描 --> <context:component-scan base-package="com.tarena" /> <!-- 支持@RequestMapping请求和Controller映射 --> <mvc:annotation-driven /> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/"/> <property name="suffix" value=".jsp"/> </bean> <!-- 声明方面组件 --> <bean id="operateLogger" class="com.tarena.aspect.OperateLogger"/> <!-- 配置AOP --> <aop:config> <aop:aspect ref="operateLogger"> <aop:before method="log1" pointcut="within(com.tarena.controller..*)"/> </aop:aspect> <aop:aspect ref="operateLogger"> <aop:around method="log2" pointcut="within(com.tarena.controller..*)"/> </aop:aspect> </aop:config> </beans>
使用Spring AOP异常通知,在每个Controller的方法发生异常时,记录异常日志。
Spring AOP使用步骤:
实现此案例需要按照如下步骤进行。
步骤一:创建方面组件
复用方面组件OperateLogger,在该类中创建新的记录日志的方法log3,代码如下:
package com.tarena.aspect; import java.text.SimpleDateFormat; import java.util.Date; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; /** * 用于记录日志的方面组件,演示Spring AOP的各种通知类型。 */ public class OperateLogger { //其他方法略 #cold_bold /** #cold_bold * 异常通知使用的方法 #cold_bold */ #cold_bold public void log3(Exception e) { #cold_bold StackTraceElement[] elems = e.getStackTrace(); #cold_bold // 将异常信息记录 #cold_bold System.out.println("-->" + elems[0].toString()); #cold_bold } }
步骤二:声明方面组件
由于复用的方面组件已经声明,因此该步骤可以省略。
步骤三:将方面组件作用到目标组件上
在applicationContext.xml中,声明方面组件的log3方法,关键代码如下:
<!-- 声明方面组件 --> <bean id="operateLogger" class="com.tarena.aspect.OperateLogger"/> <!-- 配置AOP --> <aop:config> <aop:aspect ref="operateLogger"> <aop:before method="log1" pointcut="within(com.tarena.controller..*)"/> </aop:aspect> <aop:aspect ref="operateLogger"> <aop:around method="log2" pointcut="within(com.tarena.controller..*)"/> </aop:aspect> #cold_bold <aop:aspect ref="operateLogger"> #cold_bold <aop:after-throwing method="log3" throwing="e" #cold_bold pointcut="within(com.tarena.controller..*)"/> #cold_bold </aop:aspect> </aop:config>
步骤四:测试
为了便于测试,在EmpController.find()方法中制造一个异常,代码如下:
package com.tarena.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller @RequestMapping("/emp") public class EmpController { /** * 查询员工 */ @RequestMapping("/findEmp.do") public String find() { // 模拟查询员工数据 System.out.println("查询员工数据,发送至列表页面."); #cold_bold // 制造一个异常,便于测试异常通知 #cold_bold Integer.valueOf("abc"); return "emp/emp_list.jsp"; } }
执行测试方法TestEmpController.test1(),控制台输出效果如下图:
图-3
本案例的完整代码如下所示:
OperateLogger完整代码如下:
package com.tarena.aspect; import java.text.SimpleDateFormat; import java.util.Date; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; /** * 用于记录日志的方面组件,演示Spring AOP的各种通知类型。 */ public class OperateLogger { /** * 前置通知、后置通知、最终通知使用的方法 */ public void log1() { // 记录日志 System.out.println("-->记录用户操作信息"); } /** * 环绕通知使用的方法 */ public Object log2(ProceedingJoinPoint p) throws Throwable { // 目标组件的类名 String className = p.getTarget().getClass().getName(); // 调用的方法名 String method = p.getSignature().getName(); // 当前系统时间 String date = new SimpleDateFormat( "yyyy-MM-dd hh:mm:ss").format(new Date()); // 拼日志信息 String msg = "-->用户在" + date + ",执行了" + className + "." + method + "()"; // 记录日志 System.out.println(msg); // 执行目标组件的方法 Object obj = p.proceed(); // 在调用目标组件业务方法后也可以做一些业务处理 System.out.println("-->调用目标组件业务方法后..."); return obj; } /** * 异常通知使用的方法 */ public void log3(Exception e) { StackTraceElement[] elems = e.getStackTrace(); // 将异常信息记录 System.out.println("-->" + elems[0].toString()); } }
applicationContext.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:context="http://www.springframework.org/schema/context" xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:jpa="http://www.springframework.org/schema/data/jpa" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd"> <!-- 配置数据源 --> <bean id="ds" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="url" value="jdbc:oracle:thin:@localhost:1521:xe"/> <property name="driverClassName" value="oracle.jdbc.OracleDriver"/> <property name="username" value="lhh"/> <property name="password" value="123456"/> </bean> <!-- 配置SqlSessionFactory --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="ds" /> <property name="mapperLocations" value="classpath:com/tarena/entity/*.xml"/> </bean> <!-- 配置MyBatis注解 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.tarena.dao" /> <property name="annotationClass" value="com.tarena.annotation.MyBatisRepository"/> </bean> <!-- 开启注解扫描 --> <context:component-scan base-package="com.tarena" /> <!-- 支持@RequestMapping请求和Controller映射 --> <mvc:annotation-driven /> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/"/> <property name="suffix" value=".jsp"/> </bean> <!-- 声明方面组件 --> <bean id="operateLogger" class="com.tarena.aspect.OperateLogger"/> <!-- 配置AOP --> <aop:config> <aop:aspect ref="operateLogger"> <aop:before method="log1" pointcut="within(com.tarena.controller..*)"/> </aop:aspect> <aop:aspect ref="operateLogger"> <aop:around method="log2" pointcut="within(com.tarena.controller..*)"/> </aop:aspect> <aop:aspect ref="operateLogger"> <aop:after-throwing method="log3" throwing="e" pointcut="within(com.tarena.controller..*)"/> </aop:aspect> </aop:config> </beans>
EmpController完整代码如下:
package com.tarena.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller @RequestMapping("/emp") public class EmpController { /** * 查询员工 */ @RequestMapping("/findEmp.do") public String find() { // 模拟查询员工数据 System.out.println("查询员工数据,发送至列表页面."); // 制造一个异常,便于测试异常通知 Integer.valueOf("abc"); return "emp/emp_list.jsp"; } }
使用Spring AOP注解替代XML配置,重构上面的3个案例。
Spring AOP相关注解及含义如下:
@Aspect:用于声明方面组件
@Before:用于声明前置通知
@AfterReturning:用于声明后置通知
@After:用于声明最终通知
@Around:用于声明环绕通知
@AfterThrowing:用于声明异常通知
实现此案例需要按照如下步骤进行。
步骤一:开启AOP注解扫描
在applicationContext.xml中,去掉方面组件声明及作用的XML配置,并开启AOP注解扫描,关键代码如下:
#cold_bold <!-- 声明方面组件 --> #cold_bold <!-- <bean id="operateLogger" class="com.tarena.aspect.OperateLogger"/> --> #cold_bold #cold_bold <!-- 配置AOP --> #cold_bold <!-- <aop:config> #cold_bold <aop:aspect ref="operateLogger"> #cold_bold <aop:before method="log1" #cold_bold pointcut="within(com.tarena.controller..*)"/> #cold_bold </aop:aspect> #cold_bold <aop:aspect ref="operateLogger"> #cold_bold <aop:around method="log2" #cold_bold pointcut="within(com.tarena.controller..*)"/> #cold_bold </aop:aspect> #cold_bold <aop:aspect ref="operateLogger"> #cold_bold <aop:after-throwing method="log3" throwing="e" #cold_bold pointcut="within(com.tarena.controller..*)"/> #cold_bold </aop:aspect> #cold_bold </aop:config> --> #cold_bold #cold_bold <!-- 开启AOP注解扫描 --> #cold_bold <aop:aspectj-autoproxy proxy-target-class="true"/>
步骤二:使用注解声明方面组件
在OperateLogger中,使用@Aspect注解声明方面组件,并分别用@Before、@Around、@AfterThrowing注解声明log1、log2、log3方法,将方面组件作用到目标组件上,代码如下:
package com.tarena.aspect; import java.text.SimpleDateFormat; import java.util.Date; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; /** * 用于记录日志的方面组件,演示Spring AOP的各种通知类型。 */ #cold_bold@Component #cold_bold@Aspect public class OperateLogger { /** * 前置通知、后置通知、最终通知使用的方法 */ #cold_bold @Before("within(com.tarena.controller..*)") public void log1() { // 记录日志 System.out.println("-->记录用户操作信息"); } /** * 环绕通知使用的方法 */ #cold_bold @Around("within(com.tarena.controller..*)") public Object log2(ProceedingJoinPoint p) throws Throwable { // 目标组件的类名 String className = p.getTarget().getClass().getName(); // 调用的方法名 String method = p.getSignature().getName(); // 当前系统时间 String date = new SimpleDateFormat( "yyyy-MM-dd hh:mm:ss").format(new Date()); // 拼日志信息 String msg = "-->用户在" + date + ",执行了" + className + "." + method + "()"; // 记录日志 System.out.println(msg); // 执行目标组件的方法 Object obj = p.proceed(); // 在调用目标组件业务方法后也可以做一些业务处理 System.out.println("-->调用目标组件业务方法后..."); return obj; } /** * 异常通知使用的方法 */ #cold_bold @AfterThrowing(pointcut="within(com.tarena.controller..*)",throwing="e") public void log3(Exception e) { StackTraceElement[] elems = e.getStackTrace(); // 将异常信息记录 System.out.println("-->" + elems[0].toString()); } }
步骤三:测试
执行测试方法TestEmpController.test1(),控制台输出效果如下图:
图-4
本案例的完整代码如下所示:
applicationContext.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:context="http://www.springframework.org/schema/context" xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:jpa="http://www.springframework.org/schema/data/jpa" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd"> <!-- 配置数据源 --> <bean id="ds" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="url" value="jdbc:oracle:thin:@localhost:1521:xe"/> <property name="driverClassName" value="oracle.jdbc.OracleDriver"/> <property name="username" value="lhh"/> <property name="password" value="123456"/> </bean> <!-- 配置SqlSessionFactory --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="ds" /> <property name="mapperLocations" value="classpath:com/tarena/entity/*.xml"/> </bean> <!-- 配置MyBatis注解 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.tarena.dao" /> <property name="annotationClass" value="com.tarena.annotation.MyBatisRepository"/> </bean> <!-- 开启注解扫描 --> <context:component-scan base-package="com.tarena" /> <!-- 支持@RequestMapping请求和Controller映射 --> <mvc:annotation-driven /> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/"/> <property name="suffix" value=".jsp"/> </bean> <!-- 声明方面组件 --> <!-- <bean id="operateLogger" class="com.tarena.aspect.OperateLogger"/> --> <!-- 配置AOP --> <!-- <aop:config> <aop:aspect ref="operateLogger"> <aop:before method="log1" pointcut="within(com.tarena.controller..*)"/> </aop:aspect> <aop:aspect ref="operateLogger"> <aop:around method="log2" pointcut="within(com.tarena.controller..*)"/> </aop:aspect> <aop:aspect ref="operateLogger"> <aop:after-throwing method="log3" throwing="e" pointcut="within(com.tarena.controller..*)"/> </aop:aspect> </aop:config> --> <!-- 开启AOP注解扫描 --> <aop:aspectj-autoproxy proxy-target-class="true"/> </beans>
OperateLogger完整代码如下:
package com.tarena.aspect; import java.text.SimpleDateFormat; import java.util.Date; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; /** * 用于记录日志的方面组件,演示Spring AOP的各种通知类型。 */ @Component @Aspect public class OperateLogger { /** * 前置通知、后置通知、最终通知使用的方法 */ @Before("within(com.tarena.controller..*)") public void log1() { // 记录日志 System.out.println("-->记录用户操作信息"); } /** * 环绕通知使用的方法 */ @Around("within(com.tarena.controller..*)") public Object log2(ProceedingJoinPoint p) throws Throwable { // 目标组件的类名 String className = p.getTarget().getClass().getName(); // 调用的方法名 String method = p.getSignature().getName(); // 当前系统时间 String date = new SimpleDateFormat( "yyyy-MM-dd hh:mm:ss").format(new Date()); // 拼日志信息 String msg = "-->用户在" + date + ",执行了" + className + "." + method + "()"; // 记录日志 System.out.println(msg); // 执行目标组件的方法 Object obj = p.proceed(); // 在调用目标组件业务方法后也可以做一些业务处理 System.out.println("-->调用目标组件业务方法后..."); return obj; } /** * 异常通知使用的方法 */ @AfterThrowing(pointcut="within(com.tarena.controller..*)",throwing="e") public void log3(Exception e) { StackTraceElement[] elems = e.getStackTrace(); // 将异常信息记录 System.out.println("-->" + elems[0].toString()); } }