Spring框架(中) AOP
Spring(中) AOP
(一)代理模式
1.静态代理
静态代理在使用时,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承相同父类。
举例:
package com.deserts.proxy.staticproxy;/*** @ClassName ProxyTest* @Description TODO* @Author deserts* @Date 2020/10/16 12:12*/interface IShoeFactory{void produce();
}class ShoeFactory implements IShoeFactory{@Overridepublic void produce() {System.out.println("生产鞋子");}
}class ShoeProxy implements IShoeFactory{private IShoeFactory shoeFactory;public ShoeProxy (IShoeFactory shoeFactory){this.shoeFactory = shoeFactory;}@Overridepublic void produce() {shoeFactory.produce();}
}public class ProxyTest {public static void main(String[] args) {ShoeProxy proxy = new ShoeProxy(new ShoeFactory());proxy.produce();}
}
优点:在不修改目标对象的功能前提下, 能通过代理对象对目标功能扩展
缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类;或者需要实现很多接口,代码混乱。
一旦接口增加方法,目标对象与代理对象都要维护。
2.JDK动态代理
基本介绍:
代理对象,不需要实现接口,但是目标对象要实现接口,否则不能用动态代理
代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象
动态代理也叫做:JDK代理、接口代理
举例:
package com.deserts.proxy.jdkproxy;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;/*** @ClassName JDKProxyTest* @Description TODO* @Author deserts* @Date 2020/10/16 12:23*/interface IShoeFactory{void produce();
}
class ShoeFactory implements IShoeFactory{@Overridepublic void produce() {System.out.println("生产鞋子中");}
}class ShoeProxyFactory{private Object target;public ShoeProxyFactory(Object target) {this.target = target;}public Object getProxyInstance(){return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object returnResult = method.invoke(target, args);return returnResult;}});}
}public class JDKProxyTest {public static void main(String[] args) {IShoeFactory proxy = (IShoeFactory)new ShoeProxyFactory(new ShoeFactory()).getProxyInstance();proxy.produce();}}
(二)AOP概述
1.一个动态代理的例子
1.1 要求
①执行加减乘除运算
②日志:在程序执行期间追踪正在发生的活动
1.2 实现
创建计算器接口,让计算器类去实现;创建动态代理的类,利用构造器或者方法传入计算器对象,创建获取被代理类的方法。
1.ICalculator接口
public interface ICalculator {int add(int i, int j);int sub(int i, int j);int mul(int i, int j);int div(int i, int j);
}
2.Calculator类
package com.deserts.aop.proxy;/*** @ClassName Calculator* @Description TODO* @Author deserts* @Date 2020/10/15 19:23*/
public class Calculator implements ICalculator{@Overridepublic int add(int i, int j) {int result = i + j;return result;}@Overridepublic int sub(int i, int j) {int result = i - j;return result;}@Overridepublic int mul(int i, int j) {int result = i * j;return result;}@Overridepublic int div(int i, int j) {int result = i / j;return result;}
}
3.代理类
package com.deserts.aop.proxy;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.function.Predicate;/*** @ClassName ProxyFactory* @Description TODO* @Author deserts* @Date 2020/10/15 19:26*/
public class ProxyFactory {Object target;public ProxyFactory(Object target){this.target = target;}public Object getProxy(){return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object returnResult = null;try {Log.before(method.getName(), args);returnResult = method.invoke(target, args);Log.after(method.getName(), returnResult);} catch (Exception e) {Log.exceptionInfo(method.getName(), e);} finally {Log.end();}return returnResult;}});}
}
4.封装日志功能,错误信息的类
package com.deserts.aop.proxy;import java.util.Arrays;/*** @ClassName Log* @Description TODO* @Author deserts* @Date 2020/10/15 19:54*/
public class Log {public static void before(String methodName, Object... args){System.out.println(methodName + "即将执行,参数为:" + Arrays.asList(args));}public static void after(String methodName, Object result){System.out.println(methodName + "执行结束,结果为:" + result);}public static void exceptionInfo(String methodName, Exception e){System.out.println(methodName + "执行过程出现了异常,异常信息为:" + e.getCause());}public static void end(){System.out.println("程序执行结束");}
}
5.测试类
package com.deserts.aop.proxy;/*** @ClassName Client* @Description TODO* @Author deserts* @Date 2020/10/15 19:34*/
public class Client {public static void main(String[] args) {ICalculator proxy = (ICalculator)new ProxyFactory(new Calculator()).getProxy();proxy.add(1,2);proxy.div(2, 0);}
}
2.AOP概述
AOP(Aspect-Oriented Programming,面向切面编程):是一种新的方法论,是对传统 OOP(Object-Oriented Programming,面向对象编程)的补充。
- 面向对象 纵向继承机制
- 面向切面 横向抽取机制
AOP编程操作的主要对象是切面(aspect),而切面用于模块化横切关注点(公共功能)。
在应用AOP编程时,仍然需要定义公共功能,但可以明确的定义这个功能应用在哪里,以什么方式应用,并且不必修改受影响的类。这样一来横切关注点就被模块化到特殊的类里——这样的类我们通常称之为“切面”
AOP的好处:
① 每个事物逻辑位于一个位置,代码不分散,便于维护和升级
② 业务模块更简洁,只包含核心业务代码
③ AOP图解
3.AOP术语
3.1 横切关注点
从每个方法中抽取出来的同一类非核心业务。
3.2 切面(Aspect)
封装横切关注点信息的类,每个关注点体现为一个通知方法。
3.3 通知(Advice)
切面必须要完成的各个具体工作
3.4 目标(Target)
被通知的对象
3.5 代理(Proxy)
向目标对象应用通知之后创建的代理对象
3.6 连接点(Joinpoint)
横切关注点在程序代码中的具体体现,对应程序执行的某个特定位置。例如:类某个方法调用前、调用后、方法捕获到异常后等。
3.7 切入点(pointcut)
定位连接点的方式。每个类的方法中都包含多个连接点,所以连接点是类中客观存在的事物。如果把连接点看作数据库中的记录,那么切入点就是查询条件——AOP可以通过切入点定位到特定的连接点。切点通过org.springframework.aop.Pointcut 接口进行描述,它使用类和方法作为连接点的查询条件。
3.8 图解
4.AspectJ
4.1 简介
AspectJ:Java社区里最完整最流行的AOP框架。
在Spring2.0以上版本中,可以使用基于AspectJ注解或基于XML配置的AOP。
4.2 在Spring中启用AspectJ注解支持
1.导入jar包
- com.springsource.net.sf.cglib-2.2.0.jar
- com.springsource.org.aopalliance-1.0.0.jar
- com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
- spring-aop-4.0.0.RELEASE.jar
- spring-aspects-4.0.0.RELEASE.jar
2.引入AOP命名空间
3.配置扫描包,配置被代理的类和日志类
日志类需要@Aspect注解,@Before注解将方法配置为前置通知,需要指定value值,值为切入点表达式。同时需要@Component注解让Spring组件扫描去管理。
被代理类也需要@Component注解,让Spring去管理:
测试类使用时需要创建Spring容器去管理被配置好的类的对象:
运行结果:
4.3 用AspectJ注解声明切面
① @Before:前置通知,在方法执行之前执行
② @After:后置通知,在方法执行之后执行
③ @AfterRunning:返回通知,在方法返回结果之后执行
④ @AfterThrowing:异常通知,在方法抛出异常之后执行
⑥ @Around:环绕通知,围绕着方法执行
(三) AOP细节
1.细节一:容器创建的对象
IOC容器中保存的是组件的代理对象,可以理解成这个代理对象也去实现了目标对象所实现的接口
如:
运行结果:
可以看到,该对象是class com.sun.proxy.$Proxy7代理对象,底层原理是jdk动态代理。
除了实现接口的方式去代理,spring还提供了cglib代理,为没有接口的类进行代理。
如:
测试类:
运行结果:
可以看到cglib为我们创建了代理对象
2.细节二:切入点表达式
2.1 *:
1)匹配一个或多个字符,如:execution(public int com.deserts.aop.proxy.Calculator.*(int,int))
匹配任意类型的参数,如:execution(public int com.deserts.aop.proxy.Calculator.add(*,*))
只能匹配一层路径,如:execution(public int com.deserts.aop.*.Calculator.add(int,int))
4)权限类型前不能写,可以不写权限类型。public(可选类型)。
2.2 … :
- 匹配任意多个参数,如:execution(public int com.deserts.aop.proxy.Calculator.add(…))
2)匹配多层路径,如:execution(public int com.deserts…Calculator.*(int,int))
2.3 两种类型
最精确的:execution(public int com.deserts.aop.proxy.Calculator.add(int,int))
最模糊的:execution(*.*(…)) 不建议写,见方法就切
2.4 使用&&、||、!
在AspectJ中,切入点表达式可以通过 “&&”、“||”、“!”等操作符结合起来。
表达式 | 含义 |
---|---|
execution (* *.add(int,…)) || excution( *.sub(int, …)) | |
!execution (* *.add(int,…)) | 匹配不是任意类中第一个参数为int类型的add方法 |
3.细节三:通知方法的执行顺序
正常执行:@Before(前置通知) ==== @After(后置通知) ==== @AfterReturning(正常返回)
异常执行: @Before(前置通知) ==== @After(后置通知) ==== @AfterThrowing(方法异常)
4.细节四:JoinPoint获取获取目标方法的信息
主要通过传入通知方法的JoinPoint joinPoint参数,在方法内调用参数相关方法进行获取,如:
5.细节五:使用returning、throwing来接收返回值、异常
接收返回值:
接收异常信息:
6.细节六:Spring对通知方法的约束
1)spring对通知方法的没有太大约束,唯一的约束是参数列表的参数不能乱写,如果有未知的参数,需要告诉spring这个参数是用来做什么的,因为这些通知方法是spring所管理的,主要是通过反射来调用的。
2)如returning、throwing指定接收返回值、异常的参数,参数类型要尽量的大,如Object、Exception
7.细节七:抽取可重用的切入点表达式
抽取可重用的切入点表达式:
- 1.创建一个返回值为void,参数为空的空方法
- 2.给方法加上注解@PointCut,并在参数里写入要抽取的切入点表达式
- 3.将方法名放入要使用该表达式的值里面
示例:
8.细节八:环绕通知
- 环绕通知是通知里面最强大的,可以当作其它四个通知的整合。
- 对于环绕通知来说,连接点的参数类型必须是ProceedingJoinPoint。它是 JoinPoint的子接口,允许控制何时执行,是否执行连接点。
- 在环绕通知中需要明确调用ProceedingJoinPoint的proceed()方法来执行被代理的方法。如果忘记这样做就会导致通知被执行了,但目标方法没有被执行
- 注意:环绕通知的方法需要返回目标方法执行之后的结果,即调用 joinPoint.proceed();的返回值,否则会出现空指针异常。
示例:
9.细节九:环绕通知的执行顺序&抛出异常让其它通知感受到
测试:将四个普通通知和环绕通知都打开,运行查看结果:
通知顺序:
新的顺序:
让普通通知感受到异常:将该异常抛出去
10.细节十:多切面运行顺序
都是普通通知时,会根据切面类的名称的字母顺序,小的在外层,如LogUtil类在VaAspect类的外层,先进去后出来
若想改变切面运行顺序,可以加上@Order注解,如:
value值越小,优先级越高,也就是在最外层。
当有环绕通知时,环绕通知会先于该切面类的普通通知去执行,因为该环绕通知是加在这个切面类的。
如环绕通知加到LogUtils上:
AOP使用场景
1.日志记录
2.权限验证
3.安全检查
4.事务控制
(四)以xml方式配置切面
1.基于注解的AOP
2.基于配置的AOP
步骤
在IOC容器中创建管理目标类的bean和切面类的bean
配置切面类中的通知方法,配置切面时需要引用上面配置好的切面类
测试
3.注解和配置的选择
注解:快速方便
配置:功能完善
选择:重要的用配置,不重要的用注解
Spring框架(中) AOP相关推荐
- Spring框架中的核心技术之AOP
目录 1. 什么是AOP? 2. 实现AOP技术的框架有哪些? 2.1 Spring框架中的AOP技术 2.2 Aspectj框架 2.3 小结 3. Aspectj框架中的使用AOP的方式 4. A ...
- 详解Spring框架的AOP机制
AOP是Spring框架面向切面的编程思想,AOP采用一种称为"横切"的技术,将涉及多业务流程的通用功能抽取并单独封装,形成独立的切面,在合适的时机将这些切面横向切入到业务流程指定 ...
- Spring 框架之 AOP 原理深度剖析!|CSDN 博文精选
作者 | GitChat 责编 | 郭芮 出品 | CSDN 博客 AOP(Aspect Oriented Programming)面向切面编程是 Spring 框架最核心的组件之一,它通过对程序结构 ...
- Spring框架中常用的设计模式详解
一.浅谈控制反转(IOC)与依赖注入(DI) IOC(Inversion of Control)是Spring中一个非常重要的概念,它不是什么技术,而是一种解耦的设计思想.它主要的额目的是借助于第三方 ...
- Spring框架中的设计模式(一)
设计模式有助于遵循良好的编程实践.作为最流行的Web框架之一的Spring框架也使用其中的一些. 本文将介绍Spring Framework中使用的设计模式.这是5篇专题文章的第一部分.这次我们将发现 ...
- 在Spring 框架中如何更有效的使用JDBC?
使用Spring JDBC 框架,资源管理以及错误处理的代价都会减轻.开发人员只需通过statements 和queries 语句从数据库中存取数据.Spring 框架中通过使用模板类能更有效的使用J ...
- 在Spring框架中使用SQL存储过程
Spring框架也支持对SQL存储过程的调用,SQL存储过程是一组预先定义好的SQL语句,并存储到数据库管理系统中,外部程序可以直接调用执行.本课主要讨论在Spring框架中应用程序如何调用MySQL ...
- 理解Spring框架中Bean的作用域
本篇介绍Spring Bean实例的作用范围,Spring Bean实例的作用范围由配置项scope限定.通过本篇的学习,可以达成如下目标. ● 应用scope配置项配置Bean的作用域 ● 应用单例 ...
- Spring框架中的控制反转和依赖注入
控制反转: 控制反转是用来降低代码之间的耦合度的,基本思想就是借助"第三方"实现具有依赖对象的解耦. 为什么需要控制反转,因为项目中对象或多或少存在耦合.控制反转的关键在于Ioc容 ...
- Spring框架中提取list集合类型属性注入
提取list集合类型属性注入 前言 引入名称空间 编写`xml`配置文件 运行结果 前言 对于某一个类型属性通用性较高的情况下,可以单独的提取出来,给需要的bean进行引用. 有关类的创建见<S ...
最新文章
- 将类别加入到别人的名称空间内
- JDK+TOMCAT在LINUX下简单的配置
- cvpr2019 目标检测算法_CVPR2019 | 0327日更新12篇论文及代码汇总(多目标跟踪、3D目标检测、分割等)...
- 大型网站技术架构:摘要与读书笔记
- MyBatis动态SQL,写SQL更爽
- python写前端代码_python学习之路前端-JavaScript
- u盘读写测试_aigo U395固态U盘评测,速度可能会吓到你,价格很良心
- Spark:相关错误总结
- SQL Server 分离
- 《C#图解教程》 总览
- MySQL的下载安装教程
- 谷歌浏览器启动后,图标变成空白解决办法
- Windows下打开.jar文件的方式
- 目前见到的最傻瓜全面的STRUTS入门教程^_^
- 1971旗舰cpu intel_这就是近年来Intel最良心CPU!我彻底服了
- AutoCAD Civil 3D中将CASS地形图中地形点转换成CAD点实体
- Designing Data-Intensive Applications翻译
- D. Masquerade strikes back(思维)
- 常见功能测试点的测试用例集合--51testing
- Pepper初级教程:第二章 Pepper的使用方法