2、基于XML的依赖注入详细配置
创始人
2024-04-20 12:26:40
0

一、依赖注入详细配置

1、通过P名称空间为Bean注入值

1、简化setter方式注入的配置,可以不使用子标签来为属性设置值,直接在标签中设置各依赖项的值。
2、注入值类型说明:
  • 对于String、基本类型及其包装类使用p:属性名="值"
  • 对于对象类型使用p:属性名-ref="值",表示引用一个Bean。
3、实现步骤:
  • xml中导入p名称空间:xmlns:p="http://www.springframework.org/schema/p"
  • 写带前缀属性:p:属性="值"



@Test
public void test01() {// 通过配置文件创建容器对象ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");// 获取bean对象Teacher bean = context.getBean("teacher1", Teacher.class);System.out.println(bean);
}
/*** 运行结果:* setter属性注入age* setter属性注入name* Teacher{姓名='张三', 年龄=25}*/

2、null值注入

1、Spring将默认值为空的属性的值视为空字符串。如果将value设置为null,Spring会识别成“null”字符串,如果要真正的设置为null,就应该使用标签,才表示真正的注入null值。


@Test
public void test01() {// 通过配置文件创建容器对象ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");// 获取bean对象Teacher bean = context.getBean("teacher2", Teacher.class);System.out.println(bean.getName() == null);System.out.println(bean.getAge());System.out.println(bean.getSex() == null);System.out.println(bean);
}
/*** 运行结果:* false* 18* true* Teacher{姓名='', 年龄=18', 性别=}*/

3、ref引用

1、在标签中有一个ref属性,用于将Bean的指定的属性的值设置为容器管理的另一个Bean的引用,也就是引用类型属性设置依赖的方式。
2、在为引用类型属性设置依赖之前,必须保证被引用的Bean已经被初始化,ref属性的值要与被引用的Bean的id或name属性中的一个值相等。
3、使用ref为引用类型设置依赖,获取的到的Bean与直接通过IoC容器中获取到的Bean,是同一个Bean。
public class Subject {private String name;private Teacher teacher;public Subject(String name, Teacher teacher) {this.name = name;this.teacher = teacher;System.out.println("构造器依赖注入");}public Teacher getTeacher() {return teacher;}@Overridepublic String toString() {return "Subject{" +"科目='" + name + '\'' +", 老师='" + teacher + '\'' +'}';}
}





@Test
public void test01() {// 通过配置文件创建容器对象ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");// 获取bean对象Subject subject = context.getBean("subject", Subject.class);System.out.println(subject);// 通过subject获取teacherTeacher teacher1 = subject.getTeacher();// 通过ioc容器直接获取teacherTeacher teacher2 = context.getBean("teacher3", Teacher.class);// 比较两个是否相等System.out.println("teacher1与teacher2是否相等:" + (teacher1 == teacher2));}
}
/*** 运行结果:* Subject{科目='Java', 老师='Teacher{姓名='张三', 性别='男', 年龄=32}'}* teacher1与teacher2是否相等:true*/

4、内部Bean

1、在以及一些集合标签中可以使用子标签,表示一个内部Bean,如果要为一个引用类型的属性设置依赖,在不使用ref属性或者ref标签的情况下,可以定义自己的内部的Bean。
2、与外部Bean的区别是:
  • 内部Bean不需要定义id或name属性,因为这个对象就相当外部Bean自己的对象,就算设置了id或name,容器也不会使用这些值作为Bean的名字,而且也不能通过IoC获取Bean,相当于内部Bean是一个匿名的
  • 容器在创建时也会忽略内部Bean的scope作用域属性



@Test
public void test02() {// 通过配置文件创建容器对象ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");// 获取bean对象Subject subject = context.getBean("subject1", Subject.class);System.out.println(subject);
}
/*** 运行结果:* Subject{科目='Java', 老师='Teacher{姓名='张三', 性别='男', 年龄=32}'}*/

5、集合属性的注入

1、Spring支持集合注入方式,提供了一系列的标签分别为Java中的List、Set、Map、Properties、Array类型的集合注入依赖,由于集合的元素既可以是基本类型也可以是对象甚至集合,因此集合注入非常灵活。另外集合注入支持泛型转换,注入的时候会自动将value的字符串值转换为对应泛型类型。
@Data
public class CollectionBean {private List list;private Set set;private Map map;private Properties properties;private Object[] array;
}
 

List注入示例:


111

@Test
public void test03() {// 通过配置文件创建容器对象ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");// 获取bean对象CollectionBean collectionBean = context.getBean("collectionBean", CollectionBean.class);List list = collectionBean.getList();System.out.println(list);
}
/*** 运行结果:* [111, Teacher{姓名='张三', 性别='男', 年龄=32}, Teacher{姓名='张三', 性别='男', 年龄=32}]*/
 

Set注入示例:


111

@Test
public void test04() {// 通过配置文件创建容器对象ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");// 获取bean对象CollectionBean collectionBean = context.getBean("collectionBean1", CollectionBean.class);Set set = collectionBean.getSet();System.out.println(set);
}
/*** 运行结果:* [111, Teacher{姓名='张三', 性别='男', 年龄=32}, Teacher{姓名='张三', 性别='男', 年龄=32}]*/
 

Map注入示例:




@Test
public void test05() {// 通过配置文件创建容器对象ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");// 获取bean对象CollectionBean collectionBean = context.getBean("collectionBean2", CollectionBean.class);Map map = collectionBean.getMap();System.out.println(map);
}
/*** 运行结果:* {测试=哈哈哈哈, Teacher{姓名='张三', 性别='男', 年龄=32}=Subject{科目='Java', 老师='Teacher{姓名='张三', 性别='男', 年龄=32}'}, Oracle=Teacher{姓名='李四', 性别='男', 年龄=35}}*/

Properties注入示例:


哈哈哈456

@Test
public void test06() {// 通过配置文件创建容器对象ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");// 获取bean对象CollectionBean collectionBean = context.getBean("collectionBean3", CollectionBean.class);Properties properties = collectionBean.getProperties();System.out.println(properties);
}
/*** 运行结果:* {123=456, 测试=哈哈哈}*/

Array注入示例:


111

@Test
public void test07() {// 通过配置文件创建容器对象ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");// 获取bean对象CollectionBean collectionBean = context.getBean("collectionBean4", CollectionBean.class);Object[] array = collectionBean.getArray();for (int i = 0; i < array.length; i++) {System.out.println(array[i]);}
}
/*** 运行结果:* 111* Teacher{姓名='张三', 性别='男', 年龄=32}* Teacher{姓名='张三', 性别='男', 年龄=32}*/

6、value字面量

1、对于基本类型、String、包装类类型的属性,可以直接使用value属性的字符串值来注入值。
2、在最后注入的时候Spring会自动将这些值从String 转换为属性或参数的实际类型。
3、也可以使用标签来设置值
4、上面一些示例中使用到value标签,请参考。

7、级联属性赋值

1、级联属性可以修改属性的属性,但是原来的Bean的属性值可能会被修改





@Test
public void test08() {// 通过配置文件创建容器对象ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");// 获取bean对象Teacher teacher = context.getBean("teacher3", Teacher.class);// 获取bean对象Subject subject = context.getBean("subject2", Subject.class);System.out.println(teacher);System.out.println(subject);
}
/*** 运行结果:可以看到在修改级联属性后,被引用的原Bean中的属性值被改变* Teacher{姓名='李四', 性别='男', 年龄=32}* Subject{科目='Java', 老师='Teacher{姓名='李四', 性别='男', 年龄=32}'}*/

8、parent继承(复用属性值)

1、在标签中有一个parent属性,它用于指定目标Bean将使用父Bean的属性和配置,用于相同属性以及值的复用,如果当前Bean中有与父Bean相同的属性,且当前Bean中为某一个相同的属性重新设置了值,那么当前Bean的其他属性值将复用父Bean的,重新设置值的属性将用自己的值。
2、parent属性的值与目标父Bean的id属性或name属性中的一个值相同。





@Test
public void test09() {// 通过配置文件创建容器对象ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");// 获取bean对象Teacher teacher = context.getBean("teacher5", Teacher.class);System.out.println(teacher);
}
/*** 运行结果:* Teacher{姓名='李四', 性别='男', 年龄=32}*/

9、abstract抽象(只允许被继承)

1、在标签中有一个abstract属性,当值为true时,表示当前Bean是一个抽象的,不能获取它的实例,只能被别人用来继承,类似于一个属性和值的模版;如果尝试获取则会抛出如下错误:Error creating bean with name 'XXXXXXX': Bean definition is abstract





@Test
public void test09() {// 通过配置文件创建容器对象ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");// 获取bean对象Teacher teacher5 = context.getBean("teacher5", Teacher.class);System.out.println(teacher5);Teacher teacher4 = context.getBean("teacher4", Teacher.class);System.out.println(teacher4);
}
/*** 运行结果:* Teacher{姓名='李四', 性别='男', 年龄=32}* org.springframework.beans.factory.BeanIsAbstractException: Error creating bean with name 'teacher4': Bean definition is abstract*/

10、depends-on强制顺序

1、如果某个Bean是另一个Bean的依赖项,则通常意味着这个Bean被设置为另一个Bean的属性。但是,有时Bean之间的依赖关系不太直接,他们之间没有直接的引用关系。
2、标签中的depends-on属性可以强制在初始化使用此元素的Bean之前初始化一个或多个指定的Bean,depends-on的值是先初始化的Bean的id或者name,需要指定多个时,可以使用“,”、“;”、“ ”符号分隔,Spring会按照depend-on中定义的顺序来初始化Bean。



@Test
public void test10() {// 通过配置文件创建容器对象ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
}
/*** 运行结果:* Person初始化* Teacher初始化* People初始化*/

11、scope作用域

1、标签中的scope属性用于定义Bean的作用域,Spring Framework支持六个作用域,其中后面四个作用域仅在使用web感知的ApplicationContext时可用。还可以创建自定义范围。
2、Spring 5.x支持的作用域属性如下:
  • singleton:单例的(默认值),Bean在IoC容器中仅有一个对象实例;在容器启动完成之前就已经创建好对象,只要容器在,对象一直活着,并且不会再创建;任何获取Bean的操作都是同一个对象。
  • prototype:原型的(多实例),一个Bean可以有多个实例,容器启动时不会初始化设置为prototype的Bean;在每次获取的时候都会创建一个新的Bean实例。
  • request:将单个Bean定义限定为单个HTTP请求的生命周期。也就是说,每个HTTP请求都有自己的Bean实例,该实例是在单个Bean定义的后面创建的。
  • session:将单个Bean定义限定为HTTP的生命周期Session
  • application:将单个Bean定义限定为ServletContext
  • websocket:将单个Bean定义限定为WebSocket



@Test
public void test01() {// 通过配置文件创建容器对象ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
}
/*** 运行结果:在容器初始化时候,并没有创建Teacher实例* Person初始化*/
@Test
public void test02() {// 通过配置文件创建容器对象ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");Person person1 = context.getBean("person", Person.class);Person person2 = context.getBean("person", Person.class);System.out.println("person1与person2是否相等:" + (person1 == person2));System.out.println("================================");Teacher teacher1 = context.getBean("teacher", Teacher.class);Teacher teacher2 = context.getBean("teacher", Teacher.class);System.out.println("teacher1与teacher2是否相等:" + (teacher1 == teacher2));
}/*** 运行结果:单例的Person只会初始化一次,并且每次获取实例都是相同的,多例的Teacher在每次获取时,* 才会初始化,并且每个实例不相同。* Person初始化* person1与person2是否相等:true* ================================* Teacher初始化* Teacher初始化* teacher1与teacher2是否相等:false*/

12、静态工厂方式创建Bean

1、本质上是工厂模式的应用,对象由我们创建,但是由Spring调用一次静态的创建方法,将创建的对象存入容器。静态工厂类本身并不会实例化
2、使用静态工厂方法实例化Bean时,标签中的class属性不再是要获取的Bean的全路径类名,而是静态工厂的全路径类名,并使用factory-method的属性指定获取bean对象的工厂方法的名称(注意该方法必须是静态方法)
/*** @Date: 2022/11/13* 静态工厂*/
public class StaticFactory {/*** 静态方法,调用方式:StaticFactory.getPerson()* @return*/public static Person getPerson() {System.out.println("静态工厂方法创建对象开始");Person person = new Person("张三", 20);person.setSex("男");person.setEmail("111111@qq.com");return person;}
}


@Test
public void test03() {// 通过配置文件创建容器对象ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");Person person1 = context.getBean("person1", Person.class);System.out.println(person1);
}
/*** 运行结果:* 静态工厂方法创建对象开始* Person{姓名='张三', 性别='男', 年龄=20', 邮箱=111111@qq.com}*/

13、实例工厂方式创建Bean

1、实例工厂本身需要实例化工厂类的,然后调用工厂实例中的非静态方法创建Bean
2、使用实例工厂方法实例化Bean时,标签中的class属性置空,使用factory-bean的属性指定实例工厂的名字,使用factory-method属性指定非静态工厂方法的名称
3、factory bean虽然代表一个工厂,其实例仍然交给Spring管理。
/*** @Date: 2022/11/13* 实例工厂*/
public class InstanceFactory {/*** 实例方法,调用方式:new InstanceFactory().getPerson()* @return*/public Person getPerson() {System.out.println("实例工厂方法创建对象开始");Person person = new Person("李四", 20);person.setSex("女");person.setEmail("2222222@qq.com");return person;}
}




@Test
public void test04() {// 通过配置文件创建容器对象ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");Person person1 = context.getBean("person2", Person.class);System.out.println(person1);
}
/*** 运行结果:* 实例工厂方法创建对象开始* Person{姓名='李四', 性别='女', 年龄=20', 邮箱=2222222@qq.com}*/

14、FactoryBean创建Bean

1、FactoryBean是一个工厂类接口,可以通过实现该接口定制实例化Bean 的逻辑。
2、提供了三种方法:
  • T getObject():返回此工厂创建的对象的实例。该实例可能会被共享,具体取决于该工厂是返回单例还是原型。
  • Class getObjectType():返回getObject()方法返回的对象类型,如果不知道类型,则返回null
  • default boolean isSingleton() :获取这个工厂管理的对象是否为单例的,true表示单例(默认值),false表示非单例。
3、注意:
  • isSingleton方法返回false并不一定表示返回的对象是单实例的。扩展SmartFactoryBean接口的实现可以通过其SmartFactoryBean.isPrototype()方法显式指示独立实例。
  • 如果isSingleton()实现返回false,则简单地假定未实现此扩展接口的普通FactoryBean实现始终返回独立实例
  • 默认实现返回true,因为FactoryBean通常管理一个单例实例
  • IoC容器在启动的时候不会创建实现FactoryBean接口类getObject方法返回的实例,只有从IoC容器中获取的时候才会创建对应的实例
/*** @Date: 2022/11/15* 自定义FactoryBean,实现FactoryBean接口,Spring会自动调用工厂方法创建实例*/
public class CustomFactoryBean implements FactoryBean {@Overridepublic Person getObject() throws Exception {System.out.println("自定义FactoryBean工厂");Person person = new Person("王武", 40);person.setSex("男");person.setEmail("333333@qq.com");return person;}@Overridepublic Class getObjectType() {return Person.class;}@Overridepublic boolean isSingleton() {return false;}
}

@Test
public void test05() {// 通过配置文件创建容器对象ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");// 在获取的时候才会创建customFactoryBean类中返回的对象Object customFactoryBean = context.getBean("customFactoryBean");System.out.println(customFactoryBean.toString());
}
/*** 运行结果:* 自定义FactoryBean工厂* Person{姓名='王武', 性别='男', 年龄=40', 邮箱=333333@qq.com}*/

二、生命周期回调

1、概述

1、Spring在容器初始化Bean之后(完成依赖注入后)和销毁前都提供了回调的方法,称之为生命周期的回调方法,Spring提供了三种方式来完成生命周期的回调。

2、回调接口方式

1、初始化回调:实现InitializingBean接口中的afterPropertiesSet方法。在容器为Bean设置了所有必要的属性后,该接口让Bean执行初始化工作。
2、销毁回调:实现DisposableBean接口中的destroy方法。在包含目标Bean的容器被销毁时进行回调。
3、注意:
  • 官方不建议使用InitializingBean、DisposableBean接口来完成生命周期的回调,原因是它们会将代码耦合到Spring中,建议使用下面的两种方式来完成生命周期的回调。
  • 对于单实例Bean来说生命周期顺序:容器启动(构造器)->初始化方法->容器销毁(销毁方法)
  • 对于多实例Bean来说生命周期顺序:在获取Bean的前提下(多实例Bean在获取的时候才进行初始化),顺序如下:构造器->初始化方法->容器销毁不会调用Bean的销毁方法
/*** @Date: 2022/11/15* 生命周期回调*/
public class Lifecycle implements InitializingBean, DisposableBean {private String name;private String sex;public Lifecycle() {System.out.println("Lifecycle构造方法");}public String getName() {return name;}public void setName(String name) {System.out.println("Lifecycle设置name属性值");this.name = name;}public String getSex() {return sex;}public void setSex(String sex) {System.out.println("Lifecycle设置sex属性值");this.sex = sex;}@Overridepublic String toString() {return "Lifecycle{" +"姓名='" + name + '\'' +", 性别='" + sex +'}';}@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("Lifecycle执行初始化回调方法");}@Overridepublic void destroy() throws Exception {System.out.println("Lifecycle执行销毁回调方法");}
}


@Test
public void test06() {// 通过配置文件创建容器对象ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");Lifecycle lifecycle = context.getBean("lifecycle", Lifecycle.class);System.out.println(lifecycle);// 销毁容器context.close();
}
/*** 运行结果:* Lifecycle构造方法* Lifecycle设置name属性值* Lifecycle设置sex属性值* Lifecycle执行初始化回调方法* Lifecycle{姓名='张三', 性别='男}* Lifecycle执行销毁回调方法*/
通过运行结果可以看出:Spring容器启动,初始化Bean时优先执行构造方法,并且在为属性注入值之后,才会执行初始化回调方法,容器销毁时,执行销毁回调方法

3、自定义方法方式

1、标签中的init-method、destroy-method两个属性用来指定Bean初始化回调和销毁回调方法的。
/*** @Date: 2022/11/15* 生命周期回调*/
public class Lifecycle1 {private String name;private String sex;public Lifecycle1() {System.out.println("Lifecycle1构造方法");}public String getName() {return name;}public void setName(String name) {System.out.println("Lifecycle1设置name属性值");this.name = name;}public String getSex() {return sex;}public void setSex(String sex) {System.out.println("Lifecycle1设置sex属性值");this.sex = sex;}@Overridepublic String toString() {return "Lifecycle1{" +"姓名='" + name + '\'' +", 性别='" + sex +'}';}public void init() throws Exception {System.out.println("Lifecycle1执行初始化回调方法");}public void destroy() throws Exception {System.out.println("Lifecycle1执行销毁回调方法");}
}


@Test
public void test06() {// 通过配置文件创建容器对象ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");Lifecycle1 lifecycle = context.getBean("lifecycle", Lifecycle1.class);System.out.println(lifecycle);// 销毁容器context.close();
}
/*** 运行结果:* Lifecycle1构造方法* Lifecycle1设置name属性值* Lifecycle1设置sex属性值* Lifecycle1执行初始化回调方法* Lifecycle1{姓名='张三', 性别='男}* Lifecycle1执行销毁回调方法*/

4、注解方式

1、Spring2.5中引入了注释的支持,可以使用@PostConstruct和@PreDestroy注解实现初始化回调和销毁回调。
  • @PostConstruct:在Bean创建完成并且属性注入值完成,执行初始化方法。
  • @PreDestroy:在容器销毁Bean之前,执行销毁方法。
/*** @Date: 2022/11/15* 生命周期回调*/
@Component
public class Lifecycle2 {private String name;private String sex;public Lifecycle2() {System.out.println("Lifecycle2构造方法");}@Overridepublic String toString() {return "Lifecycle2{" +"姓名='" + name + '\'' +", 性别='" + sex +'}';}@PostConstructpublic void init() throws Exception {this.name = "张三";this.sex = "男";System.out.println("Lifecycle2执行初始化回调方法");}@PreDestroypublic void destroy() throws Exception {System.out.println("Lifecycle2执行销毁回调方法");}
}
@Test
public void test06() {// 通过注解创建容器对象,配置扫描的类AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.itan.beans");Lifecycle2 lifecycle = context.getBean("lifecycle2", Lifecycle2.class);System.out.println(lifecycle);// 销毁容器context.close();
}
/*** 运行结果:* Lifecycle2构造方法* Lifecycle2执行初始化回调方法* Lifecycle2{姓名='张三', 性别='男}* Lifecycle2执行销毁回调方法*/

5、生命周期回调方法总结

1、可以将上面3种方式结合使用,如果多种方式一起使用,每种方式都配置了一个不同的方法名,那么他们的执行顺序将会如下面的顺序所示。但是如果他们都配置了同一个方法名,那么该方法只会执行一次。
2、同一个Bean配置多个生命周期机制,初始化方法不同,调用顺序如下:
  • @PostConstruct注解修饰的方法。
  • InitializingBean接口里面的afterPropertiesSet()方法。
  • 基于XML的自定义的初始化方法。
3、Destroy方法的调用顺序相同:
  • @PreDestroy注解修饰的方法。
  • DisposableBean接口里面的destroy()方法。
  • 基于XML的自定义的销毁方法。

6、BeanPostProcessor容器扩展点

1、BeanPostProcessor接口也叫后置处理器,其作用是在Bean对象在实例化和依赖注入完毕后,在显示调用初始化方法的前后添加自定义逻辑
2、方法说明:
  • postProcessBeforeInitialization:在任何Bean初始化回调(如InitializingBean的afterPropertiesSet或自定义初始化方法)之前,将此BeanPostProcessor应用于给定的新Bean实例。
  • postProcessAfterInitialization:在任何Bean初始化回调(如InitializingBean 的afterPropertiesSet或自定义初始化方法)之后,将此BeanPostProcessor应用于给定的新Bean实例。
3、注意:
  • 两个方法不能返回null,如果返回null那么在后续初始化方法将报空指针异常或者通过getBean()方法获取不到bena实例对象,因为后置处理器从Spring IoC容器中取出bean实例对象没有再次放回IoC容器中
/*** @Date: 2022/11/17* 自定义BeanPostProcessor*/
public class CustomBeanPostProcessor implements BeanPostProcessor {/*** bean在调用初始化方法之前调用* @param bean			bean实例* @param beanName		bean的名称* @return* @throws BeansException*/@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println(beanName + ":将要调用初始化方法了");return bean;}/*** bean在调用初始化方法之后调用* @param bean			bean实例* @param beanName		bean的名称* @return* @throws BeansException*/@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println(beanName + ":初始化方法执行完了");return bean;}
}


@Test
public void test07() {// 通过配置文件创建容器对象ConfigurableApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");// 销毁容器context.close();
}
/*** 运行结果:* Lifecycle1构造方法* Lifecycle1设置name属性值* Lifecycle1设置sex属性值* lifecycle:将要调用初始化方法了* Lifecycle1执行初始化回调方法* lifecycle:初始化方法执行完了* Lifecycle1执行销毁回调方法*/

三、基于XML的自动装配

1、概述

1、Spring支持某些情况下对于依赖自动注入的情况,通过检查ApplicationContext的内容,可以让Spring自动解析、配置Bean的依赖,不需要手写配置文件。
2、自动装配的优点:
  • 可以减少手动指定属性或通过构造参数为Bean注入所需要的值。
  • 自动装配可以自动更新对象的所依赖的属性的值。(比如,某个类新添加了依赖项,则无需修改配置文件即可自动装配该依赖)。
3、注意:仅支持自定义类型的Bean进行自动装配,不支持基本数据类型的自动装配

2、自动装配的几种模式

1、使用基于XML的配置元数据时,可以通过设置< bean />标签的autowire属性来设置自动注入模式,共有四种:
  • no:默认值,不启用自动装配,通过手工设置ref属性来进行装配Bean。虽然配置麻烦,但是依赖关系明确。
  • byName:通过属性名称自动装配,Spring寻找一个与需要自动装配的属性同名的Bean,并为其注入。例如,如果一个Bean定义被设置为按名称自动装配,并且它包含一个master属性(即它有一个 setMaster方法),那么IoC容器(通过ioc.getbean("xxx")方式)在配置文件中查找id或name的值为master的Bean,并使用setter方法为其注入。注意这里的自动setter相比前面的手动setter注入有限制,即setXXX方法的XXX一定要对应一个容器中实例的name才行,否则不会注入。
  • byType:通过属性类型查找Bean依赖的对象并为其注入。例如,如果一个Bean定义被设置为按类型自动装配,并且它包含一个master属性(即它有一个setMaster方法),属性master的类型为com.Master,那么IoC容器(通过ioc.getbean(xxx.class)方式)在配置文件中查找class为com.Master的Bean,然后通过setter方法为其注入。如果容器中有多个相同类型的Bean,那么就会抛出expected single matching bean but found x: xxx异常。
  • constructor:同byType装配方式一样,也是通过类型查找依赖对象,与byType的区别在于它不是使用setter方法注入,而是使用构造器注入。如果容器中有多个相同的类型的Bean,也会抛出expected single matching bean but found x: xxx异常。

3、通过名称装配

public class Room {private int length;private int width;// 依赖其他的属性private Bed bed;public void setLength(int length) {this.length = length;}public void setWidth(int width) {this.width = width;}public void setBed(Bed bed) {this.bed = bed;}@Overridepublic String toString() {return "Room{" +"长='" + length + '\'' +", 宽='" + width + '\'' +", 床=" + bed +'}';}
}
public class Bed {private String brand;private float price;public void setBrand(String brand) {this.brand = brand;}public void setPrice(float price) {this.price = price;}@Overridepublic String toString() {return "Bed{" +"品牌='" + brand + '\'' +", 价格='" + price +'}';}
}





@Test
public void test08() {// 通过配置文件创建容器对象ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");Room room = context.getBean("room", Room.class);System.out.println(room);
}
/*** 运行结果:* Room{长='8', 宽='5', 床=Bed{品牌='全友床垫', 价格='1399.0}}*/
注意:如果存在多个相同类型的Bean,并且自动装配类型设置为byType,那么会抛出如下错误:

在这里插入图片描述

4、通过构造器装配

1、构造器自动装配流程:
  • 先按照有参构造器参数的类型进行装配,有就装配,没有就直接设置为NULL
  • 如果按照类型找到了多个,就按照有参构造器的参数名作为id或name继续匹配,有就装配,没有就直接设置为NULL
2、相比较byName与byType方式的自动装配来说,构造器方式装配不会报错。

在这里插入图片描述

public class Room {private int length;private int width;// 依赖其他的属性private Bed bed;// 空参构造public Room() {}// 构造器public Room(Bed bed) {System.out.println("构造注入bed");this.bed = bed;}public void setLength(int length) {this.length = length;}public void setWidth(int width) {this.width = width;}public void setBed(Bed bed) {this.bed = bed;}@Overridepublic String toString() {return "Room{" +"长='" + length + '\'' +", 宽='" + width + '\'' +", 床=" + bed +'}';}
}







@Test
public void test08() {// 通过配置文件创建容器对象ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");Room room = context.getBean("room", Room.class);System.out.println(room);
}
/*** 运行结果:* 构造注入bed* Room{长='8', 宽='5', 床=Bed{品牌='全友床垫', 价格='1399.0}}*/

相关内容

热门资讯

鸿蒙怎样还原安卓系统,系统切换... 你有没有想过,鸿蒙系统竟然能还原安卓系统?这听起来是不是有点像魔法一样神奇?没错,今天就要带你一探究...
电脑安卓转苹果系统,系统迁移攻... 你有没有想过,有一天你的安卓手机突然变成了苹果的忠实粉丝,想要跳槽到iOS的阵营呢?这可不是什么天方...
安卓xp系统下载地址,轻松获取... 你有没有想过,手机系统也能穿越时空?没错,今天我要给你揭秘的就是这样一个神奇的存在——安卓XP系统。...
安卓系统怎么清理相册,安卓系统... 手机里的相册是不是越来越臃肿了?每次打开都感觉像是在翻山越岭,找一张照片都要费老鼻子劲。别急,今天就...
安卓系统安装ios转移,轻松实... 你有没有想过,手机系统之间的转换竟然也能如此神奇?没错,今天就要来聊聊安卓系统安装iOS转移这个话题...
安卓系统与ios系统的优势,系... 你有没有想过,为什么你的手机里装的是安卓系统而不是苹果的iOS系统呢?或者反过来,为什么你的朋友用的...
安卓系统游戏如何升级,轻松实现... 亲爱的安卓玩家们,你是否也和我一样,对安卓系统游戏升级这件事充满了好奇和期待呢?每次游戏更新,都仿佛...
安卓系统蛋仔派对,安卓系统下的... 你有没有发现,最近你的手机里多了一个超级好玩的游戏?没错,就是安卓系统上的“蛋仔派对”!这款游戏可是...
坚果3安卓原生系统,深度体验原... 你有没有听说过坚果3这款手机?它可是最近在数码圈里火得一塌糊涂呢!今天,我就要来给你详细介绍一下这款...
安卓子系统点不开,排查与解决指... 最近是不是你也遇到了安卓子系统点不开的烦恼?这可真是让人头疼啊!别急,今天就来给你详细解析一下这个问...
安卓系统经常误删文件,如何有效... 你有没有遇到过这种情况?手机里的文件突然不见了,找来找去,怎么也找不到。别急,这可能是安卓系统的小调...
安卓51系统如何破解,轻松解锁... 安卓51系统如何破解——探索未知的技术边界在数字化时代,手机已经成为我们生活中不可或缺的一部分。而安...
安卓系统怎么换回主题,安卓系统... 亲爱的手机控们,你是不是也和我一样,对安卓系统的主题换换换乐此不疲呢?不过,有时候换着换着,突然发现...
黑莓安卓系统 太垃圾,令人失望... 你有没有用过黑莓的安卓系统?别告诉我你没有,因为现在这个系统真的是太垃圾了!是的,你没听错,就是那个...
修改安卓系统权限代码,安卓系统... 你有没有想过,你的安卓手机里那些神秘的系统权限代码?它们就像隐藏在手机里的秘密通道,有时候让你觉得既...
虚拟大师安卓系统教程,教程详解... 你有没有想过,手机里的世界可以变得更加神奇?今天,就让我带你一起探索虚拟大师安卓系统的奥秘吧!想象你...
基于安卓系统个人博客,轻松构建... 你有没有想过,在这个信息爆炸的时代,拥有一片属于自己的网络小天地是多么酷的事情啊!想象每天都能在这里...
安卓怎么传到苹果系统,从安卓到... 你是不是也有过这样的烦恼:手机里存了好多好用的安卓应用,可是一换到苹果系统,就发现这些宝贝们都不见了...
安卓改系统字体app,安卓系统... 你有没有想过,手机上的字体也能变得个性十足?没错,就是那个安卓改系统字体app,它可是让手机界面焕然...
安卓系统重启密码错误,破解与预... 手机突然重启了,屏幕上竟然出现了密码输入的界面!这可怎么办?别急,让我来帮你一步步解决这个安卓系统重...