增强类型 | 注解 | 特点 |
---|---|---|
前置增强 | @Before | 目标方法前织入增强方法。 |
后置增强 | @AfterReturning | 目标方法正常执行后织入增强方法(不出现异常)。 |
异常增强 | @AfterThrowing | 目标方法发生异常后织入的增强方法,可拔插的异常处理方案。 |
最终增强 | @After | 无论目标方法是否发生异常,都会在目标方法执行完成后织入增强方法,类似异常处理机制中的finally,常用于释放资源。 |
环绕增强 | @Around | 在目标方法的前后都可以织入增强方法,环绕增强是功能最强大的增强处理,可获取或修改目标方法的参数、返回值,可对它进行异常处理。 |
因之前介绍AOP的文章已经有了 前置增强与后置增强 的用法介绍,可以看我之前的文章:SSM框架之Spring-AOP的理解与基本使用,相比较而言也是最简单的,下面的介绍也是在SSM框架之Spring-AOP的理解与基本使用项目的基础上进行修改!所以这里就不赘述了,如何搭建Spring框架也是。
由于怕篇幅过长,这里只贴主要代码。下面将介绍两种实现增强的方法–1、文件配置法 2、@AspectJ注解法
public class ErrorLogger {
private static final Logger log = Logger.getLogger(ErrorLogger.class);
// 当切入方法发生异常时,该方法会执行
public void afterThrowing(JoinPoint jp, Exception e) {
log.error(jp.getSignature().getName() + " 方法发生异常:" + e);
}
}
<bean id="theLogger" class="com.lsl.ssm.aop.ErrorLogger"/>
<aop:config>
<aop:pointcut expression="execution(* com.lsl.ssm.service.*.*(..))" id="pointcut"/>
<aop:aspect ref="theLogger">
<aop:after-throwing method="afterThrowing" pointcut-ref="pointcut" throwing="e"/>
aop:aspect>
aop:config>
public class AfterLogger {
private static final Logger log = Logger.getLogger(AfterLogger.class);
// 方法结束执行! 无论是否发生异常都会得到执行,类似finally块的作用!
public void afterLogger(JoinPoint jp) {
log.info(jp.getSignature().getName() + " 方法结束执行!(最终增强:无论是否发生异常都会得到执行,类似finally块的作用!)");
}
}
<bean id="theLogger" class="com.lsl.ssm.aop.AfterLogger"/>
<aop:config>
<aop:pointcut expression="execution(* com.lsl.ssm.service.UserService.*(..))" id="pointcut"/>
<aop:aspect ref="theLogger">
<aop:after method="afterLogger" pointcut-ref="pointcut"/>
aop:aspect>
aop:config>
public class AroundLogger {
private static final Logger log = Logger.getLogger(AroundLogger.class);
public Object aroundLogger(ProceedingJoinPoint pjp) throws Throwable {
log.info("调用 " + pjp.getTarget() + " 的 " + pjp.getSignature().getName()
+ " 方法,方法入参:" + Arrays.toString(pjp.getArgs()) + " (目标执行前执行!)");
// 目标执行前执行 = 前置增强
try {
// 目标执行后执行 = 后置增强
Object result = pjp.proceed();
log.info("调用 " + pjp.getTarget() + " 的 "
+ pjp.getSignature().getName() + " 方法,方法返回值:" + result + " (目标不出现异常时执行!)");
return result;
} catch (Throwable e) {
// 目标出现异常时执行 = 异常增强
log.error(pjp.getSignature().getName() + " 方法异常: " + e + " (目标异常时执行!)");
throw e;
} finally {
// 目标无论有没有发生异常都会执行 = 最终增强
log.info(pjp.getSignature().getName() + " 方法执行结束! (任何时候都执行!)");
}
}
}
<bean id="theLogger" class="com.lsl.ssm.aop.AroundLogger"/>
<aop:config>
<aop:pointcut expression="execution(* com.lsl.ssm.service.*.*(..))" id="pointcut"/>
<aop:aspect ref="theLogger">
<aop:around method="aroundLogger" pointcut-ref="pointcut"/>
aop:aspect>
aop:config>
小结:可以看到,使用配置文件切入增强方法是不需要修改原程序代码的!只需新建好增强方法的类再配置好切入的位置即可,配置比较集中,方便后面修改与管理。而缺点也比较明显,就是配置比较繁琐,类型不安全,配置文件过多时难以管理等。
注意:使用@AspectJ注解方式,JDK要求在5.0以上
@Aspect // 用于标注AOP类
public class ErrorLogger {
private static final Logger log = Logger.getLogger(ErrorLogger.class);
@AfterThrowing(pointcut = "execution(* com.lsl.ssm.service.*.*(..))", throwing = "e")
public void afterThrowing(JoinPoint jp, Exception e) {
log.error(jp.getSignature().getName() + " 方法发生异常:" + e);
}
}
/**
* 用户DAO类,实现UserDao接口,负责User类的持久化操作
*/
@Repository //用于标注DAO类
public class UserDaoImp implements UserDao {...}
/**
* 用户业务类,实现对User功能业务的管理
*/
@Service("service") // 用于标注业务类,这里指定bean的名称为”service“
public class UserServiceImp implements UserService {
//声明接口类型的引用,和具体实现类解耦合
@Autowired // 自动装配Bean
private UserDao dao;
...
}
<context:component-scan base-package="com.lsl.ssm.service,com.lsl.ssm.dao"/>
<bean class="com.lsl.ssm.aop.ErrorLogger"/>
<aop:aspectj-autoproxy/>
@Aspect
public class AroundLogger {
private static final Logger log = Logger.getLogger(AroundLogger.class);
@Around("execution(* com.lsl.ssm.service.*.*(..))")
public Object aroundLogger(ProceedingJoinPoint pjp) throws Throwable {
log.info("调用 " + pjp.getTarget() + " 的 " + pjp.getSignature().getName()
+ " 方法,方法入参:" + Arrays.toString(pjp.getArgs()) + " (目标执行前执行!)");
// 目标执行前执行 = 前置增强
try {
// 目标执行后执行 = 后置增强
Object result = pjp.proceed();
log.info("调用 " + pjp.getTarget() + " 的 "
+ pjp.getSignature().getName() + " 方法,方法返回值:" + result + " (目标不出现异常时执行!)");
return result;
} catch (Throwable e) {
// 目标出现异常时执行 = 异常增强
log.error(pjp.getSignature().getName() + " 方法异常: " + e + " (目标异常时执行!)");
throw e;
} finally {
// 目标无论有没有发生异常都会执行 = 最终增强
log.info(pjp.getSignature().getName() + " 方法执行结束! (任何时候都执行!)");
}
}
}
/**
* 用户DAO类,实现UserDao接口,负责User类的持久化操作
*/
@Repository("userDao") //用于标注DAO类,这里指定bean名称为”userDao“
public class UserDaoImp implements UserDao {...}
/**
* 用户业务类,实现对User功能业务的管理
*/
@Service("service") // 用于标注业务类,这里制定bean的名称为”service“
public class UserServiceImp implements UserService {
//声明接口类型的引用,和具体实现类解耦合
// @Autowired // 自动装配Bean
@Resource(name="userDao") //为dao属性注入名为userDao的Bean
private UserDao dao;
...
}
<context:component-scan base-package="com.lsl.ssm.service,com.lsl.ssm.dao"/>
<bean class="com.lsl.ssm.aop.AroundLogger"/>
<aop:aspectj-autoproxy/>
小结:使用@AspectJ注解方式,可以看到配置简单了很多,上面展示的三种增强其实差别不大,无非就是注解名称和包名不同。可以注意下@Autowired和@Resource的用法。该方式的优点是:方便、简洁、有助于增强程序的内聚性。缺点就是:注解分散在各个class文件中,不利于后期维护。
@Author 瞌睡虫
@mybatis-3.2.2
@Database: mysql 5.7.15
@Tool: MyEclipse