2017/12/31

AspectJ,一个面向切面的框架。@AspectJ 是AspectJ版本5的新增功能,所以确保JDK是5.0以上版本,负责无法使用注解技术。

(1)Spring通过集成AspectJ实现了以注解定义切面,大大减少了配置文件的工作量。

(2)java的反射机制无法获取方法参数名,Spring还利用了字节码处理框架asm处理@AspectJ描述的方法参数名。


下面通过一个实例演示:

【0】文件结构和jar包

Spring(11):使用@AspectJ 注解实现AOP日志打印与示例_spring

图1


Spring(11):使用@AspectJ 注解实现AOP日志打印与示例_sql_02

图2


【1】新建一个包(com\smbms\AopLog)新建两个增强类(UserserviceLogger、UserserviceLogger2):

UserserviceLogger.java:


package com.smbms.AopLog;

import java.util.Arrays;

import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class UserserviceLogger {
private static final Logger log = Logger.getLogger(UserserviceLogger.class);
//前置增强方法
@Before("execution(* com.smbms.pojo..*.*(..))")
public void before (JoinPoint jp) throws Throwable{
log.info("***UserserviceLogger*********before:调用了"+jp.getTarget()+" 的"+jp.getSignature().getName()
+" 方法。方法入参:"+Arrays.toString(jp.getArgs()));
try{
log.info("调用了 "+jp.getTarget()+" 的 "+jp.getSignature().getName()
+" 方法。");
}catch(Throwable e){
log.error(jp.getSignature().getName()+" 方法发生异常: "+e);
throw e;
}finally{
log.info(jp.getSignature().getName()+"方法执行结束");
}
}

@AfterReturning(pointcut = "execution(* com.smbms.pojo..*.*(..))",
returning = "result")
//后置增强方法
public void after (JoinPoint jp,Object result) throws Throwable{
log.info("****UserserviceLogger**********after:调用了 "+jp.getTarget()+" 的"+jp.getSignature().getName()
+" 方法。方法入参:"+Arrays.toString(jp.getArgs())+result);
try{
log.info("调用了 "+jp.getTarget()+" 的 "+jp.getSignature().getName()
+" 方法");
}catch(Throwable e){
log.error(jp.getSignature().getName()+" 方法发生异常: "+e);
throw e;
}finally{
log.info(jp.getSignature().getName()+"方法执行结束");
}
}
}



解释:

1、@Aspect 将某个类定义为切面,使用@Before 将before() 方法定义为前置增强,使用@AfterReturning 将after()方法定义为后置增强。

2、为了获取当前连接点的信息,添加JoinPoint类型参数,Spring会自动注入该实例。

3、对于后置增强,还可以定义一个参数,用于接收目标方法的返回值,但要在@AfterReturning的returning属性指定参数名,Spring会自动将目标方法返回值赋值给指定名称参数。



UserserviceLogger2.java:


package com.smbms.AopLog;

import java.util.Arrays;

import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class UserserviceLogger2 {
private static final Logger log = Logger.getLogger(UserserviceLogger.class);

@Pointcut("execution(* com.smbms.pojo..*.*(..))")
public void pointcut(){}

//前置增强方法
@Before("pointcut()")
public void before (JoinPoint jp) throws Throwable{
log.info("***UserserviceLogger2*********before:调用了"+jp.getTarget()+" 的"+jp.getSignature().getName()
+" 方法。方法入参:"+Arrays.toString(jp.getArgs()));
try{
log.info("调用了 "+jp.getTarget()+" 的 "+jp.getSignature().getName()
+" 方法。");
}catch(Throwable e){
log.error(jp.getSignature().getName()+" 方法发生异常: "+e);
throw e;
}finally{
log.info(jp.getSignature().getName()+"方法执行结束");
}
}

@AfterReturning(pointcut = "pointcut()",returning = "result")
//后置增强方法
public void after (JoinPoint jp,Object result) throws Throwable{
log.info("***UserserviceLogger2***********after:调用了 "+jp.getTarget()+" 的"+jp.getSignature().getName()
+" 方法。方法入参:"+Arrays.toString(jp.getArgs())+result);
try{
log.info("调用了 "+jp.getTarget()+" 的 "+jp.getSignature().getName()
+" 方法");
}catch(Throwable e){
log.error(jp.getSignature().getName()+" 方法发生异常: "+e);
throw e;
}finally{
log.info(jp.getSignature().getName()+"方法执行结束");
}
}
}


解释:

1、@Before和@AfterReturning注解分别指定各自切入点为 com.smbms.pojo.. 包下面的所有类所有方法,对于所有相同的切入点,其实可以统一定义,以便于维护重用。

2、如上所示,@Pointcut 定义切入点表达式,切入点签名通过一个普通方法表示。

【2】使用了注解,配置文件就会变得简单了,在 applicationContext-mybatis.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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">


<!-- 数据源配置 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url">
<value><![CDATA[jdbc:mysql://127.0.0.1:3306/test?
useUnicode=true&characterEncoding=utf-8]]></value>
</property>
<property name="username" value="root"></property>
<property name="password" value=""></property>
</bean>

<!-- SqlSessionFactoryBean 配置 -->
<bean id="sqlSessionFactory"
class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 引用数据源组件 -->
<property name="dataSource" ref="dataSource"></property>
<!-- 引用Mybatis配置文件的配置 -->
<property name="configLocation"
value="classpath:mybatis-config.xml"></property>
<!-- 配置SQL映射文件信息 ,逐个列出SQL映射文件麻烦,所以使用 mapperLocations 属性扫描加载SQL映射文件-->
<property name="mapperLocations">
<list>
<value>classpath:com/smbms/pojo/**/*.xml</value>
</list>
</property>
</bean>

<!-- 配置SQLSessionTemplate -->
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"></constructor-arg>
</bean>
<!-- 配置DAO组件并进入SQLSessionTemplate实例 -->
<bean id="userMapper" class="com.smbms.pojo.UserMapperImpl">
<constructor-arg name="sqlSession" ref="sqlSessionTemplate"></constructor-arg>
</bean>

<!-- 配置业务Bean并注入DAO实例 -->
<bean id="userService" class="com.smbms.pojo.UserServiceImpl">
<property name="userMapper" ref="userMapper"></property>
</bean>

<context:component-scan base-package="com.smbms"></context:component-scan>
<!-- 因为bean不被引用,所以不加id -->
<bean class="com.smbms.AopLog.UserserviceLogger"></bean>
<bean class="com.smbms.AopLog.UserserviceLogger2"></bean>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

</beans>



【3】其他的bean和接口和实现类省略,重在aop的使用。


【4】输出结果:


INFO  12-31 12:11:22,713 ***UserserviceLogger*********before:璋冪敤浜哻om.smbms.pojo.UserServiceImpl@35841320 鐨刦indUsersWithConditions 鏂规硶銆傛柟娉曞叆鍙傦細[com.smbms.pojo.User@3cce5371]  (UserserviceLogger.java:17) 
INFO 12-31 12:11:22,713 璋冪敤浜? com.smbms.pojo.UserServiceImpl@35841320 ? findUsersWithConditions 鏂规硶銆? (UserserviceLogger.java:20)
INFO 12-31 12:11:22,714 findUsersWithConditions鏂规硶鎵ц缁撴潫 (UserserviceLogger.java:26)
DEBUG 12-31 12:11:22,714 Returning cached instance of singleton bean 'com.smbms.AopLog.UserserviceLogger2#0' (AbstractBeanFactory.java:247)
INFO 12-31 12:11:22,715 ***UserserviceLogger2*********before:璋冪敤浜哻om.smbms.pojo.UserServiceImpl@35841320 鐨刦indUsersWithConditions 鏂规硶銆傛柟娉曞叆鍙傦細[com.smbms.pojo.User@3cce5371] (UserserviceLogger2.java:22)
INFO 12-31 12:11:22,715 璋冪敤浜? com.smbms.pojo.UserServiceImpl@35841320 ? findUsersWithConditions 鏂规硶銆? (UserserviceLogger2.java:25)
INFO 12-31 12:11:22,715 findUsersWithConditions鏂规硶鎵ц缁撴潫 (UserserviceLogger2.java:31)
INFO 12-31 12:11:22,716 ***UserserviceLogger*********before:璋冪敤浜哻om.smbms.pojo.UserMapperImpl@19932c16 鐨刧etUserList 鏂规硶銆傛柟娉曞叆鍙傦細[com.smbms.pojo.User@3cce5371] (UserserviceLogger.java:17)
INFO 12-31 12:11:22,716 璋冪敤浜? com.smbms.pojo.UserMapperImpl@19932c16 ? getUserList 鏂规硶銆? (UserserviceLogger.java:20)
INFO 12-31 12:11:22,717 getUserList鏂规硶鎵ц缁撴潫 (UserserviceLogger.java:26)
INFO 12-31 12:11:22,717 ***UserserviceLogger2*********before:璋冪敤浜哻om.smbms.pojo.UserMapperImpl@19932c16 鐨刧etUserList 鏂规硶銆傛柟娉曞叆鍙傦細[com.smbms.pojo.User@3cce5371] (UserserviceLogger2.java:22)
INFO 12-31 12:11:22,717 璋冪敤浜? com.smbms.pojo.UserMapperImpl@19932c16 ? getUserList 鏂规硶銆? (UserserviceLogger2.java:25)
INFO 12-31 12:11:22,718 getUserList鏂规硶鎵ц缁撴潫 (UserserviceLogger2.java:31)
DEBUG 12-31 12:11:22,723 Creating a new SqlSession (JakartaCommonsLoggingImpl.java:54)
DEBUG 12-31 12:11:22,769 SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@69c81773] was not registered for synchronization because synchronization is not active (JakartaCommonsLoggingImpl.java:54)
DEBUG 12-31 12:11:22,821 Fetching JDBC Connection from DataSource (DataSourceUtils.java:110)
DEBUG 12-31 12:11:25,399 JDBC Connection [jdbc:mysql://127.0.0.1:3306/test?
useUnicode=true&characterEncoding=utf-8, UserName=root@localhost, MySQL Connector Java] will not be managed by Spring (JakartaCommonsLoggingImpl.java:54)
DEBUG 12-31 12:11:25,407 ==> Preparing: select u.* ,r.roleName from smbms_user u ,smbms_role r where u.userName like CONCAT('%',?,'%') and u.userRole = ? and u.userRole = r.id (JakartaCommonsLoggingImpl.java:54)
DEBUG 12-31 12:11:25,479 ==> Parameters: mmb(String), 110(Integer) (JakartaCommonsLoggingImpl.java:54)
DEBUG 12-31 12:11:26,042 <== Columns: id, userCode, userName, userPassword, gender, birthday, phone, address, userRole, createBy, createDate, modifyBy, modifyDate, roleName (JakartaCommonsLoggingImpl.java:59)
DEBUG 12-31 12:11:26,045 <== Row: 1, test01, mmb02, 9876543210, 2, 1991-12-29, 0000, maoming, 110, 1, 2017-12-17 10:50:04.0, null, null, guan_li_yuan (JakartaCommonsLoggingImpl.java:59)
DEBUG 12-31 12:11:26,061 <== Row: 2, test01, mmb02, scua, 2, 1991-12-29, 0000, maoming, 110, 1, 2017-12-17 10:56:48.0, null, null, guan_li_yuan (JakartaCommonsLoggingImpl.java:59)
DEBUG 12-31 12:11:26,063 <== Row: 3, test01, mmb02, scua, 2, 1991-12-29, 0000, maoming, 110, 1, 2017-12-17 10:58:46.0, null, null, guan_li_yuan (JakartaCommonsLoggingImpl.java:59)
DEBUG 12-31 12:11:26,064 <== Row: 4, test01, mmb02, scua, 2, 1991-12-29, 0000, maoming, 110, 1, 2017-12-17 11:01:42.0, null, null, guan_li_yuan (JakartaCommonsLoggingImpl.java:59)
DEBUG 12-31 12:11:26,065 <== Row: 5, test01, mmb02, scua, 2, 1991-12-29, 0000, maoming, 110, 1, 2017-12-17 11:03:05.0, null, null, guan_li_yuan (JakartaCommonsLoggingImpl.java:59)
DEBUG 12-31 12:11:26,066 <== Total: 5 (JakartaCommonsLoggingImpl.java:54)
DEBUG 12-31 12:11:26,081 Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@69c81773] (JakartaCommonsLoggingImpl.java:54)
DEBUG 12-31 12:11:26,081 Returning JDBC Connection to DataSource (DataSourceUtils.java:327)
INFO 12-31 12:11:26,082 ***UserserviceLogger2***********after:璋冪敤浜? com.smbms.pojo.UserMapperImpl@19932c16 鐨刧etUserList 鏂规硶銆傛柟娉曞叆鍙傦細[com.smbms.pojo.User@3cce5371][com.smbms.pojo.User@51a9ad5e, com.smbms.pojo.User@5f20155b, com.smbms.pojo.User@72ade7e3, com.smbms.pojo.User@239105a8, com.smbms.pojo.User@3fce8fd9] (UserserviceLogger2.java:38)
INFO 12-31 12:11:26,082 璋冪敤浜? com.smbms.pojo.UserMapperImpl@19932c16 ? getUserList 鏂规硶 (UserserviceLogger2.java:41)
INFO 12-31 12:11:26,083 getUserList鏂规硶鎵ц缁撴潫 (UserserviceLogger2.java:47)
INFO 12-31 12:11:26,083 ****UserserviceLogger**********after:璋冪敤浜? com.smbms.pojo.UserMapperImpl@19932c16 鐨刧etUserList 鏂规硶銆傛柟娉曞叆鍙傦細[com.smbms.pojo.User@3cce5371][com.smbms.pojo.User@51a9ad5e, com.smbms.pojo.User@5f20155b, com.smbms.pojo.User@72ade7e3, com.smbms.pojo.User@239105a8, com.smbms.pojo.User@3fce8fd9] (UserserviceLogger.java:34)
INFO 12-31 12:11:26,083 璋冪敤浜? com.smbms.pojo.UserMapperImpl@19932c16 ? getUserList 鏂规硶 (UserserviceLogger.java:37)
INFO 12-31 12:11:26,083 getUserList鏂规硶鎵ц缁撴潫 (UserserviceLogger.java:43)
INFO 12-31 12:11:26,084 ***UserserviceLogger2***********after:璋冪敤浜? com.smbms.pojo.UserServiceImpl@35841320 鐨刦indUsersWithConditions 鏂规硶銆傛柟娉曞叆鍙傦細[com.smbms.pojo.User@3cce5371][com.smbms.pojo.User@51a9ad5e, com.smbms.pojo.User@5f20155b, com.smbms.pojo.User@72ade7e3, com.smbms.pojo.User@239105a8, com.smbms.pojo.User@3fce8fd9] (UserserviceLogger2.java:38)
INFO 12-31 12:11:26,084 璋冪敤浜? com.smbms.pojo.UserServiceImpl@35841320 ? findUsersWithConditions 鏂规硶 (UserserviceLogger2.java:41)
INFO 12-31 12:11:26,084 findUsersWithConditions鏂规硶鎵ц缁撴潫 (UserserviceLogger2.java:47)
INFO 12-31 12:11:26,084 ****UserserviceLogger**********after:璋冪敤浜? com.smbms.pojo.UserServiceImpl@35841320 鐨刦indUsersWithConditions 鏂规硶銆傛柟娉曞叆鍙傦細[com.smbms.pojo.User@3cce5371][com.smbms.pojo.User@51a9ad5e, com.smbms.pojo.User@5f20155b, com.smbms.pojo.User@72ade7e3, com.smbms.pojo.User@239105a8, com.smbms.pojo.User@3fce8fd9] (UserserviceLogger.java:34)
INFO 12-31 12:11:26,084 璋冪敤浜? com.smbms.pojo.UserServiceImpl@35841320 ? findUsersWithConditions 鏂规硶 (UserserviceLogger.java:37)
INFO 12-31 12:11:26,084 findUsersWithConditions鏂规硶鎵ц缁撴潫 (UserserviceLogger.java:43)
testGetUserList userCode:test01userName: mmb02userRole: 110userRoleName: guan_li_yuanuserAddress: null
testGetUserList userCode:test01userName: mmb02userRole: 110userRoleName: guan_li_yuanuserAddress: null
testGetUserList userCode:test01userName: mmb02userRole: 110userRoleName: guan_li_yuanuserAddress: null
testGetUserList userCode:test01userName: mmb02userRole: 110userRoleName: guan_li_yuanuserAddress: null
testGetUserList userCode:test01userName: mmb02userRole: 110userRoleName: guan_li_yuanuserAddress: null

继续,下面是定义异常抛出增强:Spring(12):使用注解(@AfterThrowing/@After/@Around)实现AOP异常增强与实例;