1 Spring AOP
1.1 AOP介绍
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
知识扩展: 1.面向过程编程 C语言 谭浩强!!!
2.半面向对象的程序设计 C++
3.面向对象的编程技术 java
编程方式:
1.面向接口编程
2.面向切面编程
总结: AOP(面向切面编程) 主要利用动态代理的模式 降低程序的耦合度,扩展业务功能方法.
1.2 关于AOP名词的介绍
1).连接点: 用户可以被扩展的方法
2).切入点: 用户实际扩展的方法
3).通知: 扩展方法的具体实现
4).切面: 将通知应用到切入点的过程
1.3 为什么使用AOP
问题1: 如果将业务代码直接写死,那么该代码不具有通用性.
问题2: 代码冗余 代码的耦合性高.
AOP: 面向切面编程.
AOP作用: 在不修改原有方法的条件下.对原有的方法进行扩展.
公式:AOP=切入点表达式+通知方法
1.4 通知方法
- before 目标方法执行之前执行
- afterThrowing 目标方法执行之后 抛出异常时执行
- afterReturning 目标方法执行之后 返回结果时执行
- after 目标方法执行之后执行(finally)
- around 环绕通知功能最为强大 可以控制目标方法的执行 在目标方法执行前后都要执行
关于通知方法总结:
1.环绕通知是处理业务的首选. 可以修改程序的执行轨迹
2.另外的四大通知一般用来做程序的监控.(监控系统) 只做记录
1.5 切入点表达式
- bean(bean的Id) 按照bean匹配!! Spring容器管理的对象称之为bean 粗粒度
- within(包名.类名) 按照包路径匹配 其中可以使用通配符代替
within("com.jt.service. ") 位于com.jt.service中的包的所有的类都会匹配. 粗粒度 - execution(返回值类型 包名.类名.方法名(参数列表)) 匹配的是方法参数级别 细粒度
execution(* com.jt.service…(…)) 解释:返回值类型任意 在com.jt.service的包路径中的任意类的任意方法的任意参数…
execution(* com.jt.service.userService.add*(int,String)) - @annotation(包名.注解名称) 按照注解匹配.
注解: @Find
@annotation(com.jt.anno.Find)
1.6案例
1.6.1 导入jar包
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>spring_demo_9_aop</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!--Spring核心包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.3.6</version>
</dependency>
<!--引入SpringBean-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.3.6</version>
</dependency>
<!--引入context包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.6</version>
</dependency>
<!--引入表达式jar包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>5.3.6</version>
</dependency>
<!--引入日志依赖-->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<!--引入测试包-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!--引入AOP包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.3.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.6</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.6</version>
</dependency>
</dependencies>
</project>
1.6.2 配置切面类
AOP = 切入点表达式 + 通知方法
//1.AOP需要被Spring容器管理
@Component
//2.标识该类为AOP切面
// Spring容器默认不能识别切面注解,需要手动配置
@Aspect //表示我是一个切面
public class SpringAOP {
//1.定义before通知
@Before("bean(deptServiceImpl)")
public void before(){
System.out.println("我是before通知");
}
}
1.6.3 配置类
@Configuration
@ComponentScan("com.jt")
@EnableAspectJAutoProxy(proxyTargetClass=false) //启动AOP注解 创建代理对象
//默认启用JDK动态代理,
//目标对象没有实现接口时,则采用CGLIB
//强制使用cglib proxyTargetClass=true
//JDK代理创建速度快.运行时稍慢
//CGLIB创建时速度较慢,运行时更快
public class SpringConfig {
}
1.6.4 测试
package com.jt.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import java.util.Arrays;
/*@Service
@Controller
@Repository*/
@Component //组件 将类交给spring容器管理
@Aspect //表示我是一个切面
public class RedisAOP {
//公式 aop = 切入点表达式 + 通知方法
//@Pointcut("bean(itemCatServiceImpl)")
//@Pointcut("within(com.jt.service.*)")
//@Pointcut("execution(* com.jt.service.*.*(..))") //.* 当前包的一级子目录
@Pointcut("execution(* com.jt.service..*.*(..))") //..* 当前包的所有的子目录
public void pointCut(){
}
//如何获取目标对象的相关参数?
//ProceedingJoinPoint is only supported for around advice
@Before("pointCut()")
public void before(JoinPoint joinPoint){ //连接点
Object target = joinPoint.getTarget();
Object[] args = joinPoint.getArgs();
String className = joinPoint.getSignature().getDeclaringTypeName();
String methodName = joinPoint.getSignature().getName();
System.out.println("目标对象:"+target);
System.out.println("方法参数:"+Arrays.toString(args));
System.out.println("类名称:"+className);
System.out.println("方法名称:"+methodName);
}
}