1.什么是 AOP
百度百科:在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
1.对程序进行增强:不修改源码的情况下,AOP 可以进行权限校验,日志记录,性能监控,事务控制.
2.AOP底层实现: 代理机制,Spring 的 AOP 的底层用到两种代理机制:
JDK 的动态代理 :针对实现了接口的类产生代理.
Cglib 的动态代理 :针对没有实现接口的类产生代理. 应用的是底层的字节码增强的技术 生成当前类的子类对象.
3.AOP 的开发中的相关术语:
Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在 spring 中,这些点指的是方法,因为 spring 只支持方法类型的连接点.
Pointcut(切入点):所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义.
Advice(通知/增强):所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)
Introduction(引介):引介是一种特殊的通知在不修改类代码的前提下, Introduction 可以在运行期为类动态地添加一些方法或 Field。
Target(目标对象):代理的目标对象。
Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程。spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装在期织入
Proxy(代理):一个类被 AOP 织入增强后,就产生一个结果代理类
Aspect(切面): 是切入点和通知(引介)的结合
2.AOP快速体验
基于上一篇博客,根据Spring架构图可知需要添加Spring的AOP依赖和Aspect依赖
创建一个切面类,封装可以对Bean进行增强的操作。
package com.wx.springsource1.aspect;
public class MyAspectXml {
// 前置增强:在目标方法执行之前执行.
public void before(){
System.out.println("前置增强===========");
}
}
把切面类交给Spring容器管理:
<!--配置切面类-->
<bean id="myAspectXml" class="com.wx.springsource1.aspect.MyAspectXml"></bean>
然后配置aop,对想进行增强的方法进行增强,比如现在想在订单的save()方法前面来一个增强的操作
<!--进行aop配置-->
<aop:config>
<!--配置切入点表达式:哪些类的哪些方法需要进行增强-->
<aop:pointcut id="pointcut1" expression="execution(* com.wx.springsource1.daoimp.OrderDaoImp.save(..))"></aop:pointcut>
<!--切入面配置-->
<aop:aspect ref="myAspectXml">
<!--前置配置-->
<aop:before method="before" pointcut-ref="pointcut1"></aop:before>
</aop:aspect>
</aop:config>
测试:
package com.wx.springsource1.test;
import com.wx.springsource1.dao.OrderDao;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestSpring1 {
@Autowired
private OrderDao orderDao;
@Test
public void testorder()
{
orderDao.update();
orderDao.save();
}
}
在保存订单的前面进行了增强的操作
3.AOP的通知类型
Aop的通知不仅仅只有前置增强,还有很多通知,如下:
前置通知 :在目标方法执行之前执行.
后置通知 :在目标方法执行之后执行
环绕通知 :在目标方法执行前和执行后执行
异常抛出通知:在目标方法执行出现 异常的时候 执行
最终通知 :无论目标方法是否出现异常 最终通知都会执行
4.切入点表达式
execution(表达式) 表达式: [方法访问修饰符] 方法返回值 包名.类名.方法名 (方法的参数)
public * com.wx.spring.dao.*.*(..)
* com.wx.spring.dao.*.*(..)
* com.wx.spring.dao.UserDao+.*(..)
* com.wx.spring.dao..*.*(..)
5.现在对多个方法进行不同的增强
<!--进行aop配置-->
<aop:config>
<!--配置切入点表达式:哪些类的哪些方法需要进行增强-->
<aop:pointcut id="pointcut1" expression="execution(* com.wx.springsource1.daoimp.OrderDaoImp.save(..))"></aop:pointcut>
<aop:pointcut id="pointcut2" expression="execution(* com.wx.springsource1.daoimp.OrderDaoImp.update(..))"></aop:pointcut>
<aop:pointcut id="pointcut3" expression="execution(* com.wx.springsource1.daoimp.OrderDaoImp.delete(..))"></aop:pointcut>
<aop:pointcut id="pointcut4" expression="execution(* com.wx.springsource1.daoimp.OrderDaoImp.find(..))"></aop:pointcut>
<aop:pointcut id="pointcut5" expression="execution(* com.wx.springsource1.daoimp.OrderDaoImp.importorder(..))"></aop:pointcut>
<!--切入面配置-->
<aop:aspect ref="myAspectXml">
<!--前置配置-->
<aop:before method="before" pointcut-ref="pointcut1"></aop:before>
<!--后置配置-->
<aop:after-returning method="afterreturning" pointcut-ref="pointcut2"></aop:after-returning>
<!--环绕配置-->
<aop:around method="watchPerformance" pointcut-ref="pointcut3"></aop:around>
<!--最终通知-->
<aop:after method="finalnotif" pointcut-ref="pointcut4"></aop:after>
<!--异常抛出通知-->
<aop:after-throwing method="exceptionNotification" pointcut-ref="pointcut5"></aop:after-throwing>
</aop:aspect>
</aop:config>
主要注意一下环绕配置,他需要传入链接点,然后在链接点前后执行增强逻辑。
package com.wx.springsource1.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
public class MyAspectXml {
// 前置增强:在目标方法执行之前执行.
public void before(){
System.out.println("前置增强===========");
}
//后置增强:在目标方法执行之后执行.
public void afterreturning()
{
System.out.println("后置增强==========");
}
//环绕通知:在目标方法执行前和执行后执行
public void watchPerformance(ProceedingJoinPoint joinpoint){
try{
System.out.println("begin!");
long start = System.currentTimeMillis();
joinpoint.proceed();
long end = System.currentTimeMillis();
System.out.println("end! performance took "+(end-start)+" milliseconds");
}catch(Throwable e){
System.out.println("eee!We want our money back!");
}
}
//最终通知:无论目标方法是否出现异常 最终通知都会 执行
public void finalnotif()
{
System.out.println("最终通知==========");
}
//异常抛出通知:在目标方法执行出现 异常的时候 执行
public void exceptionNotification()
{
System.out.println("目标方法抛出异常时通知==========");
}
}
package com.wx.springsource1.daoimp;
import com.wx.springsource1.dao.OrderDao;
public class OrderDaoImp implements OrderDao {
@Override
public void save() {
System.out.println("保存订单");
}
@Override
public void update() {
System.out.println("更新订单");
}
@Override
public void delete() {
System.out.println("删除订单");
}
@Override
public void find() {
System.out.println("查询订单");
}
@Override
public void importorder() {
System.out.println("导入订单");
throw new NullPointerException("null");
}
}
测试:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestSpring1 {
@Autowired
private OrderDao orderDao;
@Test
public void testorder()
{
orderDao.delete();//环绕通知的时候根本没有执行实现类的方法
orderDao.update();
orderDao.save();
orderDao.find();
orderDao.importorder();
}
}
6.AOP的注解开发方式
1.开启 aop 注解的自动代理
<!--开启 aop 注解的自动代理 aop注解开发-->
<aop:aspectj-autoproxy/>
<!--不管采取哪种方式开发,切面类还是要交给Spring来管理的-->
<bean id="myAspectXml" class="com.wx.springsource1.aspect.MyAspectXml"></bean>
2.AspectJ 的 AOP 的注解:
@Aspect:定义切面类的注解
通知类型:
* @Before :前置通知
* @AfterReturing :后置通知
* @Around :环绕通知
* @After :最终通知
* @AfterThrowing :异常抛出通知.
@Pointcut:定义切入点的注解
package com.wx.springsource1.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
@Aspect
public class MyAspectXml {
@Pointcut("execution(* com.wx.springsource1.daoimp.OrderDaoImp.save(..))")
private void pointcut1(){}
@Pointcut("execution(* com.wx.springsource1.daoimp.OrderDaoImp.update(..))")
private void pointcut2(){}
@Pointcut("execution(* com.wx.springsource1.daoimp.OrderDaoImp.delete(..))")
private void pointcut3(){}
@Pointcut("execution(* com.wx.springsource1.daoimp.OrderDaoImp.find(..))")
private void pointcut4(){}
@Pointcut("execution(* com.wx.springsource1.daoimp.OrderDaoImp.importorder(..))")
private void pointcut5(){}
// 前置增强:在目标方法执行之前执行.
@Before("MyAspectXml.pointcut1()")
public void before(){
System.out.println("前置增强===========");
}
//后置增强:在目标方法执行之后执行.
@AfterReturning("MyAspectXml.pointcut2()")
public void afterreturning()
{
System.out.println("后置增强==========");
}
//环绕通知:在目标方法执行前和执行后执行
@Around("MyAspectXml.pointcut3()")
public void watchPerformance(ProceedingJoinPoint joinpoint){
try{
System.out.println("begin!");
long start = System.currentTimeMillis();
//这里就是执行切入点的方法
joinpoint.proceed();
long end = System.currentTimeMillis();
System.out.println("end! performance took "+(end-start)+" milliseconds");
}catch(Throwable e){
System.out.println("eee!We want our money back!");
}
}
//最终通知:无论目标方法是否出现异常 最终通知都会 执行
@After("MyAspectXml.pointcut4()")
public void finalnotif()
{
System.out.println("最终通知==========");
}
//异常抛出通知:在目标方法执行出现 异常的时候 执行
@AfterThrowing("MyAspectXml.pointcut5()")
public void exceptionNotification()
{
System.out.println("目标方法抛出异常时通知==========");
}
}
执行结果和XML配置完全一致