面向切面编程(AOP)是一种设计思想,是把应用软件分成两个部分:核心关注点和横切关注点
在没有使用到AOP框架的时候我们经常会产生大量的重复的代码,尤其是在事务处理、日志记录等方面。由于这些经常是相同并且重复使用的代码,我们可以使用AOP框架将技术问题实现代码和业务处理(如日志)的实现代码分离,降低了两种代码的耦合性,利于代码的维护和重用。
我们大致上可以这样理解AOP:面向对象编程(OOP)的思想主要处理的是对象间从上到下的关系,那么AOP就是一种从左到右横切的一种关系。比如说,我们现在有好几种业务流程从上到下,但是每种流程都包含了部分的日志处理,那么我们就可以将日志处理看做是一个切面,一个横切所有流程的切面,因为在每个流程的执行过程中,遇到需要进行日志记录的部分都会转去进行日志处理,这就是一个切面。
首先看一下这样几个概念:
- 切面(Aspect):一个关注点的模块化,这个关注点可能会横切多个对象
- 连接点(Joinpoint):程序执行过程中的某个特定的点
- 通知(Advice):在切面的某个特定的连接点上执行的动作
- 切入点(Pointcut):匹配连接点的断言,在AOP中通知和一个切入点表达式关联
- 引入(Introduction):在不修改类代码的前提下,为类添加新的属性和方法
- 目标对象(Target Object):被一个或者多个切面所通知的对象
- AOP代理(AOP Proxy):AOP框架创建的对象,用来实现切面契约(aspect contract)(包括通知方法执行等功能)
- 织入(Weaving):把切面连接到其他的应用程序类型或者对象上,并创建一个被通知的对象,分为:编译时织入,类加载时织入,执行时织入
这里通过一个小的例子实现AOP的工作过程,以日志记录为例,模仿数据库的操作,同时生成日志。具体如下:
首先是业务逻辑接口IStudentDao:
public interface IStudentDao {
public boolean save(String stuNumber, String stuName);
public boolean update(String stuNumber, String stuName);
public boolean delete(String stuNumber);
}
业务逻辑实现类StudentDaoImp:
public class StudentDaoImpl implements IStudentDao {
public boolean delete(String stuNumber) {
System.out.println("执行delete!");
System.out.println("delete" + stuNumber + " 完成");
return true;
}
public boolean save(String stuNumber, String stuName) {
System.out.println("执行save!");
System.out.println("save" + stuNumber + ", save" + stuName);
return true;
}
public boolean update(String stuNumber, String stuName) {
System.out.println("ִupdate!");
System.out.println("update" + stuNumber + ", " + stuName);
return true;
}
}
创建日志功能类,在实际被调用的方法执行完成之后,执行该类的方法LogAfterReturningAdvice.java:
import java.lang.reflect.Method;
import java.text.DateFormat;
import java.util.Date;
import org.springframework.aop.AfterReturningAdvice;
public class LogAfterReturningAdvice implements AfterReturningAdvice {
public void afterReturning(Object returnValue, Method targetMethod, Object[] methodArgs,
Object targetClass) throws Throwable {
boolean success = ((Boolean)returnValue).booleanValue();
if(success){
String className = targetClass.getClass().getName();
String methodName = targetMethod.getName();
Date date = new Date();
String curTime = DateFormat.getDateTimeInstance().format(date);
String logInfo = "[" + curTime + "]" + className + "." + methodName + "() 方法被调用";
System.out.println(logInfo + "LogAfterReturningAdvice");
}
}
}
然后创建配置文件,将业务代码和日志功能代码组装在一起aop-config.xml
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="studentPointcut" class="com.chinasei.spring.aop.StudentDaoImpl" />
<bean id="logAfterReturningAdvice" class="com.chinasei.spring.aop.LogAfterReturningAdvice" />
<bean id="studentAdvisor" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>com.chinasei.spring.aop.IStudentDao</value>
</property>
<property name="interceptorNames">
<list><value>regexAdvisor</value></list>
</property>
<property name="target" ref="studentPointcut" />
</bean>
</beans>
对于上面的配置文件前面的两个bean用来定义业务处理逻辑和日志功能,其中id=studentPointcut的Bean是要代理的目标类。最后一个Bean是用来组装业务处理代码和日志功能代码的bean是使用ProxyFactoryBean类的实例(创建代理类的类)。
然后创建一个测试类SpringAOPTest
package com.chinasei.spring.aop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class SpringAOPTest {
public static void main(String[] args){
ApplicationContext context = new ClassPathXmlApplicationContext("aop-config.xml");
IStudentDao student = (IStudentDao)context.getBean("studentAdvisor");
student.save("1234561", "张三");
student.update("1234561", "李四");
student.delete("1234561");
}
}
执行结果如下:
发现在每次执行完成逻辑操作之后都会进行日志的处理。
上面的整个流程可以这样解释,首先通过ClassPathXmlApplicationContext类加载配置文件,生成上下文应用关系,然后通过applicationContext获取Bean,获取的Bean的id指向为studentAdvisor,这时,ProxyFactoryBean就会根据子元素的属性创建代理类,子元素中包含了3个标签,分别是proxyInterfaces,是要代理的类的接口,interceptorNames是要完成日志功能的类,即就是一系列的拦截器,可以认为是要通知执行的方法,最后一个子元素属性为target是实现业务处理的类。
代理类创建完成之后就如同在studentDaoImpl类中的方法上,添加了通知,在该类中的方法执行完成返回之后就会执行logAfterReturningAdvice类中的方法。
这一篇只是简单通过一个例子大致将AOP的流程串起来,具体的知识还有好多,比如Advice、Pointcut和Advisor,以及使用ProxyFactoryBean创建AOP代理,这些部分都包含了很多的内容,还需要更加深入的学习!