xiaotian1339 发表于 2022-11-29 22:30

[Spring学习记录]学习到基于XML配置的SpringIoC阶段的学习笔记

本帖最后由 xiaotian1339 于 2022-11-29 22:32 编辑

**纯小白的学习笔记,学习的还比较不到位不全面,欢迎大佬指正错误**

### 基于XML配置文件配置自行标注的注释

```xml
<?xml version="1.0" encoding="UTF-8"?>
<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">


    <bean id="userDao" class="com.demo.dao.impl.UserDaoImpl"></bean>

    <!--    class:Beam对象的全限定名称-->
    <!--    id属性:bean对象的唯一标识,会转为默认的name,若空则name变为该类全限定名称-->
    <!--    init-method属性设置的方法在构造并且注入结束之后调用,若类还实现了InitializingBean接口,则在该接口中afterPropertiesSet方法之后执行-->
    <!--    InitializingBean 接口中 afterPropertiesSet 方法在构造并注入结束之后调用 -->
    <!--    destroy-method属性设置的方法,在显式调用容器的close方法之后调用,若直接强制关闭容器则不调用,例如直接结束进程-->
    <!--    scope属性有两个值:
                singleton(默认):在ApplicationContext容器new之后直接创建单例,并存储在单例池中
                prototype: 原型方法,在ApplicationContext容器new之后不会主动创建bean对象,每调用getBean方法时创建一个新的实例对象,不会存在单例池中
            -->
    <!--    lazy-init属性:是否懒加载,true则在调用getBean方法是再new对象。::只在Spring容器中生效的属性,BeanFactory容器不生效::-->
    <!--    name属性:别名,可以取多个名字,底层是把名字存在一个map中,指向被取代的id名,若无id,则容器的名字则为name属性的第一个值-->
    <bean id="userService" class="com.demo.service.impl.UserServiceImpl" init-method="initMethod"
          destroy-method="destroyMethod">
      <!--      property:依赖注入name属性为该bean对象的setXxx方法名字中的xxx,首字母小写;ref值为要注入的bean对象-->
      <property name="userDao" ref="userDao"></property>
      <!--      constructor-arg表示调用其有参构造name表示参数名,value表示值-->
      <!--      constructor-arg只要是构造时需要的参都可以传,不只构造函数-->
      <constructor-arg name="var1" value="tc"></constructor-arg>
      <constructor-arg name="var2" value="7"></constructor-arg>
    </bean>


    <!--    配置静态工厂拿到非工厂对应实例-->
    <!--    class为工厂全限定名称-->
    <!--    factory-method为return实例的静态方法-->
    <!--    如果方法里面有参数可用constructor-arg标签定义-->
    <bean id="userDao1" class="com.demo.factory.MyStaticBeanFactory" factory-method="getUserDao"></bean>


    <!--    配置实例工厂-->
    <!--    第一步拿到工厂实例-->
    <bean id="entityBeanFactory" class="com.demo.factory.MyEntityBeanFactory"></bean>
    <!--    第二步,通过实例工厂拿到所需的实例对象-->
    <!--    如果方法里面有参数可用constructor-arg标签定义-->
    <bean id="userDao2" factory-bean="entityBeanFactory" factory-method="getUserDao">
      <!--      可以用单标签,避免idea标签空体的警告-->
      <constructor-arg name="name" value="arg1"/>
    </bean>

    <!--    实现FactoryBean规范延迟实例化Bean-->
    <bean id="userDao3" class="com.demo.factory.MyFactoryBeanFactory">
      <!--实际上,这种方式声明的
            单例池中保存的是MyFactoryBeanFactory对象,只有调用getBean方法时,才会实例化并保存在FactoryBeanObjectCache中-->
    </bean>

<!--    注入List set Map 等-->
    <bean id="deptService" class="com.demo.service.impl.DeptServiceImpl">
<!--      基本数据类型List-->
      <property name="stringList">
<!--            因为是List所以用list标签包裹-->
            <list>
<!--                注入的项目 基础数据类型-->
                <value>aaa</value>
                <value>bbb</value>
                <value>ccc</value>
            </list>
      </property>
<!--      引用数据类型List-->
      <property name="userDaoList">
            <list>
<!--                拿到的是Spring容器中的单例-->
<!--                也可以用bean标签直接配
<bean id="userDao" class="com.demo.dao.impl.UserDaoImpl"></bean>
-->
                <ref bean="userDao"></ref>
                <ref bean="userDao1"></ref>
                <ref bean="userDao2"></ref>
                <ref bean="userDao2"></ref>
            </list>
      </property>
<!--      Set与List注入方式相同 只是list标签变成set标签-->

<!--      map-->
      <property name="map">
            <map>
<!--                若为基础数据类型就不用带-ref引用数据类型就使用-ref-->
                <entry key="a" value-ref="userDao"></entry>
                <entry key="b" value-ref="userDao1"></entry>
                <entry key="c" value-ref="userDao2"></entry>
            </map>
      </property>

<!--      properties 其实就相当于键值对都为String的Map-->
      <property name="properties">
<!--            和Map一样不能有相同的Key 相同的话应该会取最后一个值-->
            <props>
                <prop key="a">aaa</prop>
                <prop key="b">bbb</prop>
                <prop key="c">ccc</prop>
                <prop key="a">ddd</prop>
            </props>
      </property>

    </bean>

<!--    上面是手动装配-->
<!--    介绍自动装配 -> byName 根据Bean中setXxx方法的Xxx找配置好的且id/name=xxx的bean实例注入-->
<!--    ......... -> byType 根据Bean中set方法的入参类型匹配到配置好的bean注入.::注意,使用byType时配置文件中匹配到的Bean只能有且仅有一个,否则会报错::-->

<!--    本例:byName中会找到AutoWireServiceImpl中setDeptService方法,找到set后面的deptService(名称匹配)的bean并注入-->
    <bean id="autoWireService" class="com.demo.service.impl.AutoWireServiceImpl" autowire="byName"></bean>
<!--    本例:byType中会找到AutoWireServiceImpl中setDeptService方法,找到与传入参数类型相同的Bean并注入,该类型的bean应保证有且只有1个-->
    <bean id="autoWireService2" class="com.demo.service.impl.AutoWireServiceImpl" autowire="byType"></bean>

</beans>
```



---

### 拿到Bean的三种方式
      
​      **1.通过bean的id来获取**

​                        所拿到的类型时Object,需要强转

```java
UserDao userDao = (UserDao) context.getBean("userDao");
```



​      **2.通过bean的类型来获取**

​                        这种方式要求Spring容器中有且仅有一种该类型的Bean,返回的即为对应类型

```java
UserDao userDao = context.getBean(UserDao.class);
```



​      **3.通过bean的id和类型来获取**

​                        这种方式无需强制类型转换,也解决了对于多个对象的同一种Bean获取会报错的问题。

```java
UserDao date = context.getBean("userDao", UserDao.class);
```

### 生成Bean的三种方式
配置文件中也有测试,不过还是单独拿出来在写一写,加深一下印象,打个标题以后也方便找
**1.**通过bean的构造函数获取**

```xml
<bean id="userDao" class="com.demo.dao.impl.UserDaoImpl"/>
```

**2.使用静态工厂创建bean**

假设有一个静态工厂

```java
public class UserDaoFactory {
   public static UserDao getUserDao() {
          return new UserDaoImpl();
   }
}
```

则,xml中

```xml
<bean id="userDao" class="com.demo.factory.UserDaoFactory" factory-method="getUser" init-method="init"/>
```

**3.使用实例工厂创建bean**

假设有一个实例工厂

```java
public class UserDaoInstanceFactory {
   public UserDao getUserDao() {
          return new UserDaoImpl();
   }
}
```

则xml

```xml
<bean id="userDaoInstanceFactory" class="com.demo.factory.UserDaoInstanceFactory"/>
<bean id="userDao" factory-bean="userDaoInstanceFactory" factory-method="getUserDao"/>
```

**注意:id为唯一标识,不能相同;可以使用beans的profile划分工作区**

---

### 后处理器

#### BeanFactoryPostProcessor

**BeanDefinitionMap填充完毕后,实例Bean前执行接口方法**

##### **1.BeanFactoryPostProcessor接口**

```java
void postProcessBeanFactory(ConfigurableListableBeanFactory var1) throws BeansException;
```

例子:

1.更改**BeanDefinitionMap**中Bean的配置信息

```java
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
    BeanDefinition userDaoBeanDefinition = configurableListableBeanFactory.getBeanDefinition("userDao");
    userDaoBeanDefinition.setBeanClassName("com.demo.service.impl.UserServiceImpl");
}
```

以上例子更改userDao的class为UserService,执行实例化后,原为UserDaoImpl的userDao会被实例化为UserServiceImpl   id名字不变



2.注册未在配置文件中配置的Bean

```java
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
    // 1.注册BeanDefinition
    BeanDefinition beanDefinition = new RootBeanDefinition();
    beanDefinition.setBeanClassName("com.demo.dao.impl.UserDaoImpl");
    // 2.强转成DefaultListableBeanFactory
    DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) configurableListableBeanFactory;
    // 3.利用DefaultListableBeanFactory将Definition注册到map中
    defaultListableBeanFactory.registerBeanDefinition("userDao",beanDefinition);
}
```

---

##### 2.BeanDefinitionRegistryPostProcessor接口

```java
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
    void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry var1) throws BeansException;
}
```



针对BeanFactoryPostProcessor接口例子2的更简单的实现

```java
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
      BeanDefinition beanDefinition = new RootBeanDefinition();
      beanDefinition.setBeanClassName("com.demo.dao.impl.UserDaoImpl");
      beanDefinitionRegistry.registerBeanDefinition("userDao",beanDefinition);
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {    }
```

---

#### BeanPostProcessor

**Bean实例化完毕后及依赖注入完成后执行接口方法**

##### BeanPostProcessor接口

```java
public interface BeanPostProcessor {
    @Nullable
    default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
      return bean;
    }

    @Nullable
    default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
      return bean;
    }
}
```

**postProcessBeforeInitialization**: 在调用init-method ( InitializingBean接口方法 [若有] ) 之前调用

**postProcessAfterInitialization**:在调用init-method( InitializingBean接口方法 [若有] )之后调用



例子动态代{过}{滤}理增强Bean功能

```java
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
      return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
      UserDao o = (UserDao) Proxy.newProxyInstance(bean.getClass().getClassLoader(),
                bean.getClass().getInterfaces(),
                (proxy, method, args) -> {
                  System.out.println("执行开始:" + new Date().getTime());
                  Object invoke = method.invoke(bean, args);
                  System.out.println("执行结束:" + new Date().getTime());
                  return invoke;
                });
      return o;
    }
```

对所有原有方法调用前后均在控制台输出一下时间

---

### 整合第三方框架

这里以**Mybatis**为例

com.demo.mapper.UserMapper.java

```java
public interface UserMapper {
    List<User> findAll();
}
```

com.demo.pojo.User.java

```java
public class User {
    private Integer id;
    private String username;
    private String password;
    @Override
    public String toString() {
      return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
    public Integer getId() {
      return id;
    }
    public void setId(Integer id) {
      this.id = id;
    }
    public String getUsername() {
      return username;
    }
    public void setUsername(String username) {
      this.username = username;
    }
    public String getPassword() {
      return password;
    }
    public void setPassword(String password) {
      this.password = password;
    }
```

com.demo.service.impl.UserServiceImpl.java

```java
public class UserServiceImpl implements UserService {
    private UserMapper userMapper;

    public void setUserMapper(UserMapper userMapper) {
      this.userMapper = userMapper;
    }

    public void printUser(){
      List<User> all = userMapper.findAll();
      for (User user : all) {
            System.out.println(user);
      }
    }
```

UserService接口只有一个printUser方法

资源路径下

com.demo.mapper.UserMapper.xml

```xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
      PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
      "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.demo.mapper.UserMapper">
    <select id="findAll" resultType="com.demo.pojo.User"><![CDATA[
      select * from user where id < 10
    ]]></select>
</mapper>
```

对于Spring的XML配置文件只需配置以下的基础信息(以druid为例)

```xml
<bean id="userService" class="com.demo.service.impl.UserServiceImpl">
            <property name="userMapper" ref="userMapper"/>
      </bean>

      <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
            <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://localhost:1234/xxx"/>
            <property name="username" value="xxxx"/>
            <property name="password" value="xxxx"/>
      </bean>

      <bean class="org.mybatis.spring.SqlSessionFactoryBean">
            <property name="dataSource" ref="dataSource"/>
      </bean>
      <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
            <property name="basePackage" value="com.demo.mapper"/>
      </bean>
```

在测试类中拿到UserService后即可打印出对应sql结果

---

L133 发表于 2022-11-30 00:10

可以很强

lfordch 发表于 2022-11-30 04:07

膜拜一下大佬,先收藏了,后面慢慢学习{:301_975:}

Sorryks 发表于 2022-11-30 07:15

大佬强,我javaSE还没学完呢{:301_971:}

zmp520 发表于 2022-11-30 08:13

膜拜一下大佬,

yzjtxwd 发表于 2022-11-30 09:27

学习一下
页: [1]
查看完整版本: [Spring学习记录]学习到基于XML配置的SpringIoC阶段的学习笔记