1、AOP概述
AOP技术即Aspect Oriented Programming的缩写,译为面向切面编程。AOP是OOP的一种延续,利用AOP技术可以对业务逻辑的各个部分进行隔离,从使得业务逻辑各部分之间的耦合性降低,提高程序的可重用性,同时提高了开发的效率。
AOP采用横向抽取机制,取代了传统纵向继承体系重复性代码,AOP可以在不修改源代码的前提下,对程序进行增强。
2、AOP技术的底层实现
- 基于jdk的动态代理:必须是面向接口的,只有实现了具体接口的类才能生成代理对象
- 基于CGLIB动态代理:对于没有实现接口的类,也可以产生代理,产生这个类的子类的方式
Spring的传统AOP中根据类是否实现接口而采用不同的代理方式,如果实现类接口,则使用jdk动态代理完成AOP,如果没有实现接口,采用CGLIB动态代理完成AOP。
JDK动态代理演示:
接口UserDao、实现类UserDaoImpl、动态代理类MyProxyUtils
1 package com.alphajuns.demo1; 2 3 public interface UserDao { 4 5 public void save(); 6 7 public void update(); 8 9 }
1 package com.alphajuns.demo1; 2 3 public class UserDaoImpl implements UserDao { 4 5 @Override 6 public void save() { 7 System.out.println("保存用户..."); 8 } 9 10 @Override 11 public void update() { 12 System.out.println("修改用户..."); 13 } 14 15 }
1 package com.alphajuns.demo1; 2 3 import java.lang.reflect.InvocationHandler; 4 import java.lang.reflect.Method; 5 import java.lang.reflect.Proxy; 6 7 /* 8 * 使用JDK的方式生成动态代理 9 */ 10 public class MyProxyUtils { 11 12 public static UserDao getProxy(final UserDao dao) { 13 // 使用Proxy类生成代理对象 14 UserDao proxy = (UserDao) Proxy.newProxyInstance( 15 dao.getClass().getClassLoader(), 16 dao.getClass().getInterfaces(), 17 new InvocationHandler() { 18 19 @Override 20 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 21 if ("save".equals(method.getName())) { 22 System.out.println("记录日志..."); 23 } 24 // 执行dao类中的方法 25 return method.invoke(dao, args); 26 } 27 }); 28 29 // 返回代理对象 30 return proxy; 31 } 32 33 }
CGLIB代理:
1 package com.alphajuns.demo2; 2 3 import java.lang.reflect.Method; 4 5 import org.springframework.cglib.proxy.Enhancer; 6 import org.springframework.cglib.proxy.MethodInterceptor; 7 import org.springframework.cglib.proxy.MethodProxy; 8 9 public class MyCglibUtils { 10 11 /* 12 * 使用Cglib方法生成代理对象 13 */ 14 public static BookDaoImpl getProxy() { 15 Enhancer enhancer = new Enhancer(); 16 // 设置父类 17 enhancer.setSuperclass(BookDaoImpl.class); 18 // 设置回调函数 19 enhancer.setCallback(new MethodInterceptor() { 20 // 代理对象的方法执行,回调函数的方法就会执行 21 @Override 22 public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { 23 if ("save".equals(method.getName())) { 24 System.out.println("记录日志..."); 25 } 26 // 正常执行 27 return methodProxy.invokeSuper(obj, args); 28 } 29 }); 30 31 // 生成代理对象 32 BookDaoImpl proxy = (BookDaoImpl) enhancer.create(); 33 return proxy; 34 } 35 36 }
3、AOP相关术语
- JoinPoint(连接点):被拦截的点。Spring中,这些点是指方法,Spring只支持方法类型的连接点。
- Pointcut(切入点):对需要被拦截的JoinPoint的定义。
- Advice(通知/增强):拦截到JointPoint之后所要做的事情就是通知。通知分为前置通知、后置通知、异常通知、最终通知、环绕通知
- Introduction(引介):引介是一种特殊的通知,在不修改代码的前提下,Introduction可以在运行期为类动态地添加一些方法或field
- Target(目标对象):代理的目标对象
- Weaving(织入):是指增强应用到目标对象来创建新的代理对象的过程
- Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类
- Aspect(切面):切入点与通知的结合
4、XML方式AOP开发步骤
- 创建WEB项目,引入jar包
- 创建Spring配置文件,引入AOP的schema约束
- 创建包结构,编写具体的接口和实现类
- 将目标类配置到Spring配置文件中
- 定义切面类
- 在配置文件中定义切面类
- 在配置文件中完成AOP配置
- 测试
5、切入点表达式
切入点表达式在下面applicationContext2中以注释形式进行了介绍。
6、AOP通知类型
- 前置通知:在目标类的方法执行之前执行
- 后置通知:在目标类方法执行之后执行
- 异常抛出通知:在抛出异常后通知
- 环绕通知:方法执行前后都执行
7、AOP应用举例
接口CustomerDao、实现类CustomerDaoImpl、切面类MyAspectXml、Spring配置文件applicationContext、applicationContext2、applicationContext3
1 package com.alphajuns.demo3; 2 3 public interface CustomerDao { 4 5 public void save(); 6 7 public void update(); 8 9 }
1 package com.alphajuns.demo3; 2 3 public class CustomerDaoImpl implements CustomerDao { 4 5 @Override 6 public void save() { 7 System.out.println("保存客户..."); 8 } 9 10 @Override 11 public void update() { 12 System.out.println("更新客户..."); 13 } 14 15 }
1 package com.alphajuns.demo3; 2 3 import org.aspectj.lang.ProceedingJoinPoint; 4 5 /* 6 * 切面类:切入点+通知 7 */ 8 public class MyAspectXml { 9 10 /* 11 * 通知(具体的增强) 12 */ 13 public void log() { 14 System.out.println("记录日志..."); 15 } 16 17 /* 18 * 最终通知:方法执行成功或出现异常,都会执行 19 */ 20 public void after() { 21 System.out.println("最终通知..."); 22 } 23 24 /* 25 * 后置通知:方法执行之后,执行后置通知,出现异常则不执行 26 */ 27 public void afterReturn() { 28 System.out.println("后置通知..."); 29 } 30 31 /* 32 * 环绕通知:方法执行之前和方法执行之后进行通知,默认情况下,目标对象的方法不执行。需要手动让目标对象的方法执行 33 */ 34 public void around(ProceedingJoinPoint joinPoint) { 35 System.out.println("环绕通知1..."); 36 try { 37 // 手动让目标对象的方法执行 38 joinPoint.proceed(); 39 } catch (Throwable e) { 40 e.printStackTrace(); 41 } 42 System.out.println("环绕通知2..."); 43 } 44 45 }
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" 5 http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 6 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- bean definitions here --> 7 8 <!-- 配置客户的dao --> 9 <bean id="customerDao" class="com.alphajuns.demo3.CustomerDaoImpl"/> 10 <!-- 配置切面类 --> 11 <bean id="myAspectXml" class="com.alphajuns.demo3.MyAspectXml"/> 12 <!-- 配置AOP --> 13 <aop:config> 14 <!-- 配置切面类:切入点+通知类型 --> 15 <aop:aspect ref="myAspectXml"> 16 <!-- 配置前置通知 --> 17 <aop:before method="log" pointcut="execution(public void com.alphajuns.demo3.CustomerDaoImpl.save())"/> 18 </aop:aspect> 19 </aop:config> 20 21 </beans>
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" 5 http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 6 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- bean definitions here --> 7 8 <!-- 配置客户的dao --> 9 <bean id="customerDao" class="com.alphajuns.demo3.CustomerDaoImpl"/> 10 <!-- 配置切面类 --> 11 <bean id="myAspectXml" class="com.alphajuns.demo3.MyAspectXml"/> 12 <!-- 配置AOP --> 13 <aop:config> 14 <!-- 配置切面类:切入点+通知类型 --> 15 <aop:aspect ref="myAspectXml"> 16 <!-- 配置前置通知 --> 17 <!-- 切入点的表达式 18 1、execution() 固定的,不能不写 19 2、public 可以省略不写 20 3、void出可以用*替代,表示返回值类型任意,不能省略不写 21 4、包的简写方式 22 5、类的写法 23 6、方法的写法 24 7、方法的参数 25 --> 26 <!-- 完整写法 --> 27 <!-- <aop:before method="log" pointcut="execution(public void com.alphajuns.demo3.CustomerDaoImpl.save())"/> --> 28 <!-- public可以省略不写 --> 29 <!-- <aop:before method="log" pointcut="execution(void com.alphajuns.demo3.CustomerDaoImpl.save())"/> --> 30 <!-- void出用*替代 --> 31 <!-- <aop:before method="log" pointcut="execution(* com.alphajuns.demo3.CustomerDaoImpl.save())"/> --> 32 <!-- 包的写法 --> 33 <!-- <aop:before method="log" pointcut="execution(* com.alphajuns.demo3.CustomerDaoImpl.save())"/> --> 34 <!-- 包的写法 --> 35 <!-- <aop:before method="log" pointcut="execution(* *..*.CustomerDaoImpl.save())"/> --> 36 <!-- 类的写法 --> 37 <!-- <aop:before method="log" pointcut="execution(* com.alphajuns.demo3.*DaoImpl.save())"/> --> 38 <!-- 方法写法 --> 39 <!-- <aop:before method="log" pointcut="execution(* com.alphajuns.demo3.CustomerDaoImpl.*save())"/> --> 40 <!-- 方法的参数 --> 41 <aop:before method="log" pointcut="execution(* com.alphajuns.demo3.CustomerDaoImpl.*save(..))"/> 42 </aop:aspect> 43 </aop:config> 44 45 </beans>
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" 5 http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 6 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- bean definitions here --> 7 8 <!-- 配置客户的dao --> 9 <bean id="customerDao" class="com.alphajuns.demo3.CustomerDaoImpl"/> 10 <!-- 配置切面类 --> 11 <bean id="myAspectXml" class="com.alphajuns.demo3.MyAspectXml"/> 12 <!-- 配置AOP --> 13 <aop:config> 14 <!-- 配置切面类:切入点+通知类型 --> 15 <aop:aspect ref="myAspectXml"> 16 <!-- 配置前置通知 --> 17 <!-- <aop:before method="log" pointcut="execution(public void com.alphajuns.demo3.CustomerDaoImpl.save())"/> --> 18 <!-- 配置后置通知 --> 19 <!-- <aop:after method="log" pointcut="execution(public void com.alphajuns.demo3.CustomerDaoImpl.save())"/> --> 20 <!-- 环绕通知 --> 21 <aop:around method="around" pointcut="execution(public void com.alphajuns.demo3.CustomerDaoImpl.save())"/> 22 </aop:aspect> 23 </aop:config> 24 25 </beans>