Spring 5
- 基本概念
- Beans
- 接口实例化
- AOP
- 面向切面的基础
- AOP原理剖析
- Spring AOP的代理选择
- Spring AOP的实现模式
- AOP源码分析
- 自定义AOP框架
- Spring Cache框架源码分析
- IOC
- 容器基本实现和组成
- IOC容器设计原理及高级特性
- IOC的注入原理
- 装配Bean方式
- BeanFactory源码分析
- BeanFactory与ApplicationContext的区别
- FactoryBean与BeanFactory的区别
- BeanDefinition与源码分析
- IOC依赖实现
- IOC中的循环依赖
- MVC
- MVC结构和原理介绍
- Spring MVC的使用与流程解析
- SpringMVC组成
- Spring MVC九大组件剖析
- MVC与IOC容器整合原理
- HandlerMapping实现原理
- HandlerAdapter实现原理
- ViewResolver实现原理
- Controller调用原理
- 动态参数匹配原理
- Spring MVC源码剖析
- Spring MVC设计模式
- Spring MVC高级技术
- Spring MVC高级实战
- Spring MVC工作流
- Spring MVC整合原理
- Spring MVC乱码问题
- SpringMVC与Structs2对比分析
- 手写实现SpringMVC框架
- SSM整合策略
- Spring5基本概念
- Spring5.x的兼容性
- Spring5的设计思想
- Spring5体系结构
- Spring5官方文档阅读技巧
- Spring5环境搭建
- Spring5的核心组件
- 通过常用的Spring拓展接口实现特殊需求
- 分析自带通用日志框架
- 多序列化数据格式绑定API
- 函数式风格的ApplicationContext
- Kotlin表达式的支持
- WebFlux模块
- WebFlux和Servlet的区别
- Testing改进
- 响应式编程
- 响应式编程基本概念
- 响应式编程特点
- 响应式流规范
- 响应式流中的异步和并行
- Spring中的响应式编程
- Project Reactor
- Spring WebFlux高级实战
- WebFlux核心响应式服务器
- Spring WebFlux源码分析
- Spring WebFlux和Spring MVC比较
- WebFlux应用
- 响应式SpringBoot
- 响应式Spring Core
- 响应式Web
- 响应式SpringData
- 响应式Spring Session
- 响应式SpringSecurity
- 响应式SpringCloud
- 响应式Spring Test
- 响应式监控
- 响应式数据库访问
- 响应式程序测试
- SpringData支持的存储类型
- SpringData接口规范与继承体系
- SpringData方法定义规范
- SpringData自定义Repository方法
- SpringData JPA源码剖析
- SpringData操作流程
- SpringSecurity应用场景
- SpringSecurity认证基本原理
- SpringSecurity过滤器链
- SpringSecurity认证流程剖析
- SpringSecurity认证实战项目
- SpringSecurity自定义认证策略
- Session管理
- CSRF防护机制
- 跨域与CORS
- SpringSecurity授权流程分析
- SpringSecurity授权实战
- SpringSecurity权限表达式
- SpringSecurity源码分析
- RBAC权限模型
基本概念
Spring的XML解析原理
Spring的作用:
- 定位: 寻找XML配置文件
- 加载: 将解析的XML配置加载到内存
- 实例化: 根据加载的XML配置进行实例化
IOC体系图:
- 所有的IOC都有继承关系,这样设计使得任何一个子类IOC都可以直接使用父类IOC加载的Bean
- 其中重要的类是BeanFactory和ApplicationContext,这是所有IOC的父接口
- BeanFactory中提供了对于Bean最基本的操作
- ApplicationContext继承BeanFactory, 同时继承MessageSource, ResourceLoader, ApplicationEventPublisher接口用来提供国际化,资源加载,时间发布高级功能 .Spring加载的XML的类是ApplicationContext的子类
- 其余重要的类:
- BeanFactory
- HierarchicalBeanFactory
- ListableBeanFactory
- DefaultListableBeanFactory
- AliasRegistry
- SimpleAliasRegistry
- ResourceLoader
- ApplicationContext
- AbstractApplicationContext
- AbstractRefreshableApplicationContext
- AbstractRefreshableConfigApplicationContext
- AbstractXmlApplicationContext
- ClassPathXmlApplicationContext
Spring中的两种标签: 标签在使用前都需要在Spring的xml配置文件里声明Namespace URI, 这样才能在解析标签时通过Namespace URI找到对应的NamespaceHandler
- Spring原生标签: 不带前缀的标签是Spring中的原生标签
- 自定义标签: 带前缀的标签是自定义标签
<bean class="com.oxford.bean.Bean" id="bean" c:id="beanId" p:name="beanName" />
- c: 通过构造器给属性赋值,是constructor-arg的简化写法
- p: 通过属性的setter方法给属性赋值,是property的简化写法
- Spring自定义扩展标签:
- 创建一个自定义标签和解析类
- 指定命名空间和NamespaceHandler
- 在META-INF的spring.handler文件中指定命名空间和NamespaceHandler的映射关系. 类似Spring的c标签和p标签一样
http://www.springframework.org/schema/c=org.springframework.beans.factory.xml.SimpleConstructorNamespaceHandler
http://www.springframework.org/schema/p=org.springframework.beans.factory.xml.SimplePropertyNamespaceHandler
Spring的XML解析流程:
Spring的Bean实例化原理
- BeanFactoryPostProcessor的类图:
- ConfigurationClassPostProcessor类完成对 @Configuration, @Bean等注解的解析注册
- FactoryMethod用于创建对象
- 可以获取 @Autowired注解标记的构造函数并且通过这些构造函数进行实例化
- BeanPostProcessor接口的继承体系:
- BeanPostProcessor接口是Spring的一个重要的扩展点,包含两个方法:
default Object postProcessBeforeInitialization() throws BeanException {
return bean;
}
default Object PostProcessAfterInitialization() throws BeanException {
return bean;
}
- 实现了BeanPostProcessor接口的对象会在实例化之前和之后调用这两个方法
Bean对象的创建包含以下几个工作:
- 初始化实例
- 解析 @PostConstruct, @PreDestroy, @Resource, @Autowired, @Value等注解
- 依赖注入
- 调用BeanPostProcessor的方法
- AOP入口
Bean对象的实例化:
- 在Bean对象实例化过程中获取所有标注 @Autowired注解的构造函数就是通过调用autowireConstructor来进行实例化
- 具体就是委托给ConstructorResolver类来进行处理,包括factoryMethod创建对象也是委托ConstructorResolver类进行处理
- 如果没有标注 @Autowired注解的构造函数就会调用instantiateBean() 方法,利用无参构造函数去创建对象并返回,这种方式也是大部分对象实例化的方式
Bean实例化过程分析:
- ApplicationContextAwareProcessor中对接口ApplicationEventPublisherAware和接口ApplicationContextAware的调用
- InitDestroyAnnotationBeanPostProcessor中对 @PostConstruct注解方法的调用
- ImportAwareBeanPostProcessor中对ImportAware类型实例setImportMetadata调用
- invokeInitMethods() 方法中对InitializingBean接口中的afterPropertiesSet() 方法和init-method属性配置的初始化方法进行调用
- applyBeanPostProcessorAfterInitialization() 方法中对BeanPostProcessor的postProcessAfterInitialization() 方法进行调用
Beans
接口实例化
基本概念
- JavaBean:
- JavaBean是遵循Sun制定的JavaBean标准的特定编码规范的类:
- 包含一个默认的公有无参构造器
- 允许通过getter和setter访问器访问类的成员属性,或者其余遵循特定命名规范的方法访问
- 实现了java.io.Serializable接口,可序列化
- POJO:
- POJO即简单Java对象
- POJO类的对象不需要通过框架来使用,也不需要在特定的应用服务环境下运行
- POJO主要用来区分简单Java对象,轻量级Java对象和重量级Java对象
- SpringBean:
- SpringBean表示由Spring框架管理的对象,也就是由Spring框架的容器进行初始化,配置和管理的Java对象
- Spring Bean是通过Spring配置文件或者通过注解定义的,在Spring容器中初始化,然后注入到应用程序中
Spring实例化Bean
- Spring寻找并解析Bean属性的方式:
- 注解的方式:
- Spring在启动后进行初始化时会扫描classpath包下符合要求的class
- 然后进行解析并注册到BeanFactory中
- xml的方式:
- 注解通过XmlBeanDefinitionReader解析Bean
- 一般情况下,主流的方式是使用注解的配置,尤其是在SpringBoot框架中
构造器实例化
- SpringBean类中的默认构造器创建Bean实例:
- Spring容器根据Spring配置文件的路径加载配置文件
- 在加载的同时 ,Spring容器会通过实现Bean类中默认的无参构造器对Bean进行实例化
静态工厂实例化
- 工厂实现类中提供一个静态工厂方法创建Bean实例:
- Spring容器根据Spring配置文件的路径加载配置文件
- 在加载的同时 ,Spring容器会通过工厂实现类StaticFactoryBean类中的静态方法getBean() 对Bean进行实例化
实例工厂实例化
- 工厂实现类直接使用成员方法创建Bean实例,同时在配置文件中:
- 需要实例化的Bean不是通过class属性直接指向实例化的类
- 而是通过class属性配置实例工厂
- 然后通过factory-bean属性指定一个实例工厂
- 最后使用factory-method属性指定使用factory-bean配置的实例工厂中的哪一个方法
- Spring容器根据Spring配置文件的路径加载配置文件
- 在加载的同时 ,Spring容器会通过配置的实例工厂类InstanceBeanFactory中的成员getBean() 对Bean进行实例化
代理Bean操作
- 实现一个动态代理接口,并且在Spring容器初始化完成之前将该代理对象注册到Spring容器中.实现可以通过 @Autowired等方法从Spring中获取该代理对象
- Spring中代理Bean操作示例
Spring中动态注入和删除Bean
- 动态注入和删除Bean:
- 使用getBean() 获取对象的操作,这些对象都是程序中事先定义好的
- 有时候需要动态地加入和删除对象. 尽管可以采用配置文件或者注解的方式,但是每次都需要重启服务
- 为了避免以上问题,需要在Spring中对Bean进行动态地管理,包括Bean对象的注入和删除
- Spring中动态管理Bean实现思路:
- Spring中管理Bean对象的是BeanFactory, 具体的是DefaultListableBeanFactory
- 通过获取到Bean类的ApplicationContext对象获取到BeanFactory
- 在类DefaultListableBeanFactory中有registerBeanDefinition() 方法使用一个BeanDefinition参数注入Bean
- 通过BeanDefinitionBuilder可以构建一个Bean类的BeanDefinition
- Spring中动态注入Bean步骤:
- 获取Bean类的ApplicationContext
- 通过ApplicationContext获取BeanFactory
- 通过BeanDefinitionBuilder获取Bean类的BeanDefinition
- 通过BeanFactory的registerBeanDefinition() 注入Bean类的BeanDefinition
- 注入完成后,可以通过ApplicationContext的getBean() 获取Bean对象
// 1. 获取Bean类的ApplicationContext
ApplicationContext ctx = SpringApplication.run(Bean.class, args);
// 2. 通过ApplicationContext获取BeanFactory
DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) ctx.getAutowireCapableBeanFactory();
// 3. 获取并创建BeanDefinition
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(Bean.class);
beanDefinitionBuilder.addPropertyValue("name", "Oxford");
BeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
// 4. 通过BeanFactory的registerBeanDefinition()注入Bean类的BeanDefinition
defaultListableBeanFactory.registerBeanDefinition("beanInterface", beanDefinition);
// 获取动态注册的Bean
Bean beanInterface = ctx.getBean(Bean.class);
- Spring中动态删除Bean步骤:
- 获取Bean类的ApplicationContext
- 通过ApplicationContext获取BeanFactory
- 通过BeanFactory的removeBeanDefinition() 删除已经注入的Bean类的BeanDefinition
// 1. 获取Bean类的ApplicationContext
ApplicationContext ctx = SpringApplication.run(Bean.class, args);
// 2. 通过ApplicationContext获取BeanFactory
DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) ctx.getAutowireCapableBeanFactory();
// 通过BeanFactory的removeBeanDefinition()删除已经注入的BeanDefinition
defaultListableBeanFactory.removeBeanDefinition("beanInterface");
- 动态注入Bean出现的问题:
-
// 使得Bean对象完成Spring初始化过程中的所有增强器检验,但是不重新创建Bean对象 applicationContext.getAutowireCapableBeanFactory().applyBeanPostProcessorsAfterInitialization(beanClass, beanClass.getClass().getName()); // 将Bean对象以单例的形式注入到容器中. 此时通过 beanClass.getClass() 和 beanClass.getClass().getName() 都可以获取到Spring容器中注入的Bean对象 defaultListableBeanFactory.registerSingleton(beanClass.getClass().getName(), beanClass);
- 多次注入同一个Bean类时,如果BeanName一样,后注入的Bean会覆盖先注入的Bean
- 多次注入同一个Bean类时,如果BeanName不一样,就会产生两个Bean
- 此时不能使用ApplicationContext.getBean(Bean.class) 方法通过ByType方式获取注入的Bean, 会产生报错
- 此时可以使用ApplicationContext.getBean(“beanInterface”) 方法通过ByName的方式获取重复注入的Bean
- 可以使用以下单例方式解决重复注入Bean的问题:
AOP
- AOP:Aspect-oriented programming. 面向切面的程序设计
- 将横切关注点进一步分离,以提高代码的模块化程度
- 在现有的项目代码的基础上增加额外的通知Advice机制,能够对声明为切点Pointcut的逻辑功能代码块进行统一的管理与装饰
- 横切关注点: 在项目的多个模块中都有出现的逻辑功能代码
- 面向切面的程序设计可以将与代码核心业务逻辑联系较弱的功能添加到程序中,同时保证业务代码的可读性
- AOP核心思想:
- 从核心关注点分离出横切关注点是面向切面程序设计的核心
- 分离关注点使得解决特定领域问题的代码从业务逻辑中独立出来
- 业务逻辑中不再含有针对特定领域问题代码的调用
- 业务逻辑和特定领域问题的关系通过切面来进行封装和维护
- AOP和OOP的比较:
- OOP: 面向对象的程序设计
- OOP引入了封装,继承和多态来建立一个对象层次结构来模拟公共行为的集合.但是OOP无法为分散的对象引入公共行为
- OOP适用于定义对象自上而下的关系,不适合定义对象从左到右的关系
- AOP: 面向切面的程序设计
- 使用横切的技术,分解封装对象的内部,将影响多个类的公共行为封装到一个可重用的模块称作切面
- AOP通过横切的关系将与业务无关,但是被业务模块共同调用的逻辑和责任封装起来,减少项目中的重复代码,降低模块之间的耦合度,提升项目的操作性和维护性
- AOP中的基本概念:
- 切面: AspectJ
- 模块化的横切关注点.也就是横向分散在多个功能模块中的特定逻辑功能
- 连接点: Join point
- 在项目执行过程中能够在项目中插入切面功能的项目中的某一个点
- Spring AOP中Joinpoint指代的是所有方法的执行点
- 这个点可以是项目中某个方法调用前,调用后,方法抛出异常后等等
- 切面利用这些连接点切入到正常的项目流程中,添加某些行为
- AOP中的Joinpoint可以有多种类型:
- 构造方法调用
- 字段的设置和获取
- 方法的调用
- 方法的执行
- 异常处理的执行
- 类的初始化
- 注意: Spring中只支持执行方法类型的joinpoint
- 通知: Advice
- AOP框架在特定的连接点处执行的动作
- Spring AOP中提供5种类型的通知Advice:
- 前置通知: Before. 在目标方法调用之前执行通知功能
- 后置通知: After. 在目标方法调用完成之后执行通知功能.无论目标方法是否发生异常,后置通知都会执行
- 后置返回通知: After-returning. 在目标方法返回值之后执行通知功能
- 后置异常通知: After-throwing. 在目标方法抛出异常之后执行通知功能
- 环绕通知: Around. 在目标方法调用之前和调用之后执行通知功能
- 切点: Pointcut
- 执行通知的一系列连接点的集合
- Pointcut是一个描述信息,用于修饰Joinpoint, 通过Pointcut,AOP就可以确定哪些Joinpoint可以编织进入Advice
- AOP通过切点定位到特定的连接点
- 一个切点匹配多个连接点
- 引入: Introduction
- 添加方法或者字段到被通知的类
- Spring支持引入接口到任何被通知的对象中
- Spring中使用Introduction, 可以通过DelegatingIntroductionInterceptor来实现通知,通过DefaultIntroductionAdvisor来配置Advice和代理类要实现的接口
- 目标对象: Target Object
- 包含连接点的对象.也就是被通知或者被代理的对象
- AOP代理: AOP Proxy
- AOP框架创建的对象,包括通知
- Spring中的AOP代理包括JDK动态代理和CGLIB代理
- 织入: Weaving
- 织入描述的是将切面应用到目标对象来创建新的代理对象的过程
- Spring AOP使用了动态代理技术,在项目运行时织入切面
- Spring AOP支持使用两种方式JDK动态代理和CGLIB代理生成代理对象.默认策略是如果目标类为接口,则使用JDK动态代理,如果目标类是一个类对象则使用CGLIB代理
- AOP实现的两种方式:
- 动态代理方式: 利用拦截方法,对拦截方法进行装饰,取代原有方法的执行
- 静态织入方式: 引入特定的语法创建切面,使得编译器在编译期间织入切面的代码
- AOP使用的四种方式:
- 基于代理模式的AOP使用
- @AspectJ注解驱动的切面
- POJO切面,通过 <aop:config /> 配置切面
- 注入式AspectJ切面,通过 <aop:aspectj-autoproxy /> 配置切面
- Spring AOP的使用方法有以下三个步骤:
- 定义增强方法,并在方法里使用AspectJ的注解标注Spring在何处调用方法
- 在横切方法上使用注解 @Component和 @Aspect
- 在 @Configuration配置类上使用注解 @EnableAspectJAutoProxy
- Spring AOP中XML配置使用示例
- SpringBoot AOP中注解式使用示例
- AOP中的切入点表达式: 切入点表达式用来指定切入点的目标
execution([modifier-pattern]? [return-type-pattern] [declaring-type-pattern]? [name-pattern]([param-pattern])[throws-pattern]?)
- modifier-pattern: 修饰符匹配
- return-type-pattern: 返回值匹配
- declaring-type-pattern: 类路径匹配
- name-pattern: 方法名匹配
- param-pattern: 参数匹配
- throws-pattern: 异常类型匹配
- ? : 表示该匹配项是可选项
- 示例:
-
execution(* *(..))
execution(public * com.oxford.service.*(..))
execution(* com.oxford.service..*.*(..))
- 匹配com.oxford.service包以及子包下面的所有方法:
- 使用 @Pointcut定义切点时,可以使用 &&, || 和 ! 逻辑运算符
面向切面的基础
- 面向切面程序设计的核心概念就是从核心关注点中分离出横切关注点:
- 分离关注点使得解决特定领域问题的逻辑功能代码从业务逻辑代码中独立出来
- 业务逻辑代码中不再含有对特定领域问题的逻辑功能代码的调用
- 业务逻辑代码同对特定领域问题的逻辑功能代码的关系通过切面来进行封装和维护
- 关注点: Concern
- 对软件工程有意义的,小的,可管理的,可描述的软件组成部分
- 一个关注点通常只和一个特定概念或者目标相关联
- 主关注点: Core Concern
- 一个软件中的最主要的关注点
- 关注点分离: Separation of concerns, SOC
- 标识,封装和操作关注点的能力
- 方法: Method
- 用来描述,设计和实现一个给定关注点的软件构造单位
- 横切: Crosscut
- 如果实现两个关注点方法存在交集,那么这两个关注点相互横切
- 支配性分解: Dominant decomposition
- 将软件分解成模块的主要方式
- 传统的程序设计语言以一种线性的文本来描述软件,只采用一种方式,比如类来将软件分解成模块
- 这样使得某些关注点比较好捕捉,容易进一步组合,扩展
- 但是仍然会存在一些关注点没有被捕捉,弥撒在整个软件内部
- 支配性分解通常是按照主关注点进行模块分解的
- 横切关注点: Crosscuting concerns
- 在传统的程序设计语言中,除了主关注点可以被支配性分解的方式捕捉外,仍然有许多没有被支配性分解方式捕捉到的关注点
- 这些关注点弥散在整个软件内部,这些关注点同主关注点是横切的
- 切面: Aspect
- 支配性分解的基础上提供的一种辅助的模块化机制
- 切面的模块化机制可以捕捉横切关注点
AOP原理剖析
- 后置处理器根据Bean是否配置Advisor判断是否需要进行动态代理
- 如果需要进行动态代理,那么就构造创建ProxyFactory对象
- 通过ProxyFactory对象的父类中的getAopProxyFactory() 方法获取AopProxyFactory对象,默认获取的是DefaultAopProxyFactory
- 通过AopProxyFactory工厂createAopProxy() 方法创建实际的代理对象
- ProxyBeanFactory:
- Spring使用工厂模式创建每一个Proxy. 每一个Class类型,都会有一个相对应的ProxyFactoryBean
- 对于生成Proxy的工厂而言,只需要知道对应的Advice信息即可
- Advice信息维护在Advised中 ,Advised可以根据特定的类名和方法名返回对应的AdviceChain, 来表示需要执行的Advice串
- JDK动态代理:
- JDK动态代理是面向接口的JdkDynamicAopProxy生成代理对象
- 溯源到JDK动态代理的字节码文件.在反编译的代码文件中可以发现代理类已经是继承了Proxy类,因为Java是单继承多实现的,所以不能再继承其余的类,但是代理类与目标类之间必须建立一种关系来保证代理对象可以被引用到,这样就只能实现接口
- 目标类实现了至少一个接口时,那么代理类就可以通过实现与目标类相同的接口,使用接口类型的变量接收代理类的实例
- JdkDynamicAopProxy类实现了AopProxy, 返回Proxy, 并且自身也实现了InvocationHandler
- 也就是说,当我们使用proxy时,对proxy对象调用的方法,都会转到这个类的invoke() 方法中
- Cglib代理:
- 基于Cglib子类继承方式CglibAopProxy生成代理对象
Spring AOP代理选择 | 代理描述 | 实现机制 | 优点 |
JDK动态代理 | 1. 通过反射接受被代理的类,并且要求被代理的类必须实现一个接口 2. JDK动态代理的核心是InvocationHandler接口和Proxy类 | 通过Java的内部反射机制实现 | 反射机制在生成类的过程中非常高效 |
CGLIB代理 | 1. 通过继承的方式动态生成目标类的代理 2. 通过修改字节码的方式来实现代理,可以在运行时动态的生成某个类的子类 3. 如果某个类使用final修饰,那么无法使用CGLIB做动态代理 4. CGLIB代理的核心是Enhancer类 | 使用ASM框架实现. ASM框架是一种可以操作字节码的框架 | ASM在类生成之后的执行过程中非常高效 |
Spring AOP的代理选择
- AOP类型的选择:
- AspectJ框架
- Spring AOP框架
- 两种AOP类型分为基于XML配置文件的风格和基于 @AspectJ注解的风格
- AspectJ和Spring AOP比较:
- AspectJ:
- AspectJ在功能完善度方法更具有优势
- AspectJ使用静态织入的方式实现AOP
- 依赖于特定的编译器ajc
- Spring AOP:
- Spring AOP中并没有提供完整的AOP功能,更加注重的是和IOC容器的结合来解决横切业务问题
- Spring AOP使用动态代理构建内部机制的动态织入的方式实现AOP
- Spring AOP只是使用了和AspectJ相同的注解,但是底层是通过动态代理技术实现AOP
- 不依赖于AspectJ特定的编译器ajc
- 基于XML配置文件的风格个基于@AspectJ注解的风格比较:
- 基于XML配置文件的风格:
- aop:config
- proxy-target-class : 是否作为代理的目标类,默认为false
- expose-proxy : 是否暴露代理的Bean. 如果暴露,就可以通过Spring AopContext获取到代理的Bean. 默认为不暴露
- aop:pointcut
- pointcut的解析是生成一个BeanDefinition, 并将配置的id,expression等属性保存到BeanDefinition中
- BeanDefinition的ID来自配置的id属性,如果没有配置就自动生成ID
- BeanDefinition的class就是AspectJExpressionPointcut
- BeanDefinition的scope为prototype
- aop:advisor
- advice-ref : 必须的属性.这里的advice必须实现org.aopalliance.aopAdvice的子接口
- pointcut-ref : 构建一个RuntimeBeanReference对象指向员pointcut的引用
- aop:aspect
- ref: 必须的属性. 用来指定AOP中的标签是哪一个bean, 类和对象的方法
- aop:declare-parents
- 优点:
- 基于XML配置文件由POJO支持,可以从配置文件中清楚地看出系统存在哪些方面Aspect
- 缺点:
- 基于XML配置文件没有完全封装在一个地方,需求点Joinpoint分为支持Bean类的声明和配置文件中的XML
- 基于XML配置文件只支持单例的实例化切入点Pointcut模型,不能组合XML中声明命名的切入点
- 基于@AspectJ注解的风格:
- 基于 @AspectJ的注解支持额外的实例化模型和丰富的切入点组合
- 基于 @AspectJ的注解可以将所有信息封装在一个模块Aspect中,将Aspect保持为模块化单元的优点
- 基于 @AspectJ的注解可以很方便地迁移到AspectJ框架中
- Spring AOP中代理的选择:
- Spring AOP中的代理机制没有默认使用的代理 : (SpringBoot中默认开启了proxyTargetClass,默认都是使用CGLIB代理)
- 如果代理的是实现接口的类,则使用JDK动态代理. 只要代理的类实现了至少一个接口,那么目标类型实现的接口都将会使用JDK动态代理
- 如果代理的是子类,没有实现任何接口,则使用CGLIB代理
- 也可以对实现接口的类强制使用CGLIB代理,需要满足以下条件:
- 不能对final修饰的方法添加通知Advice, 因为final修饰的方法无法被覆盖
- CGLIB代理中为了获取对目标类的引用,代理类必须要继承目标类,并且代理类会覆写所有public和protected方法,在内部将调用委托给目标类
- Spring 3.2之后不需要将CGLIB添加到项目类路径中,因为CGLIB已经包含在jar包spring-core中
- Spring 4.0之后,因为CGLIB代理实例通过Objenesis创建,所以代理对象的构造函数不会被调用两次 ,CGLIB代理类不会初始化构造代理类自身继承的任何成员变量.想要访问只能通过getter和setter方法. 只有当JVM不允许时,才会有SpringAOP支持的双重调用
- 使用CGLIB代理时,需要将 <aop:config /> 的proxy-target-class属性设置为true
- 如果使用 @AspectJ自动代理时需要强制使用CGLIB, 需要将 <aop:aspectj-autoproxy /> 的proxy-target-class属性设置为true
Spring AOP的实现模式
- 代理模式: 接口 + 真正实现类 + 代理类
- 真正实现类和代理类都需要实现接口
- 实例化时使用代理类进行实例化
- Spring AOP的作用就是生成一个代理类来替换真正的实现类来对外提供服务
- Spring中代理的实现模式:
- Spring AOP中使用 @EnableAspectJAutoProxy注解,将BeanPostProcessor的子类AnnotationAwareAspectJAutoProxyCreator.class注入到容器中,然后在Bean生命周期中的后置处理器里判断切面的情况进行生成代理增强器,并且利用动态代理机制生成代理类
- 真正实现类的逻辑包含在getBean() 方法中
- getBean() 方法返回一个Proxy代理类实例
- Proxy代理类实例时Spring通过JDK动态代理或者CGLIB代理动态生成的
- 因为Spring AOP中要通过getBean() 方法查找和实例化容器中的Bean, 所以Spring AOP只能作用于Spring容器中的Bean, 无法作用于不是使用Spring IOC容器管理的对象
- 总结:
- Spring AOP的核心是代理创建器AbstractAutoProxyCreator的子类,本质上是一个Bean的后置处理器
- Spring根据配置将相应的代理创建器注册到Spring容器中.如果项目中配置 @EnableAspectJAutoProxy注解 ,Spring就会将AnnotationAwareAspectJAutoProxyCreator注册到Spring容器中
- 因为代理创建器是一个Bean的后置处理器,所以会在Bean的初始化阶段被调用
- 代理创建器会判断当前的Bean是否需要被代理,如果不需要代理就直接将原Bean实例返回.如果需要代理就使用动态代理技术根据当前Bean作为目标类创建一个代理类,并且将横切代码注入到代理类中,然后生成一个代理类的实例并返回,使用这个实例作为Bean的实例
- 如果这个Bean是单例的,那么这个代理对象就会放置到Spring容器的单例池中,这样使用getBean() 时,就可以直接从Spring容器中直接获取这个代理对象
AOP源码分析
- Spring AOP的配置方式包括 : XML配置和注解配置
自定义AOP框架
Spring Cache框架源码分析
IOC
容器基本实现和组成
IOC容器设计原理及高级特性
IOC的注入原理
装配Bean方式
BeanFactory源码分析
BeanFactory与ApplicationContext的区别
FactoryBean与BeanFactory的区别
BeanDefinition与源码分析
IOC依赖实现
IOC中的循环依赖
- 循环依赖: 两个类相互依赖,类似线程死锁的问题. 当创建一个对象需要依赖另一个对象,同时另一个对象也依赖这个对象,就会出现无法确定先创建哪个对象的问题
Spring中循环依赖解决
- Spring Bean中的几种依赖注入方式:
- 构造函数注入
- 属性注入
- 通过方法注入. 可以是setter方法,也可以在方法上添加 @Autowired注解
- Spring的作用域:
- singleton
- prototype
- request
- session
- 非单例模式下发生循环依赖的问题都会直接抛出异常