目录
- SSM为SpringFramework + SpringMVC + MyBatis
- Spring的3大特性:IoC(控制反转),DI(依赖注入),AOP(面向切面编程)。
- 框架 = jar + 配置文件
SpringAOP 面向切面编程
代理
代理:将非核心逻辑剥离出来,封装这些非核心逻辑的类,对象,方法。
代理有两种具体实现方式:静态代理和动态代理技术(重点)
- 静态代理:主动创建代理类(如:要代理Calculate类需要手动创建一个CalculateProxy代理类)
- 动态代理:
- JDK动态代理:被代理的类必须实现接口,会根据该接口生成一个代理对象。目标类与代理类的关系只是继承与同一个接口。即A有接口B,JDK代理方式会生成一个有接口B的C为A的代理类。
- cglib:通过继承被代理的类实现代理,无需实现接口。代理类是目标类的子类,即会直接根据A生成子类B为A的代理类。
Spring AOP框架实际上就是对动态代理的简化
AOP(面向切面编程)
AOP是对OOP(Object Oriented Programming,面向对象编程)的补充和完善。OOP可以定义纵向的关系,但不适合定义横向的关系(如:日志功能,日志往往需要横向的分布在所有的对象层次,但OOP无法在对象中实现局部修改,即在对象中的核心方法前后加上日志代码),而AOP能够很好的完善OOP。
AOP思维主要应用场景:
- 日志记录:在方法前后记录日志
- 事务处理:在方法前开启事务,在方法后提交或回滚事务
- 安全控制:在方法前检查权限
- 性能监控:在方法前后分别记录时间戳,计算执行时间
- 异常处理:在方法执行时如果出现异常,进行异常处理(记录日志,发送邮件…)
- 缓存控制:在方法执行前检查缓存中有无数据,有则返回数据,没有则执行方法并将方法返回值存入缓存
- 动态代理:代理某个类的使用方法,实现各种功能
AOP的8个核心名词
1.横切关注点:AOP把软件系统分为:核心关注点和横切关注点。核心关注点就是业务处理的主要流程;横切关注点经常发送在核心关注点的多处,而且十分相似。
2.通知(增强):提取的重复代码存于一个方法,就是通知方法。
通知一般分为以下几个部分:
- 前置通知:在目标方法前执行
- 返回通知:在目标方法成功结束后执行
- 异常通知:在目标方法异常结束后执行
- 后置通知:在目标方法最终结束后执行
- 环绕通知:使用try_catch_finally结构围绕目标方法即以上四种通知。
3.连接点(joinpoint):能被横切的方法都叫连接点,这是一个纯逻辑概念。
4.切点(pointcut):真正被横切的方法就叫做切点。
5.切面(aspect):切点和增强形成一个切面。
6.目标(target):要代理的对象。
7.代理(proxy):生成的代理对象。
8.织入(weave):把通知应用到目标上的行为。
SpringAOP基于注解方式实现和细节
SpringAOP底层技术组成:
- 动态代理/cglib:会判断目标类是否有接口来生成对应的代理类
- AspectJ:早期的AOP实现的框架,SpringAOP借用了AspectJ的AOP注解
- 动态代理是JDK原生的,不需要导包;
- cglib已经加入spring ioc环境下,只要有springioc需要的包就可以使用;
- SpringAOP需要spring-aop包,但已经被spring-context进行了依赖传递,所以也不需要导包;
- AspectJ需要aspectj包,而Spring和AspectJ框架的整合需要spring-aspects包。但spring-aspects包含aspectj包;
- 所以如果已经导入了spring-context,就只需要到spring-aspectj;如果没有就需要spring-aspects和spring-aop。
Spring使用SpringAOP框架的步骤:
- 1.导入依赖:
- ioc/di:spring-context
- aop:
spring-aop,aspectj,spring-aspects - 测试环境:spring-test
- 2.编写核心业务并加入ioc容器(要使用SpringAOP的类需要加入到ioc容器中)
- 3.编写ioc的配置类或配置文件
- 4.设置测试环境
- 5.编写增强类,定义增强方法(存储横切关注点的代码)
- 6.增强类的配置(插入切点的位置,切点指定,切点配置等)
- 7.开启aop的配置
演示操作
1.导入依赖
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>6.0.6</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>6.0.6</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>6.0.6</version> <scope>test</scope> </dependency>2.编写核心业务并加入ioc容器
public interface Calculator { int add(int a,int b); int sub(int a,int b); int mul(int a,int b); int div(int a,int b); } @Component public class CalcalatorImpl implements Calculator { @Override public int add(int a, int b) { return a+b; } @Override public int sub(int a, int b) { return a-b; } @Override public int mul(int a, int b) { return a*b; } @Override public int div(int a, int b) { return a/b; } }3.编写ioc的配置类或配置文件
@Configuration @ComponentScan("com.qiu.service") public class JavaConfig { }4.设置测试环境
@SpringJUnitConfig(value = JavaConfig.class) public class SpringAOPTest { @Autowired private Calculator calculator; @Test public void test(){ int add = calculator.add(1, 1); System.out.println("add = " + add); } }5.编写增强类,定义增强方法
public class LogAdvice { public void start(){ System.out.println("方法开始"); } public void after(){ System.out.println("方法结束"); } public void error(){ System.out.println("方法报错"); } }6.增强类的配置
@Aspect @Component public class LogAdvice { @Before("execution(* com.qiu.service.impl.*.*(..))") public void start(){ System.out.println("方法开始"); } @After("execution(* com.qiu.service.impl.*.*(..))") public void after(){ System.out.println("方法结束"); } @AfterThrowing("execution(* com.qiu.service.impl.*.*(..))") public void error(){ System.out.println("方法报错"); } }7.开启aop的配置
@Configuration @EnableAspectJAutoProxy //使用aop注解时需要添加 @ComponentScan("com.qiu.service") public class JavaConfig { }
其中 add=2 是测试代码中输出的。
获取切点详细信息
- 获取目标方法的信息(方法名,参数,访问修饰符…):在任何增强方法的形参中加入JoinPoint,如:public void start(JoinPoint joinPoint){…}
- 获取目标方法返回的结果:
- 使用@AfterReturning
- 在形参中添加(Object result)
- 设置@AfterReturning中的returning = "result",需要与上方添加的形参的参数名一致
- 获取目标方法异常的信息:
- 使用@AfterThrowing
- 在形参中添加(Throwable t)
- 设置@AfterThrowing中的throwing= "t",需要与上方添加的形参的参数名一致
切点表达式语法
即之前出现过的"execution(* com.qiu.service.impl.*.*(..))",aop注解通过该表达式来寻找切点
- 固定语法:execution(1 2 3.4.5(6)) 示例:"execution(* com.qiu.service.impl.*.*(..))"
- 1.访问修饰符:public / private
- 2.方法的返回参数类型:String int void
- 如果不考虑1和2,两者可以合并为一个*
- 3.包的位置:
- 具体包:com.qiu.service.impl
- 单层模糊:com.qiu.service.* * 单层模糊
- 多层模糊:com..impl .. 多层模糊
- .. 不能开头
- 4.类的名称
- 具体:CalculatorImpl
- 模糊:*
- 部分模糊:*Impl
- 5.方法名:与类名一致
- 6.(6)形参列表
- ():没有参数
- (String),(String,int):有具体类型
- (..):模糊参数,一个或多个
- (String..),(..int),(String..int):部分模糊,第一个为String;最后为int;第一个为String且最后为int。
切点表达式的提取和复用
1.编写方法来提取复用
@Pointcut("execution(* com.qiu.service.impl.*.*(..))") public void pc(){} @Before("pc()") public void start(JoinPoint joinPoint){ System.out.println("方法开始"); }
2.创建类来单独维护(记得在配置类中设置ComponentScan扫描这个类)
@Component //要加到ioc容器中 public class MyPointCut { @Pointcut("execution(* com.qiu.service.impl.*.*(..))") public void pc(){} }
@Before("com.qiu.MyPointCut.pc()") public void start(JoinPoint joinPoint){ System.out.println("方法开始"); }
切面优先级
如果有多个增强类可以使用@Order()来决定顺序
@Order(值),当值越小时,优先级越高,在越外面