一、AOP简介
AOP(Aspect Oriented Programming),即面向切片编程。AOP采用横向抽取机制,取代了传统纵向继承体系重复性代码,主要体现在事务处理
、日志管理
、权限控制
、异常处理
等方面,使开发人员可以专注于核心业务,提高了代码的可维护性。
AOP的专业术语包括Jointpoint、Pointcut、Advice、Target、Weaving、Proxy和Aspect
- Jointpoint(连接点):是指那些被拦截到的点,在Spring中,可以被动态代理拦截目标类的方法。
- Pointcut(切入点):是指要对哪些Jointpoint进行拦截,即被拦截的连接点。
- Advice(通知):是指拦截到Joingpoint之后要做的事情,即对切入点增强的内容。
- Target(目标):是指代理的目标对象。
- Weaving(织入):是指把增强代码应用到目标上,生成代理对象的过程。
- Proxy(代理):是指生成的代理对象。
二、Spring 通知类型
通知(Advice)就是对目标切入点增强的内容,AOP联盟为Advice定义了org.aopalliance.aop.Advice接口,Spring通知按照在目标类方法的连接点位置,可以分为5种,如下:
- org.springframework.aop.MethodBeforeAdvice(前置通知)
在目标方法执行前实施增强,可以运用于权限管理等功能。 - org.springframework.aop.AfterReturningAdvice(后置通知)
在目标方法执行后实施增强,可以运用于关闭流,上传文件、删除临时文件等功能。 - org.aopalliance.intercept.MethodInterceptor(环绕通知)
在目标方法执行前后执行实施增强,可以运用于日志、事务管理等功能。 - org.springframework.aop.ThrowsAdvice(异常抛出通知)
在方法抛出异常后实施增强,可以运用于处理异常记录日志等功能。 - org.springframework.aop.IntroductionInterceptor(引介通知)
在目标类中添加一些新的方法和属性,可以运用于修改老版本程序。
三、声明式 Spring AOP
在Spring中创建一个AOP代理的基本方法是:使用org.springframework.aop.framework.ProxyFactoryBean,ProxyFactoryBean类中的常用可配置属性如下表
属性名称 | 描述 |
---|---|
target | 代理的目标对象 |
proxyInterfaces | 代理要实现的接口 |
proxyTargetClass | 是否对类代理而不是接口,设值为true时,使用CGLIB代理 |
interceptorNames | 需要织入目标的Advice |
singleton | 删除 index 索引处的元素 |
optimize | 当设置为 true 时,强制使用 CGLIB |
四、代码实现
在项目src目录下创建包cn.test.factorybean,在该包下创建切面类MyAspect,内容如下:
// MyAspect.java
package cn.test.factorybean;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class MyAspect implements MethodInterceptor{
@Override
public Object invoke(MethodInvocation mi) throws Throwable{
System.out.println("方法执行之前");
Object obj = mi.proceed();
System.out.println("方法执行之后");
return obj;
}
}
在cn.test.factorybean包中创建UserDao接口和UserDaoImpl实现类,内容分别为:
// UserDao.java
package cn.test.factory;
public interface UserDao{
public void save();
public void update();
public void delete();
public void find();
}
// UserDaoImpl.java
package cn.test.factory;
public class UserDaoImpl implements UserDao {
@Override
public void save() {
System.out.println("save 添加用户");
}
@Override
public void update() {
System.out.println("update 修改用户");
}
@Override
public void delete() {
System.out.println("delete 删除用户");
}
@Override
public void find() {
System.out.println("find 查询用户");
}
}
在cn.test.factorybean包中创建xml配置文件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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 目标类-->
<bean id="userDao" class="cn.test.factorybean.UserDaoImpl"></bean>
<!-- 通知 advice-->
<bean id="myAspect" class="cn.test.factorybean.MyAspect"></bean>
<!-- 生成代理对象-->
<bean id="userDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 代理实现的接口-->
<property name="interfaces" value="cn.test.factorybean.UserDao"></property>
<!-- 目标-->
<property name="target" ref="userDao"></property>
<!-- 用通知增强目标-->
<property name="interceptorNames" value="myAspect"></property>
<!-- 如何生成代理,true;使用cglib,false;使用jdk动态代理-->
<property name="proxyTargetClass" value="false"></property>
</bean>
</beans>
然后我们在cn.test.factorybean下创建测试类TestFactoryBean
// TestFactoryBean.java
package cn.test.factoryebean
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestFactoryBean{
@Test
public void demo(){
String xmlPath = "cn/test/factorybean/applicationContext.xml";
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
// 从spring容器中获取内容
UserDao userDao = (UserDao)applicationContext.getBean("userDaoProxy");
// 执行方法
userDao.save();
userDao.update();
userDao.delete();
userDao.find();
}
}