Spring的生态
-
spring是spring全家桶的基石
-
扩展性
扩展性
-
接口
-
空的方法实现(模板方法)
-
postProcessor(增强器)---beanFactoryPostProcessor ---beanPostProcessor 两个接口
onRefresh()这个方法在springboot里面做了一个很重要的扩展---内嵌tomcat
AOP术语
连接点
-
类里那些方法可以被增强,这些方法称为连接点
切入点
-
实际被增强的方法
通知
前置通知 before
-
在方法之前执行
后置通知 after
-
相当于finally里的东西,无论是否有异常都会执行
环绕通知 around
-
在方法执行前后都会执行
异常通知 afterThrowing
-
有异常的时候会执行
最终通知 afterReturning
-
返回通知,当有异常不执行
切面
-
是一个动作,把通知应用到切入点的过程
切入点表达式
-
*是权限修饰符
-
返回类型可以不用写
-
add(..)中的..表示方法中的参数
AspectJ
-
spring4和springboot1.xx.xx的正常和异常的Aop执行顺序
-
spring5和springboot2.xx.xx的正常和异常的Aop执行顺序
try {
@AfterReturning
}catch (Exception e){
@AfterThrowing
}finally {
@After
}
@Configuration
//ture表示使用cglib代理 默认false表示用jdk代理
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AopConfig {
}
//增强的类
@Aspect
@Component
@Order(1)//设置多个增强类的优先级,值越小优先级越高
public class UserProxy {
@Pointcut(value = "execution(* com.springbootTest.aopanno.UserA.add(..))")
public void pointcut() {
}
@Before(value = "pointcut()")
public void before(JoinPoint joinPoint) {
System.out.println("在方法之前执行......");
System.out.println("目标方法名为:" + joinPoint.getSignature().getName());
System.out.println("目标方法所属类的简单类名:" + joinPoint.getSignature().getDeclaringType().getSimpleName());
System.out.println("目标方法所属类的类名:" + joinPoint.getSignature().getDeclaringTypeName());
System.out.println("目标方法声明类型:" + Modifier.toString(joinPoint.getSignature().getModifiers()));
//获取传入目标方法的参数
Object[] args = joinPoint.getArgs();
for (int i = 0; i < args.length; i++) {
System.out.println("第" + (i + 1) + "个参数为:" + args[i]);
}
System.out.println("被代理的对象:" + joinPoint.getTarget());
System.out.println("代理对象自己:" + joinPoint.getThis());
}
//有没有异常都执行
@After(value = "pointcut()")
public void after() {
System.out.println("在方法之后执行......");
}
@Around(value = "pointcut()")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("环绕之前执行......");
joinPoint.proceed();
System.out.println("环绕之后执行......");
}
@AfterThrowing(value = "pointcut()")
public void afterThrowing() {
System.out.println("在方法抛出异常执行......");
}
//当有异常不执行
@AfterReturning(value = "pointcut()")
public void afterReturning() {
System.out.println("在方法返回值之后执行......");
}
}
实例化和初始化
-
实例化:在堆中申请内存空间,属性都是默认值
-
初始化:给对象的属性进行赋值操作或者初始化方法的调用
循环依赖
-
尽可能不要使用构造方法注入,这样会很难解决循环依赖,因为两个对象都没有new出来,二者谁都不会创建
-
只有单例的bean才会放入三级缓存中来解决循环依赖的问题,而非单例的bean,每次从容器中获取的都是一个新的对象,都会重新创建,所以非单例的bean是没有缓存的,不会将其放入三级缓存中。
DefaultSingletonBeanRegistry
ObjectFactory----->是函数式接口
-
一级缓存:存放成品对象
-
二级缓存:半成品对象
-
三级缓存: lambda表达式
为什么一级缓存容量大,二三级缓存容量少
-
因为二三级缓存会删的,当一个对象完成之后就把对象删掉,算个临时存储的空间
如果只有一个缓存,能否解决循环依赖的问题
-
不能,如果成品对象和半成品对象放到一起,那么从缓存中取出对象的时候可能获取到半成品对象,半成品对象不能直接暴露给外界使用的。
如果只有两个缓存,能否解决循环依赖的问题
-
二级缓存可以解决部分循环依赖问题,但是循环依赖过程中包含了代理对象的创建,那么必须要使用三级缓存了。
为什么使用三级缓存可以解决这个问题
-
在容器中不能创建两个同名的不同对象
-
当需要创建代理对象的时候,原始对象一定被创建出来了,容器中一定包含原始对象
-
如果原始对象有了,那么代理对象创建出来之后,对外暴露的是原始对象还是代理对象? 当需要对外暴露对象的时候,必须要确定一件事,到底要暴露原始对象还是代理对象,如果暴露代理对象的话就要将原始对象覆盖调。
-
你怎么知道什么时候需要对外暴露呢?怎么确定何时暴露呢? 当需要被引用的时候,来判断当前对象是否需要被代理,所以,引入lambda表达式的时候,getEarlyBeanReference方法里面就来确定对外暴露的是原始对象还是代理对象在jdk1.8之前用的是匿名内部类
执行顺序
构造器产生的循环依赖问题是无解的
Bean的生命周期
-
Bean容器找到Spring配置文件中Bean的定义;
-
Bean容器利用java 反射机制实例化Bean;
-
Bean容器为实例化的Bean设置属性值;
-
如果Bean实现了BeanNameAware接口,则执行setBeanName方法;
-
如果Bean实现了BeanClassLoaderAware接口,则执行setBeanClassLoader方法;
-
如果Bean实现了BeanFactoryAware接口,则执行setBeanFactory方法;
-
如果Bean实现了EnvironmentAware接口,则执行setEnvironment方法
-
如果Bean实现了ResourceLoaderAware接口,则执行setResourceLoader方法
-
如果Bean实现了ApplicationEventPublisherAware接口,则执行setApplicationEventPublisher方法
-
如果Bean实现了ApplicationContextAware接口,则执行setApplicationContext方法;
-
如果加载了BeanPostProcessor相关实现类,则执行postProcessBeforeInitialization方法;
-
如果Bean定义初始化方法(PostConstruct注解、配置init-method、实现了InitializingBean接口),则执行定义的初始化方法;
-
如果加载了BeanPostProcessor相关实现类,则执行postProcessAfterInitialization方法;
-
当要销毁这个Bean时,如果自定义了销毁方法(PreDestroy注解、配置destroy-method、实现了DisposableBean接口),则执行定义的销毁方法。
简单描述一下Bean的生命周期(源码版)
Spring容器帮我们去管理对象,从对象的生产和销毁环节都是由容器控制的,其中主要包括
实例化和初始化的两个关键环节,当然在整个过程中会存在一些扩展点:
-
实例化bean对象,通过反射的方法来创建对象,在源码中有一个方法叫createBeanInstance的方法专门来生成对象的
-
当bean对象创建完成之后,对象的属性值都是默认值,所以要给bean属性填充,通过populateBean方法来完成对象属性的填充,这里会出现循环依赖的问题。
-
向bean对象中设置容器属性的时候,会调用invokeAwareMethods方法来将容器对象设置到bean对象中。
-
调用BeanPostProcessor中的前置处理方法来对bean对象进行扩展工作,像ApplicationContextPostProcessor、EmbeddValueResolver
-
如果这个bean实现了InitializingBean接口,就调用afterPropertiesSet方法,之后执行init-method方法。
-
调用BeanPostProcessor的后置处理方法,完成对Bean对象的后置处理工作,AOP就是在此实现的,实现的接口的名字叫做AbstractAutoProxyCreator
-
获得完整的bean对象,通过getBean方法来获取使用
-
当对象使用完成之后,在容器关闭的时候,会销毁对象,首先会判断是否实现了DispableBean接口,然后去调用destroyMethod方法。
BeanDefinition
xml文件加载过程
protected abstract void loadBeanDefinitions(DefaultListableBeanFactory var1) throws BeansException, IOException;
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (this.logger.isInfoEnabled()) {
this.logger.info("Loading XML bean definitions from " + encodedResource.getResource());
}
Set<EncodedResource> currentResources = (Set)this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
if (!((Set)currentResources).add(encodedResource)) {
throw new BeanDefinitionStoreException("Detected cyclic loading of " + encodedResource + " - check your import definitions!");
} else {
int var5;
try {
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
var5 = this.doLoadBeanDefinitions(inputSource, encodedResource.getResource());
} finally {
inputStream.close();
}
} catch (IOException var15) {
throw new BeanDefinitionStoreException("IOException parsing XML document from " + encodedResource.getResource(), var15);
} finally {
((Set)currentResources).remove(encodedResource);
if (((Set)currentResources).isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
return var5;
}
}
BeanFactoryPostProcessor
-
对beanFactory对象进行扩展实现
-
beanFactory是程序访问的一个入口
-
将BD对象的一些占位符替换成真正的值
对象的创建
BeanPostProcessor
-
@AotoWired不能注入BeanPostProcessor类和BeanFactoryPostProcessor类,因为注入过程是在Bean的BeanPostProcessor中执行的
-
对bean对象进行扩展实现
-
AOP在生成动态代理对象的时候就是扩展
-
针对的操作对象不同
private void invokeAwareMethods(String beanName, Object bean) {
//你的bean是否实现了Aware接口
if (bean instanceof Aware) {
//如果bean是BeanNameAware实例
if (bean instanceof BeanNameAware) {
((BeanNameAware)bean).setBeanName(beanName);
}
//如果bean是BeanClassLoaderAware实例
if (bean instanceof BeanClassLoaderAware) {
//获取次工厂的类加载器以加载Bean类(即使无法使用系统ClassLoader,也只 能为空)
ClassLoader bcl = this.getBeanClassLoader();
if (bcl != null) {
((BeanClassLoaderAware)bean).setBeanClassLoader(bcl);
}
}
//如果bean是BeanFactoryAware实例
if (bean instanceof BeanFactoryAware) {
((BeanFactoryAware)bean).setBeanFactory(this);
}
}
}
refresh
@Override
public void refresh() throws BeansException, IllegalStateException {
// 来个锁,不然 refresh() 还没结束,你又来个启动或销毁容器的操作,那不就乱套了嘛
synchronized (this.startupShutdownMonitor) {
// 准备工作,记录下容器的启动时间、标记“已启动”状态、处理配置文件中的占位符
prepareRefresh();
// 这步比较关键,这步完成后,配置文件就会解析成一个个 Bean 定义,注册到 BeanFactory 中,
// 当然,这里说的 Bean 还没有初始化,只是配置信息都提取出来了,
// 注册也只是将这些信息都保存到了注册中心(说到底核心是一个 beanName-> beanDefinition 的 map)
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 设置 BeanFactory 的类加载器,添加几个 BeanPostProcessor,手动注册几个特殊的 bean
// 这块待会会展开说
prepareBeanFactory(beanFactory);
try {
// 【这里需要知道 BeanFactoryPostProcessor 这个知识点,Bean 如果实现了此接口,
// 那么在容器初始化以后,Spring 会负责调用里面的 postProcessBeanFactory 方法。】
// 这里是提供给子类的扩展点,到这里的时候,所有的 Bean 都加载、注册完成了,但是都还没有初始化
// 具体的子类可以在这步的时候添加一些特殊的 BeanFactoryPostProcessor 的实现类或做点什么事
postProcessBeanFactory(beanFactory);
// 调用 BeanFactoryPostProcessor 各个实现类的 postProcessBeanFactory(factory) 方法
invokeBeanFactoryPostProcessors(beanFactory);
// 注册 BeanPostProcessor 的实现类,注意看和 BeanFactoryPostProcessor 的区别
// 此接口两个方法: postProcessBeforeInitialization 和 postProcessAfterInitialization
// 两个方法分别在 Bean 初始化之前和初始化之后得到执行。注意,到这里 Bean 还没初始化
registerBeanPostProcessors(beanFactory);
// 初始化当前 ApplicationContext 的 MessageSource,国际化这里就不展开说了,不然没完没了了
initMessageSource();
// 初始化当前 ApplicationContext 的事件广播器,这里也不展开了
initApplicationEventMulticaster();
// 从方法名就可以知道,典型的模板方法(钩子方法),
// 具体的子类可以在这里初始化一些特殊的 Bean(在初始化 singleton beans 之前)
onRefresh();
// 注册事件监听器,监听器需要实现 ApplicationListener 接口。这也不是我们的重点,过
registerListeners();
// 重点,重点,重点
// 初始化所有的 singleton beans
//(lazy-init 的除外)
finishBeanFactoryInitialization(beanFactory);
// 最后,广播事件,ApplicationContext 初始化完成
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
// 销毁已经初始化的 singleton 的 Beans,以免有些 bean 会一直占用资源
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// 把异常往外抛
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
BeanFactory和FactoryBean
-
FactoryBean 可以让我们自定义Bean的创建过程 ,可以说为IOC容器中Bean的实现提供了更加灵活的方式,FactoryBean在IOC容器的基础上给Bean的实现加上了一个简单工厂模式和装饰模式,我们可以在getObject()方法中灵活配置。
-
getBean("&factoryBean")获取的并不是factoryBean对象,而是调用factoryBean.getObject()方法返回的对象。
ApplicationContext和BeanFactory区别
BeanFactory是访问spring容器的根接口,里面只是提供了某些基本方法的约束和规范,为了满足更多的需求,ApplicationContext实现了BeanFactory接口,并在此接口之上做了某些扩展功能,提供了更多丰富的api调用。而一般情况下我们使用的是ApplicationContext更多。
AOP底层实现原理
动态代理,aop是ioc的一个扩展功能,先有ioc,再有aop,aop只是在ioc的整个流程中新增的一个扩展点而已:BeanPostProcessor
总:aop的概念,应用场景,动态代理
分:bean的创建过程中有一个步骤可以对bean进行扩展实现,而aop本身就是一个扩展功能,在beanPostProcessor的后置处理方法中进行扩展实现。
1、代理对象的创建过程(通知Advice,切面,切点)
2、通过JDK或者cglib的方式来生成代理对象
3、在执行方法调用的时候,会调用到生成的字节码文件中,直接会找到DynamicAdvisoredInterceptor类中的intercept方法,从此方法开始执行
4、根据之前定义好的通知来生成拦截器链
5、从拦截器链中依次获取每一个通知开始进行执行,在执行过程中,为了方便找到下一个通知是哪个,会有一个InvocationInterceptor的对象,找的时候是从-1的位置一次开始查找并且执行的。
Spring的事务是如何回滚的
spring的事务是如何实现的
总:spring的事务是由aop实现的,首先要生成具体的代理对象,然后按照aop的整套流程来执行具体的操作逻辑,正常情况下要通过通知来完成核心功能,但是事务并不是通过通知来实现的,而是通过一个TransactionInterceptor来实现的,然后调用invoke来实现具体的逻辑
分:1、先做准备工作,解析各个方法上事务相关的属性,根据具体的属性来判断是否开启新事物
2、当需要开启的时候,获取数据库连接,关闭自动提交功能,开启事务
3、执行具体的sql逻辑操作
4、在操作过程中如果执行失败了,那么会通过completeTransactionAfterThrowing来完成事务的回滚操作,回滚的具体逻辑是通过doRollBack方法实现的,实现的时候也是要先获取连接对象,通过连接对象来回滚,底层是调用的jdbc的rollBack方法来进行回滚的。
5、如果执行过程中,没有任何意外情况的发生,那么会通过commitTransactionAfterReturning来完成事务的提交操作,提交的具体逻辑是通过doCommit方法来实现的,实现的时候也是先获取连接,通过连接对象来提交,底层调用的是jdbxc的commit方法来进行提交的。
6、当事务执行完毕之后,需要清除相关的事务信息cleanupTransactionInfo
Spring的事务传播
传播特性 7种
Required,Requires_new,nested,Support,Not_Support,Never,Mandatory
@Transactional(propagation=Propagation.REQUIRED) 如果有事务, 那么加入事务, 没有的话新建一个(默认情况下)
@Transactional(propagation=Propagation.NOT_SUPPORTED) 容器不为这个方法开启事务
@Transactional(propagation=Propagation.REQUIRES_NEW) 不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务
@Transactional(propagation=Propagation.MANDATORY) 必须在一个已有的事务中执行,否则抛出异常
@Transactional(propagation=Propagation.NEVER) 必须在一个没有的事务中执行,否则抛出异常(与Propagation.MANDATORY相反)
@Transactional(propagation=Propagation.SUPPORTS) 如果其他bean调用这个方法,在其他bean中声明事务,那就用事务.如果其他bean没有声明事务,那就不用事务.
PROPAGATION_NESTED
如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。
某一个事务嵌套另一个事务怎么办?
A方法调用B方法,AB方法都有事务,并且传播特性不同,那么A如果有异常B怎么办,B如果有异常A怎么办?
总:事务的传播特性指的是不同方法的嵌套调用过程中,事务应该如何进行处理,是用同一个事务还是不同的事务,当出现异常的时候会回滚还是提交,两个方法之间的相互影响,在日常工作中,使用的比较多的是Required,Requires_new,nested
分:1、先说事务的不同分类,可以分为三类:支持当前事务,不支持当前事务,嵌套事务
2、如果外层方法是Required,内层方法是Required,Requires_new,nested
3、如果外层方法是Requires_new,内层方法是Required,Requires_new,nested
4、如果外层方法是nested,内层方法是Required,Requires_new,nested
1、判断内外方法是否是同一个事务:
是,统一在外层方法中处理
不是,内层方法有可能影响到外层方法,但是外层方法是不会影响内层方法的
@AutoWired源码
-
1.属性方式注入 2.普通非静态方法的注入 3.构造方法的注入
@AutoWired注入分为两步,第一步是找到注入点,然后放入缓存,第二步是从缓存获取注入点进行注入,spring中的依赖注入@AutoWired也是通过AutowiredAnnotationBeanPostProcessor这个bean后置处理器实现的,这个后置处理器中提供了两个方法,spring分别是在实例化后初始化前调用,这两个方法是postProcessMergedBeanDefinition和postProcessProperties,其中postProcessMergedBeanDefinition是找到bena的注入点,而postProcessProperties是进行注入,先来看下如何找到注入点的源码,注入点的源码是在doCreateBean中实现的
不加@AutoWired进行注入
@Component
public class CBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
GenericBeanDefinition c = (GenericBeanDefinition) beanFactory.getBeanDefinition("c");
c.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_NAME);
System.out.println("我们拿到了c的定义" + c.getBeanClassName());
}
}
Spring用到了那些设计模式
适配器模式
-
首先,将所有的适配器放到一个集合中,当要使用适配器的时候,遍历集合找出相对应的适配器,将该适配器返回给用户,用户执行适配器中的方法传入需要适配的类型,返回需要的类型
-
每一个通知都对应一个拦截器,Spring需要将具体的通知封装成拦截器,返回给容器,这里对advice的转换就用到了适配器模式
-
想一下,你定义的advice通知方法,最终是不是要通过拦截器去执行的
public interface AdvisorAdapter {
// 判断通知类型是否匹配
boolean supportsAdvice(Advice advice);
// 获取对应的拦截器
MethodInterceptor getInterceptor(Advisor advisor);
}
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
@Override
public boolean supportsAdvice(Advice advice) {
return (advice instanceof MethodBeforeAdvice);
}
@Override
public MethodInterceptor getInterceptor(Advisor advisor) {
MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
// 通知类型匹配对应的拦截器
return new MethodBeforeAdviceInterceptor(advice);
}
}
-
在Spring MVC中,DispatcherServlet作为用户,HandlerAdapter作为期望接口,具体的适配器实现类用于对目标类进行适配,Controller作为需要适配的类
-
当Spring容器启动后,会将所有定义好的适配器对象存放在一个List集合中,当一个请求来临时,DispatcherServlet会通过handler的类型找到对应适配器,并将该适配器对象返回给用户,然后就可以统一通过适配器的hanle()方法来调用Controller中的用于处理请求的方法。
public interface HandlerAdapter {
boolean supports(Object handler);
ModelAndView handle(HttpServletRequest request, HttpServletResponse
response, Object handler) throws Exception;
}
单例模式
-
spring的bean模式都是单例的 singleton
简单工厂
-
spring中的BeanFactory就是简单工厂模式的体现,传入一个唯一的标识来获取bean对象
工厂方法
-
通常由应用程序直接使用new创建新的对象,为了将对象的创建和使用相分离,采用工厂模式,即应用程序将对象的创建及初始化职责交给工厂对象。
-
实现了FactoryBean接口的bean是一类叫做factory的bean。其特点是,spring会在使用getBean()调 用获得该bean时,会自动调用该bean的getObject()方法,所以返回的不是factory这个bean,而是这个 bean.getOjbect()方法的返回值。
动态代理
-
切面在应用运行的时刻被织入。一般情况下,在织入切面时,AOP容器会为目标对象创建动态的创建一个代理 对象。SpringAOP就是以这种方式织入切面的。
织入:把切面应用到目标对象并创建新的代理对象的过程。
模板方法
-
spring启动流程有个onRefresh()方法是空方法,留给子类来重写,在springboot中来内嵌tomcat。
装饰器模式
-
Spring中用到的包装器模式在类名上有两种表现:一种是类名中含有Wrapper,另一种是类名中含有 Decorator。
观察者模式
-
spring的事件驱动模型使用的是 观察者模式,listener的实现
策略模式
-
策略模式作为一种软件设计模式,指对象有某个行为,但是在不同的场景中,该行为有不同的实现算法,可以替代代码中大量的 if-else。
-
比如我们生活中的场景:买东西结账可以使用微信支付、支付宝支付或者银行卡支付,这些交易方式就是不同的策略。
-
在《阿里巴巴Java开发手册》中有提到当超过 3 层的 if-else 的逻辑判断代码可以使用策略模式来实现。
什么时候@Transactional失效
-
因为Spring的事务是基于代理来实现的,所以加上@Transactional注解的方法只有被代理类调用的时候事务才会生效,不是代理类调用的话不会生效
-
如果某个方法是private的,那么@Transactional会失效,因为底层Cglib是依靠生成子类来代理对象的,对于private修饰的方法,子类不能够去继承的,也就无法生效