什么是Spring
Spring是Java EE编程领域的一个轻量级开源框架,该框架由一个叫Rod Johnson的程序员在 2002 年最早提出并随后创建,是为了解决企业级编程开发中的复杂性,实现敏捷开发的应用型框架。Spring是一个开源容器框架,它集成各类型的工具,通过核心的Bean factory实现了底层的类的实例化和生命周期的管理。在整个框架中,各类型的功能被抽象成一个个的 Bean,这样就可以实现各种功能的管理,包括动态加载和切面编程。
Spring的特点是什么
(1)方便解耦,简化开发
通过Spring提供的IOC容器,我们可以将对象之间的依赖关系交由Spring进行控制,避免硬编码所造成的过度程序耦合。有了Spring,用户不必再为单实例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。
(2)AOP编程的支持
通过Spring提供的AOP功能,方便进行面向切面编程,许多不容易用传统OOP(Object Oriented Programming,面向对象编程)实现的功能可以通过AOP轻松应付。
(3)声明式事务的支持
在Spring中,我们可以从单调烦闷的事物管理代码中解脱出来,通过声明式方式灵活地进行事务的管理,提高开发效率和质量。
(4)方便程序的测试
可以用非容器依赖的编程方式进行几乎所有的测试工作,在Spring里,测试不再是昂贵的操作,而是随手可做的事情。例如:Spring对Junit4支持,可以通过注解方便的测试Spring程序。
(5)方便集成各种优秀框架
Spring不排斥各种优秀的开源框架,相反,Spring可以降低各种框架的使用难度,Spring提供了对各种优秀框架(如Struts,Hibernate、Hessian、Quartz)等的直接支持。
(6)降低Java EE API的使用难度
Spring对很多难用的Java EE API(如JDBC,JavaMail,远程调用等)提供了一个薄薄的封装层,通过Spring的简易封装,这些Java EE API的使用难度大为降低。
(7)Java 源码是经典学习范例
Spring的源码设计精妙、结构清晰、匠心独运,处处体现着大师对Java设计模式灵活运用以及对Java技术的高深造诣。Spring框架源码无疑是Java技术的最佳实践范例。如果想在短时间内迅速提高自己的Java技术水平和应用开发水平,学习和研究Spring源码将会使你收到意想不到的效果。
Spring的优势
- 低侵入式设计,代码污染极低。
- 独立于各种应用服务器,基于Spring框架的应用,可以真正实现Write Once,Run Anywhere。
- Spring的DI机制降低了业务对象替换的复杂性,提高了组件之间的解耦。
- Spring的AOP支持允许将一些通用任务如安全、事务、日志等进行集中式管理,从而提供了更好的复用。
- Spring的ORM和DAO提供了与第三方持久层框架的良好整合,并简化了底层的数据库访问。
- Spring并不强制应用完全依赖于Spring,开发者可自由选用Spring框架的部分或全部。
什么是耦合
在Spring中,通过Spring提供的IOC容器,我们可以将对象之间的依赖关系交由Spring进行控制,避免硬编码所造成的过度程序耦合。
- 耦合性,也叫耦合度,是对模块间关联程度的度量。耦合的强弱取决于模块之间接口的复杂性、调用模块的方式以及通过界面传送数据的多少。
- 模块间的耦合度是指模块之间的依赖关系,包括控制关系、调用关系、数据传递关系。模块之间的关系越多,其耦合性越强,同时表明其独立性越差。降低耦合性,可以提高其独立性。
- 在软件工程中,耦合指的就是对象之间的依赖性。对象之间的耦合越高,维护成本越高。因此对象的设计应该让类和构件之间的耦合尽可能的小。在软件设计中通常用耦合度和内聚度作为衡量模块独立程度的标准。划分模块的一个准则就是高内聚低耦合。
- 耦合是影响软件复杂度和设计质量的一个重要因素,在设计上面我们应该采用以下原则:如果模块间必须存在耦合,就尽量使用数据耦合,少使用控制耦合,限制公共耦合的范围,尽量避免使用内容耦合。
Spring的核心是什么
Spring的核心是:
- IOC(Inverse of Control 控制反转)
- AOP(Aspect Oriented Programming 面向切面编程)
IOC(控制反转)和DI(依赖注入)
传统程序设计中,我们需要使用某个对象的方法,需要先通过new创建一个该对象,我们这时是主动行为;而IOC是我们将创建对象的控制权交给IOC容器,这时是由容器帮忙创建及注入依赖对象,我们的程序被动的接受IOC容器创建的对象,控制权反转,所以叫控制反转。
由于IOC确实不够开门见山,所以提出了DI(依赖注入:Dependency Injection)的概念,即让第三方来实现注入,以移除我们类与需要使用的类之间的依赖关系。总的来说,IOC是目的,DI是手段,创建对象的过程往往意味着依赖的注入。我们为了实现IOC,让生成对象的方式由传统方式(new)反转过来,需要创建相关对象时由IOC容器帮我们注入(DI)。
简单的说,就是我们类里需要另一个类,只需要让Spring帮我们创建 ,这叫做控制反转;然后Spring帮我们将需要的对象设置到我们的类中,这叫做依赖注入。
常见的注入方式
常见的注入方法有使用有参构造方法注入和使用属性注入。现在有Person类和Car类,Spring配置文件为beans.xml。对应关系是一个Person类对象拥有一个Car类对象和一个name。代码如下:
- Person类
public class Person {
private String name;
private Car car;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Car getCar() {
return car;
}
public void setCar(Car car) {
this.car = car;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", car=" + car +
'}';
}
}
- Car类
public class Car {
private String name;
private String color;
private double price;
public Car(){}
public Car(String name, String color, double price) {
this.name = name;
this.color = color;
this.price = price;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Car{" +
"name='" + name + '\'' +
", color='" + color + '\'' +
", price=" + price +
'}';
}
}
编辑Spring的配置文件beans.xml,对其进行注入。
- 使用属性注入
<bean id="person" class="cqucc.pojo.Person">
<property name="name" value="奥利给" />
<property name="car" ref="car" />
</bean>
- 使用有参构造方法注入
<bean id="car" class="cqucc.pojo.Car">
<constructor-arg name="name" value="五菱" />
<constructor-arg name="color" value="white" />
<constructor-arg name="price" value="10.8" />
</bean>
注入完成后使用getBean()方法获取对象并输出,代码如下:
@Test
public void testPerson() {
String config = "beans.xml";
ApplicationContext ac = new ClassPathXmlApplicationContext(config);
Person p = (Person) ac.getBean("person");
System.out.println(p);
}
属性注入成功,运行结果如下:
集合属性的注入
创建一个类CollectionTest和Spring配置文件CollectionTest.xml。类中有List类型的list,Map类型的map和Set类型的set,我们将对它们进行属性注入。
- CollectionTest类
public class CollectionTest {
List<String> list;
Set<String> set;
Map<String,Integer> map;
public void setList(List<String> list) {
this.list = list;
}
public void setSet(Set<String> set) {
this.set = set;
}
public void setMap(Map<String, Integer> map) {
this.map = map;
}
public List<String> getList() {
return list;
}
public Set<String> getSet() {
return set;
}
public Map<String, Integer> getMap() {
return map;
}
}
- CollectionTest.xml文件:
<bean name="collection" class="cqucc.pojo.CollectionTest">
<property name="list">
<list>
<value>list元素1</value>
<value>list元素2</value>
<value>list元素3</value>
</list>
</property>
<property name="set">
<set>
<value>set元素s1</value>
<value>set元素s2</value>
<value>set元素s3</value>
</set>
</property>
<property name="map">
<map>
<entry key="map元素1" value="1"></entry>
<entry key="map元素2" value="2"></entry>
<entry key="map元素3" value="3"></entry>
</map>
</property>
</bean>
测试代码及运行结果如下:
Spring bean的作用域
Spring bean有五大作用域,分别是:
后三个作用域是需要使用Spring的WebApplicationContext,所以着重介绍singleton和prototype作用域。
(1)singleton作用域
配置了 scope=“singleton” 的 bean 为单例作用域。此作用域为Spring bean的默认作用域。Spring 以容器的方式,使得我们仅需配置,即可得到天然的单例模式。因为 Spring 的超强能力,所以在实际应用中,大部分 Bean 都能以单例方式运行 ,这也是 bean 的默认作用域指定为 singleton 的原因 。使用singleton的bean在同一个Spring IOC容器中只会有一个实例。将一个bean设置为singleton的方式如下:
<bean name="collection" class="cqucc.pojo.CollectionTest" scope="singleton">
(2)prototype作用域
配置了 scope=“prototype” 的 bean 为非单例作用域,即使用prototype的bean在同一个Spring IOC容器中可以有多个实例。在默认情况下, Spring 容器在启动时不实例化 prototype 的 bean。此外,Spring 容器将 prototype 的bean 交给调用者后,就不再负责管理它的生命周期。将一个bean设置为prototype的方式如下:
<bean name="collection" class="cqucc.pojo.CollectionTest" scope="prototype">
Spring bean的生命周期
创建一个类BeanLife和Spring配置文件BeanLife.xml,用于测试无参构造方法、init方法和destroy的调用顺序。
- BeanLife类
public class BeanLife {
public BeanLife() {
System.out.println("调用无参构造方法");
}
public void init() {
System.out.println("初始化bean");
}
public void destroy() {
System.out.println("销毁bean");
}
}
- BeanLife.xml文件
<bean name="life" class="cqucc.pojo.BeanLife" init-method="init" destroy-method="destroy">
</bean>
测试方法和运行结果如图:
由运行结果可以知道,类中的构造方法优先调用,然后才会调用初始化函数。销毁函数只有以ClassPathXmlApplicationContext来创建对象调用getBean()方法后,调用close()函数才会执行。
Spring bean的两种获取方式
(1)通过BeanFactory获取
BeanFactory是一个类工厂,和传统的类工厂不同,传统的类工厂仅负责构造一个类或几个类的实例;而BeanFactory可以创建并管理各种类的对象,Spring称这些被创建和管理的Java对象为Bean。BeanFactory是一个接口,Spring为BeanFactory提供了多种实现,最常用的就是XmlBeanFactory。其中,BeanFactory接口最主要的方法就是getBean(String beanName),该方法从容器中返回指定名称的Bean。此外,BeanFactory接口的功能可以通过实现它的接口进行扩展(比如ApplicationContext)。以获取上面BeanLife对象为例,代码如下:
//获取配置文件
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource rs = resolver.getResource("classpath:BeanLife.xml");
//加载配置文件并启动IOC容器
BeanFactory bf = new XmlBeanFactory(rs);
//从容器中获取Bean对象
BeanLife b = (BeanLife) bf.getBean("life");
(2)通过ApplicationContext获取
ApplicationContext由BeanFactory派生而来,提供了更多面向实际应用的功能。在BeanFactory中,很多功能需要编程方式来实现,而ApplicationContext中可以通过配置的方式来实现。ApplicationContext的主要实现类是ClassPathXmlApplicationContext和FileSystemXmlApplicationContext,前者默认从类路径加载配置文件,后者默认从文件系统中加载配置文件。
上面的对象获取方式都是通过ApplicationContext获取。以获取上面BeanLife对象为例,代码如下:
String config = "BeanLife.xml";
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext(config);
BeanLife b = (BeanLife) ac.getBean("life");
参考文章
https://blog.csdn.net/justloveyou_/article/details/73136260?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522163152014416780261951617%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=163152014416780261951617&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduend~default-1-73136260.first_rank_v2_pc_rank_v29&utm_term=spring核心&spm=1018.2226.3001.4187