1、导入依赖IoC也被称为依赖注入(DI)。这是一个过程,对象仅通过构造函数参数、工厂方法的参数或在对象实例构造或从工厂方法返回后在对象实例上设置的属性来定义它们的依赖关系(即它们使用的其他对象)。然后,容器在创建bean时注入这些依赖项。这个过程基本上是bean本身的逆过程(因此称为控制反转),通过使用类的直接构造或服务定位器模式等机制来控制其依赖项的实例化或位置。
org.springframework.beans 和org.springframework.context是Spring框架的IoC容器的基础。
2、简单实现 2.1、编写一个具体的实现类org.springframework spring-context 5.3.17 org.springframework spring-beans 5.3.17
package com.pg.pojo;
public class Dog extends Animals{
public Dog() {
}
public Dog(String name, int age) {
super(name, age);
}
@Override
public void voice() {
System.out.println("狗叫");
}
}
2.2、录入信息
通过setter方式注入
通过构造器方式注入
1、 按类型
这种方式有个弊端就是,如果我们创建的实体类中有多个相同类型的属性,那么最好不要使用这种方式,可能会得到意想不到的结果
2、按name
3、按索引
使用p名称空间进行更简洁的XML配置
2.3、测试
ApplicationContext允许您读取bean定义并访问它们,如下例所示:
import com.pg.pojo.Dog;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class test {
@Test
public void testDog(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Dog dog = context.getBean("dog", Dog.class);
System.out.println(dog);
}
}
output:
Animals{name='旺财', age=12}
关于ClassPathXmlApplicationContext,该构造器中可以传入多个文件名,如:
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
这样会更有层次化,在编写代码时,将Service层的组件和Dao层的组件相区分。
最灵活的变体是将GenericApplicationContext与读者委托相结合
import com.pg.pojo.Dog;
import org.junit.Test;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
public class test {
@Test
public void testDog(){
// ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
// Dog dog = context.getBean("dog", Dog.class);
// System.out.println(dog);
GenericApplicationContext context = new GenericApplicationContext();
new XmlBeanDefinitionReader(context).loadBeanDefinitions("beans.xml");
context.refresh();
Dog dog = context.getBean("dog", Dog.class);
System.out.println(dog);
}
}
3、进阶
3.1、嵌套类创建实例
如果要为嵌套类配置bean定义,可以使用嵌套类的二进制名称或源名称。
例如,如果在com中有一个名为SomeThing的类。这个SomeThing类有一个名为OtherThing的静态嵌套类,它们可以用美元符号($)或点(.)分隔。所以bean定义中class属性的值应该是com.example.SomeThing$OtherThing或com.example.SomeThing.OtherThing
package com.pg.pojo;
public class Dog extends Animals{
public Dog() {
}
public Dog(String name, int age) {
super(name, age);
}
@Override
public void voice() {
System.out.println("狗叫");
}
public static class FlowerDog{
private String name;
private int age;
private int dot;
public FlowerDog() {
}
public FlowerDog(String name, int age, int dot) {
this.name = name;
this.age = age;
this.dot = dot;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getDot() {
return dot;
}
public void setDot(int dot) {
this.dot = dot;
}
@Override
public String toString() {
return "FlowerDog{" +
"name='" + name + ''' +
", age=" + age +
", dot=" + dot +
'}';
}
}
}
之后添加到xml配置文件中
测试代码:
import com.pg.pojo.Dog;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class test {
@Test
public void testDog() {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Dog dog = context.getBean("dog", Dog.class);
System.out.println(dog);
Dog.FlowerDog ddd = context.getBean("hu",Dog.FlowerDog.class);
System.out.println(ddd);
}
}
3.2、使用静态工厂方法进行实例化
定义使用静态工厂方法创建的bean时,使用class属性指定包含静态工厂方法的类,并使用名为factory-method的属性指定工厂方法本身的名称。您应该能够调用此方法(使用可选参数,如下文所述),并返回一个活动对象,该对象随后将被视为是通过构造函数创建的。这种bean定义的一个用途是在遗留代码中调用静态工厂。
下面的bean定义指定通过调用工厂方法来创建bean。该定义没有指定返回对象的类型(类),只指定包含工厂方法的类。在本例中,createInstance()方法必须是静态方法。以下示例显示了如何指定工厂方法:
以下示例显示了一个可与前面的bean定义一起使用的类:
package com.pg.pojo;
public class DogFactory {
private static Dog dog = new Dog("小花",15);
public static Dog createInstance(){
return dog;
}
}
测试代码:
import com.pg.pojo.Dog;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class test {
@Test
public void testDog() {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Dog dog = context.getBean("dog", Dog.class);
System.out.println(dog);
}
}
3.3、使用实例工厂方法进行实例化
与通过静态工厂方法进行实例化类似,使用实例工厂方法进行实例化会从容器中调用现有bean的非静态方法来创建新bean。要使用这种机制,请将class属性留空,并在factory bean属性中指定当前(或父或父)容器中的bean的名称,该容器包含要调用以创建对象的实例方法。使用factory method属性设置factory方法本身的名称。下面的示例显示了如何配置这样的bean:
以下示例显示了相应的类:
package com.pg.pojo;
public class DogFactory {
private static Dog dog = new Dog("小花",15);
public Dog createInstance(){
return dog;
}
}
一个工厂类也可以包含多个工厂方法,如下例所示:
测试代码:
import com.pg.pojo.Cat;
import com.pg.pojo.Dog;
import org.junit.Test;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
public class test {
@Test
public void testDog() {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Dog dog = context.getBean("dog", Dog.class);
System.out.println(dog);
Dog dog1 = context.getBean("dog1", Dog.class);
System.out.println(dog1);
System.out.println(dog==dog1);
Cat cat = context.getBean("cat", Cat.class);
System.out.println(cat);
}
}
4、Dependencies
依赖项注入(DI)是一个过程,对象仅通过构造函数参数、工厂方法的参数或对象实例构造或从工厂方法返回后设置的属性来定义其依赖项(即与它们一起工作的其他对象)。然后,容器在创建bean时注入这些依赖项。这个过程基本上是bean本身的逆过程(因此称为控制反转),通过使用类的直接构造或服务定位器模式来控制其依赖项的实例化或位置。
使用DI原则,代码更干净,当对象具有依赖关系时,解耦更有效。对象不查找其依赖项,也不知道依赖项的位置或类别。因此,您的类变得更容易测试,尤其是当依赖项位于接口或抽象基类上时,这允许在单元测试中使用存根或模拟实现。
如果主要使用构造函数注入,则有可能创建无法解决的循环依赖场景。
例如:类A通过构造函数注入需要类B的实例,类B通过构造函数注入需要类A的实例。如果为类A和类B配置bean以相互注入,Spring IoC容器会在运行时检测到这个循环引用,并抛出BeanCurrentlyIncremationException。
一种可能的解决方案是编辑某些类的源代码,由setter而不是构造函数进行配置。或者,避免构造函数注入,只使用setter注入。
你通常可以相信Spring会做正确的事情。它在容器加载时检测配置问题,例如对不存在的bean的引用和循环依赖。Spring在bean实际创建时尽可能晚地设置属性并解析依赖项。这意味着,如果创建对象或其依赖项时出现问题,正确加载的Spring容器稍后可以在请求对象时生成异常 — 例如,bean由于缺少或无效属性而引发异常。这可能会延迟某些配置问题的可见性,这就是为什么ApplicationContext实现默认情况下会预实例化单例bean。在实际需要这些bean之前,您需要花费一些前期时间和内存来创建它们,但在创建ApplicationContext时,您会发现配置问题,而不是在以后。您仍然可以覆盖此默认行为,以便单例bean可以延迟初始化,而不是急切地预实例化。
除了上述几种配置之外,还可以通过使用和标签
4.2.2、idref元素张三 13
idref元素只是一种防错的方法,可以将容器中另一个bean的id(字符串值,而不是引用)传递给
前面的bean定义片段(在运行时)与下面的片段完全等效:
第一种形式比第二种形式更可取,因为使用idref标记可以让容器在部署时验证引用的命名bean是否确实存在。在第二个变体中,没有对传递给客户机bean的targetName属性的值执行验证。只有当客户机bean被实际实例化时,才会发现输入错误(最有可能是致命的结果)。如果客户机bean是一个原型bean,那么这个输入错误和由此产生的异常可能只有在部署容器很久之后才会被发现。
4.2.3、Inner Beans< property>或< constructor-arg>元素中的 < bean> 元素定义了一个内部bean,如下例所示:
张三 13
内部bean定义不需要定义的ID或名称。如果指定,容器不使用这样的值作为标识符。容器在创建时也会忽略scope标志,因为内部bean总是匿名的,并且总是使用外部bean创建。不可能单独访问内部bean,也不可能将它们注入到协作bean中,而不是封闭bean中。
作为一种特殊情况,可以从自定义作用域接收销毁回调 — 例如,对于单例bean中包含的请求范围的内部bean。内部bean实例的创建与其包含的bean相关联,但销毁回调允许它参与请求范围的生命周期。这种情况并不常见。内部bean通常只是共享其包含bean的范围。
< list />、< set/>、< map/>和< props/>元素分别设置Java集合类型list、set、map和properties的属性和参数。以下示例显示了如何使用它们:
张三 13 administrator@example.org support@example.org development@example.org
上面只是列举了list和props的用法,其余的也差不多。
4.2.5、Collection MergingSpring容器还支持合并集合。 应用程序开发人员可以定义父< list/>、 < map/>、< set/>或< props/>元素,并让子< list/>、< map/>、< set/>或< props/>元素继承并重写父集合中的值。也就是说,子集合的值是父集合和子集合的元素合并的结果,子集合的元素覆盖父集合中指定的值。
关于合并的这一节讨论了父子bean机制。不熟悉父bean和子bean定义的读者可能希望在继续之前阅读相关部分。
以下示例演示集合合并:
......... administrator@example.org support@example.org development@example.org ......... client@example.com
注意,在子bean定义的adminEmails属性的< props/>元素上使用了merge=true属性。当容器解析并实例化子bean时,生成的实例有一个adminEmails属性集合,其中包含将子bean的adminEmails集合与父bean的adminEmails集合合并的结果。下面的列表显示了结果:
子属性集合的值集继承父集合的所有属性元素< props/>,子集合的支持值覆盖父集合中的值。
这种合并行为同样适用于< list/>、< map/>和< set/>集合类型。在< list/>元素的特定情况下,与列表集合类型(即值的有序集合的概念)相关联的语义将得到维护。父列表的值先于子列表的所有值。对于Map、Set和Properties集合类型,不存在排序。因此,对于容器内部使用的关联映射、集合和属性实现类型下的集合类型,没有有效的排序语义。
不能合并不同的集合类型(例如map和list)。如果确实尝试这样做,则会引发相应的异常。必须在较低的继承子定义上指定merge属性。在父集合定义上指定“合并”属性是多余的,不会导致所需的合并。
如果一个bean是另一个bean的依赖项,这通常意味着一个bean被设置为另一个bean的属性。通常,您可以通过基于XML的配置元数据中的< ref/>元素来实现这一点。然而,有时候bean之间的依赖关系不那么直接。例如,需要触发类中的静态初始值设定项,例如数据库驱动程序注册。depends-on属性可以显式地强制在初始化使用该元素的bean之前初始化一个或多个bean。以下示例使用depends-on属性表示对单个bean的依赖关系:
张三 13 administrator@example.org support@example.org development@example.org
要表示对多个bean的依赖关系,请提供一个bean名称列表,作为depends-on属性的值(逗号、空格和分号是有效的分隔符):
4.4、Lazy-initialized Beans
默认情况下,ApplicationContext实现在初始化过程中急切地创建和配置所有单例bean。通常,这种预实例化是可取的,因为配置或周围环境中的错误会立即被发现,而不是几个小时甚至几天后。当这种行为不可取时,可以通过将bean定义标记为延迟初始化来防止单例bean的预实例化。惰性初始化bean告诉IoC容器在第一次请求时而不是在启动时创建bean实例。



