文章目录
AOP(面向切面)
(1)面向切面编程(方面),利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得
业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
(2)通俗描述:不通过修改源代码方式,在主干功能里面添加新功能
AOP解析
AOP 底层使用动态代理
有两种情况动态代理
第一种 有接口情况,使用 JDK 动态代理
创建接口实现类代理对象,增强类的方法
第二种 没有接口情况,使用 CGLIB 动态代理
创建子类的代理对象,增强类的方法
JDK动态代理
使用 JDK 动态代理,使用 Proxy 类里面的方法创建代理对象
Proxy类负责动态创建代理类及其实例
返回值 | 方法名 | 描述 |
---|---|---|
Static Object | newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) | 返回一个实现了指定接口的代理类实例 对接口方法的调用会被指定的调用处程序参数Interfaces是需要进行代理的接口类型,参数h是接口处理方法的调用处理程序,类加载器loader用来加载动态生成的代理类 |
InvocationHandler接口是代理方法调用处理程序,负责为代理方法提供业务逻辑,接口中的方法
返回类型 | 方法名 | 描述 |
---|---|---|
Object | invoke(Obejct proxy,Method method,Object[] args) | 用于在代理实例上处理方法调用并返回结果 代理对象实现自抽象主题接口,调用代理对象中的接口方法会执行InvocationHandler的invoke方法 |
JDK动态代理是面向接口的代理实现
所以要求被代理的目标对象必须通过抽象主题接口进行定义,否则无法实施代理
AOP相当于代理模式
但是代码的操作比起上面两种动态代理简化了很多
AOP术语
连接点
类里面哪些方法,可以被增强,这些方法都成为连接点
正常一个类中的所有方法都可以称之为连接点
切入点
实际被真正增强的方法。成为切入点
首先是本身是连接点,还没有被增强时候称之为连接点,已经被增强了就称之为切入点
通知(增强)
实际增强的业务逻辑部分。在切入点的前后等地方可以动态的添加新功能
通知有多种类型
-
前置通知(before):在目标方法执行之前执行
-
后置通知(after):在目标方执行完成后执行,如果目标方法异常,则后置通知不再执行
-
异常通知(After-throwing):目标方法抛出异常的时候执行
-
最终通知(finally);不管目标方法是否有异常都会执行,相当于try。。catch。。finally中的finally
-
环绕通知 :相当于将上面几种通知进行一个整合处理
切面
把通知应用到切入点过程
AOP注解操作
因为在实际中并不是使用到XML配置的形式。所以这里我就不多介绍了
Spring 框架一般都是基于 AspectJ 实现 AOP 操作
AspectJ 不是 Spring 组成部分,独立 AOP 框架,一般把 AspectJ 和 Spirng 框架一起使用,进行 AOP 操作
基于 AspectJ 实现 AOP 操作
(1)基于 xml 配置文件实现
(2)基于注解方式实现(使用)
引入AOP依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.6.RELEASE</version>
</dependency>
切入点表达式
切入点表达式作用:知道对哪个类里面的哪个方法进行增强
语法结构: execution([权限修饰符] [返回类型] [类全路径] 方法名称 )
举例 1:对 com.atguigu.dao.BookDao 类里面的 add 进行增强
execution(* com.atguigu.dao.BookDao.add(..))
举例 2:对 com.atguigu.dao.BookDao 类里面的所有的方法进行增强
execution(* com.atguigu.dao.BookDao.* (..))
举例 3:对 com.atguigu.dao 包里面所有类,类里面所有方法进行增强
execution(* com.atguigu.dao.*.* (..))
示例
创建一个类,作为主要功能方法
//使用业务逻辑类作为主要的功能
@Component("serviceimple")
public class ServiceImple {
// 在该类中进行打印表示该函数的主要功能
public void MainMethod(int num){
System.out.println("开始执行主函数");
// 如果输入的数字大于10抛出异常
if(num>10){
throw new IndexOutOfBoundsException();
}
}
}
创建增强类
//表示该类加入Spring容器中
@Component
//表示使用动态代理完成AOP
@Aspect
public class SMethod {
// 前置增强,在主方法执行前执行
@Before(value = "execution(* cn.service.ServiceImple.MainMethod(..))")
public void beforeMethod(){
System.out.println("作为前置增强处理");
}
// 后置增强:在没有异常的时候正常执行
@AfterReturning(value = "execution(* cn.service.ServiceImple.MainMethod(..))")
public void AfterReturningMethod(){
System.out.println("作为后置增强处理");
}
// 异常增强:当出现异常会执行的增强函数
@AfterThrowing(value = "execution(* cn.service.ServiceImple.MainMethod(..))")
public void AfterThrowingMethod(){
System.out.println("作为异常增强处理");
}
// 最终增强:无论怎样都会执行的
@After(value = "execution(* cn.service.ServiceImple.MainMethod(..))")
public void AfterMethod(){
System.out.println("作为最终增强处理");
}
}
实现类展示
实现类会根据输入的结果,是否存在异常来进行之后操作
public class SpringIOCTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("SpringTest.xml");
ServiceImple serviceimple = context.getBean("serviceimple", ServiceImple.class);
// 直接调用主函数。在不修改代码的情况下进行了添加新的功能
serviceimple.MainMethod(1);
}
}
相同切入点抽取
通过上面代码我们发现多个切入点之间存在相同,为了避免代码的重复
//相同切入点抽取
@Pointcut(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void pointdemo() {
}
//前置通知
//@Before 注解表示作为前置通知
@Before(value = "pointdemo()")
public void before() {
System.out.println("before.........");
}
有多个增强类多同一个方法进行增强,设置增强类优先级
// (1)在增强类上面添加注解@Order(数字类型值),数字类型值越小优先级越高
@Component
@Aspect
@Order(1)
public class PersonProxy{
}