IoC
工厂模式
IoC(Inverse of Control:控制反转) 是一种设计思想,而不是一个具体的技术实现。IoC 的思想就是将原本在程序中手动创建对象的控制权,交由 Spring 框架来管理。
为什么叫控制反转?
- 控制 :指的是对象创建(实例化、管理)的权力
- 反转 :控制权交给外部环境(Spring 框架、IoC 容器)
将对象之间的相互依赖关系交给 IoC 容器来管理,并由 IoC 容器完成对象的注入。
这样可以很大程度上简化应用的开发,把应用从复杂的依赖关系中解放出来。 IoC 容器就像是一个工厂一样,当我们需要创建一个对象的时候,只需要配置好配置文件/注解即可,完全不用考虑对象是如何被创建出来的。
在实际项目中一个 Service 类可能依赖了很多其他的类,假如我们需要实例化这个 Service,你可能要每次都要搞清这个 Service 所有底层类的构造函数,这可能会把人逼疯。如果利用 IoC 的话,你只需要配置好,然后在需要的地方引用就行了,这大大增加了项目的可维护性且降低了开发难度。
在 Spring 中, IoC 容器是 Spring 用来实现 IoC 的载体, IoC 容器实际上就是个 Map(key,value),Map 中存放的是各种对象。
Spring 时代我们一般通过 XML 文件来配置 Bean,后来开发人员觉得 XML 文件来配置不太好,于是 SpringBoot 注解配置就慢慢开始流行起来。
AOP前置知识
动态代理
实现方式
- jdk动态代理,使用jdk中的Proxy,Method,Invocationhandlerl创建代理对象
jdk动态代理要求目标类必须实现接口
- CGLib动态代理:目标类没有接口,第三方工具库,创建代理对象,原理是继承。通过继承目标类,创建子类。
- 字节码 aspectJ
子类就是代理对象。要求目标类不能是final的,方法也不能是final的
有接口,但是要用CGLIB,配置一下
<aop:aspectj-autoproxy proxy-target-class="true">
动态代理作用
- 在目标源代码不改变的情况下,增加功能
- 减少代码的重复
- 专注业务的逻辑代码
- 解耦和,让你的业务功能和日志,事物等非业务功能分离
概念
任何一个系统都是由不同的组件组成的,每个组件负责一块特定的功能,当然会存在很多组件是跟业务无关的,例如日志,事务,权限等核心服务组件,这些核心服务组件经常融入到具体的业务逻辑中,如果我们为每一个具体业务逻辑操作都添加这样的代码,很明显,代码冗余太多,此时我们需要将这些公共的代码逻辑抽象出来,变成一个切面,然后注入到目标对象中,aop正是基于这样的一个思路实现的,通过动态代理的方式,将需要注入切面的对象进行代理,在进行调用的时候,讲公共的逻辑添加进去,而不需要修改原有业务的逻辑代码,只需要在原来的业务逻辑基础上做一些增强功能即可。
使用aop目的
给已经存在的一些类和方法,增加额外的功能。前提是不改变原来的类的代码
术语
- Aspect 切面 表示增强的功能 事物,日志
- Join Point 连接点 连接业务方法和切面的位置
- Point cut 切入点 多个连接点方法的集合 切入表达式execution( com.cx.store.service.impl..*.(..))
- Target 给哪个类的方法增加功能
- Advice 通知,表示切面功能执行的时机
切面3要素
- 切面的功能代码 ,切面干什么 Aspect 切面
- 切面的执行位置,使用Pointcut表示执行的位置 切入表达式execution( com.cx.store.service.impl..*.(..))
- 切面的执行时机,使用Advice表示时机,在目标方法之前还是目标方法之后,有以下五种
Spring中有哪些通知类型(advice)
- Before(前置通知): 连接点之前执行,除非抛异常,否则没有能力中断执行流(@Before 注解)
- After Retuning(返回通知): 在连接点正常结束之后(没有抛出异常正常返回)执行的Advice(@AfterReturning 注解)可以得到返回值 根据返回值做出其他的操作,在通知结束后更改返回值不会改变的
- Around(环绕通知): 围绕连接点执行的Advice,就你一个方法调用(相对于合并前置后置)( @Around 注解)
- After Throwing(异常通知): 如果方法通过抛出异常来退出的话,这个Advice就会被执行(@AfterThrowing 注解)
- After(后置通知): 无论连接点是通过什么方式退出的(正常返回或者抛出异常)都会在结束后执行这些Advice(@After 注解)
在Spring AOP中关注点和横切关注点有什么不同?
关注点:我们在应用的模块中实现以解决特定业务问题的方法。比如库存管理、航运管理、用户管理等
横切关注点:贯穿整个应用程序的关注点。像事务管理、权限、日志、安全等。
Spring AOP的代理是什么?
Spring AOP是基于代理实现的,默认为标准的 JDK 动态代理。这使得任何接口(或者接口的集合)可以被代理。
Spring AOP 也使用 CGLIB 代理。如果业务对象没有实现任何接口那么默认使用CGLIB。
示例代码
/ * 切面方法修饰符 必须是public
* 返回值 如果这个方法被@Aspect修饰 返回值必须是Object
* 方法名 自定义 参数
* ProceedingJoinPoint 被Around修饰的方法必须传递这个参数
* aop不是spring内部封装技术,需要导入包
* AspeccJweaver AspeccJtools ProceedingJoinPoint 连接点,目标方法的对象
ProceedingJoinPoint pjp==invoke()中的method 参数必须在第一个,不然报错 无法绑定pointcut
*/
// 将当前类标记为切面类
@Aspect
// 交给Spring容器管理
@Component
public class TimeAspect {
@Around("execution(* com.cy.store.service.impl.*.*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
// 记录起始时间
long start = System.currentTimeMillis();
// 执行连接点方法,即切面所在位置对应的方法。本项目中表示执行注册或执行登录等
// proceed() 调用目标方法 例如 login
Object result = pjp.proceed();
// 记录结束时间
// TODO 插入数据库
long end = System.currentTimeMillis();
// 计算耗时
System.err.println(pjp.getSignature().getName() + "这个功能耗时:" + (end - start) + "ms");
// 返回连接点方法的返回值
return result;
}
}
//findHotList这个功能耗时:833ms
//login这个功能耗时:7ms
//findHotList这个功能耗时:3ms
[
](https://blog.csdn.net/weixin_39309402/article/details/100877521)
简述一下Spring AOP的设计原理
主要的分代理的创建和代理的调用两部分,比如UML图:
1)代理的创建
创建代理工厂:拦截器数组,目标对象接口数组,目标对象。
创建代理工厂时,默认会在拦截器数组尾部再增加一个默认拦截器 —— 用于最终的调用目标方法。
当调用 getProxy 方法的时候,会根据接口数量大余 0 条件返回一个代理对象(JDK or Cglib)。
注意:创建代理对象时,同时会创建一个外层拦截器,这个拦截器就是 Spring 内核的拦截器,用于控制整个 AOP 的流程。
2)代理的调用
当对代理对象进行调用时,就会触发外层拦截器。
外层拦截器根据代理配置信息,创建内层拦截器链。创建的过程中,会根据表达式判断当前拦截是否匹配这个拦截器。而这个拦截器链设计模式就是职责链模式。
当整个链条执行到最后时,就会触发创建代理时那个尾部的默认拦截器,从而调用目标方法,最后返回。