答:Spring的核心是一个轻量级的容器(Container),它是实现IoC(Inversion of Control)容器和非侵入性(No intrusive)的框架,并提供AOP(Aspect-oriented Programming)的实现方式,提供对持久层(Persistence)、事务(Transcation)的支持;提供MVC Web框架的实现,并对一些常用的企业服务API提供了一致的模型封装,是一个全方位的应用程序框架,除此之外,对现存的各种框架(Structs、JSF、Hibernate、Ibatis、Webwork等),Spring也提供了与他们相整合的方案。
细化:
1、Spring的核心是一个轻量级(Lightweight)的容器(Container)。
2、Spring是实现IoC(Inversion of Control)容器和非入侵性(No intrusive)的框架。
3、Spring提供AOP(Aspect-oriented programming)的实现方式,把应用业务逻辑和系统服务分开。
4、Spring提供对持久层(Persistence)、事物(Transcation)的支持。
5、Spring供MVC Web框架的实现,并对一些常用的企业服务API(Application Interface)提供一致的模型封装。
6、Spring提供了对现存的各种框架(Structs、JSF、Hibernate、Ibatis、Webwork等)相整合的方案。
总之,Spring是一个全方位的应用程序框架。
1、IoC/DI(控制反转/依赖注入):
1)控制反转可以分开来解读,控制和反转-> 首先控制什么?对什么进行控制? 其实此处的控制指的是程序中的一些对象或者变量的控制权,在传统的程序中都是由应用程序自己控制对象创建或者变量赋值,这是一种主动式的控制,导致组件之间的完全耦合;现在将一些对象或者变量的创建控制权交给一个叫做Ioc容器的东西,由这个容器来控制应用程序中所需要的资源,这样就变成了被动的控制,对组件之间的关系进行解耦,所以所谓的反转就是将控制权由应用程序转交到Ioc容器。
2)依赖注入:同样将DI分开来解读,依赖和注入-> 首先依赖什么?谁依赖谁? 其实应用程序中所需要的资源创建和获取都是要依赖于Ioc容器,需要IoC容器装配类之间的关系,即应用程序依赖于IoC容器的装配功能;相反,注入就是IoC容器向应用程序中进行注入应用程序所需要的资源,由应用程序主动装配对象的依赖变应用程序被动接受依赖,所以IoC容器也叫DI容器。
综上所述,控制权的主动与被动式其实都是相对于(参考物)IoC容器来说的,也可以将IoC模式看做是工厂模式的升华,不同的是Spring中的控制反转所用到的是Java的“反射”技术,通过在XML文件中定义应用程序所需要的类,再由IoC容器根据xml文件创建这些对象。所以这也形成了工厂与对象生成这两者独立分隔开,目的就是提高灵活性和可维护性。之所以将对象生成放到xml文件中,如果我们想要换一个实现的类将变得很简单,只需要修改xml文件即可。
2、AOP(面向切面编程):将具体的通用的应用从业务逻辑中分离出来,各自做各自专业的事情。
1)切面:简单的理解就是一个类,每个或者对象
2)连接点:类中的方法(可以切入的地方)
3)通知:切面在某个连接点执行的操作(分为: Before advice , After returning advice , After throwing advice , After (finally) advice , Around advice );
4)切入点:符合切点表达式的连接点,也就是真正被切入的地方
5)引入:
6)目标对象:
(1)设值注入(set方法)
(2)构造器注入
(3)工厂方法
Spring加载Bean的流程:
(1)创建一个上下文context = createApplicationContext;
(2)context中都会有一个BeanFactory(默认是DefaultListableBeanFactory),在该类的子类类xmlBeanFactory中进行xml文件的解析;
(3)在类XmlBeanDefinitionParser 中用Dom解析xml文件(DefaultXmlBeanDefinitionParser),解析xml文件中所有bean,并将bean放到BeanDefinitionHolder中,封装成BeanDefinition;
(4)再进行bean的注册,具体在BeanDefinitionReaderUtils类调用DefaultListableBeanFactory类的registerBeanDefinition进行bean的注册,在这里用了一HashMap存放bean,其中用Beanname作为键值,其封装好的beanDefinition作为值。还有用一个List存放所有的bean的名字。
1) 在设置注入需要该Bean包含这些属性的setter方法;
2) 与传统的JavaBean的写法更相似,程序开发人员更容易理解、接收。通过setter方法设定依赖关系显得更加只管;
3) 对于复杂的依赖关系,如果采用构造注入,会导致构造器参数列表复杂,难以阅读。Spring在创建Bean实例时,需要同时实例化其它依赖的全部实例,因而导致性能下降。而使用设值注入,则能避免这些问题;
4) 尤其是在某些属性可选的情况况下,多参数的构造器显得更加复杂;
但是在其他的场景,构造器注入显得更好的性能:
1) 构造注入需要该Bean包含带有这些属性的构造器;
2) 构造注入可以在构造器中决定依赖关系的注入顺序,优先依赖的优先注入;
3) 对于依赖关系无需变化的Bean,构造注入更有用处。因为没有Setter方法,所有的依赖关系全部在构造器内设定。因此,无需担心后续的代码对依赖关系产生破坏
4) 依赖关系只能在构造器中设定,则只有组件的创建者才能改变组件的依赖关系。对组件的调用者而言,组件内部的依赖关系完全透明,更符合高内聚的原则.
(1)在使用设值注入时有可能还不能保证某种依赖是否已经被注入,也就是说这时对象的依赖关系有可能是不完整的。而在另一种情况下,构造器注入则不允许生成依赖关系不完整的对象。
(2)在设值注入时如果对象A和对象B互相依赖,在创建对象A时Spring会抛出sObjectCurrentlyInCreationException异常,因为在B对象被创建之前A对象是不能被创建的,反之亦然。所以Spring用设值注入的方法解决了循环依赖的问题,因对象的设值方法是在对象被创建之前被调用的。
注:采用以设值注入为主,构造注入为辅的注入策略。对于依赖关系无需变化的注入,尽量采用构造注入;而其他的依赖关系的注入,则考虑采用设值注入。
1) Bean的建立,由BeanFactory读取Bean定义文件,并创建Bean实例;
2) 执行Bean的属性注入,Setter注入;
3) 如果Bean类实现了org.springframework.beans.factory.BeanNameAware接口,则执行其setBeanName方法;
4) 如果Bean类实现了org.springframework.beans.factory.BeanFactoryAware接口,则执行其setBeanFactory方法;
5) 如果容器中有实现org.springframework.beans.factory.BeanPostProcessors接口的实例,则任何Bean在初始化之前都会执行这个实例的processBeforeInitialization()方法;
6) 如果Bean类实现了org.springframework.beans.factory.InitializingBean接口,则执行其afterPropertiesSet()方法;
7) 调用Bean的初始化方法”init-method” (!!注意,init-method方法没有参数);
8) 如果容器中有实现org.springframework.beans.factory.BeanPostProcessors接口的实例,则任何Bean在初始化之后都会执行这个实例的processAfterInitialization()方法;
9) 使用Bean做一些业务逻辑….
10) 使用完,容器关闭,如果Bean类实现了org.springframework.beans.factory.DisposableBean接口,则执行它的destroy()方法;
11) 在容器关闭时,可以在Bean定义文件中使用“destory-method”定义的方法,销毁Bean (!!注意,destory-method方法没有参数);
ApplicationContext中bean的生命周期也是类似的。
ApplicationContext的三种较常见的实现方式:
1) ClassPathXmlApplicationContext:从classpath的xml配置文件中读取上下文,并生成上下文定义,应用程序上下文从程序环境中取得。
ApplicationContext context = new ClassPathXmlApplicationContext("****.xml");
2) FileSystemXmlApplicationContext:由文件系统中的xml配置文件读取上下文。
ApplicationContext context = new FileSystemXmlApplicationContext("****.xml");
3) XmlWebApplicationContext:由Web应用的Xml文件读取上下文。
1) Singleton: 这是默认的作用域,这种范围确保不管接受多少个请求,每个容器中只有一个bean的实例,单例模式有BeanFactory自身维护;
2) Prototype: 原形范围与单例范围相反,为每一个bean请求提供一个实例;
3) Request: 在请求bean范围内会为每一个来自客户端的网络请求创建一个实例,在请求完成以后,bean会失效并被垃圾回收器回收;
4) Session: 与请求范围类似,确保每个session中有一个bean的实例,在session过期后,bean会随之失效;
5) global-session: global-session和Portlet应用相关。当你的应用部署在Portlet容器中工作时,它包含很多portlet。如果你想要声明让所有的portlet共用全局的存储变量的话,那么这全局变量需要存储在global-session中。
基于注解的自动装配,spring默认是关闭注解模式的,所以需要在配置文件中设置
@Required: 验证bean是否被正确的设置了,需要在Ioc容器中注册RequiredAnnotationBeanPostProcessor,它是Spring中的后置处理用来验证被@Required 注解的bean属性是否被正确的设置了,若没有属性被@Required注解过的话,后置处理器会抛出一个BeanInitializationException异常。
@AutoWired: 该注解用于在bean的设值方法上自动装配bean的属性,一个参数或者带有任意名称或带有多个参数的方法。(尝试用byType 自动装配)
Spring的ApplicationContext提供了支持事务和代码中添加监听器的功能。
可以创建一个bean其实现ApplicationListener接口(类型是ApplicationEvent),当一个ApplicationEvent 被发布后,bean就自动被通知,并且在方法onApplicationEvent(…)方法中处理该事件。
public class AllApplicationEventListener implements ApplicationListener < ApplicationEvent > {
@Override
public void onApplicationEvent(ApplicationEvent applicationEvent)
{
//process event
}
}
在Spring中有5中标准的事件:
1. 上下文更新事件(ContextRefreshedEvent):该事件会在ApplicationContext被初始化或者更新时发布,也可以在调用ConfigurableApplicationContext接口中的refresh()方法时被触发。
2. 上下文开始事件(ContextStartedEvent): 当容器调用ConfigurableApplicationContext的Start()方法开始/重新开始容器时被触发。
3. 上下文停止事件(ContextStoppedEvent): 当容器调用ConfigurableApplicationContext的Stop()方法停止容器时触发该事件。
4. 上下文关闭事件(ContextClosedEvent): 当ApplicationContext被关闭时触发该事件。容器被关闭时,其管理的所有单例Bean都被销毁。
5. 请求处理事件(RequestHandledEvent): 在Web应用中,当一个http请求(request)结束触发该事件。
spring也让用户可以自定义事件类型,继承ApplicationEvent。
public class CustomApplicationEvent extends ApplicationEvent {
public CustomApplicationEvent ( Object source, final String msg )
{
super(source);
System.out.println("Created a Custom event");
}
}
为了监听这个事件,还需要创建一个监听器:
public class CustomEventListener implements ApplicationListener < CustomApplicationEvent > {
@Override
public void onApplicationEvent(CustomApplicationEvent applicationEvent) {
//handle event
}
}
之后通过applicationContext接口的publishEvent()方法来发布自定义事件。
CustomApplicationEvent customEvent = new CustomApplicationEvent(applicationContext, "Test message");
applicationContext.publishEvent(customEvent);