Spring相关jar包下载地址:https://repo.spring.io/release/org/springframework/spring/
一、认识IOC 容器 和 Bean
什么是IOC
百度百科解释:
IOC(Inversion Of Control) 控制反转,是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。
(1)控制反转,把对象的创建和对象之间的调用过程,交给Spring进行管理;
(2)最常见的方式是依赖注入(DI);
(3)使用IOC的目的:为了降低耦合度。
IOC原理
IOC底层的实现原理主要是三大方面:XML的解析、工厂模式、反射。
(1)传统方式,我们在service层调用到层时,正常直接在service层new一个dao对象,这样带来的问题是耦合度太高。比如:如果有一天我们把dao的实现名称进行了修改,或更改了的dao实现的包路径,这样我们就必须随之更改service层中的代码。
(2)工厂模式。我们可以使用工厂模式进行解耦
(3)IOC过程
IOC 容器
IOC 思想基于 IOC 容器完成,IOC 容器底层就是对象工厂。容器将创建对象,把它们连接在一起,配置它们,并管理他们的整个生命周期从创建到销毁。Spring 容器使用依赖注入(DI)来管理组成一个应用程序的组件。这些对象被称为 Spring Beans。
通过阅读配置元数据提供的指令,容器知道对哪些对象进行实例化,配置和组装。配置元数据可以通过 XML,Java 注释或 Java 代码来表示。下图是 Spring 如何工作的高级视图。 Spring IoC 容器利用 Java 的 POJO 类和配置元数据来生成完全配置和可执行的系统或应用程序。
IOC 容器 是**具有依赖注入功能的容器,它可以创建对象,IOC 容器负责实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。通常new一个实例,控制权由程序员控制,而控制反转()是指new实例工作不由程序员来做而是交给Spring容器来做。在Spring中 BeanFactory
是IOC容器的实际代表者。
Spring 提供 IOC 容器实现两种方式:(两个接口)
(1)BeanFactory:IOC 容器基本实现,是 Spring 内部的使用接口,不提供开发人员进行使用。
(2)ApplicationContext:BeanFactory 接口的子接口,提供更多更强大的功能,一般由开发人员进行使用。
区别:
(1)加载配置文件
- BeanFactory加载配置文件时候不会创建对象,在获取对象(使用)才去创建对象。
- ApplicationContext在加载配置文件时候就会把在配置文件对象进行创建,一般在程序启动时。
BeanFactory
ApplicationContext
二、IOC容器Bean管理
什么是Bean管理?Bean管理指的是两个操作:
- Spring创建对象:注册Bean
- Spring注入属性:属性赋值
Bean管理的两种实现方式:
- 基于XML配置文件方式实现
- 基于注解方式实现
2.1 基于XML 配置文件进行Bean管理
2.1.1 基于Xml方式创建对象
```java package com.haan.springdemo.xmldemo;
public class User { private String name = “user_h”;
public String getName(){
return this.name;
}
}
```xml
<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 http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 配置User对象创建-->
<bean id="user" class="com.haan.springdemo.xmldemo.User"></bean>
</beans>
- id 属性:声明bean 唯一标识
- class 属性:类全路径(包类路径)
- 此时创建对象时候,默认也是执行无参数构造方法完成对象创建。如果没有无参构造时,创建会报错。
public class MyApplication {
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:/xmldemo/beans.xml");
User user = (User) applicationContext.getBean("user");
System.out.println(user.getName());
}
}
输出打印:
user_h Process finished with exit code 0
2.1.2 基于Xml方式注入属性
DI(Dependency Inject):依赖注入,就是注入属性
2.1.2.1 使用setter方法进行注入属性
创建类,定义属性和对应的setter方法:
/**
* 演示使用 set 方法进行注入属性
*/
public class Book {
//创建属性
private String bname;
private String bauthor;
//创建属性对应的 set 方法
public void setBname(String bname) {
this. bname = bname;
}
public void setBauthor(String bauthor) {
this. bauthor = bauthor;
}
}
在Spring的xml配置文件中创建对象,配置属性注入:
<!-- 配置Book对象创建,基于setter注入属性
使用 property 完成属性注入
name:类里面属性名称
value:向属性注入的值,基本数据类型
ref: 向属性注入的值,引用数据类型
-->
<bean id="book" class="com.haan.springdemo.xmldemo.Book">
<property name="name" value="book1"/>
<property name="price" value="21.9"/>
</bean>
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:/xmldemo/beans.xml");
// User user = (User) applicationContext.getBean("user");
// System.out.println(user.getName());
//使用setter赋值属性创建的bean book
Book book = (Book) applicationContext.getBean("book");
System.out.println(book);
}
执行输出:
Book{name=’book1’, price=21.9} Process finished with exit code 0
2.1.2.2 使用有参构造方法注入属性
创建类,定义属性,创建属性对应有参数构造方法:
package com.haan.springdemo.xmldemo;
public class Order {
//属性
private String name;
private String address;
//有参数构造
public Order(String name,String address) {
this. name = name;
this. address = address;
}
@Override
public String toString() {
return "Order{" +
"name='" + name + '\'' +
", address='" + address + '\'' +
'}';
}
}
在spring配置文件中进行配置
<!-- 配置Order对象创建,基于有参构造函数注入属性
使用 constructor-arg 完成属性注入
name:类里面属性名称
value:向属性注入的值,基本数据类型
ref: 向属性注入的值,引用数据类型
-->
<bean id="order" class="com.haan.springdemo.xmldemo.Order">
<constructor-arg name="name" value="order1"/>
<constructor-arg name="address" value="山东省济南市"/>
</bean>
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:/xmldemo/beans.xml");
User user = (User) applicationContext.getBean("user");
System.out.println(user.getName());
//使用setter赋值属性创建的bean book
Book book = (Book) applicationContext.getBean("book");
System.out.println(book);
//使用有参构造函数赋值属性创建的bean order
Order order = (Order) applicationContext.getBean("order");
System.out.println(order);
}
执行输出:
user_h Book{name=’book1’, price=21.9} Order{name=’order1’, address=’山东省济南市’} Process finished with exit code 0
2.1.2.3 使用p名称空间简化setter注入(了解)
使用 p 名称空间注入,可以简化基于 xml 配置方式
(1)第一步 添加 p 名称空间在配置文件中
xmlns:p="http://www.springframework.org/schema/p"
(2)第二步 进行属性注入,在 bean 标签里面进行操作
package com.haan.springdemo.xmldemo;
public class User3 {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "User3{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
<!-- 基于p名称-->
<bean id="user3" class="com.haan.springdemo.xmldemo.User3" p:name="user3" p:age="12"/>
2.1.2.4 注入属性:字面量
(1)null 值
<!--null 值-->
<property name= "address">
<null/>
</property>
(2)属性值包含特殊符号
使用 <![CDATA[内容]]> 即可
例如
<!-- 配置 student 对象 -->
<bean id="..." class="....">
<property name="name">
<!-- 此时如果有尖括号,名字设置成<张三>,就可以这么写 -->
<value><![CDATA[<张三>]]></value>
</property>
</bean>
2.1.2.5 注入属性:集合
1 、注入数组类型属性
2 、注入 List 集合类型属性
3 、注入 Map 集合类型属性
(1)创建类,定义数组、list、map、set 类型属性,生成对应 set 方法
public class Stu {
//1 数组类型属性
private String[] courses;
//2 list 集合类型属性
private List<String> list;
//3 map 集合类型属性
private Map<String,String> maps;
//4 set 集合类型属性
private Set<String> sets;
public void setSets(Set<String> sets) {
this. sets = sets;
}
publ ic void setCourses(String[] courses) {
this. courses = courses;
}
public void setList(List<String> list) {
this. list = list;
}
public void setMaps(Map<String, String> maps) {
this. maps = maps;
}
}
(2)在 spring 配置文件进行配置
<!--1 集合类型属性注入-->
< bean id= "stu" class= "com.atguigu.spring5.collectiontype.Stu">
<!--数组类型属性注入-->
< property name= "courses">
< array>
< value>java 课程</ value>
< value>数据库课程</ value>
</ array>
</ property>
<!--list 类型属性注入-->
< property name= "list">
< list>
< value>张三</ value>
< value>小三</ value>
</ list>
</ property>
<!--map 类型属性注入-->
< property name= "maps">
< map>
< entry key= "JAVA" value= "java"></ entry>
< entry key= "PHP" value= "php"></ entry>
</ map>
</ property>
<!--set 类型属性注入-->
< property name= "sets">
< set>
< value>MySQL</ value>
< value>Redis</ value>
</ set>
</ property>
</ bean>
在集合里面设置对象类型值
<!--创建多个 course 对象-->
< bean id= "course1" class= "com.atguigu.spring5.collectiontype.Course">
< property name= "cname" value="Spring5 框架"></ property>
</ bean>
<bean id= "course2" class= "com.atguigu.spring5.collectiontype.Course">
< property name= "cname" value="MyBatis 框架"></ property>
</ bean>
<bean id ="teacher" class= "com.atguigu.spring5.collectiontype.Teacher">
<!--注入 list 集合类型,值是对象-->
< property name= "courseList">
< list>
< ref bean= "course1"></ ref>
< ref bean= "course2"></ ref>
</ list>
</ property>
</bean>
把集合注入部分提取出来
(1)在 spring 配置文件中引入名称空间 util
<? xml version="1.0" encoding="UTF-8" ?>
<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"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd">
</beans>
(2)使用 util 标签完成 list 集合注入提取
<!--1 提取 list 集合类型属性注入-->
< util :list id= "bookList">
< value>易筋经</ value>
< value>九阴真经</ value>
< value>九阳神功</ value>
</ util :list>
<!--2 提取 list 集合类型属性注入使用-->
< bean id= "book" class= "com.atguigu.spring5.collectiontype.Book">
< property name= "list" ref= "bookList"></ property>
</ bean>
2.1.2.6 注入属性:外部 bean
<!-- service 和 dao 对象创建-->
< bean id= "userService" class= "com.atguigu.spring5.service.UserService">
<!--注入 userDao 对象
name 属性:类里面属性名称
ref 属性:创建 userDao 对象 bean 标签 id 值
-->
< property name= "userDao" ref= "userDaoImpl"></ property>
</ bean>
< bean id= "userDaoImpl" class= "com.atguigu.spring5.dao.UserDaoImpl"></bean>
2.1.2.7 注入属性:内部 bean
<!--内部 bean-->
< bean id= "emp" class= "com.atguigu.spring5.bean.Emp">
<!--设置两个普通属性-->
< property name= "ename" value= "lucy"></ property>
< property name= "gender" value=" " 女" "></ property>
<!--设置对象类型属性-->
<property name= "dept">
<bean id= "dept" class= "com.atguigu.spring5.bean.Dept">
<property name= "dname" value=" " 安保部" "></ property>
</bean>
</property>
</ bean>
2.1.2.8 注入属性:级联赋值
注入属性- - 级联赋值
(1 1 )第一种写法
<!--级联赋值-->
< bean id= "emp" class= "com.atguigu.spring5.bean.Emp">
<!--设置两个普通属性-->
< property name= "ename" value= "lucy"></ property>
< property name= "gender" value="女"></ property>
<!--级联赋值-->
< property name= "dept" ref= "dept"></ property>
</ bean>
< bean id= "dept" class= "com.atguigu.spring5.bean.Dept">
< property name= "dname" value="财务部"></ property>
</bean>
<!--级联赋值-->
< bean id= "emp" class= "com.atguigu.spring5.bean.Emp">
<!--设置两个普通属性-->
< property name= "ename" value= "lucy"></ property>
< property name= "gender" value="女"></ property>
<!--级联赋值-->
< property name= "dept" ref= "dept"></ property>
< property name= "dept.dname" value="技术部"></ property>
</ bean>
< bean id= "dept" class= "com.atguigu.spring5.bean.Dept">
< property name= "dname" value="财务部"></ property>
</bean>
2.1.3 使用FactoryBean接口来注册Bean
2.1.4 基于XML Bean的作用域
当在 Spring 中定义一个 时,你必须声明该 bean 的作用域的选项。例如,为了强制 Spring 在每次需要时都产生一个新的 bean 实例,你应该声明 bean 的作用域的属性为 prototype。同理,如果你想让 Spring 在每次需要时都返回同一个 bean 实例,你应该声明 bean 的作用域的属性为 singleton。
Spring 框架支持以下五个作用域,如果你使用 web-aware ApplicationContext 时,其中三个是可用的。
作用域 | 描述 |
---|---|
singleton | 该作用域将 bean 的定义的限制在每一个 Spring IoC 容器中的一个单一实例(默认)。 |
prototype | 该作用域将单一 bean 的定义限制在任意数量的对象实例。 |
request | 该作用域将 bean 的定义限制为 HTTP 请求。只在 web-aware Spring ApplicationContext 的上下文中有效。 |
session | 该作用域将 bean 的定义限制为 HTTP 会话。 只在web-aware Spring ApplicationContext的上下文中有效。 |
global-session | 该作用域将 bean 的定义限制为全局 HTTP 会话。只在 web-aware Spring ApplicationContext 的上下文中有效。 |
<bean id="user" class="com.waitingforcode.scope.User" scope="singleton">
</bean>
2.1.5 基于XML Bean 生命周期
2.1.5.1 生命周期
2.1.5.2 测试1:无参构造+Setter方法
首先,创建类 Book
,声明无参构造函数和setter方法:
package com.haan.springdemo.xmldemo.lifecycle;
/**
* @author hanliukui
* @Date 2021/4/20 20:00
*/
public class Book {
private String name;
private String author;
public Book(){
System.out.println("Book 无参构造");
}
public String getName() {
return name;
}
public void setName(String name) {
System.out.println("Book setName");
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
System.out.println("Book setAuthor");
this.author = author;
}
}
在 classpath:/xmldeml/lifecycle.xml
文件中声明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 http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="book" class="com.haan.springdemo.xmldemo.lifecycle.Book">
<property name="name" value="静夜诗"/>
<property name="author" value="李白"/>
</bean>
</beans>
在测试类中,启动:
package com.haan.springdemo.xmldemo.lifecycle;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author hanliukui
* @Date 2021/4/20 20:01
*/
public class LifeCycleTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:/xmldemo/lifecycle.xml");
System.out.println("started...");
}
}
执行结果:
Book 无参构造 Book setName Book setAuthor started… Process finished with exit code 0
我们可以发现Bean创建的执行过程:
(1)通过构造器创建 bean 实例(无参数构造)
(2)为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)
2.1.5.3 测试2:有参构造+Setter方法
我们修改类 Book
,增加有参构造:
package com.haan.springdemo.xmldemo.lifecycle;
/**
* @author hanliukui
* @Date 2021/4/20 20:00
*/
public class Book {
private String name;
private String author;
public Book(){
System.out.println("Book 无参构造");
}
public Book(String name,String author){
System.out.println("Book 有参构造...");
this.name=name;
this.author=author;
}
public String getName() {
return name;
}
public void setName(String name) {
System.out.println("Book setName");
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
System.out.println("Book setAuthor");
this.author = author;
}
@Override
public String toString() {
return "Book{" +
"name='" + name + '\'' +
", author='" + author + '\'' +
'}';
}
}
修改文件 classpath:/xmldeml/lifecycle.xml
,添加 有参构造的Book 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 http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="book" class="com.haan.springdemo.xmldemo.lifecycle.Book">
<property name="name" value="静夜诗"/>
<property name="author" value="李白"/>
</bean>
<bean id="book2" class="com.haan.springdemo.xmldemo.lifecycle.Book">
<property name="name" value="静夜诗"/>
<property name="author" value="李白"/>
<constructor-arg name="name" value="郭论"/>
<constructor-arg name="author" value="郭德纲"/>
</bean>
</beans>
调整主函数:
public class LifeCycleTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:/xmldemo/lifecycle.xml");
System.out.println("started...");
Book book = (Book) context.getBean("book");
System.out.println(book);
Book book2 = (Book) context.getBean("book2");
System.out.println(book2);
}
}
打印输出:
Book 无参构造 Book setName Book setAuthor Book 有参构造… Book setName Book setAuthor started…
Book{name=’静夜诗’, author=’李白’}
Book{name=’静夜诗’, author=’李白’}
Process finished with exit code 0
我们可以发现。虽然我们在有参构造给book2,付了值name=郭论,author=郭德纲,但后续被setter属性值覆盖。
2.1.5.4 配置初始化方法和摧毁方法
调整Book,添加init()和destroy() 方法:
package com.haan.springdemo.xmldemo.lifecycle;
/**
* @author hanliukui
* @Date 2021/4/20 20:00
*/
public class Book {
private String name;
private String author;
public Book(){
System.out.println("Book 无参构造");
}
public Book(String name,String author){
System.out.println("Book 有参构造...");
this.name=name;
this.author=author;
}
public String getName() {
return name;
}
public void setName(String name) {
System.out.println("Book setName");
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
System.out.println("Book setAuthor");
this.author = author;
}
public void init(){
System.out.println("Book init method...");
}
public void destroy(){
System.out.println("Book destroy...");
}
@Override
public String toString() {
return "Book{" +
"name='" + name + '\'' +
", author='" + author + '\'' +
'}';
}
}
修改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 http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- <bean id="book" class="com.haan.springdemo.xmldemo.lifecycle.Book">-->
<!-- <property name="name" value="静夜诗"/>-->
<!-- <property name="author" value="李白"/>-->
<!-- </bean>-->
<!-- <bean id="book2" class="com.haan.springdemo.xmldemo.lifecycle.Book">-->
<!-- <property name="name" value="静夜诗"/>-->
<!-- <property name="author" value="李白"/>-->
<!-- <constructor-arg name="name" value="郭论"/>-->
<!-- <constructor-arg name="author" value="郭德纲"/>-->
<!-- </bean>-->
<!--
init-method:声明bean的初始化方法
destroy-method:声明bean的销毁方法
-->
<bean id="book3" class="com.haan.springdemo.xmldemo.lifecycle.Book" init-method="init" destroy-method="destroy">
<property name="name" value="静夜诗"/>
<property name="author" value="李白"/>
</bean>
</beans>
package com.haan.springdemo.xmldemo.lifecycle;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author hanliukui
* @Date 2021/4/20 20:01
*/
public class LifeCycleTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:/xmldemo/lifecycle.xml");
System.out.println("started...");
// Book book = (Book) context.getBean("book");
// System.out.println(book);
//
// Book book2 = (Book) context.getBean("book2");
// System.out.println(book2);
// 关闭容器,会销毁容器中的所有Bean
context.close();
}
}
执行输出:
Book 无参构造
Book setName
Book setAuthor
Book init method…
started…
Book destroy…
Process finished with exit code 0
总结Bean的声明周期:
(1)通过构造器创建 bean 实例
(2)为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)
(3)调用 bean 的初始化的方法(需要进行配置初始化的方法 init-method)
(4)bean 可以使用了(对象获取到了)
(5)当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法 destroy-method)
2.1.5.5 Bean 的后置处理器
在Spring中,可以通过实现 BeanPostProcessor 接口,创建后置处理器 MyBeanPostProcessor
:
package com.haan.springdemo.xmldemo.lifecycle;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("Bean 后置处理器方法:postProcessBeforeInitialization");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("Bean 后置处理器方法:postProcessAfterInitialization");
return bean;
}
}
在xml文件中声明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 http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- <bean id="book" class="com.haan.springdemo.xmldemo.lifecycle.Book">-->
<!-- <property name="name" value="静夜诗"/>-->
<!-- <property name="author" value="李白"/>-->
<!-- </bean>-->
<!-- <bean id="book2" class="com.haan.springdemo.xmldemo.lifecycle.Book">-->
<!-- <property name="name" value="静夜诗"/>-->
<!-- <property name="author" value="李白"/>-->
<!-- <constructor-arg name="name" value="郭论"/>-->
<!-- <constructor-arg name="author" value="郭德纲"/>-->
<!-- </bean>-->
<!--
init-method:声明bean的初始化方法
destroy-method:声明bean的销毁方法
-->
<bean id="book3" class="com.haan.springdemo.xmldemo.lifecycle.Book" init-method="init" destroy-method="destroy">
<property name="name" value="静夜诗"/>
<property name="author" value="李白"/>
</bean>
<!-- 声明Bean后置处理器-->
<bean id="myBeanPostProcessor" class="com.haan.springdemo.xmldemo.lifecycle.MyBeanPostProcessor"/>
</beans>
测试主函数:
package com.haan.springdemo.xmldemo.lifecycle;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author hanliukui
* @Date 2021/4/20 20:01
*/
public class LifeCycleTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:/xmldemo/lifecycle.xml");
System.out.println("started...");
// Book book = (Book) context.getBean("book");
// System.out.println(book);
//
Book book3 = (Book) context.getBean("book3");
System.out.println(book3);
// 关闭容器,会销毁容器中的所有Bean
context.close();
}
}
执行输出:
Book 无参构造
Book setName
Book setAuthor
Bean 后置处理器方法:postProcessBeforeInitialization
Book init method…
Bean 后置处理器方法:postProcessAfterInitialization
started…
Book{name=’静夜诗’, author=’李白’}
Book destroy…
Process finished with exit code 0
总结:添加Bean后置处理器,Bean的声明周期:
(1)通过构造器创建 bean 实例(无参数构造)
(2)为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)
(3)把 n bean 实例传递 n bean 后置处理器的方法 postProcessBeforeInitialization
(4)调用 bean 的初始化的方法(需要进行配置初始化的方法)
(5)把 bean 实例传递 bean 后置处理器的方法 postProcessAfterInitialization
(6)bean 可以使用了(对象获取到了)
(7)当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)
2.1.5.6 实现InitializingBean/DisposableBean
修改Book:
2.1.6 基于XML 自动装配
2.1.7 基于XML 引入外部配置文件
直接配置数据库连接信息
<!--直接配置连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url"
value="jdbc:mysql://localhost:3306/userDb?characterEncoding=utf-8"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
使用外部配置文件配置数据库信息
创建外部属性配置文件:classpath:db.properties
:
db.driverclass=com.mysql.jdbc.Driver
db.url=jdbc:mysql://localhost:3306/userDb?characterEncoding=utf-8
db.username=root
db.password=root
把外部 properties 属性文件引入到 spring 配置文件中:
<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"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--引入外部属性文件方式一:使用PropertyPlaceholderConfigurer -->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="db-connection" value="db.properties"></property>
</bean>
<!-- 引入外部属性文件方法二: 要引入context命名空间-->
<!-- <context:property-placeholder location="classpath:db.properties"/>-->
<!--配置连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${db.driverclass}"></property>
<property name="url" value="${db.url}"></property>
<property name="username" value="${db.username}"></property>
<property name="password" value="${db.password}"></property>
</bean>
</beans>
2.1.3 基于XML自动装配
2.1.3.1 什么是自动装配
在之前我们一直通过使用 XML 配置文件中的<constructor-arg>
和<property>
元素明确指出具体的属性值来注入属性,现在我们不需要在配置中明确指定哪个属性注入哪个具体值,根据指定装配规则(属性名称或属性类型),Spring会自动将匹配的属性值进行注入。
2.1.3.2 自动装配模式
以使用<bean>
元素的 **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 来自动装配。
2.1.3.3 自动装配的局限性
当自动装配始终在同一个项目中使用时,它的效果最好。如果通常不使用自动装配,它可能会使开发人员混淆的使用它来连接只有一个或两个 bean 定义。不过,自动装配可以显著减少需要指定的属性或构造器参数,但你应该在使用它们之前考虑到自动装配的局限性和缺点。
限制 | 描述 |
---|---|
重写的可能性 | 你可以使用总是重写自动装配的 |
原始数据类型 | 你不能自动装配所谓的简单类型包括基本类型,字符串和类。 |
混乱的本质 | 自动装配不如显式装配精确,所以如果可能的话尽可能使用显式装配。 |
2.1.3.4 根据属性名称自动注入
<!--实现自动装配
bean 标签属性 autowire,配置自动装配
autowire 属性常用两个值:
byName 根据属性名称注入 ,注入值 bean 的 id 值和类属性名称一样
byType 根据属性类型注入
-->
< bean id= "emp" class= "com.atguigu.spring5.autowire.Emp" autowire= "byName">
<!--<property name="dept" ref="dept"></property>-->
</ bean>
< bean id= "dept" class= "com.atguigu.spring5.autowire.Dept"></ bean>
2.1.3.5 根据属性类型自动注入
<!--实现自动装配
bean 标签属性 autowire,配置自动装配
autowire 属性常用两个值:
byName 根据属性名称注入 ,注入值 bean 的 id 值和类属性名称一样
byType 根据属性类型注入
-->
< bean id= "emp" class= "com.atguigu.spring5.autowire.Emp" autowire= "byType">
<!--<property name="dept" ref="dept"></property>-->
</ bean>
< bean id= "dept" class= "com.atguigu.spring5.autowire.Dept"></ bean>
2.1.3.6 根据构造函数自动装配
TextEditor.class
public class TextEditor {
private SpellChecker spellChecker;
private String name;
//构造函数
public TextEditor( SpellChecker spellChecker, String name ) {
this.spellChecker = spellChecker; //
this.name = name;
}
public SpellChecker getSpellChecker() {
return spellChecker;
}
public String getName() {
return name;
}
public void spellCheck() {
spellChecker.checkSpelling();
}
}
SpellChecker.class
public class SpellChecker {
public SpellChecker(){
System.out.println("Inside SpellChecker constructor." );
}
public void checkSpelling()
{
System.out.println("Inside checkSpelling." );
}
}
正常情况下的bean.xml配置文件
<!-- 定义Bean textEditor-->
<bean id="textEditor" class="com.tutorialspoint.TextEditor">
<constructor-arg ref="spellChecker" />
<constructor-arg value="Generic Text Editor"/>
</bean>
<!--定义Bean spellChecker-->
<bean id="spellChecker" class="com.tutorialspoint.SpellChecker">
</bean>
使用构造函数自动装配的配置文件bean.xml
<!-- 定义Bean textEditor-->
<bean id="textEditor" class="com.tutorialspoint.TextEditor" autowire ="constructor">
<!-- <constructor-arg ref="spellChecker" />-->
<constructor-arg value="Generic Text Editor"/>
</bean>
<!--定义Bean spellChecker-->
<bean id="spellChecker" class="com.tutorialspoint.SpellChecker">
</bean>
2.3 基于注解进行Bean管理
2.2.1 什么是注解
什么是注解
(1)注解是代码特殊标记,格式:@注解名称(属性名称=属性值, 属性名称=属性值..)
(2)使用注解,注解作用在类上面,方法上面,属性上面
(3)使用注解目的:简化 xml 配置
2.2.2 基于注解创建Bean
2.2.2.1 开启组件扫描
开启组件扫描后,base-package属性定义的包下的类(被特定注解项注解的类)才将会被扫描并创建为Bean。
<!--开启组件扫描,如果扫描多个包,多个包使用逗号隔开-->
< context:component-scan base-package= "com.atguigu"></context:component-scan>
(1)设置扫描包下的哪些内容,默认都扫描
<!--示例 1
use-default-filters="false" 表示现在不使用默认filter,自己配置 filter
context:include-filter ,设置扫描哪些内容
-->
< context :component-scan base-package= "com.atguigu" use-default-filters= "false">
< context :include-filter type= "annotation" expression= "org.springframework.stereotype.Controller"/>
< /context :component-scan>
(2)设置包下的哪些内容不被扫描
<!--示例 2
下面配置扫描包所有内容
context:exclude-filter: 设置哪些内容不进行扫描
-->
< context:component-scan base-package= "com.atguigu">
< context :exclude-filter type= "annotation"
expression= "org.springframework.stereotype.Controller"/>
</ context:component-scan>
2.2.2.2 创建对象并注解
Spring 针对 Bean 管理中创建对象提供注解
(1)@Component
(2)@Service
(3)@Controller
(4)@Repository
上面四个注解功能是一样的,都可以用来创建 bean 实例
在开启组件扫描后,并不是配置的包下的类都会被创建bean实例,只有添加了上面的四项注解的才会被创建Bean实例。
//在注解里面 value 属性值可以省略不写,
//默认值是类名称,首字母小写
//UserService -- userService
@Component(value = "userService") //<bean id="userService" class=".."/>
public class UserService {
public void add() {
System. out .println( "service add.......");
}
}
2.2.3 基于注解属性注入(自动装配)
2.2.3.1 @Autowired
根据类型进行注入
注解位置:
- setter方法上
属性字段上
@Service
public class UserService {
//定义 dao 类型属性
//不需要添加 set 方法
//添加注入属性注解
@Autowired
private UserDao userDao; ;
public void add() {
System. out .println( "service add.......");
userDao.add();
}
}
2.2.3.2
@Qualifier
据名称进行注入注解位置:
这个注解通常和@Autowired一起使用,当你想对注入的过程做更多的控制。@Autowired默认是根据类型进行注入的,因此如果有多个类型一样的Bean候选者,则需要限定其中一个候选者,否则将抛出异常
- @Qualifier限定描述符除了能根据名字进行注入,更能进行更细粒度的控制如何选择候选者
源码:
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Qualifier {
String value() default ""; //指定bean名称标识
}
使用举例:
比如:有个接口叫做HelloInterface
,然后有两个bean都实现了HelloInterface接口。
@Component
public class Bean1 implements HelloInterface {
//
}
@Component
public class Bean2 implements HelloInterface {
//
}
如果我们只使用@Autowired注解,Spring就不知道到底要注入哪一个bean。解决办法就是加上@Qualifier注解:
@Component
public class BeanA {
@Autowired
@Qualifier("bean2")
private HelloInterface dependency;
...
2.2.3.3 @Resource
根据类型或名称注入
@Resource
是JDK1.6支持的注解,默认按照名称进行装配。名称可以通过name属性进行指定, 如果没有指定name属性,当注解写在字段上时,默认取字段名进行按照名称查找,如果注解写在setter方法上默认取属性名进行装配。 当找不到与名称匹配的bean时才按照类型进行装配。
但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。当然也可以指定type属性,表示按照类型进行查找。
//@Resource //根据类型进行注入
@Resource(name = "userDaoImpl1") //根据名称进行注入
private UserDao userDao;
[@Resource](#)
和[@Autowired](#)
的比较:
https://www.cnblogs.com/terrycode/p/12188950.html
2.2.3.4 注入普通类型属性@Value
@Value(value = "abc")
private String name;