面向切面编程,就是在不改变源代码的基础上,增加新的功能,对代码进行增强处理,代理模式。
关注两件事:在什么位置,执行什么功能。由Spring AOP完成织入工作,日志、异常处理、事务控制等。
增强处理主要有:
- 前置增强 before 在切入点前边执行
- 后置增强 after-returning 在切入点后边执行,如果有异常,就不执行
- 最终增强 after 在切入点后边执行,无论怎样都会执行,
- 环绕增强 round 最强大的增强,封装了目标对象,对其进行控制。
- 异常抛出增强 after-throwing 发生异常执行
首先添加需要的架包: aopalliance-1.0.jar;aspectjweaver-1.6.9.jar;spring-aop-3.2.13.RELEASE.jar
一、配置aop
1. 定义包含增强方法的javaBean
package aop;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
public class AopLogger {
private static final Logger log = Logger.getLogger(AopLogger.class);
public void before(JoinPoint jp){
log.info("=====>前置增强: 调用 "+jp.getTarget()+"的"+jp.getSignature().getName()+" 方法,方法入参:"+jp.getArgs().toString());
}
public void after(JoinPoint jp){
log.info("=====>最终增强:调用 "+jp.getTarget()+"的"+jp.getSignature().getName()+" 方法");
}
public void afterReturning(JoinPoint jp,Object result){
log.info("=====>后置增强带返回值:调用 "+jp.getTarget()+"的"+jp.getSignature().getName()+" 方法,返回值:"+result);
}
public void afterException(JoinPoint jp,Exception e){
log.info("=====>异常抛出增强 "+ jp.getSignature().getName() + "抛出异常"+e);
}
public void around(ProceedingJoinPoint jp) throws Throwable{
//环绕增强得到了目标方法的控制权,可以获取、修改目标方法的参数和返回值,封装了目标对象,达到点对点的控制
log.info("=====>环绕增强执行 "); //相当于前置增强
try{
Object result = jp.proceed();
log.info("\n=====> 环绕增强结果 :"+result); //相当于后置增强
}catch(Throwable e){
log.info("=====>环绕增强抛出异常:"+e); //相当于异常抛出增强
throw e;
}finally{
log.info("=====>环绕增强最后 "); //相当于最终增强
}
}
}
spring 会自动注入JoinPoint 实例。
2..创建目标对象
package aop;
public class PointDemo {
public String method1(String name){
System.out.println("======> 执行 method1, name is : "+name);
return name;
}
}
method1作为切入点
3. 配置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:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd">
<!--注入实例 -->
<bean id="pointDemo" class="aop.PointDemo"></bean>
<bean id="aopLogger" class="aop.AopLogger"></bean>
<aop:config>
<!--1. 定义一个切入点 -->
<aop:pointcut expression="execution(public * method1(..))" id="pointCut"/>
<!-- 2. 引用包含增强方法的Bean -->
<aop:aspect ref="aopLogger">
<aop:before method="before" pointcut-ref="pointCut"/><!--前置增强 -->
<aop:after method="after" pointcut-ref="pointCut"/><!-- 最终曾强 不论是否发生异常都执行-->
<aop:after-returning method="afterReturning" pointcut-ref="pointCut" returning="result"/><!-- 带返回值的后置增强 有异常就不执行了 -->
<aop:after-throwing method="afterException" pointcut-ref="pointCut" throwing="e"/> <!-- 异常抛出增强 -->
<aop:around method="around" pointcut-ref="pointCut"/>
</aop:aspect>
</aop:config>
</beans>
有关aop的配置都放在<aop: config> 标签内;配置切入点的标签<aop: pointcut>的expression属性有多种表达式:
- public * method1(String name); 匹配所有返回值类型
- public void * (name); 匹配所有方法名
- public void method1(..); “..”匹配所有类型返回值;
- * com.*.*(..); 匹配com包下所有的类的方法;
- * com..*.*(..); 匹配com包及其子包所有类的方法
4. 测试
package aop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext1.xml");
PointDemo pd = (PointDemo)ac.getBean("pointDemo");
pd.method1("张三");
}
}
讲环绕增强注释掉的结果:
INFO - =====>前置增强: 调用 aop.PointDemo@72057ecf的method1 方法,方法入参:[Ljava.lang.Object;@6e171cd7
======> 执行 method1, name is : 张三
INFO - =====>最终增强:调用 aop.PointDemo@72057ecf的method1 方法
INFO - =====>后置增强带返回值:调用 aop.PointDemo@72057ecf的method1 方法,返回值:张三
加上环绕增强的结果:
INFO - =====>前置增强: 调用 aop.PointDemo@dbf57b3的method1 方法,方法入参:[Ljava.lang.Object;@52e6fdee
INFO - =====>环绕增强执行
INFO -
=====> 环绕增强结果 :张三
======> 执行 method1, name is : 张三
INFO - =====>环绕增强最后
INFO - =====>后置增强带返回值:调用 aop.PointDemo@dbf57b3的method1 方法,返回值:null
INFO - =====>最终增强:调用 aop.PointDemo@dbf57b3的method1 方法
可以看到,后置增强返回值为null,所以环绕增强最好别和其他的一起用。
5. 改造 目标类,使其发生异常,测试异常抛出增强;
package aop;
public class PointDemo {
public String method1(String name){
System.out.println("======> 执行 method1, name is : "+name);
String str="";
System.out.println(str.equals(""));
return name;
}
}
测试异常:
INFO - =====>前置增强: 调用 aop.PointDemo@dbf57b3的method1 方法,方法入参:[Ljava.lang.Object;@52e6fdee
INFO - =====>环绕增强执行
======> 执行 method1, name is : 张三INFO - =====>环绕增强抛出异常:java.lang.NullPointerException
INFO - =====>环绕增强最后INFO - =====>异常抛出增强 method1抛出异常java.lang.NullPointerException
INFO - =====>最终增强:调用 aop.PointDemo@dbf57b3的method1 方法
Exception in thread "main" java.lang.NullPointerException
at aop.PointDemo.method1(PointDemo.java:7)
at aop.PointDemo$$FastClassBySpringCGLIB$$93985287.invoke(<generated>)
抛出了异常。