- 注:这是学习过程的一些个人笔记,可能理解不够,会有错误的地方。
AOP的简介
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,AOP的提出是为了解决面对对象编程的一些弊端,比如,在一个实际项目中,如果需要测试某一类业务的性能,最传统的方法就是把这些的业务的源代码找到,然后在这些代码中编写测试性能的代码,一个业务就要编写一个测试性能代码,有多少业务就要写多少个,最后测试好了,我们还需要把这些测试代码从源代码中删除,这是特别麻烦的一件事,为了解决这么高的代码耦合度,我们可以使用动态代理模式来对一些业务进行一些增强,动态代理模式就可以让我们在不修改源代码的前提下对业务进行增强,AOP思想就是一种基于动态代理的思想,AOP思想就是将一些共性重复的代码抽取成一个独立模块,然后可以通过预编译方式,在运行期动态地将这些模块插入到需要的地方(方法)中,实现在不修改源代码的情况下给程序动态统一添加功能,从而大大降低了代码的耦合度。
AOP的专业术语
在使用AOP之前需要了解AOP的专业术语
target:目标类,即一个类中有方法要被用来增强,例如UserDaoImpl的save方法要增强,这个类就是目标类
joinPoint:连接点,即可以被用来增强的方法,例如UserDaoImpl的增删改查方法都可以被用来增强
pointCut:切入点,即要被增强的方法,就是说save要被增强,就是切入点,注意与可以增强区别
Proxy:代理,即被增强方法后的代理对象
advice:通知,添加进去的一段增强代码或者方法,也就是在原本的方法加入的一段增强的代码
weaving:织入,即将增强代码加入被增强方法的一个过程
aspect:切面,即被增强的方法+增强的方法,pointCut+advice
AOP的通知类型
前置通知:方法执行前增强
后置通知:方法执行后增强
环绕通知:方法执行前后增强
异常通知:方法抛出异常时增强
最终通知:方法无论是否抛出异常都增强
使用AOP需要的包
要使用AOP除了Spring需要的6个基本包,还需要四个包,因为我们要使用的是spring基于Aspectj的aop,因此不仅需要aop的jar包,还需要关于aspect的包。
com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
spring-aop-4.2.4.RELEASE.jar
spring-aspects-4.2.4.RELEASE.jar
AOP使用示例-XML方式
- 目标类
package com.wzm.entity;
/*
* 用来被增强的目标类
*/
public class UserDaoImpl {
public void save() {
System.out.println("普通保存");
}
public String delete() {
System.out.println("普通删除");
//这里返回一个值用来测试后置通知
return "小明";
}
public void update() {
System.out.println("普通更新");
}
public void find() {
System.out.println("普通查询");
//这里抛出一个异常用来测试异常通知
int i = 1 / 0;
}
}
- 切面类
package com.wzm.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
/*
* 通知类
*/
public class MyAspect {
//前置通知
public void before() {
System.out.println("前置通知");
}
//后置通知,参数为原本方法的返回值
public void afterReturning(Object result) {
System.out.println("后置通知通知=="+result);
}
//环绕通知,参数为一个连接点,相当于原本方法
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕前通知");
//在环绕通知中间,执行以下原本的方法,获得返回值
Object obj = joinPoint.proceed();
System.out.println("环绕前通知");
return obj;
}
//异常通知,参数为原本方法抛出的异常对象
public void afterThrowing(Throwable e) {
System.out.println("异常通知==="+e.getMessage());
}
//最终通知
public void after() {
System.out.println("最终通知");
}
}
- 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!-- beans的约束文件 -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
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/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 使用AOP需要aop的名称空间和约束文件
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
-->
<!--
在使用AOP之前需要了解AOP的专业术语
target:目标类,即一个类中有方法要被用来增强,例如UserDaoImpl的save方法要增强,这个类就是目标类
joinPoint:连接点,即可以被用来增强的方法,例如UserDaoImpl的增删改查方法都可以被用来增强
pointCut:切入点,即要被增强的方法,就是说save要被增强,就是切入点,注意与可以增强区别
Proxy:代理,即被增强方法后的代理对象
advice:通知,添加进去的一段增强代码或者方法,也就是在原本的方法加入的一段增强的代码
weaving:织入,即将增强代码加入被增强方法的一个过程
aspect:切面,即被增强的方法+增强的方法,pointCut+advice
-->
<!-- 目标类的bean -->
<bean id="userDao" class="com.wzm.entity.UserDaoImpl"></bean>
<!-- 通知类的bean -->
<bean id="myAspect" class="com.wzm.aspect.MyAspect"></bean>
<!-- aop配置 -->
<aop:config>
<!-- 定义切入点
expression为要被增强的一个方法,即切入点,在这里使用一个特殊的表达式
execution([访问类型] 返回值 包名.类名.方法名(参数)),注意空格
访问类型可以省略,返回值还有包名和类名,方法名可以写出具体的,也可以使用 * 来代表任意
参数一般为两个点 .. 代表任意参数
可以在类名后有一个加号 + 例如com.wzm.entity.UserDaoImpl+.save(..),代表包含该类下的所有子类的所有save方法
com.wzm.entity.*.save(..),代表entity包下的所有类的save方法
id 即为 切入点的id值,任意,与bean的id值类似道理
-->
<aop:pointcut expression="execution(* com.wzm.entity.UserDaoImpl.save(..))" id="userDaoSave"/>
<aop:pointcut expression="execution(* com.wzm.entity.UserDaoImpl.delete(..))" id="userDaoDelete"/>
<aop:pointcut expression="execution(* com.wzm.entity.UserDaoImpl.update(..))" id="userDaoUpdate"/>
<aop:pointcut expression="execution(* com.wzm.entity.UserDaoImpl.find(..))" id="userDaoFind"/>
<!-- 定义切面
ref:为通知类的bean的id值
-->
<aop:aspect ref="myAspect">
<!-- 前置通知,即在执行原本方法前,对方法进行增强,method为通知类中通知的方法名
pointcut-ref即为要被增强的切入点
-->
<aop:before method="before" pointcut-ref="userDaoSave"/>
<!-- 后置通知,即在执行原本方法之后,对方法进行增强,method为通知类中通知的方法名
pointcut-ref即为要被增强的切入点,后置通知可以接受原本方法的返回值
returning即接受原本方法的返回值,值可以任意,但是要与通知类中的通知方法的参数名一致
public void afterReturning(Object result),这里同样是result
-->
<aop:after-returning method="afterReturning" pointcut-ref="userDaoDelete" returning="result"/>
<!-- 环绕通知,即在原本方法执行之前和之后都增强,环绕通知需要在通知方法中传入一个参数
public Object around(ProceedingJoinPoint joinPoint),该参数就是一个连接点,其实也就是被增强的原本方法
-->
<aop:around method="around" pointcut-ref="userDaoUpdate"/>
<!-- 异常通知,异常通知只有在原本方法有出现异常时,该通知才会生效,异常通知可以获取获取原本方法抛出的异常对象
throwing:即接受原本方法抛出的异常对象,值任意,但是要与通知方法参数名一致
public void afterThrowing(Throwable e),这里同样是e
-->
<aop:after-throwing method="afterThrowing" pointcut-ref="userDaoFind" throwing="e"/>
<!-- 最终通知,即不管是否有出现异常,该通知都会生效 -->
<aop:after method="after" pointcut-ref="userDaoFind"/>
</aop:aspect>
</aop:config>
</beans>
AOP使用示例-注解方式
- 目标类
package com.wzm.entity;
import org.springframework.stereotype.Repository;
/*
* 用来被增强的目标类--使用注解的方式
*/
@Repository("userDao")
public class UserDaoImpl {
public void save() {
System.out.println("普通保存");
}
public String delete() {
System.out.println("普通删除");
//这里返回一个值用来测试后置通知
return "小明";
}
public void update() {
System.out.println("普通更新");
}
public void find() {
System.out.println("普通查询");
//这里抛出一个异常用来测试异常通知
int i = 1 / 0;
}
}
- 切面类
package com.wzm.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
/*
* 通知类 -- 注解方式
* @Aspect表明该类是一个通知类
*/
@Aspect()
@Component("myAspect")
public class MyAspect {
//前置通知
@Before(value="execution(* com.wzm.entity.UserDaoImpl.save(..))")
public void before() {
System.out.println("前置通知");
}
//后置通知,参数为原本方法的返回值
@AfterReturning(value="execution(* *.*.*.UserDaoImpl.delete(..))",returning="result")
public void afterReturning(Object result) {
System.out.println("后置通知通知=="+result);
}
//环绕通知,参数为一个连接点,相当于原本方法
@Around(value="execution(* *.*.*.UserDaoImpl.update(..))")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕前通知");
//在环绕通知中间,执行以下原本的方法,获得返回值
Object obj = joinPoint.proceed();
System.out.println("环绕前通知");
return obj;
}
//异常通知,参数为原本方法抛出的异常对象
@AfterThrowing(value="execution(* *.*.*.UserDaoImpl.find(..))",throwing="e")
public void afterThrowing(Throwable e) {
System.out.println("异常通知==="+e.getMessage());
}
//最终通知
@After(value="execution(* *.*.*.UserDaoImpl.find(..))")
public void after() {
System.out.println("最终通知");
}
}
- 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!-- beans的约束文件 -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
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/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 使用AOP需要aop的名称空间和约束文件
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
-->
<!-- 配置注解的扫描器,扫描需要使用注解的包 -->
<context:component-scan base-package="com.wzm"></context:component-scan>
<!-- 配置AOP使用注解,自动代理 -->
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
</beans>
- 测试类
package com.wzm.test;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.wzm.entity.UserDaoImpl;
//使用整合Junit的测试方式
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value="classpath:applicationContext.xml")
public class AspectTest {
//通过注解获取要被增强的对象
@Resource(name="userDao")
private UserDaoImpl userDao;
@Test
public void test1() {
userDao.save();
}
@Test
public void test2() {
userDao.delete();
}
@Test
public void test3() {
userDao.update();
}
@Test
public void test4() {
userDao.find();
}
}