我们先来说一说Spring,从总体上Spring就是一个基础框架,同时Spring给我们提供了一个Bean容器,用来装载和管理具体的Bean对象,你像我们之前创建对象的时候就是通过new关键字来实现的,但是现在我们只需要告诉容器有哪些对象,就会帮我们去创建好并且管理他的生命周期;然后呢,Spring又是Spring Boot,Spring Cloud等框架的基石,在原有Spring上去做了一些扩展和开发的;
Bean生命周期:
定义Bean:通过<Bean>标签或者(@Bean,@Component)注解来进行定义Bean,这一步Spring并不会去创建Bean,只是将Bean的描述信息存储起来,等待后续的实例化和初始化
注册Bean:将定义信息会注册到BeanFactoey或者ApplicationContext中
实例化Bean(createBeanInstance):根据注解和配置文件实例化Bean,在实例化之前有一个方法postProcessMergedBeanDefinition()对Bean进行一些额外的配置或者修改,主要是对元数据进行一些修改
DI属性注入:Spring将类通过构造器,setter方法或者接口注入到Bean实例当中
其中过程中可能会插入一些自定义的逻辑(
postProcessAfterInstantiation
)和(postProcessProperties
)一些Bean属性的修改和校验操作初始化之前调用一些Aware接口,在初始化之前去获取Bean的名称、Beanfactory、ApplicationContext方法中的一些前置操作,比如代理包装和AOP切面等
初始化Bean(initializeBean):调用initializeBean的afterPropertiesSet()方法或者通过init-method属性指定的初始化方法
初始化之后,通过一些BeanPostProcessor后置处理器进行一些处理(勾子和后置处理器)
调用Bean:初始化完成,就可以被容器中的Bean使用
销毁Bean:容器关闭时,Spring有三种销毁方法:通过@preDestory方法;实现DisposableBean接口的destory()方法;或者可以自定义销毁方法
DI:依赖注入
然后又可以提出两个核心概念IOC和AOP:
IOC(控制反转):是一种控制反转的思想,通过依赖注入实现的。IOC让对象的创建和管理都交给容器来实现,而不是对象本身
核心思想:控制其实就是控制对象的创建,ICO容器通过配置文件来创建对象,在对象的生命周期中,在不同时期通过不同配置来进行对象的创建和改造;反转其实就是关于我们创建对象和注入依赖的过程,本来是由我们程序员在代码中指定的,而反转之后,这个动作就交给ICO容器触发,由主动获取变成被动得到,在创建对象A的时候,发现依赖对象B,IOC容器就会根据配置文件,创建B,将B注入A中
依赖注入:通过构造器注入、setter注入或者接口注入,将对象所需要的依赖传递给它,而不是让对象自行创建依赖
优点:1.降低耦合度:减少代码之间的直接依赖,代码更容易维护和扩展。 2.提高可测试性:更容易替换依赖进行单元测试,测试更方便。 3.增强灵活性:依赖关系可以通过配置文件或注解更改,代码更灵活。 4.促进接口编程:有助于编写更灵活的代码,遵循面向接口编程的原则。 5.简化管理:容器管理对象的创建和生命周期,开发更高效。 6.组件重用:组件可以更方便地组合使用,减少重复代码。
AOP(面向切面编程):是一种编程范式,通过横切关注点将与业务逻辑无关的分离出来,允许开发者通过“切面”将通用功能模块化,将其应用到程序中的多个地方,避免代码重复。(例如日志记录,控制权限,安全检查,事务管理等)
核心思想:将与业务逻辑无关的横切关注点抽取出来,通过动态代理的方式应用到业务方法,而不是将代码直接写入业务逻辑
基本概念:
切面:横切关注点的模块化,比如日志、事务等。可以包含多个通知;
连接点:程序执行中的某个点或者位置,例如方法调用,异常处理、字段访问等;
通知:定义在连接点处执行的动作,可以是一些方法前后,方法抛出异常时的操作
切入点:定义在哪些连接点处的应用通知
动态代理:
JDK动态代理:通过代理接口来创建一个代理对象,代理对象实现目标类所实现的接口。依赖Java反射机制,由Proxy类和InvocationHandler接口来实现,代理类在运行时,调用其中的invoke()方法
优点:简单高效,代理类是接口的实现类,不需要第三方库,Java自带支持
缺点:只能为实现了接口的类生成代理,如果目标没事实现接口,就无法使用JDK动态代理
public class test1 {public static void main(String[] args) {Car car = new CarImpl();//生成代理对象,在调用方法时会转发到invoke方法Car proxy = (Car) Proxy.newProxyInstance(car.getClass().getClassLoader(),car.getClass().getInterfaces(),new CarInvocationHandler(car));//这里drive方法会触发invoke方法proxy.drive();} } interface Car {void drive(); } class CarImpl implements Car {@Overridepublic void drive() {System.out.println("Car is driving...");} }class CarInvocationHandler implements InvocationHandler {//在创建对象的时候,接受一个target代理目标对象private Object target;public CarInvocationHandler(Object target) {this.target = target;}//invoke方法时InvocationHander接口中必须实现的方法,所有通过代理对象调用的方法都会进入这里@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("Before method " + method.getName());Object result = method.invoke(target, args);System.out.println("After method " + method.getName());return result;} }
CGLIB动态代理:通过生成目标类的子类,并重写目标类中的方法来实现代理,代理类会覆盖目标类中的方法,并在调用方法前后进行逻辑加强
优点:能够处理没有实现接口的类,这是相比较JDK动态代理的优势,可以适用于所有类
缺点:生成目标子类,如果在大量创建代理对象的话,开销可能会很大,性能可能也会比较低,不能代理final类或者final方法,因为final不能被继承
class Car {public void drive() {System.out.println("Car is driving...");} }class CarInterceptor implements MethodInterceptor {@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("Before method " + method.getName());Object result = proxy.invokeSuper(obj, args); // 调用父类方法System.out.println("After method " + method.getName());return result;} }public class test2 {public static void main(String[] args) {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(Car.class);enhancer.setCallback(new CarInterceptor());Car carProxy = (Car) enhancer.create();carProxy.drive();} }
**循环依赖问题**:就是指两个或者多个模块,类,组件之间相互依赖,相乘闭环;简单举一个例子,当A实例化完成以后,要去进行DI属性注入,去getBean(B),但是B还没有创建,那我就转去实例化B,同样,在B中DI属性注入的时候也拿不到A,导致了循环依赖的发生;
- **解决方法**(自定):一个简单的解决方案,在Bean对象实例化完成后,就放入到一个缓存容器中,当其他Bean对象需要获取到他时可以去缓存中调用,利用缓存去提前暴露对象
- **解决方法**:其实无论用什么方法关键都是得提前暴露未创建完毕的Bean
在Spring中主要使用三级缓存来解决缓存依赖:
- 一级缓存:用于存储完全初始化完成的单例Bean
- 二级缓存:存储没有完全初始换,但是已经实例化的Bean,用于提前暴露对象,避免出现循环依赖问题
- 三级缓存:储存对象工厂,当需要时,可以通过工厂创建早期的Bean