Spring是一个开源框架,它由Rod Johnson创建。它是为了解决企业应用开发的复杂性而创建的。Spring使用基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。 Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。
Spring 有可能成为所有企业应用程序的一站式服务点,然而,Spring 是模块化的,允许你挑选和选择适用于你的模块,不必要把剩余部分也引入。下面的部分对在 Spring 框架中所有可用的模块给出了详细的介绍。
Spring 框架提供约 20 个模块,可以根据应用程序的要求来使用。
其中有七大主要的模块
核心容器由spring-core,spring-beans,spring-context,spring-context-support和spring-expression(SpEL,Spring表达式语言,Spring Expression Language)等模块组成,它们的细节如下:
spring-core模块提供了框架的基本组成部分,包括 IoC 和依赖注入功能。
spring-beans 模块提供 BeanFactory,工厂模式的微妙实现,它移除了编码式单例的需要,并且可以把配置和依赖从实际编码逻辑中解耦。
context模块建立在由core和 beans 模块的基础上建立起来的,它以一种类似于JNDI注册的方式访问对象。Context模块继承自Bean模块,并且添加了国际化(比如,使用资源束)、事件传播、资源加载和透明地创建上下文(比如,通过Servelet容器)等功能。Context模块也支持Java EE的功能,比如EJB、JMX和远程调用等。ApplicationContext接口是Context模块的焦点。spring-context-support提供了对第三方库集成到Spring上下文的支持,比如缓存(EhCache, Guava, JCache)、邮件(JavaMail)、调度(CommonJ, Quartz)、模板引擎(FreeMarker, JasperReports, Velocity)等。
spring-expression模块提供了强大的表达式语言,用于在运行时查询和操作对象图。它是JSP2.1规范中定义的统一表达式语言的扩展,支持set和get属性值、属性赋值、方法调用、访问数组集合及索引的内容、逻辑算术运算、命名变量、通过名字从Spring IoC容器检索对象,还支持列表的投影、选择以及聚合等。
它们的完整依赖关系如下图所示:
数据访问/集成层包括 JDBC,DAO,ORM,OXM,JMS 和事务处理模块,它们的细节如下:
(注:JDBC=Java Data Base Connectivity,ORM=Object Relational Mapping,OXM=Object XML Mapping,JMS=Java Message Service)
JDBC 模块提供了JDBC抽象层,它消除了冗长的JDBC编码和对数据库供应商特定错误代码的解析。
DAO 模块提供了对JDBC、Hibernate、Mybatis等DAO层支持。
ORM 模块提供了对流行的对象关系映射API的集成,包括JPA、JDO和Hibernate等。通过此模块可以让这些ORM框架和spring的其它功能整合,比如前面提及的事务管理。
OXM 模块提供了对OXM实现的支持,比如JAXB、Castor、XML Beans、JiBX、XStream等。
JMS 模块包含生产(produce)和消费(consume)消息的功能。从Spring 4.1开始,集成了spring-messaging模块。。
事务模块为实现特殊接口类及所有的 POJO 支持编程式和声明式事务管理。(注:编程式事务需要自己写beginTransaction()、commit()、rollback()等事务管理方法,声明式事务是通过注解或配置由spring自动处理,编程式事务粒度更细)
Web 层由 Web,Web-MVC,Web-Socket 和 Web-Portlet 组成,它们的细节如下:
Web 模块提供面向web的基本功能和面向web的应用上下文,比如多部分(multipart)文件上传功能、使用Servlet监听器初始化IoC容器等。它还包括HTTP客户端以及Spring远程调用中与web相关的部分。。
Web-MVC 模块为web应用提供了模型视图控制(MVC)和REST Web服务的实现。Spring的MVC框架可以使领域模型代码和web表单完全地分离,且可以与Spring框架的其它所有功能进行集成。
Web-Socket 模块为 WebSocket-based 提供了支持,而且在 web 应用程序中提供了客户端和服务器端之间通信的两种方式。
Web-Portlet 模块提供了用于Portlet环境的MVC实现,并反映了spring-webmvc模块的功能。
还有其他一些重要的模块,像 AOP,Aspects,Instrumentation,Web 和测试模块,它们的细节如下:
AOP 模块提供了面向方面的编程实现,允许你定义方法拦截器和切入点对代码进行干净地解耦,从而使实现功能的代码彻底的解耦出来。使用源码级的元数据,可以用类似于.Net属性的方式合并行为信息到代码中。
Aspects 模块提供了与 AspectJ 的集成,这是一个功能强大且成熟的面向切面编程(AOP)框架。
Instrumentation 模块在一定的应用服务器中提供了类 instrumentation 的支持和类加载器的实现。
Messaging 模块为 STOMP 提供了支持作为在应用程序中 WebSocket 子协议的使用。它也支持一个注解编程模型,它是为了选路和处理来自 WebSocket 客户端的 STOMP 信息。
测试模块支持对具有 JUnit 或 TestNG 框架的 Spring 组件的测试。
Spring 容器是 Spring 框架的核心。容器将创建对象,把它们连接在一起,配置它们,并管理他们的整个生命周期从创建到销毁。Spring 容器使用依赖注入(DI)来管理组成一个应用程序的组件。 IOC 容器具有依赖注入功能的容器,它可以创建对象,IOC 容器负责实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。通常new一个实例,控制权由程序员控制,而"控制反转"是指new实例工作不由程序员来做而是交给Spring容器来做。在Spring中BeanFactory是IOC容器的实际代表者。
Spring 提供了以下两种不同类型的容器。
序号 | 容器 & 描述 |
---|---|
1 | Spring BeanFactory 容器,它是最简单的容器,给 DI 提供了基本的支持,它用 org.springframework.beans.factory.BeanFactory 接口来定义。BeanFactory 或者相关的接口,如 BeanFactoryAware,InitializingBean,DisposableBean,在 Spring 中仍然存在具有大量的与 Spring 整合的第三方框架的反向兼容性的目的。 |
2 | Spring ApplicationContext 容器,该容器添加了更多的企业特定的功能,例如从一个属性文件中解析文本信息的能力,发布应用程序事件给感兴趣的事件监听器的能力。该容器是由 org.springframework.context.ApplicationContext 接口定义。 |
ApplicationContext 容器包括 BeanFactory 容器的所有功能,所以通常建议超过 BeanFactory。BeanFactory 仍然可以用于轻量级的应用程序,如移动设备或基于 applet 的应用程序,其中它的数据量和速度是显著。
FileSystemXmlApplicationContext:该容器从 XML 文件中加载已被定义的 bean。在这里,你需要提供给构造器 XML 文件的完整路径。
ClassPathXmlApplicationContext:该容器从 XML 文件中加载已被定义的 bean。在这里,你不需要提供 XML 文件的完整路径,只需正确配置 CLASSPATH 环境变量即可,因为,容器会从 CLASSPATH 中搜索 bean 配置文件。
WebXmlApplicationContext:该容器会在一个 web 应用程序的范围内加载在 XML 文件中已被定义的 bean。
Spring容器启动配置(web.xml文件)
<context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/applicationContext.xml</param-value> </context-param> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> 复制代码
适用场景:
<beans> <import resource=“resource1.xml” />//导入其他配置文件Bean的定义 <import resource=“resource2.xml” /> <bean id="userService" class="cn.lovepi.***.UserService" init-method="init" destory-method="destory"> </bean> <bean id="message" class="java.lang.String"> <constructor-arg index="0" value="test"></constructor-arg> </bean> </beans> 复制代码
适用场景:
1、在applicationContext.xml配置扫描包路径
<context:component-scan base-package="com.lovepi.spring"> <context:include-filter type="regex" expression="com.lovepi.spring.*"/> //包含的目标类 <context:exclude-filter type="aspectj" expression="cn.lovepi..*Controller+"/> //排除的目标类 </context:component-scan> 复制代码
Spring提供了四个注解,这些注解的作用与上面的XML定义bean效果一致,在于将组件交给Spring容器管理。组件的名称默认是类名(首字母变小写),可以自己修改:
适用场景:
步骤如下:
当在 Spring 中定义一个 bean 时,你必须声明该 bean 的作用域的选项。例如,为了强制 Spring 在每次需要时都产生一个新的 bean 实例,你应该声明 bean 的作用域的属性为 prototype。同理,如果你想让 Spring 在每次需要时都返回同一个bean实例,你应该声明 bean 的作用域的属性为 singleton。
Spring 框架支持以下五个作用域,分别为singleton、prototype、request、session和global session,5种作用域说明如下所示
作用域 | 描述 |
---|---|
singleton | 在spring IoC容器仅存在一个Bean实例,Bean以单例方式存在,默认值 |
prototype | 每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行newXxxBean() |
request | 每次HTTP请求都会创建一个新的Bean,该作用域仅适用于WebApplicationContext环境 |
session | 同一个HTTP Session共享一个Bean,不同Session使用不同的Bean,仅适用于WebApplicationContext环境 |
global-session | 一般用于Portlet应用环境,该运用域仅适用于WebApplicationContext环境 |
理解 Spring bean 的生命周期很容易。当一个 bean 被实例化时,它可能需要执行一些初始化使它转换成可用状态。同样,当 bean 不再需要,并且从容器中移除时,可能需要做一些清除工作。
为了定义安装和拆卸一个 bean,我们只要声明带有 init-method 和/或 destroy-method 参数的 。init-method 属性指定一个方法,在实例化 bean 时,立即调用该方法。同样,destroy-method 指定一个方法,只有从容器中移除 bean 之后,才能调用该方法。
使用了Spring框架之后,对象的实例不再由调用者来创建,而是由Spring容器来创建,Spring容器会负责控制程序之间的关系。这样控制权便由应用代码转移到Spring容器,控制权发生了反转,这就是Spring的控制反转
从Spring容器来看,Spring容器负责将被依赖对象赋值给调用者的成员变量,这就相当于为调用者注入了它依赖的实例,这就是Spring的依赖注入
这是最简单的注入方式,假设有一个TestController,类中需要实例化一个TestService对象,那么就可以定义一个private的TestService成员变量,然后创建TestService的set方法(这是ioc的注入入口):
public class TestController { private TestService testService; //注入testService public void setTestService(TestService testService) { this.testService = testService; } public void test() { testService.test(); } } 复制代码
public class TestService { public void test(){ System.out.println("测试方法"); } } 复制代码
spring的xml文件:
<bean id="testController111" class="com.test.day.TestController"> <property name="testService" ref="testService"></property> </bean> <bean id="testService" class="com.test.day.TestService"> </bean> 复制代码
这种方式的注入是指带有参数的构造函数注入,看下面的例子,创建成员变量TestService,但是并未设置对象的set方法,所以就不能支持第一种注入方式,这里的注入方式是在TestController的构造函数中注入,也就是说在创建TestController对象时要将TestService传进来:
public class TestController { private TestService testService; public TestController(TestService testService){ this.testService = testService; } public void test() { testService.test(); } } 复制代码
public class TestService { public void test(){ System.out.println("测试方法"); } } 复制代码
spring的xml文件:
<bean id="testController" class="com.test.day.TestController"> <constructor-arg name="testService" index="0" ref="testService"></constructor-arg> </bean> <bean id="testService" class="com.test.day.TestService"> 复制代码
静态工厂顾名思义,就是通过调用静态工厂的方法来获取自己需要的对象,为了让spring管理所有对象,我们不能直接通过"工程类.静态方法()"来获取对象,而是依然通过spring注入的形式获取:
静态工厂类:
public class StaticFactory { //静态工厂 public static final TestService getTestService(){ return new TestService(); } } 复制代码
public class TestController { private TestService testService; //注入testService public void setTestService(TestService testService) { this.testService = testService; } public void test() { testService.test(); } } 复制代码
public class TestService { public void test(){ System.out.println("测试方法"); } } 复制代码
spring的xml文件:
<bean id="testController" class="com.test.day.TestController"> <property name="testService" ref="testService"></property> </bean> <!--注意这里注入的是静态工厂类--> <bean id="testService" class="com.test.day.StaticFactory" factory-method="getTestService"> </bean> 复制代码
实例工厂的意思是获取对象实例的方法不是静态的,所以你需要首先new工厂类,再调用普通的实例方法:
实例工厂类:
public class ExamplesFactory { //实例工厂 public TestService getTestService(){ return new TestService(); } } 复制代码
public class TestController { private TestService testService; //注入testService public void setTestService(TestService testService) { this.testService = testService; } public void test() { testService.test(); } } 复制代码
public class TestService { public void test(){ System.out.println("测试方法"); } } 复制代码
spring的xml配置文件:
<bean id="testController" class="com.test.day.TestController"> <property name="testService" ref="testService"></property> </bean> <bean id="testService" factory-bean="examplesFactory" factory-method="getTestService"> </bean> <bean id="examplesFactory" class="com.test.day.ExamplesFactory"> </bean> 复制代码
五、注解注入目前使用最广泛的 @Autowired:自动装配,基于@Autowired的自动装配,默认是根据类型注入
public class TestController { @Autowired private TestService testService; public String test() { testService.test(); return null; } } 复制代码
public class TestService { public void test(){ System.out.println("测试方法"); } } 复制代码
spring的xml配置文件:
<context:component-scan base-package="com.test.day"> <context:include-filter type="regex" expression="com.test.day.*"/> </context:component-scan> 复制代码
public class Test { @org.junit.Test public void test(){ ApplicationContext ac = new ClassPathXmlApplicationContext("spring/spring-context.xml"); TestController controller = ac.getBean(TestController.class); controller.test(); } } 复制代码
学会如何使用元素来声明 bean 和通过使用 XML 配置文件中的和元素来注入 。
Spring 容器可以在不使用和 元素的情况下自动装配相互协作的 bean 之间的关系,这有助于减少编写一个大的基于 Spring 的应用程序的 XML 配置的数量。
下列自动装配模式,它们可用于指示 Spring 容器为来使用自动装配进行依赖注入。你可以使用元素的 autowire 属性为一个 bean 定义指定自动装配模式。
模式 | 描述 |
---|---|
no | 这是默认的设置,它意味着没有自动装配,你应该使用显式的bean引用来连线。你不用为了连线做特殊的事。在依赖注入章节你已经看到这个了。 |
byName | 由属性名自动装配。Spring 容器看到在 XML 配置文件中 bean 的自动装配的属性设置为 byName。然后尝试匹配,并且将它的属性与在配置文件中被定义为相同名称的 beans 的属性进行连接。 |
byType | 由属性数据类型自动装配。Spring 容器看到在 XML 配置文件中 bean 的自动装配的属性设置为 byType。然后如果它的类型匹配配置文件中的一个确切的 bean 名称,它将尝试匹配和连接属性的类型。如果存在不止一个这样的 bean,则一个致命的异常将会被抛出。 |
constructor | 类似于 byType,但该类型适用于构造函数参数类型。如果在容器中没有一个构造函数参数类型的 bean,则一个致命错误将会发生。 |
autodetect | Spring首先尝试通过 constructor 使用自动装配来连接,如果它不执行,Spring 尝试通过 byType 来自动装配。 |
byName
这种模式由属性名称指定自动装配。Spring 容器看作 beans,在 XML 配置文件中 beans 的 auto-wire 属性设置为 byName。然后,它尝试将它的属性与配置文件中定义为相同名称的 beans 进行匹配和连接。如果找到匹配项,它将注入这些 beans,否则,它将抛出异常。
直接修改之前的setter注入的代码,只需要修改一下配置文件即可
<bean id="testController111" class="com.test.day.TestController" autowire="byName"> <!-- <property name="testService" ref="testService"></property>--> </bean> <bean id="testService" class="com.test.day.TestService"> </bean> 复制代码
byType
这种模式由属性类型指定自动装配。Spring 容器看作 beans,在 XML 配置文件中 beans 的 autowire 属性设置为 byType。然后,如果它的 type 恰好与配置文件中 beans 名称中的一个相匹配,它将尝试匹配和连接它的属性。如果找到匹配项,它将注入这些 beans,否则,它将抛出异常。
和上面的例子没有什么区别,除了 XML 配置文件改成“byType”。
<bean id="testController" class="com.test.day.TestController" autowire="byType"> <!--<property name="testService" ref="testService"></property>--> </bean> <bean id="testService" class="com.test.day.TestService"> </bean> 复制代码
这种模式与 byType 非常相似,但它应用于构造器参数。Spring 容器看作 beans,在 XML 配置文件中 beans 的 autowire 属性设置为 constructor。然后,它尝试把它的构造函数的参数与配置文件中 beans 名称中的一个进行匹配和连线。如果找到匹配项,它会注入这些 bean,否则,它会抛出异常。
修改一下Controller,使用构造器的方式
public class TestController { private TestService testService; public TestController(TestService testService){ this.testService = testService; } public void test() { testService.test(); } } 复制代码
配置文件
<bean id="testController" class="com.test.day.TestController" autowire="constructor"> <!--<property name="testService" ref="testService"></property>--> </bean> <bean id="testService" class="com.test.day.TestService"> </bean> 复制代码
Spring 框架的一个关键组件是面向方面的编程(AOP)框架。面向方面的编程需要把程序逻辑分解成不同的部分称为所谓的关注点。跨一个应用程序的多个点的功能被称为横切关注点,这些横切关注点在概念上独立于应用程序的业务逻辑。有各种各样的常见的很好的方面的例子,如日志记录、审计、声明式事务、安全性和缓存等。
项 | 描述 |
---|---|
Aspect | 一个模块具有一组提供横切需求的 APIs。例如,一个日志模块为了记录日志将被 AOP 方面调用。应用程序可以拥有任意数量的方面,这取决于需求。 |
Join point | 在你的应用程序中它代表一个点,你可以在插件 AOP 方面。你也能说,它是在实际的应用程序中,其中一个操作将使用 Spring AOP 框架。 |
Advice | 这是实际行动之前或之后执行的方法。这是在程序执行期间通过 Spring AOP 框架实际被调用的代码。 |
Pointcut | 这是一组一个或多个连接点,通知应该被执行。你可以使用表达式或模式指定切入点正如我们将在 AOP 的例子中看到的。 |
Introduction | 引用允许你添加新方法或属性到现有的类中。 |
Target object | 被一个或者多个方面所通知的对象,这个对象永远是一个被代理对象。也称为被通知对象。 |
Weaving | Weaving 把方面连接到其它的应用程序类型或者对象上,并创建一个被通知的对象。这些可以在编译时,类加载时和运行时完成。 |
通知 | 描述 |
---|---|
前置通知 | 在一个方法执行之前,执行通知。 |
后置通知 | 在一个方法执行之后,不考虑其结果,执行通知。 |
返回后通知 | 在一个方法执行之后,只有在方法成功完成时,才能执行通知。 |
抛出异常后通知 | 在一个方法执行之后,只有在方法退出抛出异常时,才能执行通知。 |
环绕通知 | 在建议方法调用之前和之后,执行通知。 |
搞个简单的例子
为了测试异常,改一下service
public class TestService { public void test(){ System.out.println("测试方法"); int i = 1 / 0; } } 复制代码
添加一个切面类
@Aspect @Component public class LogAsp { private final static Logger logger = LoggerFactory.getLogger(LogAsp.class); // 声明一个切入点 @Pointcut("execution(public * com.test.day.TestService.*(..))") public void aspect() { } @Before("aspect()") public void beforeAdvice(){ logger.info("前置通知。。。"); } @After("aspect()") public void afterAdvice(){ logger.info("后置通知。。。"); } @AfterReturning(pointcut = "aspect()", returning="retVal") public void afterReturningAdvice(Object retVal){ logger.info("返回后通知。。。" ); } @AfterThrowing(pointcut = "aspect()", throwing = "ex") public void AfterThrowingAdvice(Exception ex){ logger.info("抛出异常后通知..."); } // @Around("aspect()") // public Object aroundAdvice(ProceedingJoinPoint pjp){ // Object obj = null; // try { // Object[] args = pjp.getArgs();// 得到方法所需的参数 // logger.info("环绕通知:前置..."); // //明确调用业务层方法 // obj = pjp.proceed(args); // logger.info("环绕通知:后置..."); // return obj; // } catch (Throwable throwable) { // logger.info("环绕通知:异常..."); // throw new RuntimeException(throwable); // }finally { // logger.info("环绕通知:最终..."); // } // } } 复制代码
修改一下spring配置文件
<context:component-scan base-package="com.test.day"> <context:include-filter type="regex" expression="com.test.day.*"/> </context:component-scan> <!--通过配置织入@Aspectj切面--> <aop:aspectj-autoproxy expose-proxy="true"></aop:aspectj-autoproxy> 复制代码
注意: aop:aspectj-autoproxy
正常是需要配置在 spring-mvc.xml 中的,我是为了测试只有一个配置文件就放在一起了。
启动测试类,查看打印结果,么得问题
一个数据库事务是一个被视为单一的工作单元的操作序列。这些操作应该要么完整地执行,要么完全不执行。事务管理是一个重要组成部分,RDBMS 面向企业应用程序,以确保数据完整性和一致性。事务的概念可以描述为具有以下四个关键属性说成是 ACID:
原子性:事务应该当作一个单独单元的操作,这意味着整个序列操作要么是成功,要么是失败的。
一致性:这表示数据库的引用完整性的一致性,表中唯一的主键等。
隔离性:可能同时处理很多有相同的数据集的事务,每个事务应该与其他事务隔离,以防止数据损坏。
持久性:一个事务一旦完成全部操作后,这个事务的结果必须是永久性的,不能因系统故障而从数据库中删除。
局部事务是特定于一个单一的事务资源,如一个 JDBC 连接,而全局事务可以跨多个事务资源事务,如在一个分布式系统中的事务。
局部事务管理在一个集中的计算环境中是有用的,该计算环境中应用程序组件和资源位于一个单位点,而事务管理只涉及到一个运行在一个单一机器中的本地数据管理器。局部事务更容易实现。
全局事务管理需要在分布式计算环境中,所有的资源都分布在多个系统中。在这种情况下事务管理需要同时在局部和全局范围内进行。分布式或全局事务跨多个系统执行,它的执行需要全局事务管理系统和所有相关系统的局部数据管理人员之间的协调。
Spring 支持两种类型的事务管理:
编程式事务管理 :这意味着你在编程的帮助下有管理事务。这给了你极大的灵活性,但却很难维护。
声明式事务管理 :这意味着你从业务代码中分离事务管理。你仅仅使用注释或 XML 配置来管理事务。
MVC 框架提供了模型-视图-控制的体系结构和可以用来开发灵活、松散耦合的 web 应用程序的组件。MVC 模式导致了应用程序的不同方面(输入逻辑、业务逻辑和 UI 逻辑)的分离,同时提供了在这些元素之间的松散耦合。
模型封装了应用程序数据,并且通常它们由 POJO 组成。
视图主要用于呈现模型数据,并且通常它生成客户端的浏览器可以解释的 HTML 输出。
控制器主要用于处理用户请求,并且构建合适的模型并将其传递到视图呈现。
Spring Web 模型-视图-控制(MVC)框架是围绕 DispatcherServlet 设计的,DispatcherServlet 用来处理所有的 HTTP 请求和响应。Spring Web MVC DispatcherServlet 的请求处理的工作流程如下图所示:
上面所提到的所有组件,即 HandlerMapping、Controller 和 ViewResolver 是 WebApplicationContext 的一部分,而 WebApplicationContext 是带有一些对 web 应用程序必要的额外特性的 ApplicationContext 的扩展。