一、传统方式创建对象
缺点:
- 耦合太高:比如UserService的类有了新的实现,那么就需要修改所有调用UserService的地方
示例:
package com.wells.demo.ioc.tradition;
/**
* Description 传统方式: 通过 new 对象来调用,带来的问题:
* 1、耦合高:如果userDao有了新的实现,需要修改所有调用userDao的地方;
* 因此,为了降低耦合,所以需要工厂模式来做,而Spring恰恰用了:xml配置(注解)、工厂模式、反射来进行bean的创建
* Created by wells on 2020-07-18 08:26:41
*/
public class UserService {
public void delUser() {
System.out.println("delete user");
}
public static void main(String[] args) {
UserService userService = new UserService();
userService.delUser();
}
}
二、基于XML方式创建对象
- 在 spring 配置文件中,使用 bean 标签创建对象;标签里面添加对应属性,就可以实现属性注入
- 在 bean 标签有很多属性,介绍常用的属性
- id 属性:唯一标识
- class 属性:类全路径(包类路径)
- 创建对象时候,默认执行无参数构造方法完成创建对象
示例:
package com.wells.demo.ioc.xml;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Description 通过ApplicationContext读取xml配置文件创建对象
* Created by wells on 2020-07-18 08:26:41
*/
public class UserService {
public void addUser() {
System.out.println("add user");
}
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
UserService userService = applicationContext.getBean("userService", UserService.class);
userService.addUser();
}
}
<!-- 使用xml方式创建对象 -->
<bean id="userService" class="com.wells.demo.ioc.xml.UserService"/>
三、基于 XML 方式注入基础属性
DI:依赖注入,就是注入属性
2.1、使用set方式注入属性
package com.wells.demo.ioc.xml;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Description 通过set方法注入属性
* Created by wells on 2020-07-18 22:08:35
*/
public class Book {
private String bname;
private String bauthor;
public void setBname(String bname) {
this.bname = bname;
}
public void setBauthor(String bauthor) {
this.bauthor = bauthor;
}
@Override
public String toString() {
return "Book{" +
"bname='" + bname + '\'' +
", bauthor='" + bauthor + '\'' +
'}';
}
/**
* 通过set方法注入属性
*/
@Test
public void testWiredPropertyBySetter() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
Book book = applicationContext.getBean("book", Book.class);
System.out.println(book.toString());
}
}
<!-- 使用set方式注入属性 -->
<bean id="book" class="com.wells.demo.ioc.xml.Book">
<!-- 使用 property 完成属性注入
name:类里面属性名称
value:向属性注入的值
-->
<property name="bname" value="西游记"/>
<property name="bauthor" value="吴承恩"/>
</bean>
2.2、使用有参构造方法注入属性
package com.wells.demo.ioc.xml;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Description 使用有参构造方法注入属性
* Created by wells on 2020-07-18 22:14:14
*/
public class User {
private String name;
private Integer age;
public User(String name, Integer age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
/**
* 通过有参构造方法注入属性
*/
@Test
public void testWiredPropertyByConstructor() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
User user = applicationContext.getBean("user", User.class);
System.out.println(user.toString());
}
}
<!-- 使用有参构造方法注入属性 -->
<bean id="user" class="com.wells.demo.ioc.xml.User">
<constructor-arg name="name" value="wells"/>
<constructor-arg name="age" value="18"/>
</bean>
2.3、使用 p 名称空间注入(了解)
package com.wells.demo.ioc.xml;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Description 通过P名称空间注入属性
* Created by wells on 2020-07-18 22:31:53
*/
public class Person {
private String name;
private Integer age;
public void setName(String name) {
this.name = name;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Test
public void testWiredPropertyByPTag() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
Person person = applicationContext.getBean("person", Person.class);
System.out.println(person.toString());
}
}
<!-- 通过p名称空间注入属性 -->
<bean id="person" class="com.wells.demo.ioc.xml.Person" p:name="Tom" p:age="18"/>
四、基于XML注入其他类型属性
4.1、注入 Null 值
<!-- 使用set方式注入属性 -->
<bean id="book" class="com.wells.demo.ioc.xml.Book">
<!-- 使用 property 完成属性注入
name:类里面属性名称
value:向属性注入的值
-->
<property name="bname" value="西游记"/>
<property name="bauthor" value="吴承恩"/>
<!-- 注入Null值 -->
<property name="addr">
<null/>
</property>
</bean>
4.2、属性值包含特殊符号
例如: <、>
<!-- 使用set方式注入属性 -->
<bean id="book" class="com.wells.demo.ioc.xml.Book">
<!-- 使用 property 完成属性注入
name: 类里面属性名称
value: 向属性注入的值
-->
<property name="bname" value="西游记"/>
<property name="bauthor" value="吴承恩"/>
<!-- 注入 Null 值 -->
<property name="addr">
<null/>
</property>
<!-- 注入特殊符号值 -->
<property name="btime">
<value><![CDATA[<2020-07-18 22:50:00>]]></value>
</property>
</bean>
4.3、注入自定义bean
4.3.1、外部创建方式
- 创建两个类:service 类和 dao 类
- 在 service 调用 dao 里面的方法
- 在 spring 配置文件中进行配置
<!-- 通过set方法注入自定义bean: 外部创建 -->
<bean id="empOut" class="com.wells.demo.ioc.xml.Emp">
<property name="ename" value="Jerry"/>
<property name="gender" value="男"/>
<property name="dept" ref="deptOut"/>
</bean>
<bean id="deptOut" class="com.wells.demo.ioc.xml.Dept">
<property name="dname" value="技术部"/>
</bean>
4.3.2、内部创建方式
部门和员工关系
<!-- 通过set方法注入自定义bean: 内部创建 -->
<bean id="empIn" class="com.wells.demo.ioc.xml.Emp">
<property name="ename" value="Lily"/>
<property name="gender" value="女"/>
<property name="dept">
<bean class="com.wells.demo.ioc.xml.Dept">
<property name="dname" value="测试部"/>
</bean>
</property>
</bean>
4.5、注入自定义bean-级联属性
4.5.1、外部注入
<!-- 在注入自定义bean时,注入级联属性,比如:dept的dname -->
<bean id="empLinkOne" class="com.wells.demo.ioc.xml.Emp">
<property name="ename" value="Tom"/>
<property name="gender" value="男"/>
<property name="dept" ref="dept"/>
</bean>
<bean name="dept" class="com.wells.demo.ioc.xml.Dept">
<property name="dname" value="安全部"/>
</bean>
4.5.2、内部注入
<!-- 在注入自定义bean时,注入级联属性,比如:dept的dname -->
<bean id="empLinkTwo" class="com.wells.demo.ioc.xml.Emp">
<property name="ename" value="Daivd"/>
<property name="gender" value="男"/>
<property name="dept" ref="dept"/>
<!-- 需要dept属性的get方法 -->
<property name="dept.dname" value="业务部"/>
</bean>
4.6、集合属性注入
- 注入数组类型属性
- 注入List集合类型属性:基本类型、自定义类型、抽取
- 注入Map集合类型属性
<!-- 注入集合属性 -->
<bean id="student" class="com.wells.demo.ioc.xml.Student">
<property name="course">
<array>
<value>线性代数</value>
<value>高等数学</value>
</array>
</property>
<!-- 注入基本属性集合 -->
<property name="books">
<list>
<value>《SpringBoot编程思想》</value>
<value>《SpringCloud微服务实战》</value>
</list>
</property>
<property name="teachers">
<map>
<entry key="大学物理" value="Tom"/>
<entry key="高等数学" value="Jerry"/>
</map>
</property>
<!-- 注入自定义bean集合 -->
<property name="courseList">
<list>
<ref bean="c1"/>
<ref bean="c2"/>
</list>
</property>
<property name="friends" ref="friends"/>
</bean>
<bean id="c1" class="com.wells.demo.ioc.xml.Course" p:cname="大学物理"/>
<bean id="c2" class="com.wells.demo.ioc.xml.Course" p:cname="编译原理"/>
<!-- 提取list -->
<util:list id="friends">
<value>Lily</value>
<value>Wells</value>
</util:list>
五、FactoryBean
Spring 有两种类型 bean,一种普通 bean,另外一种工厂 bean(FactoryBean)
- 普通 bean:在配置文件中定义 bean 类型就是返回类型
- 工厂 bean:在配置文件定义 bean 类型可以和返回类型不一样
- 第一步 创建类,让这个类作为工厂 bean,实现接口 FactoryBean
- 第二步 实现接口里面的方法,在实现的方法中定义返回的 bean 类型
package com.wells.demo.ioc.xml;
import org.junit.Test;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Description FactoryBean
* Created by wells on 2020-07-19 09:21:56
*/
public class MyBean implements FactoryBean<Course> {
public Course getObject() throws Exception {
Course course = new Course();
course.setCname("大学英语");
return course;
}
public Class<?> getObjectType() {
return null;
}
public boolean isSingleton() {
return false;
}
@Test
public void testFactoryBean(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
Course course = applicationContext.getBean("myBean", Course.class);
System.out.println(course.toString());
}
}
<!-- FactoryBean -->
<bean id="myBean" class="com.wells.demo.ioc.xml.MyBean"/
六、Bean实例数
在 Spring 里面,默认情况下,bean 是单实例对象
如何设置单实例还是多实例?
- 在 spring 配置文件 bean 标签里面有属性(scope)用于设置单实例还是多实例
- scope 属性值
- 第一个值 singleton,默认值,表示是单实例对象
- 第二个值 prototype,表示是多实例对象
singleton 和 prototype 区别
- 第一 singleton单实例,prototype多实例
- 第二 设置 scope 值是 singleton 时候,加载 spring 配置文件时候就会创建单实例对象;设置 scope 值是 prototype 时候,不是在加载 spring 配置文件时候创建对象,而是在调用 getBean 方法时候创建多实例对象;
七、Bean生命周期
生命周期:从对象创建到对象销毁的过程
7.1、无处理器的bean生命周期(五步)
- 通过构造器创建 bean 实例(无参数构造)
- 为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)
- 调用 bean 的初始化的方法(需要进行配置初始化的方法)
- bean 可以使用了(对象获取到了)
- 当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)
package com.wells.demo.ioc.xml;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Description Bean实例生命周期
* Created by wells on 2020-07-19 09:30:56
*/
public class Order {
private String oname;
public Order() {
System.out.println("第一步: 通过无参构造方法创建bean实例");
}
public void setOname(String oname) {
this.oname = oname;
System.out.println("第二步: 通过set方法注入属性");
}
/**
* Bean初始化方法
*/
public void initMethod() {
System.out.println("第三步: 调用bean初始化方法");
}
/**
* Bean销毁方法
*/
public void destroyMethod() {
System.out.println("第五步: 调用bean销毁方法");
}
@Override
public String toString() {
return "Order{" +
"oname='" + oname + '\'' +
'}';
}
@Test
public void testBeanLife() {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
Order order = applicationContext.getBean("order", Order.class);
System.out.println("第四步: 使用bean");
System.out.println(order.toString());
// 手动调用销毁bean实例
applicationContext.close();
}
}
<!-- bean生命周期 -->
<bean id="order" class="com.wells.demo.ioc.xml.Order" init-method="initMethod" destroy-method="destroyMethod" p:oname="裤子"/>
7.2、有处理器的bean生命周期(七步)
- 通过构造器创建 bean 实例(无参数构造)
- 为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)
- 把 bean 实例传递到 bean 后置处理器的方法 postProcessBeforeInitialization
- 调用 bean 的初始化的方法(需要进行配置初始化的方法)
- ==把 bean 实例传递到 bean 后置处理器的方法 postProcessAfterInitialization ==
- bean 可以使用了(对象获取到了)
- 当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)
需要特别注意的是:processor是对所有的bean生效,因此如果创建多个bean,会被执行多次
package com.wells.demo.ioc.xml;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
/**
* Description Bean处理器,需要特别注意的是:这个处理器是对所有的bean生效;所以会出现有几个bean,就执行几次
* Created by wells on 2020-07-19 09:58:12
*/
public class BeanProcess implements BeanPostProcessor {
/**
* 处理器前置方法
*/
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("第处理器前置方法");
return bean;
}
/**
* 处理器后缀方法
*/
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("处理器后置方法");
return bean;
}
}
八、XML自动装配
什么是自动装配?
- 根据指定装配规则(属性名称或者属性类型),Spring 自动将匹配的属性值进行注入
8.1、根据属性名称自动注入
需要注意的是:类中的属性名,需要与xml中的id名相同
public class Emp {
// ... 略
private Dept dept;
public void setDept(Dept dept) {
this.dept = dept;
}
// ... 略
@Test
public void testAutoWiredByNameInXML(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans-auto-wired.xml");
Emp emp = applicationContext.getBean("empByName", Emp.class);
System.out.println(emp.toString());
}
}
<bean id="empByName" class="com.wells.demo.ioc.xml.Emp" autowire="byName"/>
<bean id="dept" class="com.wells.demo.ioc.xml.Dept">
<property name="dname" value="运维部"/>
8.2、根据属性类型自动注入
public class Emp {
// ... 略
private Dept dept;
public void setDept(Dept dept) {
this.dept = dept;
}
// ... 略
@Test
public void testAutoWiredByTypeInXML(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans-auto-wired.xml");
Emp emp = applicationContext.getBean("empByType", Emp.class);
System.out.println(emp.toString());
}
}
<bean id="empByType" class="com.wells.demo.ioc.xml.Emp" autowire="byType"/>
<bean id="dept2" class="com.wells.demo.ioc.xml.Dept">
<property name="dname" value="运维部"/>
</bean>
需要注意的是:xml中不能出现多个类型一样的bean实例,否则会报错,如下图:
九、引入外部属性
package com.wells.demo.ioc.xml;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Description
* Created by wells on 2020-07-19 10:19:12
*/
public class Blog {
private String bname;
public void setBname(String bname) {
this.bname = bname;
}
@Override
public String toString() {
return "Blog{" +
"bname='" + bname + '\'' +
'}';
}
@Test
public void testImportPorp() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans-import-prop.xml");
Blog blog = applicationContext.getBean("blog", Blog.class);
System.out.println(blog.toString());
}
}
blog.properties文件内容:
blog.bname=wells
<?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: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/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 引入外部属性文件 -->
<context:property-placeholder location="blog.properties"/>
<bean id="blog" class="com.wells.demo.ioc.xml.Blog">
<property name="bname" value="${blog.bname}"/>
</bean>
</beans>