一 简介
面向切面编程(Aspect Oriented Programming,AOP)是软件编程思想发展到一定阶段的产物,是对面向对象编程(Object Oriented Programm ing,OOP)的有益补充。AOP -般适用于具有横切逻辑的场合,如访问控制、事务管理、性能监测等。
什么是横切逻辑呢?我们先来看下面的程序代码。
/**
* 学生业务类,实现对Student功能的业务管理
*/
public class StudentBiz implements IStudentBiz {
private static final Logger log = Logger.getLogger(StudentBiz.class);
// 声明接口类型的引用和具体实现类解耦合
private IDao dao;
// dao属性的setter访问器,会被Spring调用,实现设值注入
public void setDao(IDao dao) {
this.dao = dao;
}
public void addNewStudent(Student student) {
log.info("添加学生" + student.getName());
Transaction tx = null;
try {
tx = HibernateSessionFactory.getSession().beginTransaction();
// 调用学生Dao的方法,保存学生信息
dao.save(Studnet);
tx.commit();
} catch (Exception e) {
log.error("添加学生" + student.getName() + "失败", e);
if (tx != null) {
tx.rollback();
}
}
}
}
这是一个再“典型”不过的业务处理方法。日志、异常处理、事务控制等,都是一个健壮的业务系统所必需的。但是为了保证系统健壮可用,就要在众多的业务方法中反复编写类似的代码,使得原本就很复杂的业务处理代码变得更加复杂。业务功能的开发者还要考虑这些“额外”的代码是否处理正确、是否有遗漏的地方。如果需要修改日志信息的格式或者安全验证的规则,或者再增加新的辅助功能,都会导致业务代码频繁而大量的修改。
在业务系统中,总有一些散落、渗透到系统各处且不得不处理的事情,这些穿插在既定业务中的操作就是所谓的“横切逻辑”,也称为“切面”。我们怎样才能不受这些附加要求的干扰,专心于真正的业务逻辑上呢?我们很容易想到可以将这些重复性的代码抽取出来,放在专门的类和方法中,这样就便于管理和维护了。但即便如此,依然无法实现既定业务和横切逻辑的彻底解耦合,因为业务代码中还要保留这些方法的调用代码,当需要增加或减少横切逻辑的时候,还是要修改业务方法中的调用代码才能实现。我们希望无须编写显式的调用,在需要的时候,系统能够“自动”调用所需的功能,这正是AOP要解决的主要问题。
面向切面编程,简单地说就是在不改变原程序的基础上为代码段增加新的功能,对代码段进行增强处理。它的设计思想来源于代理设计模式,下面以图示的方式进行简单的说明。通常情况下调用对象的方法如图所示。
在代理模式中可以为该对象设置一个代理对象,代理对象为fun()提供一个代理方法,当通过代理对象的fun()方法调用原对象的fun()方法时,就可以在代理方法中添加新的功能,也就是所谓的增强处理,增强的功能既可以插到原对象的fun()方法前面,也可以插到后面,如图所示。
在这种模式下,给编程人员的感觉是在原有代码乃至原业务流程都不修改的情况下,直接在业务流程中切入新代码,增加新功能,这就是所谓的面向切面编程。对面向切面编程有了感性认识以后,还需要了解它的以下一些基本概念。
Ø 增强处理(Advice)类型:在上图中,在原对象的fun()方法之前插入的增强处理为前置增强,该方法正常执行完以后插入的增强处理为后置增强,此外还有环绕增强、异常抛出增强、最终增强等类型。
Ø 切人点(Pointcut):可以插入增强处理的方法,图1.8中原对象的fun()方法就是一个切入点。
Advice直译为“通知”,但这种叫法并不确切,在此处翻译成“增强处理”更便于大家理解。
二 具体案例
2.1 前置增强处理
1. 建立工程,导入相关jar包
建立spring核心配置文件
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<bean id="userDao" class="com.obtk.dao.UserDaoImpl"></bean>
<!--要素1 增强处理的代码 -->
<bean id="beforAdvise" class="com.obtk.advise.BeforAdvise"></bean>
<aop:config>
<!--要素2 切入点 -->
<aop:pointcut id="mycut"
expression="execution(public int com.obtk.dao.UserDaoImpl.saveUser(com.obtk.entitys.UserEntity))"/>
<!--要素3 织入 -->
<aop:advisor advice-ref="beforAdvise" pointcut-ref="mycut"/>
</aop:config>
</beans>
=================UserDaoImpl.java===========================
package com.obtk.dao;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.session.SqlSession;
import com.obtk.entitys.UserEntity;
import com.obtk.utils.MybatisUtil;
public class UserDaoImpl implements IUserDao{
public int saveUser(UserEntity theUser) {
System.out.println("方法执行前....");
int result=0;
SqlSession session=null;
try {
//4.得到session
session=MybatisUtil.getSession();
//5.执行语句
result=session.insert("user.saveOne", theUser);
session.commit();
//添加完成可以取出启动增长的主键
System.out.println("添加成功!");
} catch (Exception e) {
session.rollback();
e.printStackTrace();
}finally{
MybatisUtil.closeSession();
}
return result;
}
}
mybatis相关配置在这里省略,请参考我的mybatis相关博文。
增强处理代码:
============BeforAdvise.java================
package com.obtk.advise;
import java.lang.reflect.Method;
import java.util.Arrays;
import org.springframework.aop.MethodBeforeAdvice;
import com.obtk.entitys.UserEntity;
public class BeforAdvise implements MethodBeforeAdvice{
@Override
public void before(Method arg0, Object[] arg1, Object arg2)
throws Throwable {
UserEntity user=(UserEntity)arg1[0];
user.setEmail("1111111111");
System.out.println("在调用"+arg2+"的方法"+arg0.getName()+"之前调用了这里的代码"
+",方法的参数:"+Arrays.toString(arg1));
}
}
测试类
========TestAop1.java=====
package com.obtk.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.obtk.dao.IUserDao;
import com.obtk.entitys.UserEntity;
public class TestAop1 {
public static void main(String[] args) {
ApplicationContext ctx=
new ClassPathXmlApplicationContext("ApplicationContext.xml");
IUserDao userDao=(IUserDao)ctx.getBean("userDao");
UserEntity user=new UserEntity("mike", "123", "mike@qq.com");
userDao.saveUser(user);
}
}
三 关于切入点的配置
1. 最明确的写法
execution(public int com.obtk.dao.UserDaoImpl.saveUser(com.obtk.entitys.UserEntity))
2 【..】表示匹配方法里面的任意参数类型及个数
execution(public int com.obtk.dao.UserDaoImpl.saveUser(..))
3. *表示匹配dao层下面所要类
execution(public int com.obtk.dao.*.saveUser(..))
4. 表示匹配dao层下面所有类的所有返回int方法
execution(public int com.obtk.dao.*.*(..))
5. 表示匹配dao层下面所有类的所有返回任意类型方法
execution(* com.obtk.dao.*.*(..))
6. 表示匹配dao层下面所有的类及其子包的类里面的所有返回任意类型方法
execution(* com.obtk.dao..*.*(..))