Spring是个java企业级应用的开源开发框架。Spring主要用来开发Java应用,但是有些扩展是针对构建J2EE平台的web应用。Spring框架目标是简化Java企业级应用开发,并通过POJO为基础的编程模型促进良好的编程习惯。
1、Spring是一个开源框架,主要是为简化企业级应用开发而生。可以实现EJB可以实现的功能,Spring是一个IOC和AOP容器框架。♧控制反转(IOC):Spring容器使用了工厂模式为我们创建了所需要的对象,我们使用时不需要自己去创建,直接调用Spring为我们提供的对象即可,这就是控制反转的思想。♧依赖注入(DI):Spring使用Java Bean对象的Set方法或者带参数的构造方法为我们在创建所需对象时将其属性自动设置所需要的值的过程就是依赖注入的基本思想。♧面向切面编程(AOP):在面向对象编程(OOP)思想中,我们将事物纵向抽象成一个个的对象。而在面向切面编程中,我们将一个个对象某些类似的方面横向抽象成一个切面,对这个切面进行一些如权限验证,事物管理,记录日志等公用操作处理的过程就是面向切面编程的思想。
2、在Spring中,所有管理的都是JavaBean对象,而BeanFactory和ApplicationContext就是Spring框架的那个IOC容器,现在一般使用ApplicationContext,其不但包括了BeanFactory的作用,同时还进行了更多的扩展。
1、轻量:Spring是轻量的,基本的版本大约2MB。
2、控制反转:Spring通过控制反转实现了松散耦合,对象们给出它们的依赖,而不是创建或查找依赖的对象们。
3、面向切面的编程(AOP):Spring支持面向切面的编程,并且把应用业务逻辑和系统服务分开。
4、容器:Spring包含并管理应用中对象的生命周期和配置。
5、MVC框架:Spring的WEB框架是个精心设计的框架,是Web框架的一个很好的替代品。
6、事务管理:Spring提供一个持续的事务管理接口,可以扩展到上至本地事务下至全局事务(JTA)。
7、异常处理:Spring提供方便的API把具体技术相关的异常(比如由JDBC,Hibernate or JDO抛出的)转化为一致的unchecked异常。
截止到目前Spring框架已集成了20多个模块。这些模块主要被分如下图所示的核心容器、数据访问/集成、Web、AOP(面向切面编程)、工具、消息和测试模块。
以下是Spring框架的基本模块:
Core module
Bean module
Context module
Expression Language moduleJDBC module
ORM module
OXM module
Java Messaging Service(JMS) moduleTransaction module
Web module
Web-Servlet module
Web-Struts module
Web-Portlet module
1、单例模式
2、原型模式
3、工厂模式
4、适配器模式
5、包装模式
6、代理模式
7、观察者模式
8、策略模式
9、模板模式
Spring支持以下ORM:Hibernate
iBatis
JPA (Java Persistence API)TopLink
JDO (Java Data Objects)OJB
轻量级- Spring在代码量和透明度方面都很轻便。IOC -控制反转
AOP -面向切面编程可以将应用业务逻辑和系统服务分离,以实现高内聚。容器- Spring负责创建和管理对象(Bean)的生命周期和配置。
MVC -对web应用提供了高度可配置性,其他框架的集成也十分方便。
事务管理-提供了用于事务管理的通用抽象层。Spring的事务支持也可用于容器较少的环境。JDBC异常- Spring的JDBC抽象层提供了一个异常层次结构,简化了错误处理策略。
Spring配备构建Web应用的全功能MVC框架。Spring可以很便捷地和其他MVC框架集成,如Struts,Spring的MVC框架用控制反转把业务对象和控制逻辑清晰地隔离。它也允许以声明的方式把请求参数和业务对象绑定。
@Controller注解
是在Spring的
org.springframework.stereotype包下,org.springframework.stereotype.Controller注解类型用于指示Spring类的实例是一个控制器
使用@Controller注解的类不需要继承特定的父类或者实现特定的接口,相对之前的版本实现Controller接口变的更加简单。
而Controller接口的实现类只能处理一个单一的请求动作,而@Controller注解注解的控制器可以同时支持处理多个请求动作,使程序开发变的更加灵活。@Controller用户标记一个类,使用它标记的类就是一个Spring MVC Controller对象,即:一个控制器类。Spring使用扫描机制查找应用程序中所有基于注解的控制器类,分发处理器会扫描使用了该注解的方法,并检测该方法是否使用了@RequestMapping注解,而使用@RequestMapping注解的方法才是真正处理请求的处理器。为了保证Spring能找到控制器,我们需要完成两件事:
@RequestParam注解
下面来说
org.springframework.web.bind.annotation包下的第三个注解,即:@RequestParam注解,该注解类型用于将指定的请求参数赋值给方法中的形参。那么@RequestParam注解有什么属性呢?它有4种属性,下面将逐一介绍这四种属性:
1、name属性该属性的类型是String类型,它可以指定请求头绑定的名称;
2、value属性该属性的类型是String类型,它可以设置是name属性的别名;
3、required属性该属性的类型是boolean类型,它可以设置指定参数是否必须绑定;
4、defalutValue属性该属性的类型是String类型,它可以设置如果没有传递参数可以使用默认值。
@PathVaribale注解
下面来说
org.springframework.web.bind.annotation包下的第四个注解,即:@PathVaribale注解,该注解类型可以非常方便的获得请求url中的动态参数。@PathVaribale注解只支持一个属性value,类型String,表示绑定的名称,如果省略则默认绑定同名参数。
基于XML的配置
基于注解的配置
基于Java的配置
首先说一下Servlet的生命周期:实例化,初始init,接收请求service,销毁destroy;Spring上下文中的Bean生命周期也类似,如下:
1、实例化Bean:
对于BeanFactory容器,当客户向容器请求一个尚未初始化的bean时,或初始化bean的时候需要注入另一个尚未初始化的依赖时,容器就会调用createBean进行实例化。对于ApplicationContext容器,当容器启动结束后,通过获取BeanDefinition对象中的信息,实例化所有的bean。
2、设置对象属性(依赖注入):
实例化后的对象被封装在BeanWrapper对象中,紧接着,Spring根据BeanDefinition中的信息以及通过BeanWrapper提供的设置属性的接口完成依赖注入。
3、处理Aware接口:
接着,Spring会检测该对象是否实现了xxxAware接口,并将相关的xxxAware实例注入给Bean:如果这个Bean已经实现了BeanNameAware接口,会调用它实现的setBeanName(StringbeanId)方法,此处传递的就是Spring配置文件中Bean的id值;
如果这个Bean已经实现了BeanFactoryAware接口,会调用它实现的setBeanFactory()方法,传递的是Spring工厂自身。
如果这个Bean已经实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文;
4、BeanPostProcessor:
如果想对Bean进行一些自定义的处理,那么可以让Bean实现了BeanPostProcessor接口,那将会调用
postProcessBeforeInitialization(Object obj, String s)方法。由于这个方法是在Bean初始化结束时调用的,所以可以被应用于内存或缓存技术;
5、InitializingBean与init-method:
如果Bean在Spring配置文件中配置了init-method属性,则会自动调用其配置的初始化方法。
6、如果这个Bean实现了BeanPostProcessor接口,将会调用
postProcessAfterInitialization(Object obj, String s)方法;
以上几个步骤完成后,Bean就已经被正确创建了,之后就可以使用这个Bean了。
7、DisposableBean:
当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用其实现的destroy()方法;
8、destroy-method:
最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。
在类图关系上BeanFactory是ApplicationContext接口的父接口
BeanFactory
是spring中比较原始的Factory。如XMLBeanFactory就是一种典型的BeanFactory。原始的BeanFactory无法支持spring的许多插件,如AOP功能、Web应用等。
ApplicationContext
ApplicationContext接口是由BeanFactory接口派生而来,因而具有BeanFactory所有的功能。ApplicationContext以一种更向面向框架的方式工作以及对上下文进行分层和实现继承,ApplicationContext包还提供了以下的功能
.MessageSource,提供国际化的消息访问
.资源访问,如URL和文件
.事件传播
.载入多个(有继承关系)上下文,使得每一个上下文都专注于一个特定的层次,比如应用的web层,其实通过上面的类图大家应该也能看的出来BeanFactory和ApplicationContext的区别的。
肯定不是线程安全的,当多用户同时请求一个服务时,容器会给每一个请求分配一个线程,这是多个线程会并发执行该请求多对应的业务逻辑(成员方法),此时就要注意了,如果该处理逻辑中有对该单列状态的修改(体现为该单列的成员属性),则必须考虑线程同步问题.
Spring框架并没有对单例bean进行任何多线程的封装处理。关于单例bean的线程安全和并发问题需要开发者自行去搞定。但实际上,大部分的Spring bean并没有可变的状态(比如Serview类和DAO类),所以在某种程度上说Spring的单例bean是线程安全的。如果你的bean有多种状态的话(比如View Model对象),就需要自行保证线程安全。最浅显的解决办法就是将多态bean的作用域由“singleton”变更为“prototype”。
在一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域,因为Spring对一些Bean中非线程安全状态采用ThreadLocal进行处理,解决线程安全问题。
ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。同步机制采用了“时间换空间”的方式,仅提供一份变量,不同的线程在访问前需要获取锁,没获得锁的线程则需要排队。而ThreadLocal采用了“空间换时间”的方式。
ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。
当一个bean仅被用作另一个bean的属性时,它能被声明为一个内部bean,为了定义inner bean,在Spring的基于XML的配置元数据中,可以在或\元素内使用元素,内部bean通常是匿名的,它们的Scope一般是prototype。
改为内部bean的方式
内部bean也支持构造器注入
id或name值在bean类是没有必要以一个内部bean呈现
singleton:这种bean范围是默认的,这种范围确保不管接受到多少个请求,每个容器中只有一个bean的实例,单例的模式由bean factory自身来维护。
prototype:原形范围与单例范围相反,为每一个bean请求提供一个实例。
request:在请求bean范围内会每一个来自客户端的网络请求创建一个实例,在请求完成以后,bean会失效并被垃圾回收器回收。
Session:与请求范围类似,确保每个session中有一个bean的实例,在session过期后,bean会随之失效。global-session:global-session和Portlet应用相关。当你的应用部署在Portlet容器中工作时,它包含很多portlet。如果你想要声明让所有的portlet共用全局的存储变量的话,那么这全局变量需要存储在global-session中。
因为controller是默认单例模式,高并发下全局变量会出现线程安全问题现这种问题如何解决呢?
1、既然是全局变量惹的祸,那就将全局变量都编程局部变量,通过方法参数来传递。
2、jdk提供了java.lang.ThreadLocal,它为多线程并发提供了新思路。
3、使用@Scope("session"),会话级别
4、将控制器的作用域从单例改为原型,即在spring配置文件Controller中声明scope="prototype",每次都创建新的controller
Spring提供以下几种集合的配置元素:
控制器提供一个访问应用程序的行为,此行为通常通过服务接口实现。控制器解析用户输入并将其转换为一个由视图呈现给用户的模型。Spring用一个非常抽象的方式实现了一个控制层,允许用户创建多种用途的控制器。
Spring DAO使得JDBC,Hibernate或JDO这样的数据访问技术更容易以一种统一的方式工作。这使得用户容易在持久性技术之间切换。它还允许您在编写代码时,无需考虑捕获每种技术不同的异常。
JdbcTemplate
SimpleJdbcTemplate
NamedParameterJdbcTemplate
SimpleJdbcInsert
SimpleJdbcCall
Spring提供了以下5种标准的事件:
1、上下文更新事件(ContextRefreshedEvent):在调用
ConfigurableApplicationContext接口中的refresh()方法时被触发。
2、上下文开始事件(ContextStartedEvent):当容器调用
ConfigurableApplicationContext的Start()方法开始/重新开始容器时触发该事件。
3、上下文停止事件(ContextStoppedEvent):当容器调用
ConfigurableApplicationContext的Stop()方法停止容器时触发该事件。
4、上下文关闭事件(ContextClosedEvent):当ApplicationContext被关闭时触发该事件。容器被关闭时,其管理的所有单例Bean都被销毁。
5、请求处理事件(RequestHandledEvent):在Web应用中,当一个http请求(request)结束触发该事件。
如果一个bean实现了ApplicationListener接口,当一个ApplicationEvent被发布以后,bean会自动被通知。
no:这是Spring框架的默认设置,在该设置下自动装配是关闭的,开发者需要自行在bean定义中用标签明确的设置依赖关系。
byName:该选项可以根据bean名称设置依赖关系。当向一个bean中自动装配一个属性时,容器将根据bean的名称自动在在配置文件中查询一个匹配的bean。如果找到的话,就装配这个属性,如果没找到的话就报错。
byType:该选项可以根据bean类型设置依赖关系。当向一个bean中自动装配一个属性时,容器将根据bean的类型自动在在配置文件中查询一个匹配的bean。如果找到的话,就装配这个属性,如果没找到的话就报错。
constructor:构造器的自动装配和byType模式类似,但是仅仅适用于与有构造器相同参数的bean,如果在容器中没有找到与构造器参数类型一致的bean,那么将会抛出异常。
autodetect:该模式自动探测使用构造器自动装配或者byType自动装配。首先,首先会尝试找合适的带参数的构造器,如果找到的话就是用构造器自动装配,如果在bean内部没有找到相应的构造器或者是无参构造器,容器就会自动选择byTpe的自动装配方式。
1、用户发送出请求到前端控制器DispatcherServlet。
2、DispatcherServlet收到请求调用HandlerMapping(处理器映射器)。
3、HandlerMapping找到具体的处理器(可查找xml配置或注解配置),生成处理器对象及处理器拦截器(如果有),再一起返回给DispatcherServlet。
4、DispatcherServlet调用HandlerAdapter(处理器适配器)。
5、HandlerAdapter经过适配调用具体的处理器(Handler/Controller)。
6、Controller执行完成返回ModelAndView对象。
7、HandlerAdapter将Controller执行结果ModelAndView返回给DispatcherServlet。
8、DispatcherServlet将ModelAndView传给ViewReslover(视图解析器)。
9、ViewReslover解析后返回具体View(视图)。
10、DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。
11、DispatcherServlet响应用户。
WebApplicationContext继承了ApplicationContext并增加了一些WEB应用必备的特有功能,它不同于一般的ApplicationContext,因为它能处理主题,并找到被关联的servlet。
@Controller -用于Spring MVC项目中的控制器类。@Service -用于服务类。
@RequestMapping -用于在控制器处理程序方法中配置URI映射。
@ResponseBody -用于发送Object作为响应,通常用于发送XML或JSON数据作为响应。@PathVariable -用于将动态值从URI映射到处理程序方法参数。
@Autowired -用于在spring bean中自动装配依赖项。
@Qualifier -使用@Autowired注解,以避免在存在多个bean类型实例时出现混淆。@Scope -用于配置spring bean的范围。
@Configuration,@ComponentScan和@Bean -用于基于java的配置。@Aspect,@Before,@After,@Around,@Pointcut -用于切面编程(AOP)
Spring的MVC框架是围绕DispatcherServlet来设计的,它用来处理所有的HTTP请求和响应。
@Component:这将java类标记为bean。它是任何Spring管理组件的通用构造型。spring的组件扫描机制现在可以将其拾取并将其拉入应用程序环境中。
@Controller:这将一个类标记为Spring Web MVC控制器。标有它的Bean会自动导入到IoC容器中。
@Service:此注解是组件注解的特化。它不会对@Component注解提供任何其他行为。您可以在服务层类中使用@Service而不是@Component,因为它以更好的方式指定了意图。@Repository:这个注解是具有类似用途和功能的@Component注解的特化。它为DAO提供了额外的好处。它将DAO导入IoC容器,并使未经检查的异常有资格转换为Spring DataAccessException。
通过类型来实现自动注入bean。和@Qualifier注解配合使用可以实现根据name注入bean。
和@Autowired一块使用,在同一类型的bean有多个的情况下可以实现根据name注入的需求
默认是根据name注入bean的,可以通过设置类型来实现通过类型来注入
该注解表明该类扮演控制器的角色,Spring不需要你继承任何其他控制器基类或引用Servlet API。
@RequestMapping注解用于将特定HTTP请求方法映射到将处理相应请求的控制器中的特定类/方法。此注解可应用于两个级别:
类级别:映射请求的URL
方法级别:映射URL以及HTTP请求方法
@Required应用于bean属性setter方法。此注解仅指示必须在配置时使用bean定义中的显式属性值或使用自动装配填充受影响的bean属性。如果尚未填充受影响的bean属性,则容器将抛出
BeanInitializationException。
首先最核心的一点他们的拦截侧重点是不同的,SpringMVC中的拦截器是依赖JDK的反射实现的,SpringMVC的拦截器主要是进行拦截请求,通过对Handler进行处理的时候进行拦截,先声明的拦截器中的preHandle方法会先执行,然而它的postHandle方法(他是介于处理完业务之后和返回结果之前)和afterCompletion方法却会后执行。并且Spring的拦截器是按照配置的先后顺序进行拦截的。
而Servlet的filter是基于函数回调实现的过滤器,Filter主要是针对URL地址做一个编码的事情、过滤掉没用的参数、安全校验(比较泛的,比如登录不登录之类)
初始化环境—>加载配置文件—>实例化Bean—>调用Bean显示信息
首先从大的几个核心步骤来去说明,因为Spring中的具体加载过程和用到的类实在是太多了。
1、首先是先从AbstractBeanFactory中去调用doGetBean(name, requiredType, final Object[] args,boolean typeCheckOnly【这个是判断进行创建bean还是仅仅用来做类型检查】)方法,然后第一步要做的就是先去对传入的参数name进行做转换,因为有可能传进来的name=“&XXX”之类,需要去除&符号
2、然后接着是去调用getSingleton()方法,其实在上一个面试题中已经提到了这个方法,这个方法就是利用“三级缓存”来去避免循环依赖问题的出现的。【这里补充一下,只有在是单例的情况下才会去解决循环依赖问题】
3、对从缓存中拿到的bean其实是最原始的bean,还未长大,所以这里还需要调用getObjectForBeanInstance(Object beanInstance, String name, String beanName,RootBeanDefinition mbd)方法去进行实例化。
4、然后会解决单例情况下尝试去解决循环依赖,如果
isPrototypeCurrentlyInCreation(beanName)返回为true的话,会继续下一步,否则throw new BeanCurrentlyInCreationException(beanName);
5、因为第三步中缓存中如果没有数据的话,就直接去parentBeanFactory中去获取bean,然后判断containsBeanDefinition(beanName)中去检查已加载的XML文件中是否包含有这样的bean存在,不存在的话递归去getBean()获取,如果没有继续下一步
6、这一步是吧存储在XML配置文件中的GernericBeanDifinition转换为RootBeanDifinition对象。这里主要进行一个转换,如果父类的bean不为空的话,会一并合并父类的属性
7、这一步核心就是需要跟这个Bean有关的所有依赖的bean都要被加载进来,通过刚刚的那个RootBeanDifinition对象去拿到所有的beanName,然后通过registerDependentBean(dependsOnBean, beanName)注册bean的依赖
8、然后这一步就是会根据我们在定义bean的作用域的时候定义的作用域是什么,然后进行判断在进行不同的策略进行创建(比如isSingleton、isPrototype)
9、这个是最后一步的类型装换,会去检查根据需要的类型是否符合bean的实际类型去做一个类型转换。Spring中提供了许多的类型转换器
1、IOC就是控制反转,是指创建对象的控制权的转移,以前创建对象的主动权和时机是由自己把控的,而现在这种权力转移到Spring容器中,并由容器根据配置文件去创建实例和管理各个实例之间的依赖关系,对象与对象之间松散耦合,也利于功能的复用。DI依赖注入,和控制反转是同一个概念的不同角度的描述,即应用程序在运行时依赖IoC容器来动态注入对象需要的外部资源。
2、最直观的表达就是,IOC让对象的创建不用去new了,可以由spring自动生产,使用java的反射机制,根据配置文件在运行时动态的去创建对象以及管理对象,并调用对象的方法的。
3、Spring的IOC有三种注入方式:构造器注入、setter方法注入、根据注解注入。
IoC让相互协作的组件保持松散的耦合,而AOP编程允许你把遍布于应用各层的功能分离出来形成可重用的功能组件。
IoC的一些好处是:
它将最小化应用程序中的代码量。
它将使您的应用程序易于测试,因为它不需要单元测试用例中的任何单例或JNDI查找机制。它以最小的影响和最少的侵入机制促进松耦合。
它支持即时的实例化和延迟加载服务。
1、前置通知(Before advice):在某连接点(join point)之前执行的通知,但这个通知不能阻止连接点前的执行(除非它抛出一个异常)。
2、返回后通知(After returning advice):在某连接点(join point)正常完成后执行的通知:例如,一个方法没有抛出任何异常,正常返回。
3、抛出异常后通知(After throwing advice):在方法抛出异常退出时执行的通知。
4、后通知(After (finally) advice):当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。
5、环绕通知(Around Advice):包围一个连接点(join point)的通知,如方法调用。这是最强大的一种通知类型。环绕通知可以在方法调用前后完成自定义的行为。它也会选择是否继续执行连接点或直接返回它们自己的返回值或抛出异常来结束执行。环绕通知是最常用的一种通知类型。大部分基于拦截的AOP框架,例如Nanning和JBoss4,都只提供环绕通知。
同一个aspect,不同advice的执行顺序:
①没有异常情况下的执行顺序:
around before advice
before advicetarget method执行around after adviceafter adviceafterReturning
②有异常情况下的执行顺序:
around before advicebefore advicetarget method执行around after adviceafter advice
afterThrowing:异常发生
java.lang.RuntimeException:异常发生
解释一下Spring AOP里面的几个名词:
1、切面(Aspect):被抽取的公共模块,可能会横切多个对象。在Spring AOP中,切面可以使用通用类(基于模式的风格)或者在普通类中以@AspectJ注解来实现。
2、连接点(Join point):指方法,在Spring AOP中,一个连接点总是代表一个方法的执行。
3、通知(Advice):在切面的某个特定的连接点(Join point)上执行的动作。通知有各种类型,其中包括“around”、“before”和“after”等通知。许多AOP框架,包括Spring,都是以拦截器做通知模型,并维护一个以连接点为中心的拦截器链。
4、切入点(Pointcut):切入点是指我们要对哪些Join point进行拦截的定义。通过切入点表达式,指定拦截的方法,比如指定拦截add
search。
5、引入(Introduction):(也被称为内部类型声明(inter-type declaration))。声明额外的方法或者某个类型的字段。Spring允许引入新的接口(以及一个对应的实现)到任何被代理的对象。例如,你可以使用一个引入来使bean实现IsModified接口,以便简化缓存机制。
6、目标对象(Target Object):被一个或者多个切面(aspect)所通知(advise)的对象。也有人把它叫做被通知(adviced)对象。既然Spring AOP是通过运行时代理实现的,这个对象永远是一个被代理(proxied)对象。
7、织入(Weaving):指把增强应用到目标对象来创建新的代理对象的过程。Spring是在运行时完成织入。
切入点(pointcut)和连接点(join point)匹配的概念是AOP的关键,这使得AOP不同于其它仅仅提供拦截功能的旧技术。切入点使得定位通知(advice)可独立于OO层次。例如,一个提供声明式事务管理的around通知可以被应用到一组横跨多个对象中的方法上(例如服务层的所有业务操作)。
AOP(
Aspect-OrientedProgramming,面向方面编程):是OOP的补充和完善。OOP引入了封装、继承、多态性等建立一种对象层次结构(从上到下的关系)。当需要为分散的对象引入公共行为的时候(从左到右的关系),OOP就显得无能为力。例如:日志功能。日志代码往往水平的散步所有对象层次中,与对象的核心功能毫无关系。这种代码被称为横切(cross-cutting)代码还有像安全性、异常处理、透明的持续性等都称为横切代码。在OOP设计中,它们导致了大量代码的重复,不利于模块的重用。
AOP与OOP相反,利用“横切”技术将影响多个类的公共行为封装到一个可重用模块,称为Aspect。简单点,就是将那些与业务无关,却被业务模块所共同调用的逻辑封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。AOP的核心思想就是“将应用程序中的商业逻辑同对其提供支持的通用服务进行分离。” Spring提供了两种方式生成代理对象:JDKProxy和Cglib具体使用哪种方式生成由AopProxyFactory根据AdvisedSupport对象的配置来决定。默认的策略是如果目标类是接口,则使用JDK动态代理技术,否则使用Cglib来生成代理。
关注点是应用中一个模块的行为,一个关注点可能会被定义成一个我们想实现的一个功能。
横切关注点是一个关注点,此关注点是整个应用都会使用的功能,并影响整个应用,比如日志,安全和数据传输,几乎应用的每个模块都需要的功能。因此这些都属于横切关注点。
Spring中AOP底层的实现其实是基于JDK的动态代理和cglib动态创建类进行动态代理来实现的:
1、第一种基于JDK的动态代理的原理是:
需要用到的几个关键成员InvocationHandler(你想要通过动态代理生成的对象都必须实现这个接口)真实的需要代理的对象(帮你代理的对象)Proxy对象(是JDK中java.lang.reflect包下的)
下面是具体如何动态利用这三个组件生成代理对象
首先你的真是要代理的对象必须要实现InvocationHandler这个接口,并且覆盖这个接口的invoke(Object proxyObject, Method method, Object[] args)方法,这个Invoker中方法的参数的proxyObject就是你要代理的真实目标对象,方法调用会被转发到该类的invoke()方法,method是真实对象中调用方法的Method类,Object[] args是真实对象中调用方法的参数
然后通过Proxy类去调用newProxyInstance(classLoader, interfaces, handler)方法,classLoader是指真实代理对象的类加载器,interfaces是指真实代理对象需要实现的接口,还可以同时指定多个接口,handler方法调用的实际处理者(其实就是帮你代理的那个对象),代理对象的方法调用都会转发到这里,然后直接就能生成你想要的对象类了。
Spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是无法提供事务功能的。真正的数据库层的事务提交和回滚是通过binlog或者redo log实现的。
1、Spring事务的种类:
spring支持编程式事务管理和声明式事务管理两种方式:微信搜索公众号:Java专栏,获取最新面试手册
①编程式事务管理使用TransactionTemplate。
②声明式事务管理建立在AOP之上的。其本质是通过AOP功能,对方法前后进行拦截,将事务处理的功能编织到拦截的方法中,也就是在目标方法开始之前加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。
声明式事务最大的优点就是不需要在业务逻辑代码中掺杂事务管理的代码,只需在配置文件中做相关的事务规则声明或通过@Transactional注解的方式,便可以将事务规则应用到业务逻辑中。
声明式事务管理要优于编程式事务管理,这正是spring倡导的非侵入式的开发方式,使业务代码不受污染,只要加上注解就可以获得完全的事务支持。唯一不足地方是,最细粒度只能作用到方法级别,无法做到像编程式事务那样可以作用到代码块级别。
2、spring的事务传播行为:
spring事务的传播行为说的是,当多个事务同时存在的时候,spring如何处理这些事务的行为。
①PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。
②PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。‘
③PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。
④PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务。
⑤PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
⑥PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
⑦PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则按REQUIRED属性执行。
3、Spring中的隔离级别:
①ISOLATION_DEFAULT:这是个
PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别。
②
ISOLATION_READ_UNCOMMITTED:读未提交,允许另外一个事务可以看到这个事务未提交的数据。
③ISOLATION_READ_COMMITTED:读已提交,保证一个事务修改的数据提交后才能被另一事务读取,而且能看到该事务对已有记录的更新。
④ISOLATION_REPEATABLE_READ:可重复读,保证一个事务修改的数据提交后才能被另一事务读取,但是不能看到该事务对已有记录的更新。
⑤ISOLATION_SERIALIZABLE:一个事务在执行的过程中完全看不到其他事务对数据库所做的更新。
1、PROPAGATION_REQUIRED,那么由于执行ServiceA.methodA的时候,ServiceA.methodA已经起了事务,这时调用ServiceB.methodB,ServiceB.methodB看到自己已经运行在ServiceA.methodA的事务内部,就不再起新的事务。而假如ServiceA.methodA运行的时候发现自己没有在事务中,他就会为自己分配一个事务。这样,在ServiceA.methodA或者在ServiceB.methodB内的任何地方出现异常,事务都会被回滚。即使ServiceB.methodB的事务已经被提交,但是ServiceA.methodA在接下来fail要回滚,ServiceB.methodB也要回滚。
2、PROPAGATION_SUPPORTS ——支持当前事务,如果当前没有事务,就以非事务方式执行。
3、PROPAGATION_MANDATORY ——支持当前事务,如果当前没有事务,就抛出异常。
4、PROPAGATION_REQUIRES_NEW ——支持当前事务,如果当前没有事务,就将当前事务挂起。如ServiceA.methodA的事务级别为PROPAGATION_REQUIRED,ServiceB.methodB的事务级别为PROPAGATION_REQUIRES_NEW,那么当执行到ServiceB.methodB的时候,ServiceA.methodA所在的事务就会挂起,ServiceB.methodB会起一个新的事务,等待ServiceB.methodB的事务完成以后,A才继续执行。他与PROPAGATION_REQUIRED的事务区别在于事务的回滚程度了。因为ServiceB.methodB是新起一个事务,那么就是存在两个不同的事务。如果ServiceB.methodB已经提交,那么ServiceA.methodA失败回滚,ServiceB.methodB是不会回滚的。如果ServiceB.methodB失败回滚,如果他抛出的异常被ServiceA.methodA捕获,ServiceA.methodA事务仍然可能提交。
5、PROPAGATION_NOT_SUPPORTED ——以非事务方式执行当前操作,如果当前存在事务,就把事务挂起来。/
6、PROPAGATION_NEVER ——以非事务方式执行,如果当前存在事务,则抛异常。
7、PROPAGATION_NESTED——如果当前存在事务,则在嵌套事务内执行,关键是savepoint。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。与PROPAGATION_REQUIRES_NEW的区别是NESTED的事务和他的父事务是相依的,它的提交是要等父事务一块提交。也就是说,如果父事务最后回滚,它也要回滚。
Spring事务管理主要包括3个接口,Spring事务主要由以下三个共同完成的:
1、
PlatformTransactionManager:事务管理器,主要用于平台相关事务的管理。主要包括三个方法:①、commit:事务提交。②、rollback:事务回滚。③、getTransaction:获取事务状态。
2、TransacitonDefinition:事务定义信息,用来定义事务相关属性,给事务管理器
PlatformTransactionManager使用这个接口有下面四个主要方法:①、getIsolationLevel:获取隔离级别。②、getPropagationBehavior:获取传播行为。③、getTimeout获取超时时间。④、isReadOnly:是否只读(保存、更新、删除时属性变为false--可读写,查询时为true--只读)事务管理器能够根据这个返回值进行优化,这些事务的配置信息,都可以通过配置文件进行配置。
3、TransationStatus:事务具体运行状态,事务管理过程中,每个时间点事务的状态信息。例如:
①、hasSavepoint():返回这个事务内部是否包含一个保存点。②、isCompleted():返回该事务是否已完成,也就是说,是否已经提交或回滚。③、isNewTransaction():判断当前事务是否是一个新事务。
它为不同的事务API如JTA,JDBC,Hibernate,JPA和JDO,提供一个不变的编程模式。它为编程式事务管理提供了一套简单的API而不是一些复杂的事务API如它为编程式事务管理提供了一套简单的API而不是一些复杂的事务API如它支持声明式事务管理。它支持声明式事务管理。它和Spring各种数据访问抽象层很好得集成。它和Spring各种数据访问抽象层很好得集成。
1.配置文件中开启扫描
2.加载容器
如果是web项目的话,在web.xml文件添加如下配置:
如果是java项目可以通过BeanFactory或ApplicationContext来直接加载Spring容器
配置事务的方法有两种:
1、基于XML的事务配置。
2、基于注解方式的事务配置。
@Transactional:直接在Java源代码中声明事务的做法让事务声明和将受其影响的代码距离更近了,而且一般来说不会有不恰当的耦合的风险,因为,使用事务性的代码几乎总是被部署在事务环境中。
主要在类中定义事务注解@Transactional,如下:
通过这张图能大致看懂spring的生命周期,详解:
首先会先进行实例化bean对象然后是进行对bean的一个属性进行设置接着是对BeanNameAware(其实就是为了让Spring容器来获取bean的名称)、BeanFactoryAware(让bean的BeanFactory调用容器的服务)、ApplicationContextAware(让bean当前的applicationContext可以来取调用Spring容器的服务)
然后是实现BeanPostProcessor这个接口中的两个方法,主要是对调用接口的前置初始化
postProcessBeforeInitialization这里是主要是对xml中自己定义的初始化方法init-method = “xxxx”进行调用然后是继续对BeanPostProcessor这个接口中的后置初始化方法进行一个调用postProcessAfterInitialization()其实到这一步,基本上这个bean的初始化基本已经完成,就处于就绪状态然后就是当Spring容器中如果使用完毕的话,就会调用destory()方法最后会去执行我们自己定义的销毁方法来进行销毁,然后结束生命周期。
1、如果你使用BeanFactory作为Spring Bean的工厂类,则所有的bean都是在第一次使用该Bean的时候实例化
2、如果你使用ApplicationContext作为Spring Bean的工厂类,则又分为以下几种情况:如果bean的scope是singleton的,并且lazy-init为false(默认是false,所以可以不用设置),则ApplicationContext启动的时候就实例化该Bean,并且将实例化的Bean放在一个map结构的缓存中,下次再使用该Bean的时候,直接从这个缓存中取如果bean的scope是singleton的,并且lazy-init为true,则该Bean的实例化是在第一次使用该Bean的时候进行实例化。如果bean的scope是prototype的,则该Bean的实例化是在第一次使用该Bean的时候进行实例化。
BeanFactory和ApplicationContext是Spring的两大核心接口,都可以当做Spring的容器。其中ApplicationContext是BeanFactory的子接口。
1、BeanFactory:是Spring里面最底层的接口,包含了各种Bean的定义,读取bean配置文档,管理bean的加载、实例化,控制bean的生命周期,维护bean之间的依赖关系。ApplicationContext接口作为BeanFactory的派生,除了提供BeanFactory所具有的功能外,还提供了更完整的框架功能:
继承MessageSource,因此支持国际化。统一的资源文件访问方式。
提供在监听器中注册bean的事件。同时加载多个配置文件。
载入多个(有继承关系)上下文,使得每一个上下文都专注于一个特定的层次,比如应用的web层。
2、
BeanFactroy采用的是延迟加载形式来注入Bean的,即只有在使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化。这样,我们就不能发现一些存在的Spring的配置问题。如果Bean的某一个属性没有注入,BeanFacotry加载后,直至第一次使用调用getBean方法才会抛出异常。ApplicationContext,它是在容器启动时,一次性创建了所有的Bean。这样,在容器启动时,我们就可以发现Spring中存在的配置错误,这样有利于检查所依赖属性是否注入。ApplicationContext启动后预载入所有的单实例Bean,通过预载入单实例bean ,确保当你需要的时候,你就不用等待,因为它们已经创建好了。
相对于基本的BeanFactory,ApplicationContext唯一的不足是占用内存空间。当应用程序配置Bean较多时,程序启动较慢。
BeanFactory通常以编程的方式被创建,ApplicationContext还能以声明的方式创建,如使用ContextLoader。
BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册。
Bean工厂是工厂模式的一个实现,提供了控制反转功能,用来把应用的配置和依赖从正真的应用代码中分离。
最常用的BeanFactory实现是XmlBeanFactory类。
最常用的就是
org.springframework.beans.factory.xml.XmlBeanFactory,它根据XML文件中的定义加载beans。该容器从XML文件读取配置元数据并用它去创建一个完全配置的系统或应用。
singleton作用域
在默认情况下,spring的ApplicationContext容器在启动时,自动实例化所有singleton的Bean并缓存于容器中.虽然启动时会花费一些时间,但带来两个好处:首先对Bean提前的实例化操作会及早发现一些潜在的配置问题.其次Bean以缓存的方式保存,当运行时使用到该Bean时就无须再实例化了,加快了运行效率.如果用户不希望在容器启动时提前实例化singleton的Bean,可以通过lazy-init属性进行控制.
prototype
在默认情况下,spring容器在启动时不实例化prototype的Bean.此外,spring容器将prototype的Bean交给调用者后,就不再管理它的生命周期.
request作用域
每次HTTP请求都会创建一个新的Bean,HTTP请求处理完毕后,销毁这个Bean.该作用域仅适用于webApplicationContext环境.
session作用域
同一个HTTP session共享一个Bean,不同HTTP session使用不同的Bean,当HTTP Session结束后,实例才被销毁.该作用域仅适用于webApplicationContext环境
globalSession作用域
同一个全局session共享一个Bean,一般用于portlet应用环境,该作用域仅适用于webApplicationContext环境.