1.AOP
1.1 什么是AOP
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方
式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
要理解切面编程,就需要先理解什么是切面。用刀把一个西瓜分成两瓣,切开的切口就是切面;炒菜,锅与炉子共同来完成炒菜,锅与炉子就是切面。web层级设计中,web层->网关层->服务层->数据层,每一层之间也是一个切面。编程中,对象与对象之间,方法与方法之间,模块与模块之间都是一个个切面。
我们一般做活动的时候,一般对每一个接口都会做活动的有效性校验(是否开始、是否结束等等)、以及这个接口是不是需要用户登录。
按照正常的逻辑,我们可以这么做。
这有个问题就是,有多少接口,就要多少次代码copy。对于一个“懒人”,这是不可容忍的。好,提出一个公共方法,每个接口都来调用这个接口。这里有点切面的味道了。
同样有个问题,我虽然不用每次都copy代码了,但是,每个接口总得要调用这个方法吧。于是就有了切面的概念,我将方法注入到接口调用的某个地方(切点)。
同样有个问题,我虽然不用每次都copy代码了,但是,每个接口总得要调用这个方法吧。于是就有了切面的概念,我将方法注入到接口调用的某个地方(切点)。
这样接口只需要关心具体的业务,而不需要关注其他非该接口关注的逻辑或处理。
红框处,就是面向切面编程。
1.2 AOP能做什么
1.就是帮助我们减少重复代码的书写,从而提升可读性和开发效率
2.在不修改源码的情况下,对程序进行增强
3.AOP可以进行权限校验,日志记录,性能监控,事务控制
1.3 底层实现
AOP依赖于IOC来实现,在AOP中,使用一个代理类来包装目标类,在代理类中拦截目标类的方法执行并织入辅助功能。在Spring容器启动时,创建代理类bean替代目标类注册到IOC中,从而在应用代码中注入的目标类实例其实是目标类对应的代理类的实例,即使用AOP处理目标类生成的代理类。
代理机制:
Spring 的 AOP 的底层用到两种代理机制:
JDK 的动态代理 :针对实现了接口的类产生代理.
Cglib 的动态代理 :针对没有实现接口的类产生代理. 应用的是底层的字节码增强的技术 生成当前类的子类对象.
1.4 AOP相关术语
- Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在 spring 中,这些点指的是方法,因为
spring 只支持方法类型的连接点.
- Advice(通知/增强):所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知.通知分为前置
通知,后置 通知,异常通知,最终通知,环绕通知(切面要完成的功能)
- Pointcut(切入点):所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义
- Introduction(引介):引介是一种特殊的通知在不修改类代码的前提下, Introduction 可以在运行
期为类 动态地添加一些方法或 Field.
- Target(目标对象): 代理的目标对象
- Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程,spring 采用动态代理
织入,而 AspectJ 采用编译期织入和类装在期织入
- Proxy(代理):一个类被 AOP 织入增强后,就产生一个结果代理类
- Aspect(切面): 是切入点和通知(引介)的结合
1.5 AOP怎么用
1.动态代理
package com.tledu.dao.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class DynamicProxy implements InvocationHandler {
private Object obj;
public DynamicProxy(Object obj){
this.obj = obj;
}
/**
* @param proxy 代理的对象
* @param method 代理的方法
* @param args 方法的参数
* @return 方法的返回值
* @throws Throwable 异常
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//前置操作
before(method);
method.invoke(obj,args);
//后置操作
after(method);
return null;
}
private void before(Method method) {
System.out.println(method.getName()+" 开始调用");
}
private void after(Method method) {
System.out.println(method.getName()+" 调用结束");
}
}
测试
public static void main(String[] args) {
UserDaoImpl userDao = new UserDaoImpl();
/**
第一个参数: 真实对象的类解析器
第二个参数: 实现的接口数组
第三个参数: 调用代理方法的时候,会将方法分配给该参数
*/
IUserDao iu = (IUserDao) Proxy.newProxyInstance(userDao.getClass().getClassLoader(),
userDao.getClass().getInterfaces(),new DynamicProxy(userDao));
User u = new User();
u.setId(3);
u.setName("aaa");
iu.add(u);
}
newProxyInstance,方法有三个参数:
loader: 用哪个类加载器去加载代理对象
interfaces:动态代理类需要实现的接口
InvocationHandler: 传入要代理的对象创建一个代理,动态代理方法在执行时,会调用创建代理类里面的invoke方法去执行
注意:
JDK动态代理的原理是根据定义好的规则,用传入的接口创建一个新类,这就是为什么采用动态代理时为什么只能用接口引用指向代理,而不能用传入的类引用执行动态类。
2.注解方式
还是之前IOC的 再次引入三个就行,因为Spring的AOP是基于AspectJ开发的
在配置文件中引入AOP约束
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd