Spring
Posted lc19149
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring相关的知识,希望对你有一定的参考价值。
什么是Spring
Spring是开源的控制反转(Inversion of Control)和面向切面(Aspect Oriented Programming)的容器框架。
IoC
创建对象并且组装对象之间的关系。应用程序本身不负责依赖对象的创建和维护,依赖对象的创建和维护是由外部容器负责。这样控制权就由应用程序转移到了外部容器,控制权的转移就是所谓的反转。 IoC对于整个系统的资源做一个全局的管理,其一可以减少耦合性,其二达到资源的复用。
依赖注入
Dependency Injection,DI是IoC的另外一种说法。DI是由Martin Fowler在2004年初的一篇论文中首次提出的。他总结:控制的什么被反转了?就是:获得依赖对象的方式反转了。
SpringIoC中三种依赖注入方式
1.setter方法注入
2.构造方法注入
3.接口注入
对于习惯了传统javabean的开发人员,通过setter方法设定依赖关系更加直观。在构造期间完成对象创建,可以集中体现依赖关系。不过对于基于Spring Framework开发的应用而言,setter方法使用更加广泛。
BeanFactory和ApplicationContext
BeanFactory是Spring框架最核心的接口,它提供了IoC的配置机制,通过xml配置文件加载bean。ApplicationContext继承BeanFacotry接口,提供了更多的扩展功能。比如文本国际化;统一加载资源;AOP。
BeanFactroy采用延迟加载的形式来注入Bean,只有在使用到某个Bean时,才对该Bean进行加载实例化。而ApplicationContext则相反,它是在容器启动时一次性创建所有的Bean。在容器启动时可以发现Spring中存在的配置错误(ApplicationContext可以为Bean配置lazy-init=true来让Bean延迟实例化)。
ApplicationContext接口的常用实现类介绍
1.ClassPathXmlApplicationContext
从类路径ClassPath中寻找指定的XML配置文件,找到并装载完成ApplicationContext的实例化工作。例如:
//装载单个配置文件实例化ApplicationContext容器
ApplicationContext cxt = new ClassPathXmlApplicationContext("applicationContext.xml");
//装载多个配置文件实例化ApplicationContext容器
String[] configs = {"bean1.xml","bean2.xml","bean3.xml"};
ApplicationContext cxt = new ClassPathXmlApplicationContext(configs);
2.FileSystemXmlApplicationContext
从指定的文件系统路径中寻找指定的XML配置文件,找到并装载完成ApplicationContext的实例化工作。例如:
//装载单个配置文件实例化ApplicationContext容器
ApplicationContext cxt = new FileSystemXMLApplicationContext("beans.xml");
//装载多个配置文件实例化ApplicationContext容器
String[] configs = {"c:/beans1.xml","c:/beans2.xml"};
ApplicationContext cxt = new FileSystemXmlApplicationContext(configs);
3.XmlWebApplicationContext
从Web应用中寻找指定的XML配置文件,找到并装载完成ApplicationContext的实例化工作。这是为Web工程量身定制的,使用WebApplicationContextUtils类的getRequiredWebApplicationContext方法可在JSP与Servlet中取得IoC容器的引用。
Spring实例化bean的三种方式
1.使用类构造器实例化(最常用)
<bean name="userImpDao" class="com.lz.service.imp.UserImpDao"></bean>
2.使用静态工厂方法实例化
<bean name="userStaticFactory" class="com.lz.factory.UsersFactory" factory-method="createStaticUserBean"></bean>
3.使用实例工厂方法实例化
<bean name="userFactory" class="com.lz.factory.UsersFactory"></bean>
<bean name="userFactoryBean" factory-bean="userFactory" factory-method="createUserBean"></bean>
Spring中bean的作用域
1.singleton:在Spring IoC容器中仅存在一个bean实例,bean以单例方式存在,默认值。
2.prototype:每次从容器中调用bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行new XxxBean()。
3.request:每次HTTP请求都会创建一个新的bean,该作用域仅适用于WebApplicationContent环境。
4.session:同一个HTTP Session共享一个bean,该作用域仅适用于WebApplicationContent环境。
5.globalsession:一般用于Portlet应用环境,该作用域仅适用于WebApplicationContent环境。
Java在创建对象时,需要进行内存申请;销毁实例时,需要完成垃圾回收,这些工作都会增加系统开销。因此,Bean的作用域越大,销毁代价比较大。
Spring中bean的生命周期
1.从XML文件中读取bean的定义,并实例化bean。
2.根据bean的定义填充所有的属性。
3.如果bean实现了BeanNameAware接口,Spring传递bean的id到setBeanName方法。
4.如果bean实现了BeanFactoryAware接口,Spring传递beanfactory给setBeanFactory方法。
5.如果有任何与bean相关联的BeanPostProcessors,Spring会在postProcesserBeforeInitialization()方法内调用它们。
6.如果bean实现IntializingBean了,调用它的afterPropertySet方法,如果bean声明了初始化方法,调用此初始化方法。
7.如果有BeanPostProcessors和bean关联,这些bean的postProcessAfterInitialization()方法将被调用。
8.如果bean实现了DisposableBean,它将调用destroy()方法。
AOP
Aspect Oriented Programming,面向切面编程。可以在一个方法前,方法后,或者类实例化前,实例化后处理一些自己的代码,一般用在权限拦截、事务处理(Spring著名的申明性事务),和动态的进行log记录。把某个方面的功能提出来与一批对象进行隔离,这样与一批对象之间降低了耦合性,可以就某个功能进行编程。
AOP的基本概念:
1.切面(Aspect):其实就是共有功能的实现。如日志切面、权限切面、事务切面等。在实际应用中通常是一个存放共有功能实现的普通Java类,之所以能被AOP容器识别成切面,是在配置中指定的。
2.通知(Advice):是切面的具体实现。以目标方法为参照点,根据放置的地方不同,可分为前置通知(Before)、后置通知(AfterReturning)、异常通知(AfterThrowing)、最终通知(After)与环绕通知(Around)5种。在实际应用中通常是切面类中的一个方法,具体属于哪类通知,同样是在配置中指定的。
3.连接点(Joinpoint):就是程序在运行过程中能够插入切面的地点。例如,方法调用、异常抛出或字段修改等,但spring只支持方法级的连接点。
4.切入点(Pointcut):用于定义通知应该切入到哪些连接点上。不同的通知通常需要切入到不同的连接点上,这种精准的匹配是由切入点的正则表达式来定义的。
5.目标对象(Target):就是那些即将切入切面的对象,也就是那些被通知的对象。这些对象中已经只剩下干干净净的核心业务逻辑代码了,所有的共有功能代码等待AOP容器的切入。
6.代理对象(Proxy):将通知应用到目标对象之后被动态创建的对象。可以简单地理解为,代理对象的功能等于目标对象的核心业务逻辑功能加上共有功能。代理对象对于使用者而言是透明的,是程序运行过程中的产物。
7.织入(Weaving):将切面应用到目标对象从而创建一个新的代理对象的过程。这个过程可以发生在编译期、类装载期及运行期,当然不同的发生点有着不同的前提条件。譬如发生在编译期的话,就要求有一个支持这种AOP实现的特殊编译器;发生在类装载期,就要求有一个支持AOP实现的特殊类装载器;只有发生在运行期,则可直接通过Java语言的反射机制与动态代理机制来动态实现。
为什么使用Spring?
1:降低组件之间的耦合度,实现各层之间的解耦。
2:可以使用容器提供的众多服务。如:事务管理服务、JMS、Spring core核心服务、持久化服务。
3:提供了单例模式支持。开发人员不需要自己编写实现代码。
4:提供了AOP技术。实现如:权限拦截,运行监控等功能。
5:提供了众多的辅助类。如JDBC Template,HIbernate Template。
6:对主流的应用框架提供了集成支持。集成Struts,Mybatis,Hibernate。
Spring有几种配置方式?
1.基于XML的配置
2.基于注解的配置
3.基于Java的配置
什么是bean装配?
装配是指在Spring容器中把bean组装到一起,前提是容器需要知道bean的依赖关系,如何通过依赖注入来把它们装配到一起。
如何开启基于注解的自动装配?
<beans>
<context:annotation-config />
</beans>
Spring框架中的单例bean是线程安全的么?
不,Spring框架中的单例bean不是线程安全的。最浅显的解决办法就是将bean的作用域由“singleton”变更为“prototype”。
Spring框架中都用到了哪些设计模式?
1.代理模式 在AOP和remoting中被用的比较多。
2.单例模式 bean默认为单例模式。
3.模板方法 用来解决代码重复的问题。比如RestTemplate,JmsTemplate,JpaTemplate。
4.工厂模式 BeanFactory用来创建对象的实例。
Spring五个事务隔离级别和七个事务传播行为
隔离级别是指若干个并发的事务之间的隔离程度。TransactionDefinition接口中定义了五个表示隔离级别的常量:
ISOLATION_DEFAULT这是一个PlatfromTransactionManger默认的隔离级别,使用数据库默认的事务隔离级别,另外四个与JDBC的隔离级别相对应。
ISOLATION_READ_UNCOMMITTED这是事务最低的隔离级别,它允许另外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻像读。
ISOLATION_READ_COMMITTED保证一个事务修改的数据提交后才能被另一个事务读取。另外一个事务不能读取该事务未提交的数据。这种事务隔离可以避免脏读的出现,但是可能出现不可重复读和幻像读。
ISOLATION_REPEATABLE_READ这种事务隔离可以防止脏读,不可重复读。但是可能出现幻像读。它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。
ISOLATION_SERIALIZABLE这是花费最高代价最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读,不可重复读外,还避免了幻像读。
所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。在TransactionDefinition定义中包括了如下几个表示传播行为的常量:
PROPAGATION_REQUIRED:如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。
PROPAGATION_SUPPORTS:如果存在一个事务,支持当前事务。如果没事务,就以非事务方式执行。
PROPAGATION_MANDATORY:如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常。
PROPAGATION_REQUIRES_NEW:总是开启一个新的事务。如果一个事务已经存在,则将这个事务挂起。
PROPAGATION_NOT_SUPPORTED:总是非事务地执行,并挂起任何存在的事务。
PROPAGATION_NEVER:总是非事务的执行,如果存在一个活动事务,则抛出异常。
PROPAGATION_NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中,如果没有活动事务,则按PROPAGATION_REQUIRED属性执行。
脏读(Dirty reads)脏读发生在一个事务读取了另一个事务改写但尚未提交的数据时。如果改写在稍后被回滚了,那么第一个事务获取的数据就是无效的。
不可重复读(Nonrepeatable read)不可重复读发生在一个事务执行相同的查询两次或两次以上,但是每次都得到不同的数据时。这通常是因为另一个并发事务在两次查询期间进行了更新。
幻读(Phantom read)幻读与不可重复读类似。它发生在一个事务(T1)读取了几行数据,接着另一个并发事务(T2)插入了一些数据时。在随后的查询中,第一个事务(T1)就会发现多了一些原本不存在的记录。
事务有四个特性:ACID
原子性(Atomicity):事务是一个原子操作,由一系列动作组成。事务的原子性确保动作要么全部完成,要么完全不起作用。
一致性(Consistency):一旦事务完成(不管成功还是失败),系统必须确保它所建模的业务处于一致的状态,而不会是部分完成部分失败。在现实中的数据不应该被破坏。
隔离性(Isolation):可能有许多事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏。
持久性(Durability):一旦事务完成,无论发生什么系统错误,它的结果都不应该受到影响,这样就能从任何系统崩溃中恢复过来。通常情况下,事务的结果被写到持久化存储器中。
Spring提供了对编程式事务和声明式事务的支持,编程式事务允许用户在代码中精确定义事务的边界,而声明式事务(基于AOP)有助于用户将操作与事务规则进行解耦。简单地说,编程式事务侵入到了业务代码里面,但是提供了更加详细的事务管理;而声明式事务由于基于AOP,所以既能起到事务管理的作用,又可以不影响业务代码的具体实现。
如何实现编程式事务?
Spring提供两种方式的编程式事务管理,分别是:使用TransactionTemplate和直接使用PlatformTransactionManager。
Spring声明式事务的5种配置方式
1.每个Bean都有一个代理。
2.所有Bean共享一个代理基类。
3.使用拦截器。
4.使用tx标签配置的拦截器。
5.全注解,此时在DAO上需加上Transactional注解。
<!-- 全注解 -->
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
Spring生成的bean默认都是单例,通过scope(丝狗铺)属性可以更改为多例。
Spring单例bean带来的好处
1.只在初始化时实例化一次,提高了效率。
2.减少了内存开销,减少了对象创建和垃圾收集的时间。
SpringMVC中的Controller有线程安全问题吗?
SpringMVC中的Controller是单例的,存在线程安全问题。在使用过程中,应避免在Controller中定义实例变量。
Spring历史版本变迁
1.Spring 1.x
2003年发布,包含了核心的IoC和AOP功能。完全是xml的配置。
2.Spring 2.X
增加了对注解的支持,支持基于注解的配置。
3.Spring 3.X
开始在GitHub上托管代码。不再提供一个大的完整的jar包,而是拆成20个小的jar包。
4.Spring 4.x
SpringBoot诞生,支持Java8。
5.Spring 5.X
2017年发布,支持Java9。
ThreadLocal和线程同步机制相比有什么优势呢?他们都是为了解决多线程中相同变量的访问冲突问题。
在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。
而ThreadLocal则从另一个角度来解决多线程的并发访问。ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。ThreadLocal<String> context = new ThreadLocal();
以上是关于Spring的主要内容,如果未能解决你的问题,请参考以下文章