目录
1. AOP 1.1 AOP介绍 1.1.1 什么是AOP1.1.2 AOP实现原理1.1.3 AOP术语【掌握】1.2 AOP的底层实现(了解) 1.2.1 JDK动态代理1.2.2 Cglib动态代理1.2.3 总结2. Spring的AOP的开发(AspectJ的XML方式) 2.1 Spring的AOP简介2.2 AOP入门开发 2.2.1 准备工程和jar包2.2.2 引入Spring的配置文件2.2.3 编写目标类并完成配置2.2.4 测试类2.2.5 编写一个切面类2.2.6 将切面类交给Spring2.2.7 通过AOP的配置实现动态代理2.2.8 测试类中运行结果,save实现了增强2.3 Spring中的通知类型 2.3.1 前置通知2.3.2 后置通知:2.3.3 环绕通知:2.3.4 异常抛出通知:2.3.5 最终通知:2.3.6 引介通知(不用会)2.4 切入点表达式【掌握】小结:
一个线是一个特殊的面。 一个切入点和一个通知,组成成一个特殊的面。 详解如图01:
详解如图02:
准备工作:新建web项目,不需要导包
代理对象UserDao
package com.itzhouq.spring.demo1; public interface UserDao { public void save(); public void update(); public void find(); public void delete(); }实现类
package com.itzhouq.spring.demo1; public class UserDaoImpl implements UserDao { @Override public void save() { System.out.println("保存用户"); } @Override public void update() { System.out.println("更新用户"); } @Override public void find() { System.out.println("查找用户"); } @Override public void delete() { System.out.println("删除用户"); } }使用JDK产生UserDao的代理类
package com.itzhouq.spring.demo1; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /* * 使用JDK动态代理对UserDao产生代理 */ public class JDKProxy implements InvocationHandler { // 将被增强的对象传递到代理总 private UserDao userDao; public JDKProxy(UserDao userDao) { this.userDao = userDao; } /* * 产生UserDao代理的方法 */ public UserDao createProxy() { UserDao userDaoProxy = (UserDao) Proxy.newProxyInstance( userDao.getClass().getClassLoader(), userDao.getClass().getInterfaces(), this); return userDaoProxy; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 判断方法名是不是save if("save".equals(method.getName())) { // 增强 System.out.println("权限校验=============="); return method.invoke(userDao, args); } return method.invoke(userDao, args); } }测试类
package com.itzhouq.spring.demo1; import org.junit.Test; public class SpringDemo1 { @Test //JDK动态代理 public void test1() { UserDao userDao = new UserDaoImpl(); // 创建代理 UserDao proxy = new JDKProxy(userDao).createProxy(); proxy.save(); proxy.find(); proxy.delete(); proxy.update(); //权限校验============== //保存用户 //查找用户 //删除用户 //更新用户 } }Cglib是第三方开源代码生成类库,动态添加类的属性和方法
cglib的运行原理:在运行时创建目标类的子类从而对目标类进行增强。
因为Spring核心包中包含了cglib的包,所以引入Spring的4+2必备包就可以使用Cglib了
目标类:没有实现接口
package com.itzhouq.spring.demo2; public class CustomerDao { public void save() { System.out.println("保存客户"); } public void update() { System.out.println("更新客户"); } public void find() { System.out.println("查找客户"); } public void delete() { System.out.println("删除客户"); } }Cglib代理类
package com.itzhouq.spring.demo2; import java.lang.reflect.Method; import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; /* * Cglib动态代理 */ public class CglibProxy implements MethodInterceptor { private CustomerDao customerDao; public CglibProxy(CustomerDao customerDao) { this.customerDao = customerDao; } // 使用Cglib产生代理的方法 public CustomerDao createProxy() { // 1. 创建cglib的核心类对象 Enhancer enhancer = new Enhancer(); // 2. 设置父类 enhancer.setSuperclass(customerDao.getClass()); // 3. 设置回调(类似于InvocationHandler对象) enhancer.setCallback(this); // 4. 创建代理对象 CustomerDao proxy = (CustomerDao) enhancer.create(); return proxy; } @Override public Object intercept(Object proxy, Method method, Object[] arg, MethodProxy methodProxy) throws Throwable { // 判断方法是否为save if("save".equals(method.getName())) { // 增强 System.out.println("权限校验========"); return methodProxy.invokeSuper(proxy, arg); } return methodProxy.invokeSuper(proxy, arg); } }测试
package com.itzhouq.spring.demo2; import org.junit.Test; public class SpringDemo2 { /* * cglib的测试 */ @Test public void test1() { CustomerDao customerDao = new CustomerDao(); CustomerDao proxy = new CglibProxy(customerDao).createProxy(); proxy.save(); proxy.find(); proxy.update(); proxy.delete(); //权限校验======== //保存客户 //查找客户 //更新客户 //删除客户 } }目标接口
package com.itzhouq.spring.demo3; public interface ProductDao { public void save(); public void update(); public void find(); public void delete(); }目标类
package com.itzhouq.spring.demo3; public class ProductDaoImpl implements ProductDao { @Override public void save() { System.out.println("保存商品"); } @Override public void update() { System.out.println("更新商品"); } @Override public void find() { System.out.println("查找商品"); } @Override public void delete() { System.out.println("删除商品"); } }配置目标对象
<!-- 配置目标对象:被增强的对象 --> <bean id="productDao" class="com.itzhouq.spring.demo3.ProductDaoImpl"></bean>切面:多个通知和多个切入点的组合。
package com.itzhouq.spring.demo3; /* * 切面类 */ public class MyAspectXML { public void checkPri() { System.out.println("权限校验。。。"); } }配置
<!-- 配置切面 --> <aop:aspect ref="myAspect"> <aop:before method="checkPri" pointcut-ref="pointcut1"/> </aop:aspect>在目标方法执行之前进行操作
可以获得切入点的信息
比如在切面类中加入参数JoinPoint
public class MyAspectXML { public void checkPri(JoinPoint joinPoint) { System.out.println("权限校验。。。"+joinPoint); } }测试打印的效果
// 权限校验。。。execution(void com.itzhouq.spring.demo3.ProductDao.save()) // 保存商品 // 更新商品 // 查找商品 // 删除商品在目标方法之后操作,可以获得返回值
修改接口ProductDao和实现类ProductDaoImpl类的delete方法的返回值为String
配置后置通知
<!-- 通过AOP的配置完成对目标类产生代理 --> <aop:config> <!-- 表达式配置哪些类的哪些方法需要进行增强 --> <aop:pointcut expression="execution(* com.itzhouq.spring.demo3.ProductDaoImpl.save(..))" id="pointcut1"/> <aop:pointcut expression="execution(* com.itzhouq.spring.demo3.ProductDaoImpl.delete(..))" id="pointcut2"/> <!-- 配置切面 --> <aop:aspect ref="myAspect"> <!-- 前置通知============= --> <aop:before method="checkPri" pointcut-ref="pointcut1"/> <!-- 后置通知============= --> <aop:after-returning method="writeLog" pointcut-ref="pointcut2" returning="result"/> </aop:aspect> </aop:config>在后置切面类中添加记录日志的方法
/* * 后置通知演示 */ public void writeLog(Object result) { System.out.println("日志记录======="+result); }测试
权限校验。。。execution(void com.itzhouq.spring.demo3.ProductDao.save()) // 保存商品 // 更新商品 // 查找商品 // 删除商品 // 日志记录=======kkk在目标方法执行之前 和之后进行操作
环绕通知可以组织目标方法的执行
举例:需求---在修改方法update前后添加性能监控
在切面类中添加一个方法用来测试环绕通知
/** * 性能监控 * @throws Throwable */ public Object around(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("环绕前通知==========="); Object obj = joinPoint.proceed();//这一步相当于执行目标程序 System.out.println("环绕后通知========="); return obj; }配置环绕通知
<aop:config> <!-- 表达式配置哪些类的哪些方法需要进行增强 --> <aop:pointcut expression="execution(* com.itzhouq.spring.demo3.ProductDaoImpl.save(..))" id="pointcut1"/> <aop:pointcut expression="execution(* com.itzhouq.spring.demo3.ProductDaoImpl.delete(..))" id="pointcut2"/> <aop:pointcut expression="execution(* com.itzhouq.spring.demo3.ProductDaoImpl.update(..))" id="pointcut3"/> <!-- 配置切面 --> <aop:aspect ref="myAspect"> <!-- 前置通知============= --> <aop:before method="checkPri" pointcut-ref="pointcut1"/> <!-- 后置通知============= --> <aop:after-returning method="writeLog" pointcut-ref="pointcut2" returning="result"/> <!-- 环绕通知 --> <aop:around method="around" pointcut-ref="pointcut3"/> </aop:aspect> </aop:config>测试
// 权限校验。。。execution(void com.itzhouq.spring.demo3.ProductDao.save()) // 保存商品 // 环绕前通知=========== // 更新商品 // 环绕后通知========= // 查找商品 // 删除商品 // 日志记录=======kkk在程序抛出异常时候进行操作,可以得到异常信息
在find方法上模拟一个异常
@Override public void find() { System.out.println("查找商品"); int i = 1 / 0; }切面类中添加一个方法用于测试异常抛出通知
/* * 异常抛出通知 */ public void afterThrowing(Throwable ex) { System.out.println("异常抛出通知=======" + ex); }配置异常通知
<aop:config> <!-- 表达式配置哪些类的哪些方法需要进行增强 --> <aop:pointcut expression="execution(* com.itzhouq.spring.demo3.ProductDaoImpl.save(..))" id="pointcut1"/> <aop:pointcut expression="execution(* com.itzhouq.spring.demo3.ProductDaoImpl.delete(..))" id="pointcut2"/> <aop:pointcut expression="execution(* com.itzhouq.spring.demo3.ProductDaoImpl.update(..))" id="pointcut3"/> <aop:pointcut expression="execution(* com.itzhouq.spring.demo3.ProductDaoImpl.find(..))" id="pointcut4"/> <!-- 配置切面 --> <aop:aspect ref="myAspect"> <!-- 前置通知============= --> <aop:before method="checkPri" pointcut-ref="pointcut1"/> <!-- 后置通知============= --> <aop:after-returning method="writeLog" pointcut-ref="pointcut2" returning="result"/> <!-- 环绕通知 --> <aop:around method="around" pointcut-ref="pointcut3"/> <!-- 异常抛出通知 --> <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut4" throwing="ex"/> </aop:aspect> </aop:config>测试
// 权限校验。。。execution(void com.itzhouq.spring.demo3.ProductDao.save()) // 保存商品 // 环绕前通知=========== // 更新商品 // 环绕后通知========= // 查找商品 // 异常抛出通知=======java.lang.ArithmeticException: / by zero在切面类中添加方法测试最终通知
/* * 最终通知:相当于finally代码块中的内容 */ public void after() { System.out.println("最终通知========="); }配置最终通知
<!-- 配置最终通知 --> <aop:after method="after" pointcut-ref="pointcut4"/>测试
无论是否有异常,最终通知都会执行。
转载于:https://www.cnblogs.com/itzhouq/p/spring3.html
相关资源:Spring5_AOP.pdf