文章目录
Spring 设计思想
IOC 控制反转
概念:
- IOC(Inversion of Control) 控制反转。是面向对象编程中的一种设计思想,可以用来降低代码之间的耦合度,符合依赖倒置原则。
- 使用对象时由原先的 new 创建,转换为有外部容器提供对象。此过程中,系统的对象创建控制权从程序转移到外部,该编程实现称之为控制反转。通俗的话来说就是:“将 new 对象的权利转交给 Spring,需要时从Spring 中获取就可”。
Spring IoC:
- Spring 提供 IoC 容器,充当 IoC 思想中的“外部”。
- IoC 容器负责对象的创建,初始化等工作,被管理的类对象在该容器中统称为 Bean。
- 在IoC容器中将有依赖关系的 bean 进行关系绑定(DI 依赖注入)。
DI 依赖注入
概念:
- DI(Dependency Injection)依赖注入,在容器中建立 bean 与 bean 之间的依赖关系的整个过程,称为依赖注入。
- 客户端不仅可以直接从 IoC 容器中获取,并且获取到的 bean 已经绑定了所有的依赖关系。
DIP 依赖倒置
**概念:**依赖倒置原则(Dependence Inversion Principle),简称 DIP,主要倡导面向抽象编程,面向接口编程,不要面向具体编程,让上层不再依赖下层,下面改动了,上面的代码不会受到牵连。这样可以大大降低程序的耦合度,耦合度低了,扩展力就强了,同时代码复用性也会增强。(软件七大开发原则都是在为解耦合服务)
OCP 开闭解耦
概念:
- 开闭原则是面向对象的可复用设计的第一块基石,它是最重要的面向对象设计原则。
- 开闭原则定义为:一个软件实体应当对扩展开放,对修改关闭。即软件实体应尽量在不修改原有代码的情况下进行扩展。
- 软件实体从不同的粒度可以理解为模块、类、方法或者属性。
Spring 框架搭建
理论概述
概念:
- Spring 是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。
- Spring 最初的出现是为了解决 EJB 臃肿的设计,以及难以测试等问题。
- Spring 为简化开发而生,让程序员只需关注核心业务的实现,尽可能的不再关注非业务逻辑代码(事务控制,安全日志等)。
特点:
- 轻量 (完整的 Spring 框架可以在一个大小只有 1MB 多的 JAR 文件里发布。并且 Spring 所需的处理开销也是微不足道的)。
- 控制反转。
- 面向切面。
相关依赖
<!-- Spring6 版本的 jar 包未有正式版上传 Maven 中央仓库(截止至2023.3.5) -->
<repositories>
<repository>
<id>repository.spring.milestone</id>
<name>Spring Milestone Repository</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
<dependencies>
<!--spring context 依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.0-M2</version>
</dependency>
<!--Spring 对 junit 的支持相关依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>6.0.0-M2</version>
</dependency>
<!-- Spring 提供的 JDBC 模版工具依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>6.0.0-M2</version>
</dependency>
<!--spring aop 依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>6.0.0-M2</version>
</dependency>
<!--spring + aspects 依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>6.0.0-M2</version>
</dependency>
</dependencies>
集成日志
Spring 集成 Log4j 日志框架:
- 引入 Log4j2 的依赖。
<!--log4j2的依赖--> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.19.0</version> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j2-impl</artifactId> <version>2.19.0</version> </dependency>
- 根路径下提供 log4j2.xml 配置文件。
<?xml version="1.0" encoding="UTF-8"?> <configuration> <loggers> <!-- ALL < TRACE < DEBUG < INFO < WARN < ERROR < FATAL < OFF --> <root level="DEBUG"> <appender-ref ref="spring6log"/> </root> </loggers> <appenders> <!--输出日志信息到控制台--> <console name="spring6log" target="SYSTEM_OUT"> <!--控制日志输出的格式--> <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss SSS} [%t] %-3level %logger{1024} - %msg%n"/> </console> </appenders> </configuration>
- 获取日志打印对象。
Logger logger = LoggerFactory.getLogger(FirstSpringClass.class); logger.info("This is log from FirstSpringClass");
IOC 容器实现
Bean 基础配置
- 准备 Bean 类。
// 数据访问Dao层 public interface BookDao { void save(); } public class BookDaoImpl implements BookDao { public void save() { System.out.println("DAO:添加书籍到数据库"); } } // 业务逻辑层 public interface BookService { void save(); } public class BookServiceImpl implements BookService { private BookDao bookDao = new BookDaoImpl(); //实现业务方法 @Override public void save() { System.out.println("业务层:调用添加书籍的方法"); bookDao.save(); } }
- 定义 Spring 配置文件 applicationContext.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标签:表示配置bean id属性:表示给bean起名字 class属性:表示给bean定义类型 name定义该bean的别名,可定义多个,使用逗号、空格、分号隔开 --> <bean id="bookService" class="com.lxl.service.impl.BookServiceImpl" name="bookImpl,bookSI"> </bean> </beans>
- 初始化IoC容器(Spring核心容器),通过容器获取bean对象。
public class App { public static void main(String[] args) { //1.创建IoC容器对象,加载spring核心配置文件 ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); //2 从IOC容器中获取Bean对象(BookService对象) BookService bookService= (BookService) ctx.getBean("bookService"); //3 调用Bean对象(BookService对象)的方法 bookService.save(); } }
Bean 作用域
<bean id="bookService"
class="com.lxl.service.impl.BookServiceImpl"
scope="singleton">
</bean>
<!--
scope属性值,常用范围取值:
1.singleton:单例(默认),该容器始终提供同一个 bean 对象。
2.prototype:多例,每次获取该bean对象,容器都会创建一个新的返回。
扩展:scope 的取值不仅仅只有 singleton 和 prototype,在 SpringMVC 环境下还有 request、session、application、 websocket,表示创建出的对象放置在web容器(tomcat)对应的位置。比如:request 表示保存到 request 域中。
-->
Bean 实例化
- 构造器实例化。
<bean id="bookDao" class="com.lxl.dao.impl.BookDaoImpl"/> <!-- 注意:无参构造方法如果不存在,将抛出异常 BeanCreationException -->
- 静态工厂实例化。
public class BookDaoFactory { //定义静态工厂创建 Dao 类 public static BookDao getBookDao(){ System.out.println("BookDaoFactory setup...."); return new BookDaoImpl(); } }
<bean id="bookDao" class="com.lxl.factory.BookDaoFactory" factory-method="getBookDao"/>
- 实例工厂实例化。
public class BookDaoFactory { public BookDao getBookDao(){ return new BookDaoImpl(); } }
<bean id="bookFactory" class="com.lxl.factory.BookDaoFactory"/> <bean id="bookDao" factory-method="getBookDao" factory-bean="bookFactory"/>
- FactoryBean 工厂实例化。
public class BookDaoFactory implements FactoryBean<BookDao> { @Override public BookDao getObject() throws Exception { return new BookDaoImpl(); } @Override public Class<BookDao> getObjectType() { return BookDao.getClass(); } @Override public boolean isSingleton() { // true表示单例,false表示原型 return true; } }
<bean id="bookDao" class="com.lxl.beanfactory.BookDaoFactory"/>
- 选择性实例化(需使用 context 命名空间)。
<?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=" ttp://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"> <!-- 包含法: 1.将 use-default-filters 设置为 false 2.context:include-filter 标签列出哪些注解标注的 Bean 参与实例化 use-default-filters 属性控制 spring 默认实例化规则 该属性为 false 时表示即使有 ( Component、Controller、Service、Repository ) 这些注解标注,也不再实例化。 --> <context:component-scan base-package="com.lxl.bean" use-default-filters="false"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> <!-- 排除法: 1.将 use-default-filters 设置为 true 2.exclude-filter 标签列出哪些注解标注的 Bean 不参与实例化 --> <context:component-scan base-package="com.lxl.bean"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> </beans>
Bean 生命周期
概念:
- 生命周期:从创建到消亡的完整过程。
- bean 生命周期:bean 从创建到销毁的整体过程。
- bean 生命周期控制:在 bean 创建后到销毁前做一些事情。
Bean 生命周期/构建流程:
- 实例化 Bean:
- 对于 BeanFactory 容器,当客户向容器请求一个尚未初始化的 bean 时,或初始化 bean 的时候需要注入另一个尚未初始化的依赖时,容器就会调用 createBean 进行实例化。
- 对于 ApplicationContext 容器,当容器启动结束后,通过获取 BeanDefinition 对象中的信息,实例化所有的 bean。
- 设置对象属性(依赖注入):
- 实例化后的对象被封装在 BeanWrapper 对象中,紧接着,Spring 根据 BeanDefinition 中的信息 以及 通过 BeanWrapper 提供的设置属性的接口完成依赖注入。
- 处理 Aware 接口:
- 如果这个 Bean 已经实现了 BeanNameAware 接口,会调用它实现的 setBeanName() 方法,此处传递的就是 Spring 配置文件中 Bean 的 id 值;
- 如果这个 Bean 已经实现了 BeanFactoryAware 接口,会调用它实现的 setBeanFactory() 方法,传递的是 Spring 工厂自身。
- 如果这个 Bean 已经实现了 ApplicationContextAware 接口,会调用 setApplicationContext() 方法,传入 Spring 上下文;
- BeanPostProcessor (before):
- 如果想对 Bean 进行一些自定义的处理,那么可以在当前 xml 文件中配置实现了 BeanPostProcessor 接口的 bean,配置后该 xml 文件中的所有其他 bean 初始化时前将会调用其 postProcessBeforeInitialization(Object obj, String s) 方法。由于这个方法是在当前 XML 文件中所有 Bean 初始化前调用的,所以应位于 beans 的第一条配置,以管理其他 bean。
- InitializingBean (前初始化):
- 如果检查该 bean 实现了 InitializingBean 接口,调用接口方法进行初步初始化。
- init-method (后初始化):
- 如果 Bean 在 Spring 配置文件中配置了 init-method 属性,则会自动调用其配置的初始化方法进行二次初始化。
- BeanPostProcessor (after):
- 如果当前 xml 文件配置了 BeanPostProcessor 接口的实现类 bean,该配置文件中所有其他 bean 初始化后将会调用该 BeanPostProcessor bean 处理器的 postProcessAfterInitialization(Object obj, String s) 方法。
- 以上几个步骤完成后,Bean 就已经被正确创建了,之后就可以使用这个 Bean 了。
- DisposableBean (前销毁):
- 当 Bean 不再需要时,会经过清理阶段,如果 Bean 实现了 DisposableBean 这个接口,会调用其实现的 destroy()方法。
- destroy-method (后销毁):
- 最后,如果这个 Bean 的 Spring 配置中配置了 destroy-method 属性,会自动调用其配置的销毁方法。
Bean 销毁时机:
- 容器关闭前销毁.
关闭容器方式:
- 手动关闭容器,调用容器对象的 close() 方法。
- 注册钩子函数,在JVM关闭前先关闭容器,调用容器对象 registerShutdownHook() 方法。
Bean 生命周期与作用域:
- 对于 singleton 作用域的 Bean,Spring 能够精确地知道该 Bean 何时被创建,何时初始化完成,以及何时被销毁。
- 对于 prototype 作用域的 Bean,Spring 只负责创建,当容器创建了 Bean 的实例后,Bean 的实例就交给客户端代码管理,Spring 容器将不再跟踪其生命周期(只负责到 Bean 生命周期的第七步)。
代码示例:
public class User implements BeanNameAware, BeanClassLoaderAware, BeanFactoryAware, InitializingBean, DisposableBean { private String name; public User() { System.out.println("1.实例化Bean"); } public void setName(String name) { this.name = name; System.out.println("2.Bean属性赋值"); } public void initBean(){ System.out.println("6.初始化Bean"); } public void destroyBean(){ System.out.println("10.销毁Bean"); } @Override public void setBeanClassLoader(ClassLoader classLoader) { System.out.println("3.类加载器:" + classLoader); } @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { System.out.println("3.Bean工厂:" + beanFactory); } @Override public void setBeanName(String name) { System.out.println("3.bean名字:" + name); } @Override public void destroy() throws Exception { System.out.println("9.DisposableBean destroy"); } @Override public void afterPropertiesSet() throws Exception { System.out.println("5.afterPropertiesSet执行"); } } public class MyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("4.Bean后处理器的before方法执行,即将开始初始化"); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("7.Bean后处理器的after方法执行,已完成初始化"); return bean; } }
<!-- init-method:设置 bean 初始化生命周期回调函数 --> <!-- destroy-method:设置 bean 销毁生命周期函数,仅适用于单例对象 --> <bean id="user" class="com.lxl.dao.impl.User" init-method="initBean" destroy-method="destroyBean"> <property name="name" value="LXL"></perproties> </bean> <bean id="myBeanPostProcessor" class="com.lxl.beanutil.MyBeanPostProcessor"></bean>
Bean 依赖注入
简单/复杂注入
依赖注入:
- 简单类型注入,通过 value 标签或属性完成。
- 复杂类型注入,通过 ref 标签或属性完成。
BeanUtils 规定简单类型:
public class BeanUtils{ public static boolean isSimpleValueType(Class<?> type) { return (Void.class != type && void.class != type && (ClassUtils.isPrimitiveOrWrapper(type) || Enum.class.isAssignableFrom(type) || CharSequence.class.isAssignableFrom(type) || Number.class.isAssignableFrom(type) || Date.class.isAssignableFrom(type) || Temporal.class.isAssignableFrom(type) || URI.class == type || URL.class == type || Locale.class == type || Class.class == type)); } } /* * 基本数据类型 * 基本数据类型对应的包装类 * String或其他的CharSequence子类 * Number子类 * Date子类 * Enum子类 * URI * URL * Temporal子类 * Locale * Class */
setter 注入
- Bean 类中删除 new 创建依赖属性的代码。
- 提供依赖对象对应的 setter 方法。
public class BookServiceImpl implements BookService { //删除使用new的形式创建对象的代码,解除对象之间的耦合度 private BookDao bookDao; //提供依赖对象对应的setter方法 public void setBookDao(BookDao bookDao) { this.bookDao = bookDao; } //实现业务方法 @Override public void save() { System.out.println("业务层:调用添加书籍的方法"); bookDao.save(); } }
- 配置 Service 与 Dao 依赖关系。
<!-- bean标签:表示配置bean。 id属性:定义 bean 的名字。 class属性:定义 bean 的类型。 property 标签:配置当前 bean 中依赖属性,底层调用对应 setter 方法。 name 属性:依赖属性的名称。 ref 属性:复杂类型注入时对应 bean 的 id。 value 属性:简单类型注入时的值。 --> <bean id="bookDao" class="com.lxl.dao.impl.BookDaoImpl"/> <bean id="bookService" class="com.lxl.service.impl.BookServiceImpl"> <property name="bookDao" ref="bookDao"/> </bean>
setter 注入原理:
- 通过bean类的无参构造器创建对象。
- 通过配置文件从容器中获取所需依赖对象,并通过反射机制调用对应的setter方法为其赋值。
constructor 注入
- Bean 类中提供有参构造器。
- 配置 Service 与 Dao 依赖关系。
<bean id="bookDao" class="com.lxl.dao.impl.BookDaoImpl"/> <bean id="bookService" class="com.lxl.service.impl.BookServiceImpl"> <!-- 指定有参构造器的参数名注入--> <constructor-arg name="bookDao" ref="bookDao"/> <!-- 指定参数位置注入,0表示第一个参数,1表示第二个参数,以此类推--> <constructor-arg index="0" ref="bookDao"/> <!-- 通过类型自动匹配注入,注入多个相同类型时不可用该方法--> <constructor-arg ref="bookDao"/> </bean>
setter 多场景注入
<!-- 外部注入:注入的 bean 定义在外部 -->
<bean id="bookDao" class="com.lxl.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.lxl.service.impl.BookServiceImpl">
<property name="bookDao" ref="bookDao"/>
</bean>
<!-- 内部注入:注入的 bean 定义在自己内部(了解即可) -->
<bean id="bookService" class="com.lxl.service.impl.BookServiceImpl">
<property name="bookDao">
<bean class="com.lxl.dao.impl.BookDaoImpl"></bean>
</property>
</bean>
<!-- 级联属性注入,必须先父后子顺序 user -> user.name -->
<bean id="user" class="com.lxl.pojo.User"></bean>
<bean id="bookDao" class="com.lxl.dao.impl.BookDaoImpl">
<property name="portNumber" value="10"/>
<property name="user" ref="user"/>
<property name="user.name" value="小明"/>
</bean>
<!-- 简单数据类型-数组、List、Set注入 -->
<!--数组、List、Set数据类型由于注入方式相同,故其 array、set、list 标签可混用-->
<property name="arrayName">
<array>
<value>100</value>
<value>200</value>
<value>300</value>
</array>
</property>
<property name="listName">
<list>
<value>aaa</value>
<value>bbb</value>
<value>ccc</value>
</list>
</property>
<property name="setName">
<set>
<value>aaa</value>
<value>bbb</value>
<value>ccc</value>
</set>
</property>
<!-- 复杂数据类型-数组注入 -->
<bean id="good1" class="com.lxl.pojo.Good">
<property name="name" value="苹果"/>
</bean>
<bean id="good2" class="com.lxl.pojo.Good"/>
<property name="name" value="香蕉"/>
</bean>
<bean id="order" class="com.lxl.pojo.Order">
<property name="goods">
<array>
<!-- 这里使用ref标签即可 -->
<ref bean="good1"/>
<ref bean="good2"/>
</array>
</property>
</bean>
<!-- 简单数据类型-Map集合注入 -->
<property name="mapName">
<map>
<entry key="1" value="aaa"/>
<entry key="2" value="bbb"/>
<entry key="3" value="ccc"/>
</map>
</property>
<!-- 简单数据类型 properties 集合注入 -->
<property name="propertiesName">
<props>
<prop key="1">aaa</prop>
<prop key="2">bbb</prop>
<prop key="3">ccc</prop>
</props>
</property>
<!-- 复杂数据类型-Map集合注入 -->
<property name="goods">
<map>
<entry key-ref="1" value-ref="good1"/>
<entry key-ref="2" value-ref="good2"/>
</map>
</property>
Bean 自动装配
自动依赖装配
概述:
- IoC容器根据bean所依赖的资源在容器中自动查找并注入到bean中的过程称为自动装配
自动装配方式:
- 按类型(常用)byType
- 按名称 byName
- 不启用自动装配 no
自动装配特征:
- 自动装配用于引用类型依赖注入,不能对简单类型进行操作。
- 使用按类型装配时(byType)必须保障容器中相同类型的 bean 唯一,推荐使用。
- 使用按名称装配时(byName)必须保障容器中具有指定名称的 bean,因变量名与配置耦合,不推荐使用。将查找其类中所有的 set 方法名,例如 setCat,获得将set去掉并且首字母小写的字符串,即 cat。后去spring容器中寻找是否有此字符串名称 id 的对象。如果有,就取出注入;如果没有,就报空指针异常。
- 自动装配优先级低于 setter 注入与构造器注入,同时出现时,自动装配配置失效。
- XML配置中的autowire,底层调用 bean 类 setter 方法注入
自动装配用例:
<bean id="bookDao" class="com.lxl.dao.impl.BookDaoImpl"/> <bean id="bookService" class="com.lxl.service.impl.BookServiceImpl" autowire="byType" />
Bean 命名空间
P 命名空间
<!-- p命名空间简化 setter 注入配置,需要对应的属性提供 setter 方法 -->
<?xml version="1.0" encoding="UTF-8"?>
<!--第一:在XML头部信息中添加p命名空间的配置信息 xmlns:p-->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
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="bookDao" class="com.lxl.dao.impl.BookDaoImpl"/>
<bean id="bookService"
class="com.lxl.service.impl.BookServiceImpl"
p:bookDao-ref="bookDao"
p:portNumber="10" />
<!-- 简单类型 p:属性名 注入,非简单类型 p:birth-ref="beanId/beanName" -->
</beans>
C 命名空间
<!-- c 命名空间简化构造器注入,需要对应的属性提供构造器方法 -->
<?xml version="1.0" encoding="UTF-8"?>
<!--第一:在XML头部信息中添加 c 命名空间的配置信息 xmlns:c-->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:c="http://www.springframework.org/schema/c"
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="bookDao" class="com.lxl.dao.impl.BookDaoImpl"/>
<!-- 指定参数名注入-->
<bean id="bookService1"
class="com.lxl.service.impl.BookServiceImpl"
c:bookDao-ref="bookDao"
c:portNumber="10" />
<!-- 指定参数位置注入,0表示第一个参数,1表示第二个参数,以此类推-->
<bean id="bookService2"
class="com.lxl.service.impl.BookServiceImpl"
c:_0-ref="bookDao"
c:_1="10" />
<!-- 索引注入 c:_索引 ,参数名注入 c:day="1" -->
</beans>
Util 命名空间
<!-- 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: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">
<!-- util:properties 定义一个 Properties 类型的 bean -->
<!--
默认通过 location 指定的文件中定义的键值对拥有更高的优先级。
local-override 为 true 时 prop 标签定义的值优先级更高
-->
<util:properties id="prop" location="classpath:jdbc.properties"
local-override="false">
<prop key="driver">com.mysql.cj.jdbc.Driver</prop>
<prop key="url">jdbc:mysql://localhost:3306/test</prop>
<prop key="username">root</prop>
<prop key="password">123456</prop>
</util:properties>
<!-- util:list 定义一个 java.util.List 类型的 bean -->
<!--
value-type 属性默认为 java.lang.String
list-class 属性默认为 java.util.ArrayList
-->
<util:list id="utilList" value-type="java.lang.Integer"
list-class="java.util.LinkedList">
<value>1</value>
<value>2</value>
</util:list>
<!-- util:list 定义一个 java.util.Set 类型的 bean -->
<!--
value-type 属性默认为 java.lang.String
set-class 属性默认为 java.util.LinkedHashSet
-->
<util:set id="utilSet" value-type="java.lang.Integer"
set-class="java.util.HashSet">
<value>1</value>
<value>2</value>
</util:set>
<!-- util:map 定义一个 java.util.Map 类型的 bean -->
<!--
value-type 属性默认为 java.lang.String
-->
<util:map id="utilMap">
<entry key="a" value="1"
value-type="java.lang.Integer"/>
<entry key-ref="utilSet" value-ref="utilSet"/>
</util:map>
</beans>
context 命名空间
<?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/springcontext.xsd">
<!-- context:property-placeholder 标签用于加载 property 属性文件 -->
<context:property-placeholder location="jdbc.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${db.driver}"/>
<property name="url" value="${db.url}"/>
<property name="username" value="${db.username}"/>
<!-- 不可直接使用 ${username},因为优先读取系统属性 username -->
<property name="password" value="${db.password}"/>
</bean>
<!--
自动扫描,它会将 base-package 指定位置下的具有 spring 对应注解
@Component,@Service,@Repository,@Controller 等
的类在加载容器时都注入 springIOC 容器中
-->
<context:component-scan base-package="com.lxl"/>
</beans>
Bean 依赖循环
spring 中的依赖循环:
- 在 singleton+set 注入的情况下,循环依赖是没有问题的。
- 在 singleton+constructor 注入的情况下,无法解决循环依赖。
- 循环依赖的所有 Bean的scope=“prototype” 时产生的循环依赖,会出现 BeanCurrentlyInCreationException 异常。
- 循环依赖的两个 Bean ,如果其中一个是 singleton,另一个是 prototype,循环依赖是没有问题的。
Spring 解决循环依赖的机理(步骤):
- 此时可以先不给属性赋值,可以提前将该 Bean 对象“曝光”给外界。
- 所有的单例 Bean 全部实例化完成之后,再调用 setter 方法给属性赋值。
Spring 的三级缓存:
- Cache of singleton objects: bean name to bean instance。单例对象的缓存:key 存储 bean 名称,value 存储 Bean 对象【一级缓存】,缓存单例完全体 Bean。
- Cache of early singleton objects: bean name to bean instance。早期单例对象的缓存:key 存储 bean 名称,value 存储 Bean 对象【二级缓存】,缓存单例残缺体 Bean。
- Cache of singleton factories: bean name to ObjectFactory.单例工厂缓存:key 存储 bean 名称,value 存储该 Bean 对应的 ObjectFactory 对象【三级缓存】,缓存 Bean 对应的 ObjectFactory 对象,处理 AOP 代理问题。
Spring 整合 Druid (XML)
- 导入Maven依赖坐标
<dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.2.8</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.46</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.10.RELEASE</version> </dependency>
- 配置Druid连接池对象为Bean
<?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/springcontext.xsd"> <context:property-placeholder location="jdbc.properties"/> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${db.driver}"/> <property name="url" value="${db.url}"/> <property name="username" value="${db.username}"/> <property name="password" value="${db.password}"/> </bean> </beans>
- IoC容器中获取DruidDataSource对象
// 1.加载类路径下的配置文件 ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); // 2.从IoC容器获取bean DataSource dataSource = (DataSource) context.getBean("dataSource"); Connection connection = dataSource.getConnection(); // 3.关闭容器 context.close();