spring基础知识-IOC

1 Spring概述

  1. Spring 是轻量级的开源的 JavaEE 框架

  2. Spring 有两个核心部分:IOC 和 AOP。

    1. IOC:控制反转,把创建对象过程交给 Spring 进行管理
    2. AOP:面向切面,不修改源代码进行功能增强
  3. Spring 特点 :

    1. 方便解耦,简化开发
    2. Aop 编程支持
    3. 方便程序测试
    4. 方便和其他框架进行整合
    5. 方便进行事务操作
    6. 降低 API 开发难度

2 IOC

概念:控制反转,把对象创建和对象的调用过程交给spring进行管理。

目标:降低耦合度。

底层原理:xml,反射,工厂模式

2.1 两大接口

Spring提供IOC容器两种实现方式(两个接口)

  1. BeanFactory:Spring内部使用的接口,不提倡开发人员使用。特点:加载配置文件时不会创建对象,获取对象时才会创建对象。

  2. ApplicationContext:BeanFactory的子接口,提供了更多更强大的功能,一般由开发人员使用。特点:加载配置文件时会把配置文件里的对象进行创建。

    1. FileSystemXmlApplicationContext:绝对路径,从盘符开始算起
    2. ClassPathXmlApplicationContext:相对路径,从src开始算起
  3. Bean管理是指两个操作:Spring创建对象 和 Spring注入属性。

    1. Bean管理有两种操作方式:基于xml配置文件方式实现 和 基于注解方式实现

2.2 基于XML操作Bean

在基于xml创建对象的时候需要做到:

image-20210719101725911

  1. 在Spring配置文件中使用bean标签来创建对象
  2. bean标签有很多属性,常用属性:
    • id:唯一标识
    • class:类路径
  3. 创建对象时,默认执行无参构造函数

方法一:

  1. 为对应类提供set方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    public class User {

    private String userName;
    private String userAge;

    public void setUserName(String userName) {
    this.userName = userName;
    }

    public void setUserAge(String userAge) {
    this.userAge = userAge;
    }

    public String getUserName() {
    return userName;
    }

    public String getUserAge() {
    return userAge;
    }
    }

  2. 在xml配置文件中通过property标签进行属性注入

    1
    2
    3
    4
    5
    <!--配置User对象-->
    <bean id="user" class="com.oymn.spring5.User">
    <property name="userName" value="haha"></property>
    <property name="userAge" value="18"></property>
    </bean>
  3. 创建对象

    1
    2
    3
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean1.xml");
    User user = applicationContext.getBean("user", User.class);
    System.out.println(user.getUserName() + " " + user.getUserAge());

注入值

空值
1
2
3
4
<!--配置User对象-->
<bean id="user" class="com.oymn.spring5.User">
<property name="userName"> <null/> </property>
</bean>
属性值含有特殊符号

假设现在userName属性需要赋值为 < haha >

如果像上面那样直接在value中声明的话会报错,因为包含特殊符号 <>

image-20240414102602634

注入外部Bean

有两个类:UserService和UserDaoImpl,其中UserDaoImpl实现UserDao接口

1
2
3
4
5
6
7
8
9
10
11
12
public class UserService {

private UserDao userDao;

public void setUserDao(UserDao userDao){
this.userDao = userDao;
}

public void add(){
System.out.println("add");
}
}

使用ref来指定外部bean

1
2
3
4
5
<bean id="userDaoImpl" class="com.oymn.spring5.UserDaoImpl"></bean>

<bean id="userService" class="com.oymn.spring5.UserService">
<property name="userDao" ref="userDaoImpl"></property>
</bean>
注入内部bean

嵌套bean标签实现

1
2
3
4
5
6
7
8
9
10
11
12
<!--内部 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>

方法二:

  1. 提供有参构造

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class User {

    private String userName;
    private String userAge;

    public User(String userName, String userAge){
    this.userName = userName;
    this.userAge = userAge;
    }
    }

  2. xml中通过constructor-arg标签进行属性注入

    1
    2
    3
    4
    5
    <!--配置User对象-->
    <bean id="user" class="com.oymn.spring5.User">
    <constructor-arg name="userName" value="haha"></constructor-arg>
    <constructor-arg name="userAge" value="18"></constructor-arg>
    </bean>

注入值

级联赋值

emp类中有ename和dept两个属性,其中dept有dname属性,写法二需要emp提供dept属性的get方法。

1
2
3
4
5
6
7
8
9
10
11
12
<!--级联赋值-->
<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>
注入属性集合-数组/List/Map/Set
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Stu {

private String[] courses;
private List<String> list;
private Map<String,String> map;
private Set<String> set;

public void setCourses(String[] courses) {
this.courses = courses;
}

public void setList(List<String> list) {
this.list = list;
}

public void setMap(Map<String, String> map) {
this.map = map;
}

public void setSet(Set<String> set) {
this.set = set;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<bean id="stu" class="com.oymn.spring5.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="map">
<map>
<entry key="JAVA" value="java"></entry>
<entry key="PHP" value="php"></entry>
</map>
</property>
<!--Set类型属性注入-->
<property name="set">
<set>
<value>Mysql</value>
<value>Redis</value>
</set>
</property>
</bean>

如果是对象的话,写法:集合+外部bean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!--创建多个 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>

<!--注入 list 集合类型,值是对象-->
<property name="courseList">
<list>
<ref bean="course1"></ref>
<ref bean="course2"></ref>
</list>
</property>

把集合注入部分提取出来

使用 util 标签,这样不同的bean都可以使用相同的集合注入部分了。

1
2
3
4
5
6
7
8
9
<!--将集合注入部分提取出来-->
<util:list id="booklist">
<value>易筋经</value>
<value>九阳神功</value>
</util:list>

<bean id="book" class="com.oymn.spring5.Book">
<property name="list" ref="booklist"></property>
</bean>

Bean的作用域

在Spring中,默认Bean是单实例对象。但是可以通过修改Bean标签中的scope属性,让单例对象变为多实例对象。

image-20210719113500730

Bean的生命周期

  1. 通过构造器创建 bean 实例(无参数构造)
  2. 为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)
  3. 把 bean 实例传递 bean 后置处理器的方法 postProcessBeforeInitialization
  4. 调用 bean 的初始化的方法(需要进行配置初始化的方法)
  5. 把 bean 实例传递 bean 后置处理器的方法 postProcessAfterInitialization
  6. bean 可以使用了(对象获取到了)
  7. 当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Orders {
private String orderName;

public Orders() {
System.out.println("第一步:执行无参构造方法创建bean实例");
}

public void setOrderName(String orderName) {
this.orderName = orderName;
System.out.println("第二步:调用set方法设置属性值");
}

//初始化方法
public void initMethod(){
System.out.println("第四步:执行初始化方法");
}

//销毁方法
public void destroyMethod(){
System.out.println("第七步:执行销毁方法");
}
}

后置处理器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//实现后置处理器,需要实现BeanPostProcessor接口
public class MyBeanPost implements BeanPostProcessor {

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("第三步:将bean实例传递给bean后置处理器的postProcessBeforeInitialization方法");
return bean;
}

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("第五步:将bean实例传递给bean后置处理器的postProcessAfterInitialization方法");
return bean;
}
}

xml创建对象

1
2
3
4
5
6
<bean id="orders" class="com.oymn.spring5.Orders" init-method="initMethod" destroy-method="destroyMethod">
<property name="orderName" value="hahah"></property>
</bean>

<!--配置bean后置处理器,这样配置后整个xml里面的bean用的都是这个后置处理器-->
<bean id="myBeanPost" class="com.oymn.spring5.MyBeanPost"></bean>

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
@Test
public void testOrders(){

ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");

Orders orders = context.getBean("orders", Orders.class);

System.out.println("第六步:获取bean实例对象");
System.out.println(orders);

//手动让bean实例销毁
context.close();
}

执行结果

image-20210720122628081

XML自动装配

  • 根据指定的装配规则(属性名称或者属性类型),Spring自动将匹配的属性值进行注入。
  • 根据属性名称自动装配:要求 emp中属性的名称dept 和 bean标签的id值dept 一样,才能识别。
  • 根据属性类型自动装配:要求同一个xml文件中不能有两个相同类型的bean,否则无法识别是哪一个

通过外部文件操作Bean

  1. 导入德鲁伊连接池jar包
  2. 创建外部属性文件,properties格式文件,写数据库信息

image-20210731004522456

  1. 引入context名称空间,并通过context标签引入外部属性文件,使用“${}”来获取文件中对应的值

image-20210731010320233

2.3 基于注解操作Bean

  • 格式:@注解名称(属性名=属性值,属性名=属性值,……)
  • 注解可以作用在类,属性,方法。
  • 使用注解的目的:简化xml配置

创建对象

  • @Component:将该类标记为一个组件,Spring会自动扫描该类,并将其实例化为一个Bean。
  • @Service:一般用于Service层
  • @Controller:一般用于web层
  • @ Repository:一般用于Dao层

基于注解进行属性注入

@Autowired:根据属性类型自动装配

1
2
3
4
public interface StuDao {
public void add();
}

1
2
3
4
5
6
7
@Repository
public class StuDaoImpl implements StuDao {
@Override
public void add() {
System.out.println("StuDaoImpl");
}
}
1
2
3
4
5
6
7
8
9
10
11
@Component(value="stuService")
public class StuService {

@Autowired
public StuDao stuDao;

public void add(){
System.out.println("addService");
stuDao.add();
}
}

@Qualifier:根据属性名称自动装配

当遇到一个接口有很多实现类时,只通过@Autowire是无法完成自动装配的,所以需要再使用@Qualifier通过名称来锁定某个类

1
2
3
4
5
6
7
8
9
10
11
12
@Component(value="stuService")
public class StuService {

@Autowired
@Qualifier(value="stuDaoImpl") //这样就能显式指定stuDaoImpl这个实现类
public StuDao stuDao;

public void add(){
System.out.println("addService");
stuDao.add();
}
}

@Resource:可以根据类型注入,也可以根据名称注入

1
2
3
4
5
6
7
8
9
10
11
12
@Component(value="stuService")
public class StuService {

//@Resource //根据类型进行注入
@Resource(name="stuDaoImpl") //根据名称进行注入
public StuDao stuDao;

public void add(){
System.out.println("addService");
stuDao.add();
}
}

@Value:注入普通类型属性

1
2
@Value(value = "abc")
private String name;

完全基于注解开发

创建配置类替代XML文件

1
2
3
4
@Configuration    //表明为一个配置类
@ComponentScan(basePackages = "com.oymn") //开启组件扫描
public class SpringConfig {
}

spring基础知识-IOC
https://baijianglai.cn/spring基础知识-IOC/245be0057711/
作者
Lai Baijiang
发布于
2024年4月13日
许可协议