[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结果
--- 可以很强 膜拜一下大佬,先收藏了,后面慢慢学习{:301_975:} 大佬强,我javaSE还没学完呢{:301_971:} 膜拜一下大佬, 学习一下
页:
[1]