Top

JAVA SPRINGMYBATIS02 UNIT05

  1. Spring AOP前置通知案例
  2. Spring AOP环绕通知案例
  3. Spring AOP异常通知案例
  4. Spring AOP注解使用案例

1 Spring AOP前置通知案例

1.1 问题

使用Spring AOP前置通知,在访问Controller中每个方法前,记录用户的操作日志。

1.2 方案

Spring AOP使用步骤:

1.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:创建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。请自己尝试将前置通知类型改为后置通知、最终通知,并执行测试方法,观察控制台的输出情况。

1.4 完整代码

本案例的完整代码如下所示:

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();
	}

}

2 Spring AOP环绕通知案例

2.1 问题

使用Spring AOP环绕通知,在访问Controller中每个方法前,记录用户的操作日志。

2.2 方案

Spring AOP使用步骤:

2.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:创建方面组件

复用方面组件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

2.4 完整代码

本案例的完整代码如下所示:

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>

3 Spring AOP异常通知案例

3.1 问题

使用Spring AOP异常通知,在每个Controller的方法发生异常时,记录异常日志。

3.2 方案

Spring AOP使用步骤:

3.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:创建方面组件

复用方面组件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

3.4 完整代码

本案例的完整代码如下所示:

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";
	}

}

4 Spring AOP注解使用案例

4.1 问题

使用Spring AOP注解替代XML配置,重构上面的3个案例。

4.2 方案

Spring AOP相关注解及含义如下:

@Aspect:用于声明方面组件

@Before:用于声明前置通知

@AfterReturning:用于声明后置通知

@After:用于声明最终通知

@Around:用于声明环绕通知

@AfterThrowing:用于声明异常通知

4.3 步骤

实现此案例需要按照如下步骤进行。

步骤一:开启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

4.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());
	}
	
}