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>