Spring学习笔记

一、什么是Spring

Spring是个java企业级应用的开源开发框架。Spring主要用来开发java应用。Spring框架的目标是简化java企业级应用开发,并通过POJO为基础的编程模型促进良好的编程习惯。有两个核心部分:IOC、AOP

二、Spring特点

方便解耦,简化开发

支持AOP

方便进行事务操作

方便和其他框架进行整合

方便程序测试

三、Spring中都用到了哪些设计模式

简单工厂模式:Spring中的BeanFactory就是简单工厂模式的体现,根据传入的唯一标识来获得Bean对象

工厂模式:Spring中的FactoryBean就是是典型的工厂方法模式,实现了 FactoryBean 接口的 bean 是一类叫做 factory 的 bean。其特点是,spring 在使用 getBean() 调用获得该 bean 时,会自动调 用该 bean 的 getObject() 方法,所以返回的不是 factory 这个 bean,而是这个 bean.getOjbect() 方法的返回值。

单例模式:在 spring 中用到的单例模式有: scope="singleton" ,注册式单例模式,bean 存放于 Map 中。bean name 当做 key,bean 当做 value。

原型模式:在 spring 中用到的原型模式有: scope="prototype" ,每次获取的是通过克隆生成的新 实例,对其进行修改时对原有实例对象不造成任何影响。

代理模式:Spring 中经典的 AOP,就是使用动态代理实现的,分 JDK 和 CGlib 动态代理。

适配器模式:SpringAOP的增强或通知(Advice)使用了适配器模式

模板方法模式:Spring中jdbcTemplate等以Template结尾的对数据库操作的类,都会用到模板方法设计模式

四、Spring中有哪些核心模块

Spring Core:Spring的核心,是框架最基础的部分,提供了IOC和DI

Spring Context:Spring上下文容器,他是BeanFactory功能加强的一个子接口

Spring Web:它提供了对Web应用开发的支持

Spring MVC:它是针对WEB应用中MVC思想的实现

Spring DAO:提供了JDBC抽象层,简化了JDBC编码,是编码更具有健壮性

Spring ORM:支持与流行的ORM框架整合

Spring AOP:与AOP兼容的编程实现

五、说一下你理解的IOC

IOC是一个容器,是用来装载对象的,它的核心思想是控制反转

控制反转就是把对象的控制权交给了Spring,由Spring容器进行管理,我们不需要进行任何操作,目的是为了降低耦合度

六、Spring中的IOC容器有哪些?有什么区别

Spring主要提供两种IOC容器,BeanFactory与ApplicationContext

区别:

BeanFactory只提供了最基本的实例化对象和拿对象的功能,BeanFactory在加载配置文件时不会创建对象,只有在我们获取对象时才会去创建

ApplicationContext是继承了BeanFactory的一个子接口,提供了更强大的功能,如 支持注解注入、国际化等。ApplicationContext在加载配置文件是就会去创建对象

七、什么是Bean管理

Bean管理就是指 创建对象、注入属性 两个操作

创建对象方式有两种:在配置文件中使用bean标签、使用注解@Component、@Controller、@Service、@Repository

注入属性方式有两种:setter注入、构造器注入,使用注解

@Autowired:根据属性类型进行自动装配 

@Qualifier:根据名称进行注入

@Resource:可以根据类型注入,可以根据名称注入

@Value:注入普通类型属性

八、BeanFactory和FactoryBean的区别

BeanFactory是IOC容器,是用来承载对象的,辅助生产和管理bean的一个工厂接口

主要方法有:

        getBean(String name):返回给定名称的bean实例

        getBean(Class<T> type):返回给定类型的bean实例

        containsBean(String name):判断工厂中是否包含给定名称的bean,有则返回true

        isSingleton(String name):判断给定名称的bean是否是单例

        isPrototype(String name):判断给定名称的bean是否为多例模式

        getAliases(String name):返回给定bean名称的所有别名

FactoryBean是Spring提供的另外一种创建Bean的方式,也是一个接口,是一个工厂bean,当在IOC容器中的bean实现了FactoryBean后,通过getBean(beanName)得到的Bean对象并不是FactoryBean的实现类对象,而是这个实现类中的getObject方法返回的对象,只有通过getBean(&beanName)获取到的才是FactoryBean的实现类对象

主要方法:

       getObject():返回的对象实例

       getObjectType:返回对象实例的类型

       isSingleton():指定返回的对象实例是单例韩式多例,true为单例,false为非单例 

九、@Repository、@Service、@Component、@Controller它们有什么区别?

这四个注解本质都一样,都是将被该注解标识的对象放入Spring容器中,只是为了在使用上区分不同的应用分层

@Repository:dao层

@Service:service层

@Controller:controller层

@Compent:其他不属于以上三层的统一使用该注解

十、DI是什么

DI就是依赖注入,是指在程序运行时动态的向某个对象提供其所依赖的对象。Spring的DI是通过反射实现的

比如我们需要注入一个对象 A,而这个对象 A 依赖一个对象 B,那么我们就需要把这个对象 B 注入到对象 A 中,这就是依赖注入

控制反转是目标,依赖注入是实现控制反转的手段

谁依赖谁:应用程序依赖IOC容器

为什么需要依赖:应用程序需要IOC容器来提供组件需要的外部资源

谁注入谁:IOC容器注入应用程序依赖的对象

注入了什么:注入了某个对象所需要的外部资源(对象、资源、常量数据)

Spring基于XML注入bean的方式:

        setter注入

        构造器注入:通过index设置参数文职,通过type设置参数类型

Spring基于注解的注入方式:

        @Autowired、@Qualifier、@Resource、@Value

十一、说说AOP是什么

AOP就是可以在不通过修改源代码的方式,实现对主干功能的增强,可以对业务逻辑的各个部分进行隔离,降低耦合度,提高程序可重用性

AOP主要分两类:静态AOP、动态AOP

静态AOP:是在编译阶段对程序源代码进行修改,生成静态AOP代理类

动态AOP:是在运行阶段动态生成代理对象(JDK动态代理、CGlib动态代理)

        Spring是中的AOP是使用动态代理实现的,如果代理对象实现了某个接口,就会用JDK动态代理,否则就用CGlib动态代理

AOP有五种通知类型:  

        @Before:在目标方法调用前去通知

        @AfterReturning:在目标执行完通知,返回结果后

        @AfterThrowing:在目标方法出现异常时调用

        @After:在目标方法异常后调用

        @Around:环绕通知,在目标方法前后都会执行,将目标方法封装起来,自己确定调用时机

十二、静态代理和动态代理区别

静态代理:

由程序员创建或由特定工具自动生成源代码,再对其编译。

静态代理通常只代理一个类并且事先知道要代理的是什么

动态代理:

在程序运行时,运用反射机制动态创建而成

动态代理是代理一个接口下的多个实现类,动态代理不知道要代理什么东西,只有在运行时才知道

十三、JDK动态代理和CGlib动态代理区别

JDK 动态代理要求业务类必须要实现某个接口,它是基于反射的机制实现的,生成一个实现同样接口的一个代理类,然后通过重写InvocationHandler接口的invoke方法的方式,实现对代码的增强。

使用Proxy类的静态方法Proxy.newProxyInstance(类加载器,要增强方法所在的类的实现接口,InvocationHandler接口)创建接口代理对象,重写InvocationHandler的invoke方法实现增强

CGLIB 动态代理是通过字节码技术为一个类创建子类,然后重写父类的方法,实现对代码的增强。

十四、SpringAOP和AspectJ AOP有什么区别

Spring AOP 是运行时增强,是通过动态代理实现

AspectJ AOP 是编译时增强,是通过「修改代码来实现」的,在编译某一个java文件时会检查你有没有@Before、@After等注解,如果有,他会进一步解析你要切的是哪个方法,根据这些信息最终会去增强被切的那个类的字节码,在编译的时候把切面逻辑直接加入到被切的那个类的字节码中,这样被增强的类生成的对象就拥有了增强的逻辑。支持三种织入方式:                                                 编译时织入:在编译字节码的时候织入相关代理类                                                                             编译后织入:编译完成,初始化类时返现需要AOP增强,然后织入相关代码                                     类加载时织入:在加载类的时候织入

SpringAOP只不过是支持了Aspectj的那几个注解,会自己去解析注解生成代理对象,照样还是用的动态代理

十五、Spring中Bean的生命周期

SpringBean的生命周期大致分为4个阶段

实例化 ====> 注入属性 ====> 初始化 ====> 销毁

实例化:实例化该bean对象,默认调用无参构造器

注入属性:给bean赋值,注入这个bean以来的其他bean对象

初始化:如果bean实现了xxxAware接口,就会通过其接口获取对应容器资源。

               BeanNameAware会回调setBeanName方法

               BeanClassLoaderAware会回调setBeanClassLoader方法

               BeanFactoryAware会回调setBeanFactory方法

如果实现了BeanPostProcessor接口,则会回调改接口的前置后置增强处理器

如果配置了init-method方法,则会执行该方法

最终会被注册到singletonBeanRegistry的Map中,初始化完成,Bean就被正确创建了,就可以使用了

销毁:如果实现了DisposableBean接口,则会回调该接口的destroy方法

           如果配置了destroy-method方法,则会执行destroy-method配置的方法

十六、Spring是怎么解决循环依赖的

循环依赖就是两个对象相互依赖,形成了一个环形的调用链路 A ---> B ---> A

Spring是在属性注入阶段通过三级缓存来解决循环依赖的,核心逻辑就是把实例化和初始化的步骤分开,然后放入缓存中

第一级缓存:用来保存实例化、初始化都完成的对象

第二级缓存:用来保存实例化完成,但初始化尚未完成的对象

第三季缓存:用来保存一个对象工厂,提供一个匿名内部类,用于创建二级缓存中的对象

大致流程:

A完成实例化后,会去创建一个对象工厂,并放入三级缓存中

如果A被AOP代理,那么通过这个工厂获取到的就是A代理后的对象,如果没有被AOP代理,那么这个工厂获取到的就是A实例化的对象

A进行属性注入时,去创建B

B进行属性注入时,需要A,就会从三级缓存中去A工厂代理对象并注入,删除三级缓存中的A工厂,将A放入二级缓存

B完成后续属性注入,知道初始化结束,将B放入一级缓存

A从一级缓存取到B并注入B,知道完后后续操作,将A从二级缓存删除,并放入一级缓存

至此循环依赖结束


Spring无法解决多例Bean和构造器注入的循环依赖

构造器注入:

        单例bean是在实例化后,也就是执行了构造方法后,创建完成后才会有对象的地址,才能提前暴露到三级缓存中,因此如果使用构造器注入依赖对象的话,三级缓存中并没有提前暴露对象,因此调用getBean方法时,不能从缓存中取,所以无法解决

举个例子:A依赖B , B依赖A。调用A的构造方法创建对象时,需要传入B,此时Spring的三个缓存中并没有B对应的信息,所以Spring回去实例化B,在调用B的构造方法时发现又需要A,此时又会去获取A,而A都还没有创建出来,所以就会出现代码死锁,导致谁都无法创建成功

多例bean:

        因为多例bean并不是在IOC容器启动时开始生命周期的,而是在调用getBean方法时才会走生命周期,每调用一次都会重新生成一个新的对象,多例bean是无法利用缓存的,只有单例bean才可以,所以无法解决

十七、如果只有一级缓存,能不能解决循环依赖

不能。因为一级缓存是存放完整的bean(实例化初始化都完成的),如果只有一级缓存,意味着半成品对象(只完成实例化,未完成初始化)要和完整的bean放在一起,这样在调用getBean对象时,就有可能拿到半成品对象,半成品对象还没完成属性注入过程,属性值都是null,此时如果直接使用有可能发生空指针异常。所以不能解决

十八、为什么要使用三级缓存,二级缓存不能解决吗

可以,三级缓存的功能是只有真正发生循环依赖的时候,才会去提前生成代理对象,否则只会去创建一个工厂,并将其放入三级缓存,但并不会去通过这个工厂去真正创建对象

如果使用二级缓存解决循环依赖,意味着所有的bean在实例化后就要完成AOP代理,这样为未被了spring的设计原则,在spring设计之初就是在bean初始化完后,才会去创建对应的代理对象

十九、只有一级缓存、三级缓存的话,能不能解决

如果不涉及代理的话,可以。一级缓存存放完整的对象,三级缓存存放暴露出来的对象工厂

但是如果涉及了,就不行了,因为三级缓存中的对象工厂每执行一次就会创建一个对象,此时需要借助另一个缓存来存放getObject方法创建的代理对象,所以不能解决

(如果要注入的对象被AOP代理,那么其他对象在注入这个对象时,就需要注入对应的代理对象,不能能是原对象)

二十、@Autowired 和 @Resource 有什么区别?

@Resource Java 自己的注解,@Resource 有两个属性是比较重要的,分是 name 和 type;Spring 将 @Resource 注解的 name 属性解析为 bean 的名字,而 type 属性则解析为 bean 的类型。所以如果使用 name 属性,则使用 byName 的自动注入策略,而使用 type 属性时则使用 byType 自动注入策略。如果既不指定 name 也不指定 type 属性,这时将通过反射机制使用 byName 自动注入策略。

@Autowired spring 的注解,是 spring2.5 版本引入的,Autowired 只根据 type 进行注入,不会去匹配 name。如果涉及到 type 无法辨别注入对象时,那需要依赖 @Qualifier 或 @Primary 注解一起来修饰。

二十一、Spring事务隔离级别有哪些

  • DEFAULT:采用 DB 默认的事务隔离级别
  • READ_UNCOMMITTED:读未提交
  • READ_COMMITTED:读已提交
  • REPEATABLE_READ:可重复读
  • SERIALIZABLE:串行化 

二十二、Spring事务的传播行为有哪些

事务传播行为:多个声明了事务的方法,在相互调用时,事务应该如何去传递

Spring提供了7中事务传播行为

propagation_required 默认:如果当前存在事务就加入到这个事务中执行,如果不存在就新建一个事务执行

(如果被调用端发生异常,那么调用端和被调用端事务都将回滚)

propagation_supports:表示支持当前事务,如果当前不存在事务,就以非事务方式执行

propagation_mandatory:表示强制事务执行,如果当前不存在事务,就抛出个异常

propagation_nested:如果当前存在事务,就嵌套在当前事务中去执行,如果当前没有事务,就新建一个事务

propagation_never:以非事务方式执行,如果当前存在事务 就抛出个异常

propagation_requires_new:不管是否存在事务,都要新开启一个事务来执行,新老事务之间是互相独立的,外部事务抛出异常不会影响内部事务的正常提交

propagation_not_supported:表示以非事务方式执行,如果当前存在事务就把当前事务挂起

         

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值