Spring framework核心
这一部分涵盖了Spring框架绝对不可或缺的所有技术。
1、IOC容器
1.1Spring IoC容器和beans介绍
org.springframework.beans和org.springframework.context包是springframework的IoC容器的基础。
BeanFactory接口提供了一种高级配置机制,能够管理任何类型的对象。
ApplicationContext是BeanFactory的一个子接口。它增加了与Spring的AOP特性更容易的集成;消息资源处理(用于国际化)、事件发布;和应用程序层特定的上下文,例如用于网络应用程序的WebApplicationContext 。
简而言之,BeanFactory提供了配置框架和基本功能,而ApplicationContext添加了更多特定于企业的功能。ApplicationContext是BeanFactory的一个完整超集,在本章中专门用于描述Spring的IoC容器。
在Spring中,构成应用程序主干并由Spring IoC容器管理的对象称为beans。bean是由Spring IoC容器实例化、组装和管理的对象。否则,bean只是应用程序中许多对象中的一个。Beans以及它们之间的依赖关系反映在容器使用的配置元数据中。
1.2.容器概述
接口org . Spring framework . context . application context代表Spring IoC容器,负责实例化、配置和组装上述beans。容器通过读取配置元数据来获得关于实例化、配置和组装什么对象的指令。配置元数据用XML、Java注释或Java代码表示。它允许您表达组成应用程序的对象以及这些对象之间丰富的相互依赖关系。
Spring提供了几个现成的应用程序上下文接口实现。在独立的应用程序中,通常创建ClassPathXmlApplicationContext 或FileSystemXmlApplicationContext的实例。虽然XML是定义配置元数据的传统格式,但是您可以通过提供少量的XML配置来声明性地支持这些附加的元数据格式,从而指示容器使用Java注释或代码作为元数据格式。
在大多数应用程序场景中,不需要显式用户代码来实例化Spring IoC容器的一个或多个实例。例如,在一个web应用程序场景中,应用程序的web.xml文件中简单的八行:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/daoContext.xml /WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
您的应用程序类与配置元数据相结合,这样在创建和初始化应用程序上下文之后,您就拥有了一个完全配置和可执行的系统或应用程序。
1.2.1配置元数据
Spring IoC容器消耗了一种形式的配置元数据;这种配置元数据代表了作为应用程序开发人员,您如何告诉Spring容器在您的应用程序中实例化、配置和组装对象。
配置元数据传统上以简单直观的XML格式提供,这是本章的大部分内容用来传达Spring IoC容器的关键概念和功能。
基于XML的元数据不是唯一允许的配置元数据形式。Spring IoC容器本身完全脱离了实际编写配置元数据的格式。如今,许多开发人员为他们的Spring应用程序选择基于Java的配置。
Spring 2.5引入了对基于注释的配置元数据的支持。
从Spring 3.0开始,Spring JavaConfig项目提供的许多特性成为了核心Spring框架的一部分。因此,您可以通过使用Java而不是XML文件来定义应用程序类外部的beans。要使用这些新功能,请参见@Configuration, @Bean, @Import and @DependsOn 注释。
Spring配置由容器必须管理的至少一个(通常不止一个)bean定义组成。基于XML的配置元数据将这些bean配置为顶层< beans/>元素中的< bean/>元素。Java配置通常在@Configuration类中使用@Bean注释方法。
这些bean定义对应于组成应用程序的实际对象。通常,您定义服务层对象、数据访问对象(DAOs)、表示对象(如Struts操作实例)、基础结构对象(如Hibernate会话工厂、JMS队列等)。通常不在容器中配置细粒度的域对象,因为创建和加载域对象通常是Dao和业务逻辑的责任。但是,您可以使用Spring与AspectJ的集成来配置在IoC容器控制之外创建的对象。
以下示例显示了基于XML的配置元数据的基本结构:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>
<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>
<!-- more bean definitions go here -->
</beans>
id属性是一个字符串,用于标识单个bean定义。class属性定义bean的类型,并使用完全限定的类名。id属性的值指的是协作对象。
1.2.2.实例化容器
实例化Spring IoC容器很简单。提供给ApplicationContext构造函数的一个或多个位置路径实际上是资源字符串,它们允许容器从各种外部资源加载配置元数据,如本地文件系统、Java类路径等。
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
以下示例显示了服务层对象(services.xml)配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- services --> <bean id="petStore" class="org.springframework.samples.jpetstore.services.PetStoreServiceImpl">
<property name="accountDao" ref="accountDao"/>
<property name="itemDao" ref="itemDao"/>
<!-- additional collaborators and configuration for this bean go here -->
</bean> <!-- more bean definitions for services go here --> </beans>
以下示例显示了数据访问对象daos.xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="accountDao"
class="org.springframework.samples.jpetstore.dao.jpa.JpaAccountDao">
<!-- additional collaborators and configuration for this bean go here -->
</bean> <bean id="itemDao" class="org.springframework.samples.jpetstore.dao.jpa.JpaItemDao">
<!-- additional collaborators and configuration for this bean go here -->
</bean> <!-- more bean definitions for data access objects go here --> </beans>
服务层由类PetStoreServiceImpl和两个类型为JpaAccountDao和JpaItemDao(基于JPA对象/关系映射标准)的数据访问对象组成。属性名称元素引用JavaBean属性的名称,ref元素引用另一个Bean定义的名称。id和ref元素之间的这种联系表达了协作对象之间的依赖关系。
编写基于XML的配置元数据
让bean定义跨越多个XML文件可能很有用。通常,每个单独的XML配置文件代表您的体系结构中的一个逻辑层或模块。
您可以使用应用程序上下文构造函数从所有这些XML片段中加载bean定义。这个构造函数采用多个资源位置,如前一节所示。或者,使用一个或多个< import/>元素从另一个或多个文件加载bean定义。
<beans>
<import resource="services.xml"/>
<import resource="resources/messageSource.xml"/>
<import resource="/resources/themeSource.xml"/> <bean id="bean1" class="..."/>
<bean id="bean2" class="..."/>
</beans>
在前面的示例中,外部bean定义是从三个文件加载的:services.xml、messageSource.xml和themeSource.xml。所有位置路径都是相对于执行导入的定义文件的,因此services.xml必须与执行导入的文件位于同一目录或类路径位置,而messageSource.xml和themeSource.xml必须位于导入文件位置下方的资源位置。如您所见,前导斜杠被忽略,但鉴于这些路径是相对的,最好不要使用斜杠。根据Spring模式,被导入文件的内容,包括顶层的< bean/>元素,必须是有效的XML bean定义。
可以使用相对引用父目录中的文件,但不建议这样做"../"路径。这样做会创建对当前应用程序之外的文件的依赖。特别是,对于“类classpath:”网址(例如,“类classpath:../services.xml),运行时解析过程选择“最近的”类路径根,然后查看其父目录。类路径配置更改可能会导致选择不同的不正确目录。
您可以始终使用完全限定的资源位置,而不是相对路径:例如,“文件:C:/config/services.xml”或“classpath:/config/services.xml”。但是,请注意,您正在将应用程序的配置耦合到特定的绝对位置。对于这种绝对位置,通常最好保持间接性,例如,通过运行时根据JVM系统属性解析的“$ {……}”占位符。
1.2.3.使用容器
ApplicationContext是高级工厂的接口,能够维护不同beans及其依赖项的注册表。使用 getBean(String name, Class<T> requiredType) 方法可以检索Bean的实例。
// create and configure beans
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml"); // retrieve configured instance
PetStoreService service = context.getBean("petStore", PetStoreService.class); // use configured instance
List<String> userList = service.getUsernameList();
最灵活的变体是与读取器委托相结合的GenericApplicationContext,例如,对于XML文件,使用XmlBeanDefinitionReader:
GenericApplicationContext context = new GenericApplicationContext();
new XmlBeanDefinitionReader(context).loadBeanDefinitions("services.xml", "daos.xml");
context.refresh();
如果需要,这样的读取器委托可以在同一个应用程序上下文中混合和匹配,从不同的配置源中读取bean定义。
然后,您可以使用getBean来检索Bean的实例。ApplicationContext接口有一些其他的方法来检索beans,但是理想情况下,您的应用程序代码不应该使用它们。事实上,您的应用程序代码应该根本没有对getBean()方法的调用,因此根本不依赖于Spring APIs。例如,Spring与网络框架的集成为各种网络框架组件(如控制器和JSF管理的bean)提供了依赖注入,允许您通过元数据(如自动连接注释)声明对特定bean的依赖。
1.3.Bean概述
Spring IoC容器管理一个或多个beans。这些beans是用您提供给容器的配置元数据创建的,例如,以XML <bean/>定义的形式。
在容器本身中,这些bean定义被表示为BeanDefinition对象,这些对象包含(除其他信息外)以下元数据:
包限定的类名:通常是被定义的bean的实际实现类。
Bean行为配置元素,声明bean在容器中的行为(范围、生命周期回调等等)。
对bean完成工作所需的其他bean的引用;这些引用也被称为协作者或依赖者。
在新创建的对象中设置的其他配置设置,例如,在管理连接池的bean中使用的连接数,或者连接池的大小限制。
这些元数据转化为一组属性,组成了每个bean定义。
除了包含如何创建特定bean的信息的bean定义之外,ApplicationContext实现还允许注册用户在容器外创建的现有对象。这是通过方法getBeanFactory()访问应用程序上下文的BeanFactory来完成的,该方法返回BeanFactory实现DefaultListableBeanFactory。DefaultListableBeanFactory通过方法registerSingleton(..)和registerBeanDefinition(..).然而,典型的应用程序只使用通过元数据bean定义定义的bean。
Bean元数据和手动提供的单例实例需要尽早注册,以便容器在自动连接和其他自省步骤中能够正确地推理它们。虽然在某种程度上支持覆盖现有的元数据和现有的单例实例,但是官方不支持在运行时注册新的bean(同时对工厂进行实时访问),这可能会导致并发访问异常和/或bean容器中的状态不一致.
1.3.1.命名Bean
每个bean都有一个或多个标识符。这些标识符在承载bean的容器中必须是唯一的。一个bean通常只有一个标识符,但是如果它需要多个标识符,多余的标识符可以被认为是别名。
在基于XML的配置元数据中,您使用id和/name属性来指定bean标识符。id属性允许您只指定一个id。传统上,这些名称是字母数字(‘MyBean’、‘FooServiCe’等)。),但也可能包含特殊字符。如果您想为bean引入其他别名,也可以在name属性中指定它们,用逗号(,,分号(;),或者空白。作为一个历史记录,在Spring 3.1之前的版本中,id属性被定义为xsd:ID类型,它约束可能的字符。从3.1开始,它被定义为xsd:string类型。请注意,bean id的唯一性仍然由容器强制执行,尽管不再由XML解析器强制执行。
您不需要为bean提供名称或id。如果没有显式提供名称或id,容器将为该bean生成一个唯一的名称。但是,如果您想通过名称引用该bean,通过使用引用元素或服务定位器样式查找,您必须提供一个名称。不提供名称的动机与使用内部beans和自动连接协作者有关。
Bean命名约定
约定是在命名beans时使用标准的Java约定作为实例字段名称。也就是说,bean名称以小写字母开头,并且从那时起是骆驼大小写。这类名称的例子有(不带引号)'accountManager', 'accountService', 'userDao', 'loginController'等等
在Bean定义之外别名Bean
在bean定义本身中,您可以为bean提供多个名称,方法是使用由id属性指定的最多一个名称和name属性中任意数量的其他名称的组合。这些名称可以是同一个bean的等效别名,在某些情况下很有用,例如允许应用程序中的每个组件通过使用特定于该组件本身的bean名称来引用公共依赖关系。
然而,指定实际定义bean的所有别名并不总是足够的。有时需要为在别处定义的bean引入一个别名。这在大型系统中是常见的情况,在大型系统中,配置被分割到每个子系统中,每个子系统都有自己的一组对象定义。在基于XML的配置元数据中,您可以使用< alias/>元素来实现这一点。
<alias name="fromName" alias="toName"/>
在这种情况下,一个名为fromName的bean(在同一个容器中)在使用这个别名定义后,也可以被称为toName。
例如,子系统A的配置元数据可以通过subsystemA-dataSource的名称引用数据源。子系统B的配置元数据可以通过subsystemB-dataSource的名称引用数据源。当组成使用这两个子系统的主应用程序时,主应用程序以MyApp-数据源的名称引用数据源。要使所有三个名称都引用同一个对象,可以将以下别名定义添加到配置元数据中:
<alias name="myApp-dataSource" alias="subsystemA-dataSource"/>
<alias name="myApp-dataSource" alias="subsystemB-dataSource"/>
现在,每个组件和主应用程序都可以通过一个唯一的名称来引用数据源,并保证不与任何其他定义冲突(有效地创建了一个名称空间),但它们引用的是同一个bean。
如果您使用的是Java配置,那么@Bean注释可以用来提供别名。有关详细信息,请参见使用@Bean注释。
1.3.2.实例化beans
bean定义本质上来说是创建一个或多个对象的方法。当问及一个命名bean时,容器会查看这个方法并使用bean定义中封装的配置元数据创建(或取得)一个实际的对象。
如果使用基于XML的配置元数据,则需要在< bean/>元素的class属性中指定要实例化的对象的类型(或类)。这个类属性在内部是BeanDefinition实例上的一个Class属性,通常是强制性的。(有关异常,请参见使用实例工厂方法和Bean定义继承的实例化。)您可以通过两种方式之一使用Class属性:
通常,在容器本身通过反射调用其构造函数直接创建bean的情况下,指定要构造的bean类,这在某种程度上等同于使用new操作符的Java代码。
要指定包含为创建对象而调用的静态工厂方法的实际类,在容器调用类上的静态工厂方法来创建bean的情况下就不太常见了。从静态工厂方法调用返回的对象类型可以是相同的类,也可以完全是另一个类。
内部类名
如果要为静态嵌套类配置bean定义,必须使用嵌套类的二进制名称。
例如,如果您在com.example包中有一个名为Foo的类,并且这个Foo类有一个名为Bar的静态嵌套类,那么bean定义上的“类”属性值将是…com.example.Foo$Bar
请注意,名称中使用了$字符来分隔嵌套类名和外部类名。
用构造函数实例化
Spring IoC容器几乎可以管理任何您希望它管理的类;它不限于管理真正的JavaBeans。大多数Spring用户更喜欢实际的JavaBeans,它只有一个默认的(无参数)构造函数和适当的设置器和获取器,它们是根据容器中的属性建模的。您的容器中还可以有更多外来的非bean风格的类。例如,如果您需要使用一个完全不符合JavaBean规范的遗留连接池,Spring也可以管理它。
使用基于XML的配置元数据,您可以按如下方式指定bean类:
<bean id="exampleBean" class="examples.ExampleBean"/> <bean name="anotherExample" class="examples.ExampleBeanTwo"/>
有关向构造函数提供参数(如果需要)以及在构造对象后设置对象实例属性的机制的详细信息,请参见注入依赖项。
用静态工厂方法实例化
定义使用静态工厂方法创建的bean时,可以使用class 属性指定包含静态工厂方法的类,并使用名为factory-method 的属性指定工厂方法本身的名称。您应该能够调用此方法(带有后面描述的可选参数)并返回一个活动对象,该对象随后被视为是通过构造函数创建的。这种bean定义的一个用途是在遗留代码中调用静态工厂
以下bean定义指定将通过调用factory-method来创建bean。定义没有指定返回对象的类型(类),只指定包含工厂方法的类。在本例中,createInstance()方法必须是static方法。
<bean id="clientService"
class="examples.ClientService"
factory-method="createInstance"/>
public class ClientService {
private static ClientService clientService = new ClientService();
private ClientService() {} public static ClientService createInstance() {
return clientService;
}
}
有关为工厂方法提供(可选)参数以及在对象从工厂返回后设置对象实例属性的机制的详细信息,请参见详细的依赖关系和配置。
使用实例工厂方法的实例化
实例工厂方法的实例化从容器调用现有bean的非静态方法来创建新bean。要使用这种机制,将class
属性保留为空,并在factory-bean属性中,指定当前(或父/祖先)容器中的bean的名称,该容器包含要调用来创建对象的实例方法。用factory-method属性设置工厂方法本身的名称。
<!-- the factory bean, which contains a method called createInstance() -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
<!-- inject any dependencies required by this locator bean -->
</bean> <!-- the bean to be created via the factory bean -->
<bean id="clientService"
factory-bean="serviceLocator"
factory-method="createClientServiceInstance"/>
public class DefaultServiceLocator { private static ClientService clientService = new ClientServiceImpl(); public ClientService createClientServiceInstance() {
return clientService;
}
}
一个工厂类也可以包含多个工厂方法,如下所示:
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
<!-- inject any dependencies required by this locator bean -->
</bean> <bean id="clientService"
factory-bean="serviceLocator"
factory-method="createClientServiceInstance"/> <bean id="accountService"
factory-bean="serviceLocator"
factory-method="createAccountServiceInstance"/>
public class DefaultServiceLocator { private static ClientService clientService = new ClientServiceImpl(); private static AccountService accountService = new AccountServiceImpl(); public ClientService createClientServiceInstance() {
return clientService;
} public AccountService createAccountServiceInstance() {
return accountService;
}
}
这种方法表明,工厂bean本身可以通过依赖注入(DI)来管理和配置。请参见详细的依赖关系和配置。
在Spring文档中,factory bean是指在Spring容器中配置的bean,它将通过实例或静态工厂方法创建对象。相比之下,FactoryBean(注意大写)指的是Spring特定的FactoryBean。
1.4.依赖性
1.4.1.依赖注入
依赖注入(DI)是一个过程,通过这个过程,对象定义它们的依赖关系,也就是说,它们使用的其他对象,只通过构造函数参数、工厂方法的参数,或者在对象实例被构造或从工厂方法返回后在对象实例上设置的属性。然后,容器在创建bean时注入这些依赖项。这个过程从根本上说是控制反转(IoC)的逆过程,即bean本身通过使用类的直接构造或服务定位器模式来控制其依赖关系的实例化或位置。
DI有两种主要的变体,基于构造函数的依赖注入和基于Setter的依赖注入。
基于构造函数的依赖注入
基于构造函数的DI是通过容器调用一个带有多个参数的构造函数来实现的,每个参数代表一个依赖关系。调用带有特定参数的静态工厂方法来构造bean几乎是等价的,并且本讨论将参数类似地对待构造函数和静态工厂方法。下面的示例显示了一个只能通过构造函数注入进行依赖注入的类。请注意,这个类没有什么特别之处,它是一个POJO,不依赖于容器特定的接口、基类或注释。
public class SimpleMovieLister { // the SimpleMovieLister has a dependency on a MovieFinder
private MovieFinder movieFinder; // a constructor so that the Spring container can inject a MovieFinder
public SimpleMovieLister(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
} // business logic that actually uses the injected MovieFinder is omitted...
}
构造函数参数解析
使用参数的类型进行构造函数参数解析匹配。如果bean定义的构造函数参数中不存在潜在的模糊性,那么在bean定义中定义构造函数参数的顺序就是在实例化bean时将这些参数提供给适当的构造函数的顺序。
package x.y; public class Foo { public Foo(Bar bar, Baz baz) {
// ...
}
}
不存在潜在的歧义,假设Bar和Baz类没有继承关系。因此,以下配置工作正常,并且您不需要在< constructor-arg/>元素中显式指定构造函数参数indexes/type。
<beans>
<bean id="foo" class="x.y.Foo">
<constructor-arg ref="bar"/>
<constructor-arg ref="baz"/>
</bean> <bean id="bar" class="x.y.Bar"/> <bean id="baz" class="x.y.Baz"/>
</beans>
当引用另一个bean时,类型是已知的,并且可以进行匹配(就像前面的例子一样)。当使用简单类型时,如< value>true</value >,Spring无法确定值的类型,因此在没有帮助的情况下无法按类型匹配。考虑以下类别:
package examples; public class ExampleBean { // Number of years to calculate the Ultimate Answer
private int years; // The Answer to Life, the Universe, and Everything
private String ultimateAnswer; public ExampleBean(int years, String ultimateAnswer) {
this.years = years;
this.ultimateAnswer = ultimateAnswer;
}
}
构造函数参数类型匹配
在前面的场景中,如果使用type属性显式指定构造函数参数的类型,容器可以使用简单类型的类型匹配。例如:
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg type="int" value="7500000"/>
<constructor-arg type="java.lang.String" value="42"/>
</bean>
构造函数参数索引
使用index属性显式指定构造函数参数的索引。例如:
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg index="0" value="7500000"/>
<constructor-arg index="1" value="42"/>
</bean>
除了解决多个简单值的不确定性之外,指定索引还解决了构造函数有两个相同类型参数的不确定性。请注意,索引是基于0的。
构造函数参数名
您还可以使用构造函数参数名称来消除值的歧义:
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg name="years" value="7500000"/>
<constructor-arg name="ultimateAnswer" value="42"/>
</bean>
请记住,要使这一点开箱即用,您的代码必须在启用调试标志的情况下编译,以便Spring可以从构造函数中查找参数名称。如果不能用调试标志编译代码(或者不想),可以使用@ConstructorProperties JDK注释来显式命名构造函数参数。
package examples; public class ExampleBean { // Fields omitted @ConstructorProperties({"years", "ultimateAnswer"})
public ExampleBean(int years, String ultimateAnswer) {
this.years = years;
this.ultimateAnswer = ultimateAnswer;
}
}
基于Setter的依赖注入
基于Setter的DI是通过容器调用bean上的setter方法,然后调用无参数构造函数或无参数静态工厂方法来实例化bean来实现的。
下面的示例显示了一个只能使用纯setter注入进行依赖注入的类。这个类是常规Java。它是一个POJO,不依赖于容器特定的接口、基类或注释。
public class SimpleMovieLister { // the SimpleMovieLister has a dependency on the MovieFinder
private MovieFinder movieFinder; // a setter method so that the Spring container can inject a MovieFinder
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
} // business logic that actually uses the injected MovieFinder is omitted...
}
因为您可以混合基于构造函数和基于设置函数的DI,所以对于强制依赖项使用构造函数,对于可选依赖项使用设置方法或配置方法是一个很好的经验法则。请注意,在setter方法上使用@Required注释可以使属性成为必需的依赖项。
Spring团队通常提倡构造函数注入,因为它使人们能够将应用程序组件实现为不可变的对象,并确保所需的依赖关系不为空。此外,构造函数注入的组件总是以完全初始化的状态返回给客户端(调用)代码。
Setter注入应该主要用于可选的依赖项,这些依赖项可以在类中被赋予合理的默认值。否则,必须在代码使用依赖关系的任何地方执行非空检查。setter注入的一个好处是,setter方法使该类的对象可以在以后重新配置或重新注入。
使用对特定类最有意义的DI样式。有时候,当处理第三方类时,你没有源代码,选择是为你做的。例如,如果第三方类没有公开任何setter方法,那么构造函数注入可能是DI唯一可用的形式。
依赖性解决过程
容器执行bean依赖关系解析,如下所示:
ApplicationContext是用描述所有beans的配置元数据创建和初始化的。配置元数据可以通过XML、Java代码或注释来指定。
对于每个bean,它的依赖关系以属性、构造函数参数或静态工厂方法的参数的形式表示,如果您使用的是静态工厂方法而不是普通的构造函数。这些依赖关系是在实际创建bean时提供给bean的。
每个属性或构造函数参数都是要设置的值的实际定义,或者是对容器中另一个bean的引用。
作为值的每个属性或构造函数参数都从其指定格式转换为该属性或构造函数参数的实际类型。默认情况下,Spring可以将字符串格式的值转换为所有内置类型,如int、long、string、boolean等。
如果主要使用构造函数注入,就有可能创建一个无法解析的循环依赖场景。
比如:A类通过构造函数注入需要B类的一个实例,B类通过构造函数注入需要A类的一个实例。如果您将类A和类B的beans配置为相互注入,Spring IoC容器会在运行时检测到此循环引用,并抛出一个BeanCurrentLincreationException。
一个可能的解决方案是编辑一些类的源代码,由设置者而不是构造者来配置。或者,避免构造函数注入,只使用setter注入。
与典型情况(没有循环依赖)不同,bean A和bean B之间的循环依赖迫使其中一个bean在完全初始化之前被注入到另一个bean中(典型的先有鸡还是先有蛋的场景)。
一般可以相信Spring做的是对的。它在容器加载时检测配置问题,例如对不存在的beans和循环依赖的引用。在实际创建bean时,Spring尽可能晚地设置属性和解决依赖关系。
这意味着,如果在创建一个对象或其依赖项时出现问题,正确加载的Spring容器可以在您请求该对象时生成异常。例如,由于缺少属性或属性无效,bean会引发异常。这种对某些配置问题的潜在延迟可见性是应用程序上下文实现默认预实例化单例beans的原因。在实际需要之前创建这些beans需要一些前期时间和内存,在创建应用程序上下文时,而不是稍后,您会发现配置问题。您仍然可以覆盖这个默认行为,这样单例beans将会延迟初始化,而不是预先实例化。
如果不存在循环依赖,当一个或多个协作bean被注入到一个依赖bean中时,每个协作bean在被注入到依赖bean之前都被完全配置好了。这意味着,如果bean A对bean B有依赖关系,那么Spring IoC容器在调用bean A上的setter方法之前会对bean B进行完全配置,换句话说,bean被实例化(如果不是预实例化的单例),它的依赖关系被设置,相关的生命周期方法(比如配置的init方法或者InitializingBean回调方法)被调用。
依赖注入的例子
以下示例将基于XML的配置元数据用于基于setter的DI。Spring XML配置文件的一小部分指定了一些bean定义:
<bean id="exampleBean" class="examples.ExampleBean">
<!-- setter injection using the nested ref element -->
<property name="beanOne">
<ref bean="anotherExampleBean"/>
</property> <!-- setter injection using the neater ref attribute -->
<property name="beanTwo" ref="yetAnotherBean"/>
<property name="integerProperty" value="1"/>
</bean> <bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
public class ExampleBean { private AnotherBean beanOne; private YetAnotherBean beanTwo; private int i; public void setBeanOne(AnotherBean beanOne) {
this.beanOne = beanOne;
} public void setBeanTwo(YetAnotherBean beanTwo) {
this.beanTwo = beanTwo;
} public void setIntegerProperty(int i) {
this.i = i;
}
}
在前面的示例中,设置器被声明为与XML文件中指定的属性相匹配。以下示例使用基于构造函数的DI:
<bean id="exampleBean" class="examples.ExampleBean">
<!-- constructor injection using the nested ref element -->
<constructor-arg>
<ref bean="anotherExampleBean"/>
</constructor-arg> <!-- constructor injection using the neater ref attribute -->
<constructor-arg ref="yetAnotherBean"/> <constructor-arg type="int" value="1"/>
</bean> <bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
public class ExampleBean { private AnotherBean beanOne; private YetAnotherBean beanTwo; private int i; public ExampleBean(
AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {
this.beanOne = anotherBean;
this.beanTwo = yetAnotherBean;
this.i = i;
}
}
bean定义中指定的构造函数参数将用作ExampleBean构造函数的参数。
现在考虑这个例子的一个变体,其中Spring被告知调用一个静态工厂方法来返回对象的一个实例,而不是使用一个构造函数:
<bean id="exampleBean" class="examples.ExampleBean" factory-method="createInstance">
<constructor-arg ref="anotherExampleBean"/>
<constructor-arg ref="yetAnotherBean"/>
<constructor-arg value="1"/>
</bean> <bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
public class ExampleBean { // a private constructor
private ExampleBean(...) {
...
} // a static factory method; the arguments to this method can be
// considered the dependencies of the bean that is returned,
// regardless of how those arguments are actually used.
public static ExampleBean createInstance (
AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) { ExampleBean eb = new ExampleBean (...);
// some other operations...
return eb;
}
}
1.4.2.详细的依赖关系和配置
您可以将bean属性和构造函数参数定义为对其他托管bean(协作者)的引用,或者定义为内联定义的值。Spring基于XML的配置元数据支持其< property/>和< constructor-arg/>元素中的子元素类型。
直接值(原语、字符串等)
< property/>元素的value属性将属性或构造函数参数指定为人类可读的字符串表示形式。Spring的转换服务用于将这些值从字符串转换为属性或参数的实际类型。
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<!-- results in a setDriverClassName(String) call -->
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mydb"/>
<property name="username" value="root"/>
<property name="password" value="masterkaoli"/>
</bean>
以下示例使用p命名空间进行更简洁的XML配置。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close"
p:driverClassName="com.mysql.jdbc.Driver"
p:url="jdbc:mysql://localhost:3306/mydb"
p:username="root"
p:password="masterkaoli"/> </beans>
您还可以将java.util.Properties实例配置为:
<bean id="mappings"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <!-- typed as a java.util.Properties -->
<property name="properties">
<value>
jdbc.driver.className=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mydb
</value>
</property>
</bean>
Spring容器使用JavaBeans属性编辑器机制将< value/>元素中的文本转换为java.util.Properties实例。这是一个很好的快捷方式,并且是Spring团队支持使用嵌套的< value/>元素而不是value属性样式的少数地方之一。
idref元素
idref元素只是将容器中另一个bean的id(字符串值,而不是引用)传递给< constructor-arg/>或< property/>元素的一种防错方式。
<bean id="theTargetBean" class="..."/> <bean id="theClientBean" class="...">
<property name="targetName">
<idref bean="theTargetBean"/>
</property>
</bean>
上面的bean定义片段(在运行时)与下面的片段完全相同:
<bean id="theTargetBean" class="..." /> <bean id="client" class="...">
<property name="targetName" value="theTargetBean"/>
</bean>
第一种形式优于第二种形式,因为使用idref标记允许容器在部署时验证引用的命名bean是否确实存在。在第二种变体中,对传递给客户端bean的targetName属性的值不执行验证。错别字只有在客户端bean实际实例化时才会被发现(最有可能导致致命的结果)。如果客户端bean是一个原型bean,这个错别字和产生的异常可能只有在部署容器之后很久才会被发现。
< idref/>元素带来价值的一个常见之处(至少在Spring 2.0之前的版本中)是在一个ProxyFactoryBean定义中配置AOP拦截器。当您指定拦截器名称时,使用< idref/>元素可以防止您拼错拦截器id。
对其他beans(协作者)的引用
ref元素是<constructor-arg/>或<property/>定义元素中的最后一个元素。在这里,您将一个bean的指定属性的值设置为对由容器管理的另一个bean(协作者)的引用。被引用的bean是其属性将被设置的bean的依赖项,并且在设置属性之前根据需要对其进行初始化。(如果协作者是单例bean,它可能已经被容器初始化了。)所有引用最终都是对另一个对象的引用。作用域和验证取决于您是否通过bbean
, local,
parent
属性指定了另一个对象的id/name。
通过< ref/>bean属性的值可以与目标bean的id属性相同,也可以是目标bean的name属性中的一个值。
<ref bean="someBean"/>
通过parent
属性指定目标bean会创建对当前容器的父容器中的bean的引用。parent
属性的值可以与目标bean的id属性相同,也可以是目标bean的name属性中的一个值,并且目标bean必须位于当前容器的父容器中。当您有一个容器层次结构,并且您想用一个与父bean同名的代理来包装父容器中的现有bean时,您主要使用这个bean引用变体。
<!-- in the parent context -->
<bean id="accountService" class="com.foo.SimpleAccountService">
<!-- insert dependencies as required as here -->
</bean>
<!-- in the child (descendant) context -->
<bean id="accountService" <!-- bean name is the same as the parent bean -->
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target">
<ref parent="accountService"/> <!-- notice how we refer to the parent bean -->
</property>
<!-- insert other configuration and dependencies as required here -->
内部bean
< property/>或< constructor-arg/>元素中的< bean/>元素定义了一个所谓的内部bean。
<bean id="outer" class="...">
<!-- instead of using a reference to a target bean, simply define the target bean inline -->
<property name="target">
<bean class="com.example.Person"> <!-- this is the inner bean -->
<property name="name" value="Fiona Apple"/>
<property name="age" value="25"/>
</bean>
</property>
</bean>
内部bean定义不需要已定义的id或名称;如果指定,容器不使用这样的值作为标识符。容器还忽略了创建时的范围标志:内部bean总是匿名的,并且它们总是与外部bean一起创建。除了封闭bean之外,不可能将内部bean注入到协作bean中,也不可能独立访问它们。
集合
在<list/>, <set/>, <map/>,和<props/>元素中,分别设置了Java集合类型List
,Set
,Map
, 和Properties
的属性和参数。
<bean id="moreComplexObject" class="example.ComplexObject">
<!-- results in a setAdminEmails(java.util.Properties) call -->
<property name="adminEmails">
<props>
<prop key="administrator">administrator@example.org</prop>
<prop key="support">support@example.org</prop>
<prop key="development">development@example.org</prop>
</props>
</property>
<!-- results in a setSomeList(java.util.List) call -->
<property name="someList">
<list>
<value>a list element followed by a reference</value>
<ref bean="myDataSource" />
</list>
</property>
<!-- results in a setSomeMap(java.util.Map) call -->
<property name="someMap">
<map>
<entry key="an entry" value="just some string"/>
<entry key ="a ref" value-ref="myDataSource"/>
</map>
</property>
<!-- results in a setSomeSet(java.util.Set) call -->
<property name="someSet">
<set>
<value>just some string</value>
<ref bean="myDataSource" />
</set>
</property>
</bean>
map key或value或set value的值也可以是以下任何元素:
bean | ref | idref | list | set | map | props | value | null
集合合并
子属性集合的值集从父<props />继承所有属性元素,子支持值的值覆盖父集合中的值。
集合合并的局限性
您不能合并不同的集合类型(如映射和列表),如果您尝试这样做,则会引发适当的异常。必须在较低的继承子定义上指定merge属性;在父集合定义上指定merge属性是多余的,并且不会导致所需的合并。
强类型集合
public class Foo { private Map<String, Float> accounts; public void setAccounts(Map<String, Float> accounts) {
this.accounts = accounts;
}
}
<beans>
<bean id="foo" class="x.y.Foo">
<property name="accounts">
<map>
<entry key="one" value="9.99"/>
<entry key="two" value="2.75"/>
<entry key="six" value="3.99"/>
</map>
</property>
</bean>
</beans>
Null 和 “”字符串
<bean class="ExampleBean">
<property name="email" value=""/>
</bean>
exampleBean.setEmail("");
<bean class="ExampleBean">
<property name="email">
<null/>
</property>
</bean>
exampleBean.setEmail(null);
带有p命名空间的XML快捷方式
p-namespace使您能够使用bean元素的属性而不是嵌套的< property/>元素来描述您的属性值和/或协作bean。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean name="classic" class="com.example.ExampleBean">
<property name="email" value="foo@bar.com"/>
</bean> <bean name="p-namespace" class="com.example.ExampleBean" p:email="foo@bar.com"/>
</beans>
该示例在bean定义中显示了一个名为email的p-namespace属性。这告诉Spring包含一个属性声明。如前所述,p-namespace没有模式定义,因此您可以将属性的名称设置为属性名称。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean name="john-classic" class="com.example.Person">
<property name="name" value="John Doe"/>
<property name="spouse" ref="jane"/>
</bean> <bean name="john-modern" class="com.example.Person" p:name="John Doe" p:spouse-ref="jane"/> <bean name="jane" class="com.example.Person">
<property name="name" value="Jane Doe"/>
</bean>
</beans>
如您所见,此示例不仅包括使用p命名空间的属性值,还使用特殊格式来声明属性引用。第一个bean定义使用< property name= "spouse" ref="jane"/>来创建从bean john到bean jane的引用,而第二个bean定义使用p:spouse
-ref="jane "作为属性来做同样的事情。在这种情况下,spouse是属性名,而-ref部分表明这不是一个直接的值,而是对另一个bean的引用。
带有c-namespace的XML快捷方式
与带有p-namespace的XML快捷方式类似,Spring 3.1中新引入的c-namespace允许使用内联属性来配置构造函数参数,而不是嵌套的构造函数参数元素。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="bar" class="x.y.Bar"/>
<bean id="baz" class="x.y.Baz"/> <!-- traditional declaration -->
<bean id="foo" class="x.y.Foo">
<constructor-arg ref="bar"/>
<constructor-arg ref="baz"/>
<constructor-arg value="foo@bar.com"/>
</bean> <!-- c-namespace declaration -->
<bean id="foo" class="x.y.Foo" c:bar-ref="bar" c:baz-ref="baz" c:email="foo@bar.com"/> </beans>
对于构造函数参数名不可用的极少数情况(通常是在没有调试信息的情况下编译字节码),可以使用回退到参数索引:
<!-- c-namespace index declaration -->
<bean id="foo" class="x.y.Foo" c:_0-ref="bar" c:_1-ref="baz"/>
实际上,构造函数解析机制在匹配参数方面非常有效,所以除非真的需要,否则我们建议在整个配置中使用名称符号。
复合属性名
设置bean属性时,可以使用复合或嵌套属性名,只要路径的所有组件(除了最终属性名)都不为空。
<bean id="foo" class="foo.Bar">
<property name="fred.bob.sammy" value="123" />
</bean>
foo bean有一个fred属性,它有一个bob属性,它有一个sammy属性,最终sammy属性被设置为值123。为了实现这一点,foo的fred属性和fred的bob属性在构造bean后不能为null,否则会引发NullPointerException。
1.4.3使用Using depends-on
如果一个bean是另一个bean的依赖项,这通常意味着一个bean被设置为另一个bean的属性。通常,您可以使用基于XML的配置元数据中的< ref/>元素来实现这一点。但是,有时候beans之间的依赖关系不那么直接;例如,类中的静态初始化器需要被触发,比如数据库驱动注册。
在初始化使用此元素的bean之前,depends-on属性可以显式强制初始化一个或多个bean。以下示例使用depends-on属性来表示对单个bean的依赖:
<bean id="beanOne" class="ExampleBean" depends-on="manager"/>
<bean id="manager" class="ManagerBean" />
要表示对多个bean的依赖,请提供一个bean名称列表作为依赖属性的值,用逗号、空格和分号作为有效分隔符:
<bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao">
<property name="manager" ref="manager" />
</bean> <bean id="manager" class="ManagerBean" />
<bean id="accountDao" class="x.y.jdbc.JdbcAccountDao" />
bean定义中的dependent-on属性既可以指定初始化时的依赖关系,初始化时,被依赖的bean是先被初始化的;也可以指定相应的销毁时的依赖关系(仅限于单例bean)。depends-on适用于表面上看起来两个bean之间没有使用属性之类的强连接的bean,但是两个bean又确实存在前后依赖关系的情况,使用了depends-on的时候,依赖他人的bean是先于被依赖bean销毁的,因此依赖也可以控制关机顺序。
depends-on 强制的说明在该Bean 初始化之前,那些Bean必须先初始化!
ref通常用在一个Bean的属性指向另外一个Bean,这个Bean必须先初始化。
1.4.4.惰性初始化的beans
默认情况下,作为初始化过程的一部分,应用上下文实现急切地创建和配置所有的单例beans。通常,这种预实例化是可取的,因为配置或周围环境中的错误会立即被发现,而不是几小时甚至几天后。当这种行为不可取时,您可以通过将bean定义标记为惰性初始化来防止单例bean的预实例化。惰性初始化的bean告诉IoC容器在第一次被请求时创建一个bean实例,而不是在启动时。
在XML中,这种行为由< bean/>元素上的lazy-init属性控制;例如:
<bean id="lazy" class="com.foo.ExpensiveToCreateBean" lazy-init="true"/>
<bean name="not.lazy" class="com.foo.AnotherBean"/>
当前面的配置被应用程序上下文使用时,名为lazy的bean在应用程序上下文启动时不会被提前实例化,而not.lazy bean会被提前实例化。
然而,当延迟初始化的bean是未延迟初始化的单例bean的依赖项时,应用程序上下文会在启动时创建延迟初始化的bean,因为它必须满足单例bean的依赖项。惰性初始化的bean被注入到未惰性初始化的单例bean中。
您还可以通过在< beans/>元素上使用default-lazy-init属性来控制容器级别的惰性初始化;例如:
<beans default-lazy-init="true">
<!-- no beans will be pre-instantiated... -->
</beans>
1.4.5.Autowiring 协作者
Spring容器可以用autowire 协作beans之间的关系。您可以允许Spring通过检查ApplicationContext的内容来为您的bean自动解析协作者(其他bean)。自动布线有以下优点:
自动连接可以大大减少指定属性或构造函数参数的需要。
自动布线可以随着对象的发展更新配置。例如,如果您需要向一个类添加一个依赖项,该依赖项可以自动得到满足,而无需修改配置。因此,自动连接在开发过程中特别有用,当代码库变得更加稳定时,不排除切换到显式连接的选项。
no (默认)无自动布线。Bean引用必须通过ref元素来定义。对于较大的部署,不建议更改默认设置,因为显式指定协作者可以提供更好的控制和清晰度。在某种程度上,它记录了一个系统的结构。
byName 按属性名自动布线。Spring寻找与需要自动连接的属性同名的bean。例如,如果一个bean定义按名称设置为autowire,并且它包含一个master属性(也就是说,它有一个setMaster(..)方法),Spring寻找一个名为master的bean定义,并使用它来设置属性。
byType 如果容器中恰好存在一个属性类型的bean,则允许自动连接属性。如果存在多个bean,则会引发致命异常,这表明您不能对该bean使用byType自动连接。
constructor 类似于byType,但适用于构造函数参数。如果容器中没有恰好一个构造函数参数类型的bean,则会引发致命错误。
自动布线autowiring的局限性和缺点
当自动布线在整个项目中一致使用时,效果最佳。如果通常不使用自动连接,开发人员可能会对使用它来连接一个或两个bean定义感到困惑。
1、property
和constructor-arg设置中的显式依赖关系总是覆盖自动连接。您不能自动连接所谓的简单属性,如原语、字符串和类(以及此类简单属性的数组)。这个限制是被设计出来的。
2、自动布线不如显式布线精确。尽管如上表所述,Spring小心翼翼地避免在可能产生意外结果的模糊情况下进行猜测,但是Spring管理的对象之间的关系不再被明确记录。
3、布线信息可能不适用于可能从Spring容器生成文档的工具。
4、容器中的多个bean定义可能与要自动连接的setter方法或构造函数参数指定的类型相匹配。对于数组、集合或映射,这不一定是个问题。然而,对于期望单个值的依赖项,这种模糊性并不是任意解决的。如果没有唯一的bean定义可用,则会引发异常。
1、放弃自动布线,支持显式布线。
2、通过将bean定义的autowire-candidate属性设置为false来避免自动连接bean定义。
3、通过将< bean/>元素的primary 属性设置为true,将单个bean定义指定为主要候选对象。
4、实现基于注释的配置中可用的更细粒度的控制。
从autowiring中排除一个bean
基于每个bean,您可以从自动连接中排除一个bean。在Spring的XML格式中,将< bean/>元素的autowire-candidate属性设置为false该容器使特定的bean定义对自动连线基础结构(包括注释样式配置,如@自动连线)不可用。
autowire-candidate属性设计为仅影响基于类型的自动布线。它不影响按名称的显式引用,即使指定的bean没有被标记为autowire-candidate对象,该引用也会被解析。因此,如果名称匹配,按名称自动连接仍然会注入一个bean。
您还可以根据bean名称的模式匹配来限制自动连接候选对象。顶层< beans>元素在其default-autowire-candidates属性中接受一个或多个模式。例如,要将自动连线候选状态限制为名称以存储库结尾的任何bean,请提供*存储库的值。要提供多个模式,请在逗号分隔的列表中定义它们。
bean定义autowire-candidates属性的显式值true或false总是优先,对于这种bean,模式匹配规则不适用。
这些技术对于您永远不想通过自动连接注入到其他bean中的bean非常有用。这并不意味着排除的bean本身不能使用自动连接进行配置。相反,bean本身并不是自动连接其他bean的候选对象。
Spring framework核心的更多相关文章
- 【架构】spring framework核心框架体系结构
Spring官方文档,用的版本为4.3.11版本. 一.引用官方文档 2.2.1核心集装箱 所述核心容器由以下部分组成spring-core, spring-beans,spring-context, ...
- Spring Framework核心概念之Bean生命周期管理
目录 Spring Bean的生命周期 相关接口的分类 测试SpringBean生命周期的Demo程序 小结 Spring Bean的生命周期 Spring容器既Application或者WebApp ...
- spring framework核心框架体系结构
很实用,稀里糊涂的我算是看清了. 很多人都在用spring开发java项目,但是配置maven依赖的时候并不能明确要配置哪些spring的jar,经常是胡乱添加一堆,编译或运行报错就继续配置jar依赖 ...
- spring framework核心框架体系结构(转载)
作者:Dreawer 很多人都在用spring开发java项目,但是配置maven依赖的时候并不能明确要配置哪些spring的jar,经常是胡乱添加一堆,编译或运行报错就继续配置jar依赖,导致spr ...
- 1.Spring Framework 5.0 入门篇
1.为什么学习Spring? 随着对Java EE的不断接触和理解,你会发现Spring 在各个企业和项目中发挥着越来越重要的作用.掌握Spring 已成为我们IT行业生存必学的本领之一. Spri ...
- Java工程师之Spring Framework深度剖析专栏
系列前言 关于本系列 本系列章节目录 Spring Framework核心篇 重新来认识你的老朋友Spring框架 Spring容器装配Bean的三种方式 Spring Framework核心概念之B ...
- 小马哥讲Spring栈核心编程思想 Spring IoC+Bean+Framework
小马哥出手的Spring栈核心编程思想课程,可以说是非常专业和权威的Spring课程.课程主要的方向与核心是Spring Framework总览,带领同学们重新认识重新认识IoC,Spring IoC ...
- 浅谈对Spring Framework的认识
Spring Framework,作为一个应用框架,官方的介绍如下: The Spring Framework provides a comprehensive programming and con ...
- 手动创建Spring项目 Spring framework
之前学习框架一直是看的视频教程,并且在都配套有项目源码,跟着视频敲代码总是很简单,现在想深入了解,自己从官网下载文件手动搭建,就遇到了很多问题记载如下. 首先熟悉一下spring的官方网站:http: ...
随机推荐
- waitpid()系统调用学习
waitpid()的头文件 #include <sys/types.h> #include <sys/wait.h> pid_t waitpid(pid_t pid,int ...
- Dance Dance Revolution
今天我们来讲 Dance Dance Revolution这题 本题原网址 注意本题为多组输入输出,直到输入单个零而止(题面有点小问题) 很明显,此题为一道动态规划题(请不要妄想用贪心算法过这题,尽管 ...
- 划分问题(Java 动态规划)
Description 给定一个正整数的集合A={a1,a2,-.,an},是否可以将其分割成两个子集合,使两个子集合的数加起来的和相等.例A = { 1, 3, 8, 4, 10} 可以分割:{1, ...
- Linux(centos6.8)配置Tomcat环境
1.下载Linux版的Tomcat包 (1)通过官方下载 tomcat官方:https://tomcat.apache.org/download-80.cgi (2)通过分享下载 如网盘分享等途径 2 ...
- mysql 5.7添加server_audit 安全审计功能
mysql 5.7添加server_audit 安全审计功能 一.根据链接下载插件 参考链接下载 http://blog.itpub.net/31441024/viewspace-2213103 l ...
- 安装mongodb扩展
curl -O https://pecl.php.net/get/mongodb-1.2.3.tgz tar zxf mongodb-1.2.3.tgzcd mongodb-1.2.3 phpize ...
- C# Random类的正确应用
Random类介绍 Random类一个用于产生伪随机数字的类.这里的伪随机表示有随机性但是可以基于算法模拟出随机规律. Random类的构造方式有两种. Random r= new Random(). ...
- 在FL Studio中如何制作和优化你的人声和弦(Vocal Chords)
人声和弦在Future Bass.Melodic Dubstep等类型的电子音乐中被常用.与一般的和弦相同,其主要起到为主旋律做铺垫的效果,但是人声和弦加入了人声的因素,可以使得和弦更有趣,更有电子音 ...
- docker和k8s的概念-IaaS、PaaS、SaaS 的区别
docker和k8s 参考: 什么是Docker? Kubernetes概述 openstack,docker,mesos,k8s什么关系? IaaS.PaaS.SaaS的概念 SaaS:软件服务,S ...
- iOS UITextFeild获取高亮部分的长度
获取原因: 中英文混输时,会遇到长度统计不准的问题. 获取方法: NSString *toBeString = textField.text; NSString *lang = [[UITextInp ...