学习来源:B站
https://www.bilibili.com/video/BV1Ft4y1g7Fb/?spm_id_from=333.337.search-card.all.click&vd_source=07c8a1a7d89af39fe20c3a6894f5ff6a
资料来源:百度网盘
链接:https://pan.baidu.com/s/1yXl8Kg80utfqazkuaE_Q8g?pwd=dljd
提取码:dljd
Spring6笔记将分为3部分,typora复制过来的图片不能直接使用非常头痛只能一个一个换,所以去年11月就写完的笔记到现在都没有完全整理到CSDN上,后面有空再慢慢更新。
阅读以下代码
package com.powernode.spring6.dao;
/**
* 持久层接口
* @author ShiningSong
* @version 1.0
* @since 1.0
* @className UserDao
*/
public interface UserDao{
/**
* 根据id删除用户信息
*/
void deleteById();
}
package com.powernode.spring6.dao.impl;
import com.powernode.spring6.dao.UserDao;
/**
* 持久层接口实现类
* @author shanglinsong
* @version 1.0
* @className com.powernode.spring6.service.impl.UserDaoForMySQL
* @date 2022/11/8
* @since 1.0
*/
public class UserDaoImplForMySQL implements UserDao {
@Override
public void deleteById() {
System.out.println("MySQL数据库正在删除用户信息。。。。。。");
}
}
package com.powernode.spring6.service;
/**
* 业务层接口
* @author ShiningSong
* @version 1.0
* @since 1.0
*/
public interface UserService {
/**
* 删除用户信息
*/
void deleteUser();
}
package com.powernode.spring6.service.impl;
import com.powernode.spring6.dao.UserDao;
import com.powernode.spring6.dao.impl.UserDaoImplForMySQL;
import com.powernode.spring6.service.UserService;
/**
* 业务层接口实现类
* @author shanglinsong
* @version 1.0
* @className com.powernode.spring6.service.impl.UserServiceImpl
* @date 2022/11/8
* @since 1.0
*/
public class UserServiceImpl implements UserService {
private UserDao userDao = new UserDaoImplForMySQL();
@Override
public void deleteUser() {
userDao.deleteById();
}
}
package com.powernode.spring6.web;
import com.powernode.spring6.service.UserService;
import com.powernode.spring6.service.impl.UserServiceImpl;
/**
* 表示层
* @author shanglinsong
* @version 1.0
* @className com.powernode.spring6.web.UserAction
* @date 2022/11/8
* @since 1.0
*/
public class UserAction {
private UserService userService = new UserServiceImpl();
/**
* 删除用户信息请求
*/
public void deleteRequest(){
userService.deleteUser();
}
}
package com.powernode.spring6.client;
import com.powernode.spring6.web.UserAction;
/**
* 测试类
* @author shanglinsong
* @version 1.0
* @className com.powernode.spring6.client.Test
* @date 2022/11/8
* @since 1.0
*/
public class Test {
public static void main(String[] args) {
UserAction userAction = new UserAction();
userAction.deleteRequest();
}
}
可以看出,UserDaoImplForMySQL中主要是连接MySQL数据库进行操作。如果更换到Oracle数据库上,则需要再提供一个UserDaoImplForOracle,如下:
package com.powernode.spring6.dao.impl;
import com.powernode.spring6.dao.UserDao;
/**
* @author shanglinsong
* @version 1.0
* @className com.powernode.spring6.dao.impl.UserDaoForOrcle
* @date 2022/11/8
* @since 1.0
*/
public class UserDaoForOrcle implements UserDao {
@Override
public void deleteById() {
System.out.println("Orcle数据库正在删除用户信息。。。。。。");
}
}
很明显,以上的操作正在进行功能的扩展,添加了一个新的类UserDaoImplForOracle来应付数据库的变化,这里的变化会引起连锁反应吗?当然会,如果想要切换到Oracle数据库上,UserServiceImpl类代码就需要修改,如下:
package com.powernode.spring6.service.impl;
import com.powernode.spring6.dao.UserDao;
import com.powernode.spring6.dao.impl.UserDaoForOrcle;
import com.powernode.spring6.dao.impl.UserDaoImplForMySQL;
import com.powernode.spring6.service.UserService;
/**
* @author shanglinsong
* @version 1.0
* @className com.powernode.spring6.service.impl.UserServiceImpl
* @date 2022/11/8
* @since 1.0
*/
public class UserServiceImpl implements UserService {
// private UserDao userDao = new UserDaoImplForMySQL();
// 修改为
private UserDao userDao = new UserDaoForOrcle();
@Override
public void deleteUser() {
userDao.deleteById();
}
}
这样一来就违背了开闭原则OCP。
开闭原则是这样说的:
因为一旦修改之前运行正常的程序,就会导致项目整体要进行全方位的重新测试。
如下图所示:
依赖倒置原则(Dependence Inversion Principle),简称DIP,主要倡导面向抽象编程,面向接口编程,不要面向具体编程,让上层不再依赖下层,下面改动了,上面的代码不会受到牵连。
这样可以大大降低程序的耦合度,耦合度低了,扩展力就强了,同时代码复用性也会增强。(软件七大开发原则都是在为解耦合服务)
你可能会说,上面的代码已经面向接口编程了呀:
1. OCP开闭原则
* 什么是OCP?
OCP是软件七大开发原则当中最基本的一个原则:开闭原则
对什么开?对扩展开放。
对什么闭?对修改关闭。
* OCP原则是最核心的,最基本的,其他的六个原则都是为这个原则服务的。
* OCP开闭原则的核心是什么?
只要你在扩展系统功能的时候,没有修改以前写好的代码,那么你就是符合OCP原则的。
反之,如果在扩展系统功能的时候,你修改了之前的代码,那么这个设计是失败的,违背OCP原则。
* 当进行系统功能扩展的时候,如果动了之前稳定的程序,修改了之前的程序,之前所有程序都需要进行重新测试。这是不想看到的,因为非常麻烦。
2. 依赖倒置原则(DIP原则)
* 什么是依赖倒置原则?
面向接口编程,面向抽象编程,不要面向具体编程。
* 依赖倒置原则的目的?
降低程序的耦合度,提高扩展力。
* 什么叫做符合依赖倒置?
上 不依赖 下,就是符合。
* 什么叫做违背依赖倒置?
上 依赖 下,就是违背。
只要“下”一改动,“上”就受到牵连。
3. 当前程序的设计,显然既违背OCP,又违背DIP,怎么办?
可以采用“控制反转”这种编程思想来解决这个问题。
4. 什么是控制反转?
控制反转:IoC(Inversion of Control)
反转是什么呢?
反转的是两件事:
第一件事:我不在程序中采用硬编码的方式来new对象了。(new对象我不管了,new对象的权利交出去了。)
第二件事:我不在程序中采用硬编码的方式来维护对象的关系了。(对象之间关系的维护权,我也不管了,交出去了。)
控制反转:是一种编程思想。或者叫做一种新型的设计模式。由于出现的比较新,没有被纳入GoF23种设计模式范围内。
5. Spring框架
* Spring框架实现了控制反转IoC这种思想
Spring框架可以帮你new对象。
Spring框架可以帮你维护对象和对象之间的关系。
* Spring是一个实现了IoC思想的容器。
* 控制反转的实现方式有多种,其中比较重要的叫做:依赖注入(Dependency Injection,简称DI)。
* 控制反转是思想。依赖注入是这种思想的具体实现。
* 依赖注入DI,又包括常见的两种方式:
第一种:set注入(执行set方法给属性赋值)
第二种:构造方法注入(执行构造方法给属性赋值)
* 依赖注入 中 “依赖”是什么意思? “注入”是什么意思?
依赖:A对象和B对象的关系。
注入:是一种手段,通过这种手段,可以让A对象和B对象产生关系。
依赖注入:对象A和对象B之间的关系,靠注入的手段来维护。而注入包括:set注入和构造注入。
6. 注意术语:
OCP:开闭原则(开发原则)
DIP:依赖倒置原则(开发原则)
IoC:控制反转(一种思想,一种新型的设计模式)
DI:依赖注入(控制反转思想的具体实现方式)
Spring是一个开源框架,它由Rod Johnson创建。它是为了解决企业应用开发的复杂性而创建的。
从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。
Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。
Spring最初的出现是为了解决EJB臃肿的设计,以及难以测试等问题。
Spring为简化开发而生,让程序员只需关注核心业务的实现,尽可能的不再关注非业务逻辑代码(事务控制,安全日志等)。
Spring Core模块
Spring Context模块
如果说核心模块中的BeanFactory使Spring成为容器的话,那么上下文模块就是Spring成为框架的原因。
这个模块扩展了BeanFactory,增加了对国际化(I18N)消息、事件传播、验证的支持。
另外提供了许多企业服务,例如电子邮件、JNDI访问、EJB集成、远程以及时序调度(scheduling)服务。
也包括了对模版框架例如Velocity和FreeMarker集成的支持
Spring AOP模块
Spring DAO模块
Spring ORM模块
Spring Web MVC模块
Spring WebFlux模块
Spring Web模块
轻量
控制反转
面向切面
容器
框架
我们即将要学习的就是Spring Framework。
怎么下载呢?
点击spring-5.3.9-dist.zip下载spring框架。
将下载的zip包解压:
打开libs目录,会看到很多jar包:
我们来看一下spring框架都有哪些jar包:
JAR文件 | 描述 |
---|---|
spring-aop-5.3.9.jar | 这个jar 文件包含在应用中使用Spring 的AOP 特性时所需的类 |
spring-aspects-5.3.9.jar | 提供对AspectJ的支持,以便可以方便的将面向切面的功能集成进IDE中 |
spring-beans-5.3.9.jar | 这个jar 文件是所有应用都要用到的,它包含访问配置文件、创建和管理bean 以及进行Inversion ofControl / Dependency Injection(IoC/DI)操作相关的所有类。如果应用只需基本的IoC/DI 支持,引入spring-core.jar 及spring-beans.jar 文件就可以了。 |
spring-context-5.3.9.jar | 这个jar 文件为Spring 核心提供了大量扩展。可以找到使用Spring ApplicationContext特性时所需的全部类,JDNI 所需的全部类,instrumentation组件以及校验Validation 方面的相关类。 |
spring-context-indexer-5.3.9.jar | 虽然类路径扫描非常快,但是Spring内部存在大量的类,添加此依赖,可以通过在编译时创建候选对象的静态列表来提高大型应用程序的启动性能。 |
spring-context-support-5.3.9.jar | 用来提供Spring上下文的一些扩展模块,例如实现邮件服务、视图解析、缓存、定时任务调度等 |
spring-core-5.3.9.jar | Spring 框架基本的核心工具类。Spring 其它组件要都要使用到这个包里的类,是其它组件的基本核心,当然你也可以在自己的应用系统中使用这些工具类。 |
spring-expression-5.3.9.jar | Spring表达式语言。 |
spring-instrument-5.3.9.jar | Spring3.0对服务器的代理接口。 |
spring-jcl-5.3.9.jar | Spring的日志模块。JCL,全称为"Jakarta Commons Logging",也可称为"Apache Commons Logging"。 |
spring-jdbc-5.3.9.jar | Spring对JDBC的支持。 |
spring-jms-5.3.9.jar | 这个jar包提供了对JMS 1.0.2/1.1的支持类。JMS是Java消息服务。属于JavaEE规范之一。 |
spring-messaging-5.3.9.jar | 为集成messaging api和消息协议提供支持 |
spring-orm-5.3.9.jar | Spring集成ORM框架的支持,比如集成hibernate,mybatis等。 |
spring-oxm-5.3.9.jar | 为主流O/X Mapping组件提供了统一层抽象和封装,OXM是Object Xml Mapping。对象和XML之间的相互转换。 |
spring-r2dbc-5.3.9.jar | Reactive Relational Database Connectivity (关系型数据库的响应式连接) 的缩写。这个jar文件是Spring对r2dbc的支持。 |
spring-test-5.3.9.jar | 对Junit等测试框架的简单封装。 |
spring-tx-5.3.9.jar | 为JDBC、Hibernate、JDO、JPA、Beans等提供的一致的声明式和编程式事务管理支持。 |
spring-web-5.3.9.jar | Spring集成MVC框架的支持,比如集成Struts等。 |
spring-webflux-5.3.9.jar | WebFlux是 Spring5 添加的新模块,用于 web 的开发,功能和 SpringMVC 类似的,Webflux 使用当前一种比较流程响应式编程出现的框架。 |
spring-webmvc-5.3.9.jar | SpringMVC框架的类库 |
spring-websocket-5.3.9.jar | Spring集成WebSocket框架时使用 |
注意:
如果你只是想用Spring的IoC功能,仅需要引入:spring-context即可。将这个jar包添加到classpath当中。
如果采用maven只需要引入context的依赖即可。
spring bean依赖
<repositories>
<repository>
<id>repository.spring.milestoneid>
<name>Spring Milestone Repositoryname>
<url>https://repo.spring.io/milestoneurl>
repository>
repositories>
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>6.0.0-M2version>
dependency>
dependencies>
前期准备:
第一步:添加spring context的依赖,pom.xml配置如下
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>com.powernodegroupId>
<artifactId>spring6-002-firstartifactId>
<version>1.0-SNAPSHOTversion>
<packaging>jarpackaging>
<repositories>
<repository>
<id>repository.spring.milestoneid>
<name>Spring Milestone Repositoryname>
<url>https://repo.spring.io/milestoneurl>
repository>
repositories>
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>6.0.0-M2version>
dependency>
dependencies>
<properties>
<maven.compiler.source>17maven.compiler.source>
<maven.compiler.target>17maven.compiler.target>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
properties>
project>
注意:打包方式jar。
当加入spring context的依赖之后,会关联引入其他依赖:
第二步:添加junit依赖
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.13.2version>
<scope>testscope>
dependency>
第三步:定义bean:User
package com.powernode.spring6.bean;
/**
* bean,封装用户信息
* @author shanglinsong
* @version 1.0
* @className com.powernode.spring6.bean.User
* @date 2022/11/8
* @since 1.0
*/
public class User {
}
第四步:编写spring的配置文件:beans.xml。该文件放在类的根路径下。
上图是使用IDEA工具自带的spring配置文件的模板进行创建。
配置文件中进行bean的配置。
beans.xml
<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="userBean" class="com.powernode.spring6.bean.User" />
beans>
第五步:编写测试程序
package com.powernode.spring6.test;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author shanglinsong
* @version 1.0
* @className com.powernode.spring6.test.Spring6Test
* @date 2022/11/8
* @since 1.0
*/
public class Spring6Test {
@Test
public void testFirstSpring(){
// 初始化Spring容器上下文,解析beans.xml文件,创建所有的bean对象
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
// 根据id获取bean对象
Object userBean = applicationContext.getBean("userBean");
System.out.println(userBean);
}
}
第七步:运行测试程序
beans.xml
<bean id="userBean" class="com.powernode.spring6.bean.User"/>
Spring6Test.testFirstSpring
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
Object userBean = applicationContext.getBean("userBean");
bean标签的id属性可以重复吗?
package com.powernode.spring6.bean;
/**
* @author shanglinsong
* @version 1.0
* @className com.powernode.spring6.bean.Vip
* @date 2022/11/8
* @since 1.0
*/
public class Vip {
}
<bean id="userBean" class="com.powernode.spring6.bean.User" />
<bean id="userBean" class="com.powernode.spring6.bean.Vip" />
答:通过测试得出:在spring的配置文件中id是不能重名。
底层是怎么创建对象的,是通过反射机制调用无参数构造方法吗?
public User() {
System.out.println("User的无参数构造方法执行。。。");
}
答:通过测试得知:创建对象时确实调用了无参数构造方法。
public User(String username) {
System.out.println("User的无参数构造方法执行。。。");
}
答:通过测试得知:spring是通过调用类的无参数构造方法来创建对象的,所以要想让spring给你创建对象,必须保证无参数构造方法是存在的。
// dom4j解析beans.xml文件,从中获取class的全限定类名
// 通过反射机制调用无参数构造方法创建对象
Class clazz = Class.forName("com.powernode.spring6.bean.User");
Object obj = clazz.newInstance();
把创建好的对象存储到一个什么样的数据结构当中了呢?
答:Map集合。
spring配置文件的名字必须叫做beans.xml吗?
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
答:通过以上的java代码可以看出,这个spring配置文件名字是我们负责提供的,显然spring配置文件的名字是随意的。
像这样的beans.xml文件可以有多个吗?
<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="vipBean" class="com.powernode.spring6.bean.Vip" />
beans>
@Test
public void testFirstSpring(){
// 初始化Spring容器上下文,解析beans.xml文件,创建所有的bean对象
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml", "spring.xml");
// 根据id获取bean对象
Object userBean = applicationContext.getBean("userBean");
Object vipBean = applicationContext.getBean("vipBean");
System.out.println(userBean);
System.out.println(vipBean);
}
答:通过测试得知,spring的配置文件可以有多个,在ClassPathXmlApplicationContext构造方法的参数上传递文件路径即可。
在配置文件中配置的类必须是自定义的吗,可以使用JDK中的类吗,例如:java.util.Date?
<bean id="dateBean" class="java.util.Date" />
@Test
public void testFirstSpring(){
// 初始化Spring容器上下文,解析beans.xml文件,创建所有的bean对象
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml", "spring.xml");
// 根据id获取bean对象
Object userBean = applicationContext.getBean("userBean");
Object vipBean = applicationContext.getBean("vipBean");
Object dateBean = applicationContext.getBean("dateBean");
System.out.println(userBean);
System.out.println(vipBean);
System.out.println(dateBean);
}
答:通过测试得知,在spring配置文件中配置的bean可以任意类,只要这个类不是抽象的,并且提供了无参数构造方法。
getBean()方法调用时,如果指定的id不存在会怎样?
@Test
public void testFirstSpring(){
// 初始化Spring容器上下文,解析beans.xml文件,创建所有的bean对象
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml", "spring.xml");
// 根据id获取bean对象
Object noThisBean = applicationContext.getBean("noThisBean");
Object userBean = applicationContext.getBean("userBean");
Object vipBean = applicationContext.getBean("vipBean");
Object dateBean = applicationContext.getBean("dateBean");
System.out.println(userBean);
System.out.println(vipBean);
System.out.println(dateBean);
}
通过测试得知,当id不存在的时候,会出现异常。
getBean()方法返回的类型是Object,如果访问子类的特有属性和方法时,还需要向下转型,有其它办法可以解决这个问题吗?
@Test
public void testFirstSpring(){
// 初始化Spring容器上下文,解析beans.xml文件,创建所有的bean对象
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml", "spring.xml");
// 根据id获取bean对象
// Object noThisBean = applicationContext.getBean("noThisBean");
// Object userBean = applicationContext.getBean("userBean");
User userBean = applicationContext.getBean("userBean", User.class);
Object vipBean = applicationContext.getBean("vipBean");
Object dateBean = applicationContext.getBean("dateBean");
System.out.println(userBean);
System.out.println(vipBean);
System.out.println(dateBean);
}
ClassPathXmlApplicationContext是从类路径中加载配置文件,如果没有在类路径当中,又应该如何加载配置文件呢?
<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="vipBean1" class="com.powernode.spring6.bean.Vip" />
beans>
@Test
public void testFirstSpring(){
// 初始化Spring容器上下文,解析beans.xml文件,创建所有的bean对象
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml", "spring.xml");
// 配置文件不在类路径当中,在系统路径下时,解析配置文件的方法
ApplicationContext applicationContext1 = new FileSystemXmlApplicationContext("d:/spring.xml");
// 根据id获取bean对象
Vip vipBean1 = applicationContext1.getBean("vipBean1", Vip.class);
System.out.println(vipBean1);
// Object noThisBean = applicationContext.getBean("noThisBean");
// Object userBean = applicationContext.getBean("userBean");
User userBean = applicationContext.getBean("userBean", User.class);
Object vipBean = applicationContext.getBean("vipBean");
Object dateBean = applicationContext.getBean("dateBean");
System.out.println(userBean);
System.out.println(vipBean);
System.out.println(dateBean);
}
答:没有在类路径中的话,需要使用FileSystemXmlApplicationContext类进行加载配置文件。
ApplicationContext的超级父接口BeanFactory。
@Test
public void testFirstSpring(){
// 初始化Spring容器上下文,解析beans.xml文件,创建所有的bean对象
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml", "spring.xml");
// 配置文件不在类路径当中,在系统路径下时,解析配置文件的方法
ApplicationContext applicationContext1 = new FileSystemXmlApplicationContext("d:/spring.xml");
// 使用BeanFactory读取配置文件
BeanFactory beanFactory = new ClassPathXmlApplicationContext("spring.xml");
// 根据id获取bean对象
Vip vipBean2 = beanFactory.getBean("vipBean", Vip.class);
System.out.println(vipBean2);
Vip vipBean1 = applicationContext1.getBean("vipBean1", Vip.class);
System.out.println(vipBean1);
// Object noThisBean = applicationContext.getBean("noThisBean");
// Object userBean = applicationContext.getBean("userBean");
User userBean = applicationContext.getBean("userBean", User.class);
Object vipBean = applicationContext.getBean("vipBean");
Object dateBean = applicationContext.getBean("dateBean");
System.out.println(userBean);
System.out.println(vipBean);
System.out.println(dateBean);
}
BeanFactory是Spring容器的超级接口。ApplicationContext是BeanFactory的子接口。
从Spring5之后,Spring框架支持集成的日志框架是Log4j2.如何启用日志框架:
第一步:引入Log4j2的依赖
<dependency>
<groupId>org.apache.logging.log4jgroupId>
<artifactId>log4j-coreartifactId>
<version>2.19.0version>
dependency>
<dependency>
<groupId>org.apache.logging.log4jgroupId>
<artifactId>log4j-slf4j2-implartifactId>
<version>2.19.0version>
dependency>
第二步:在类的根路径下提供log4j2.xml配置文件(文件名固定为:log4j2.xml,文件必须放到类根路径下。)
<configuration>
<loggers>
<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>
第三步:使用日志框架
@Test
public void testLog4j2(){
Logger logger = LoggerFactory.getLogger(Spring6Test.class);
logger.info("我是一条日志消息");
}
控制反转是一种思想。
控制反转是为了降低程序耦合度,提高程序扩展力,达到OCP原则,达到DIP原则。
控制反转,反转的是什么?
控制反转这种思想如何实现呢?
依赖注入实现了控制反转的思想。
Spring通过依赖注入的方式来完成Bean管理的。
Bean管理说的是:Bean对象的创建,以及Bean对象中属性的赋值(或者叫做Bean对象之间关系的维护)。
依赖注入:
依赖注入常见的实现方式包括两种:
新建模块:spring6-003-dependency-injection
set注入,基于set方法实现的,底层会通过反射机制调用属性对应的set方法然后给属性赋值。这种方式要求属性必须对外提供set方法。
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>com.powernodegroupId>
<artifactId>spring6-003-dependency-injectionartifactId>
<version>1.0-SNAPSHOTversion>
<packaging>jarpackaging>
<repositories>
<repository>
<id>repository.spring.milestoneid>
<name>Spring MileStone Repositoryname>
<url>https://repo.spring.io/milestoneurl>
repository>
repositories>
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>6.0.0-M2version>
dependency>
<dependency>
<groupId>org.apache.logging.log4jgroupId>
<artifactId>log4j-coreartifactId>
<version>2.19.0version>
dependency>
<dependency>
<groupId>org.apache.logging.log4jgroupId>
<artifactId>log4j-slf4j2-implartifactId>
<version>2.19.0version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.13.2version>
<scope>testscope>
dependency>
dependencies>
<properties>
<maven.compiler.source>17maven.compiler.source>
<maven.compiler.target>17maven.compiler.target>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
properties>
project>
package com.powernode.spring6.dao;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author shanglinsong
* @version 1.0
* @className com.powernode.spring6.dao.UserDao
* @date 2022/11/8
* @since 1.0
*/
public class UserDao {
// 使用log4j2日志工具,定义logger
private static final Logger logger = LoggerFactory.getLogger(UserDao.class);
public void insert(){
// System.out.println("正在保存用户数据。。。");
logger.info("数据库正在保存用户信息。。。");
}
}
package com.powernode.spring6.service;
import com.powernode.spring6.dao.UserDao;
/**
* @author shanglinsong
* @version 1.0
* @className com.powernode.spring6.service.UserService
* @date 2022/11/8
* @since 1.0
*/
public class UserService {
private UserDao userDao;
// set注入的话,必须提供一个set方法。
// Spring容器会调用这个set方法,来给userDao属性赋值。
// 我自己写一个set方法,不使用IDEA工具生成的。不符合javabean规范。
// 至少这个方法是以set单词开始的。前三个字母不能随便写,必须是"set"
/*public void setMySQLUserDao(UserDao xyz){
this.userDao = xyz;
}*/
// 使用set方式注入,必须提供set方法。
// 反射机制要调用这个方法给属性赋值的。
// 这个set方法是IDEA工具生成的,符合javabean规范。
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
/**
* 保存用户信息到数据库
*/
public void saveUser(){
userDao.insert();
}
}
<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="userDaoBean" class="com.powernode.spring6.dao.UserDao" />
<bean id="userServiceBean" class="com.powernode.spring6.service.UserService">
<property name="userDao" ref="userDaoBean"/>
bean>
beans>
package com.powernode.spring6.test;
import com.powernode.spring6.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author shanglinsong
* @version 1.0
* @className com.powernode.spring6.test.SpringDITest
* @date 2022/11/8
* @since 1.0
*/
public class SpringDITest {
@Test
public void testSetDI(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
UserService userService = applicationContext.getBean("userServiceBean", UserService.class);
userService.saveUser();
}
}
重点内容是,什么原理:
<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="userDaoBean" class="com.powernode.spring6.dao.UserDao" />
<bean id="userServiceBean" class="com.powernode.spring6.service.UserService">
<property name="userDao" ref="userDaoBean"/>
bean>
beans>
package com.powernode.spring6.service;
import com.powernode.spring6.dao.UserDao;
/**
* @author 动力节点
* @version 1.0
* @className UserService
* @since 1.0
**/
public class UserService {
private UserDao aaa;
// 使用set方式注入,必须提供set方法。
// 反射机制要调用这个方法给属性赋值的。
public void setUserDao(UserDao userDao) {
this.aaa = userDao;
}
public void save(){
aaa.insert();
}
}
<bean id="userServiceBean" class="com.powernode.spring6.service.UserService">
<property name="userDao">
<ref bean="userDaoBean"/>
property>
bean>
总结:set注入的核心实现原理:通过反射机制调用set方法来给属性赋值,让两个对象之间产生关系。
package com.powernode.spring6.dao;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author shanglinsong
* @version 1.0
* @className com.powernode.spring6.dao.OrderDao
* @date 2022/11/8
* @since 1.0
*/
public class OrderDao {
private static final Logger logger = LoggerFactory.getLogger(OrderDao.class);
public void deleteById(){
logger.info("正在删除订单。。。");
}
}
package com.powernode.spring6.service;
import com.powernode.spring6.dao.OrderDao;
/**
* @author shanglinsong
* @version 1.0
* @className com.powernode.spring6.service.OrderService
* @date 2022/11/8
* @since 1.0
*/
public class OrderService {
private OrderDao orderDao;
// 通过反射机制调用构造方法给属性赋值
public OrderService(OrderDao orderDao) {
this.orderDao = orderDao;
}
public void delete(){
orderDao.deleteById();
}
}
<bean id="orderDaoBean" class="com.powernode.spring6.dao.OrderDao"/>
<bean id="orderServiceBean" class="com.powernode.spring6.service.OrderService">
<constructor-arg index="0" ref="orderDaoBean"/>
bean>
@Test
public void testConstrutorDI(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
OrderService orderServiceBean = applicationContext.getBean("orderServiceBean", OrderService.class);
orderServiceBean.delete();
}
如果构造方法有两个参数:
package com.powernode.spring6.service;
import com.powernode.spring6.dao.OrderDao;
import com.powernode.spring6.dao.UserDao;
/**
* @author shanglinsong
* @version 1.0
* @className com.powernode.spring6.service.OrderService
* @date 2022/11/8
* @since 1.0
*/
public class OrderService {
private OrderDao orderDao;
private UserDao userDao;
// 通过反射机制调用构造方法给属性赋值
public OrderService(OrderDao orderDao, UserDao userDao) {
this.orderDao = orderDao;
this.userDao = userDao;
}
public void delete(){
orderDao.deleteById();
userDao.insert();
}
}
<bean id="orderDaoBean" class="com.powernode.spring6.dao.OrderDao"/>
<bean id="orderServiceBean" class="com.powernode.spring6.service.OrderService">
<constructor-arg index="0" ref="orderDaoBean"/>
<constructor-arg index="1" ref="userDaoBean"/>
bean>
<bean id="userDaoBean" class="com.powernode.spring6.dao.UserDao" />
@Test
public void testConstrutorDI(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
OrderService orderServiceBean = applicationContext.getBean("orderServiceBean", OrderService.class);
orderServiceBean.delete();
}
不使用参数下标,使用参数的名字可以吗?
<bean id="orderDaoBean" class="com.powernode.spring6.dao.OrderDao"/>
<bean id="orderServiceBean" class="com.powernode.spring6.service.OrderService">
<constructor-arg name="orderDao" ref="orderDaoBean"/>
<constructor-arg name="userDao" ref="userDaoBean"/>
bean>
<bean id="userDaoBean" class="com.powernode.spring6.dao.UserDao" />
不指定参数下标,不指定参数名字,可以吗?
<bean id="orderDaoBean" class="com.powernode.spring6.dao.OrderDao"/>
<bean id="orderServiceBean" class="com.powernode.spring6.service.OrderService">
<constructor-arg ref="orderDaoBean"/>
<constructor-arg ref="userDaoBean"/>
bean>
<bean id="userDaoBean" class="com.powernode.spring6.dao.UserDao" />
配置文件中构造方法参数的类型顺序和构造方法参数的类型顺序不一致呢?
<bean id="orderDaoBean" class="com.powernode.spring6.dao.OrderDao"/>
<bean id="orderServiceBean" class="com.powernode.spring6.service.OrderService">
<constructor-arg ref="userDaoBean"/>
<constructor-arg ref="orderDaoBean"/>
bean>
<bean id="userDaoBean" class="com.powernode.spring6.dao.UserDao" />
通过测试得知,通过构造方法注入的时候:
Spring在装配方面做的还是比较健壮的。
在之前4.2.1中使用的案例就是注入外部Bean的方式。
<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="userDaoBean" class="com.powernode.spring6.dao.UserDao" />
<bean id="userServiceBean" class="com.powernode.spring6.service.UserService">
<property name="userDao" ref="userDaoBean"/>
bean>
beans>
外部Bean的特点:bean定义到外面,在property标签中使用ref属性进行注入。
内部Bean的方式:在bean标签中嵌套bean标签。
<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="userServiceBean" class="com.powernode.spring6.service.UserService">
<property name="userDao">
<bean class="com.powernode.spring6.dao.UserDao"/>
property>
bean>
beans>
@Test
public void testInnerBean(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-inner-bean.xml");
UserService userServiceBean = applicationContext.getBean("userServiceBean", UserService.class);
userServiceBean.saveUser();
}
这种方式作为了解。
我们之前在进行注入的时候,对象的属性是另一个对象。
那如果对象的属性是int类型呢?
public class User{
private int age;
public void setAge(int age){
this.age = age;
}
}
编写程序给一个User对象的age属性赋值20:
package com.powernode.spring6.bean;
/**
* @author shanglinsong
* @version 1.0
* @className com.powernode.spring6.bean.User
* @date 2022/11/9
* @since 1.0
*/
public class User {
private int age;
@Override
public String toString() {
return "User{" +
"age=" + age +
'}';
}
public void setAge(int age) {
this.age = age;
}
}
<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="userBean" class="com.powernode.spring6.bean.User">
<property name="age">
<value>20value>
property>
bean>
beans>
@Test
public void testInnerBean(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-inner-bean.xml");
UserService userServiceBean = applicationContext.getBean("userServiceBean", UserService.class);
userServiceBean.saveUser();
}
简单类型包括哪些呢?可以通过Spring的源码来分析一下:BeanUtils类
public class BeanUtils{
//.......
/**
* Check if the given type represents a "simple" property: a simple value
* type or an array of simple value types.
* See {@link #isSimpleValueType(Class)} for the definition of simple
* value type.
*
Used to determine properties to check for a "simple" dependency-check.
* @param type the type to check
* @return whether the given type represents a "simple" property
* @see org.springframework.beans.factory.support.RootBeanDefinition#DEPENDENCY_CHECK_SIMPLE
* @see org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#checkDependencies
* @see #isSimpleValueType(Class)
*/
public static boolean isSimpleProperty(Class<?> type) {
Assert.notNull(type, "'type' must not be null");
return isSimpleValueType(type) || (type.isArray() && isSimpleValueType(type.getComponentType()));
}
/**
* Check if the given type represents a "simple" value type: a primitive or
* primitive wrapper, an enum, a String or other CharSequence, a Number, a
* Date, a Temporal, a URI, a URL, a Locale, or a Class.
* {@code Void} and {@code void} are not considered simple value types.
* @param type the type to check
* @return whether the given type represents a "simple" value type
* @see #isSimpleProperty(Class)
*/
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));
}
//........
}
经典案例:给数据源的属性注入值:
假设我们现在要自己手写一个数据源,我们都知道所有的数据源都要实现javax.sql.DataSource接口,并且数据源中应该有连接数据库的信息,例如:driver、url、username、password等。
package com.powernode.spring6.bean;
import javax.sql.DataSource;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;
/**
* @author shanglinsong
* @version 1.0
* @className com.powernode.spring6.bean.MyDataSource
* @date 2022/11/9
* @since 1.0
*/
public class MyDataSource implements DataSource {
private String driver;
private String url;
private String username;
private String password;
public void setDriver(String driver) {
this.driver = driver;
}
public void setUrl(String url) {
this.url = url;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "MyDataSource{" +
"driver='" + driver + '\'' +
", url='" + url + '\'' +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
@Override
public Connection getConnection() throws SQLException {
return null;
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
return null;
}
@Override
public PrintWriter getLogWriter() throws SQLException {
return null;
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
}
@Override
public int getLoginTimeout() throws SQLException {
return 0;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return null;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
}
<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="myDataSourceBean" class="com.powernode.spring6.bean.MyDataSource">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/spring"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
bean>
beans>
@Test
public void testMyDataSource(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-datasource.xml");
MyDataSource myDataSourceBean = applicationContext.getBean("myDataSourceBean", MyDataSource.class);
System.out.println(myDataSourceBean);
}
接下来,我们编写一个程序,把所有的简单类型全部测试一遍:
package com.powernode.spring6.bean;
import java.net.URI;
import java.net.URL;
import java.time.LocalDate;
import java.util.Date;
import java.util.Locale;
/**
* @author shanglinsong
* @version 1.0
* @className com.powernode.spring6.bean.A
* @date 2022/11/9
* @since 1.0
*/
public class A {
private byte b;
private short s;
private int i;
private long l;
private float f;
private double d;
private boolean flag;
private char c;
private Byte b1;
private Short s1;
private Integer i1;
private Long l1;
private Float f1;
private Double d1;
private Boolean flag1;
private Character c1;
private String str;
private Date date;
private Season season;
private URI uri;
private URL url;
private LocalDate localDate;
private Locale locale;
private Class clazz;
@Override
public String toString() {
return "A{" +
"b=" + b +
", s=" + s +
", i=" + i +
", l=" + l +
", f=" + f +
", d=" + d +
", flag=" + flag +
", c=" + c +
", \nb1=" + b1 +
", s1=" + s1 +
", i1=" + i1 +
", l1=" + l1 +
", f1=" + f1 +
", d1=" + d1 +
", flag1=" + flag1 +
", c1=" + c1 +
", \nstr='" + str + '\'' +
", date=" + date +
", season=" + season +
", uri=" + uri +
", url=" + url +
", \nlocalDate=" + localDate +
", locale=" + locale +
", clazz=" + clazz +
'}';
}
public void setB(byte b) {
this.b = b;
}
public void setS(short s) {
this.s = s;
}
public void setI(int i) {
this.i = i;
}
public void setL(long l) {
this.l = l;
}
public void setF(float f) {
this.f = f;
}
public void setD(double d) {
this.d = d;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
public void setC(char c) {
this.c = c;
}
public void setB1(Byte b1) {
this.b1 = b1;
}
public void setS1(Short s1) {
this.s1 = s1;
}
public void setI1(Integer i1) {
this.i1 = i1;
}
public void setL1(Long l1) {
this.l1 = l1;
}
public void setF1(Float f1) {
this.f1 = f1;
}
public void setD1(Double d1) {
this.d1 = d1;
}
public void setFlag1(Boolean flag1) {
this.flag1 = flag1;
}
public void setC1(Character c1) {
this.c1 = c1;
}
public void setStr(String str) {
this.str = str;
}
public void setDate(Date date) {
this.date = date;
}
public void setSeason(Season season) {
this.season = season;
}
public void setUri(URI uri) {
this.uri = uri;
}
public void setUrl(URL url) {
this.url = url;
}
public void setLocalDate(LocalDate localDate) {
this.localDate = localDate;
}
public void setLocale(Locale locale) {
this.locale = locale;
}
public void setClazz(Class clazz) {
this.clazz = clazz;
}
}
<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="aBean" class="com.powernode.spring6.bean.A">
<property name="b" value="1"/>
<property name="s" value="1"/>
<property name="i" value="1"/>
<property name="l" value="1"/>
<property name="f" value="1"/>
<property name="d" value="1"/>
<property name="flag" value="false"/>
<property name="c" value="a"/>
<property name="b1" value="2"/>
<property name="s1" value="2"/>
<property name="i1" value="2"/>
<property name="l1" value="2"/>
<property name="f1" value="2"/>
<property name="d1" value="2"/>
<property name="flag1" value="true"/>
<property name="c1" value="a"/>
<property name="str" value="zhangsan"/>
<property name="date" value="Fri Sep 30 00:00:00 CST 2022"/>
<property name="season" value="WINTER"/>
<property name="uri" value="/save.do"/>
<property name="url" value="http://www.baidu.com"/>
<property name="localDate" value="EPOCH"/>
<property name="locale" value="CHINESE"/>
<property name="clazz" value="java.lang.String"/>
bean>
beans>
@Test
public void testA(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-all-simple-type.xml");
Object aBean = applicationContext.getBean("aBean", A.class);
System.out.println(aBean);
}
需要注意的是:
com.powernode.spring6.bean.Clazz
package com.powernode.spring6.bean;
/**
* @author shanglinsong
* @version 1.0
* @className com.powernode.spring6.bean.Clazz
* @date 2022/11/9
* @since 1.0
*/
public class Clazz {
private String name;
@Override
public String toString() {
return "Clazz{" +
"name='" + name + '\'' +
'}';
}
public Clazz() {
}
public Clazz(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
package com.powernode.spring6.bean;
/**
* @author shanglinsong
* @version 1.0
* @className com.powernode.spring6.bean.Student
* @date 2022/11/9
* @since 1.0
*/
public class Student {
private String name;
private Clazz clazz;
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", clazz=" + clazz +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Clazz getClazz() {
return clazz;
}
public void setClazz(Clazz clazz) {
this.clazz = clazz;
}
public Student(String name, Clazz clazz) {
this.name = name;
this.clazz = clazz;
}
public Student() {
}
}
<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="clazzBean" class="com.powernode.spring6.bean.Clazz"/>
<bean id="studentBean" class="com.powernode.spring6.bean.Student">
<property name="name" value="张三"/>
<property name="clazz" ref="clazzBean"/>
<property name="clazz.name" value="高三一班"/>
bean>
beans>
@Test
public void testCascade(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-cascade.xml");
Student studentBean = applicationContext.getBean("studentBean", Student.class);
System.out.println(studentBean);
}
要点:
当数组中的元素是简单类型:
package com.powernode.spring6.bean;
import java.util.Arrays;
/**
* @author shanglinsong
* @version 1.0
* @className com.powernode.spring6.bean.Person
* @date 2022/11/9
* @since 1.0
*/
public class Person {
private String[] favoriteFoods;
@Override
public String toString() {
return "Person{" +
"favoriteFoods=" + Arrays.toString(favoriteFoods) +
'}';
}
public void setFavoriteFoods(String[] favoriteFoods) {
this.favoriteFoods = favoriteFoods;
}
}
<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="personBean" class="com.powernode.spring6.bean.Person">
<property name="favoriteFoods">
<array>
<value>鸡排value>
<value>汉堡value>
<value>鹅肝value>
array>
property>
bean>
beans>
@Test
public void testArraySimple(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-array-simple.xml");
Person personBean = applicationContext.getBean("personBean", Person.class);
System.out.println(personBean);
当数组中的元素是非简单类型:一个订单中包含多个商品。
package com.powernode.spring6.bean;
/**
* @author shanglinsong
* @version 1.0
* @className com.powernode.spring6.bean.Goods
* @date 2022/11/9
* @since 1.0
*/
public class Goods {
private String name;
@Override
public String toString() {
return "Goods{" +
"name='" + name + '\'' +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Goods(String name) {
this.name = name;
}
public Goods() {
}
}
package com.powernode.spring6.bean;
import java.util.Arrays;
/**
* @author shanglinsong
* @version 1.0
* @className com.powernode.spring6.bean.Order
* @date 2022/11/9
* @since 1.0
*/
public class Order {
// 一个订单中有多个商品
private Goods[] goods;
@Override
public String toString() {
return "Order{" +
"goods=" + Arrays.toString(goods) +
'}';
}
public void setGoods(Goods[] goods) {
this.goods = goods;
}
public Order() {
}
public Order(Goods[] goods) {
this.goods = goods;
}
}
<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="goodsBean1" class="com.powernode.spring6.bean.Goods">
<property name="name" value="香蕉"/>
bean>
<bean id="goodsBean2" class="com.powernode.spring6.bean.Goods">
<property name="name" value="葡萄"/>
bean>
<bean id="orderBean" class="com.powernode.spring6.bean.Order">
<property name="goods">
<array>
<ref bean="goodsBean1" />
<ref bean="goodsBean2" />
array>
property>
bean>
beans>
@Test
public void testArray(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-array.xml");
Order orderBean = applicationContext.getBean("orderBean", Order.class);
System.out.println(orderBean);
}
要点:
List集合:有序可重复
package com.powernode.spring6.bean;
import java.util.List;
/**
* @author shanglinsong
* @version 1.0
* @className com.powernode.spring6.bean.People
* @date 2022/11/9
* @since 1.0
*/
public class People {
// 一个人有多个名字
private List<String> names;
@Override
public String toString() {
return "People{" +
"names=" + names +
'}';
}
public void setNames(List<String> names) {
this.names = names;
}
}
<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="peopleBean" class="com.powernode.spring6.bean.People">
<property name="names">
<list>
<value>铁锤value>
<value>张三value>
<value>张三value>
<value>张三value>
<value>狼value>
list>
property>
bean>
beans>
@Test
public void testCollection(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-collection.xml");
People peopleBean = applicationContext.getBean("peopleBean", People.class);
System.out.println(peopleBean);
}
注意:注入List集合的时候使用list标签,如果List集合中是简单类型使用value标签,反之使用ref标签。
Set集合:无序不可重复
com.powernode.spring6.bean.People
package com.powernode.spring6.bean;
import java.util.List;
import java.util.Set;
/**
* @author shanglinsong
* @version 1.0
* @className com.powernode.spring6.bean.People
* @date 2022/11/9
* @since 1.0
*/
public class People {
// 一个人有多个名字
private List<String> names;
// 一个人有多个电话
private Set<String> phones;
@Override
public String toString() {
return "People{" +
"names=" + names +
", phones=" + phones +
'}';
}
public void setPhones(Set<String> phones) {
this.phones = phones;
}
public void setNames(List<String> names) {
this.names = names;
}
}
spring-collection.xml
<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="peopleBean" class="com.powernode.spring6.bean.People">
<property name="names">
<list>
<value>铁锤value>
<value>张三value>
<value>张三value>
<value>张三value>
<value>狼value>
list>
property>
<property name="phones">
<set>
<value>110value>
<value>110value>
<value>120value>
<value>120value>
<value>119value>
<value>119value>
set>
property>
bean>
beans>
测试程序
@Test
public void testCollection(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-collection.xml");
People peopleBean = applicationContext.getBean("peopleBean", People.class);
System.out.println(peopleBean);
}
要点:
com.powernode.spring6.bean.People
package com.powernode.spring6.bean;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author shanglinsong
* @version 1.0
* @className com.powernode.spring6.bean.People
* @date 2022/11/9
* @since 1.0
*/
public class People {
// 一个人有多个名字
private List<String> names;
// 一个人有多个电话
private Set<String> phones;
// 一个人有多个地址
private Map<Integer, String> addrs;
@Override
public String toString() {
return "People{" +
"names=" + names +
", phones=" + phones +
", addrs=" + addrs +
'}';
}
public void setAddrs(Map<Integer, String> addrs) {
this.addrs = addrs;
}
public void setPhones(Set<String> phones) {
this.phones = phones;
}
public void setNames(List<String> names) {
this.names = names;
}
}
spring-collection.xml
<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="peopleBean" class="com.powernode.spring6.bean.People">
<property name="names">
<list>
<value>铁锤value>
<value>张三value>
<value>张三value>
<value>张三value>
<value>狼value>
list>
property>
<property name="phones">
<set>
<value>110value>
<value>110value>
<value>120value>
<value>120value>
<value>119value>
<value>119value>
set>
property>
<property name="addrs">
<map>
<entry key="1" value="北京大兴区"/>
<entry key="2" value="上海浦东区"/>
<entry key="3" value="深圳宝安区"/>
map>
property>
bean>
beans>
测试程序
@Test
public void testCollection(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-collection.xml");
People peopleBean = applicationContext.getBean("peopleBean", People.class);
System.out.println(peopleBean);
}
java.util.Properties继承java.util.Hashtable,所以Properties也是一个Map集合。
com.powernode.spring6.bean.People
package com.powernode.spring6.bean;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
/**
* @author shanglinsong
* @version 1.0
* @className com.powernode.spring6.bean.People
* @date 2022/11/9
* @since 1.0
*/
public class People {
// 一个人有多个名字
private List<String> names;
// 一个人有多个电话
private Set<String> phones;
// 一个人有多个地址
private Map<Integer, String> addrs;
private Properties properties;
@Override
public String toString() {
return "People{" +
"names=" + names +
", phones=" + phones +
", addrs=" + addrs +
", \nproperties=" + properties +
'}';
}
public void setProperties(Properties properties) {
this.properties = properties;
}
public void setAddrs(Map<Integer, String> addrs) {
this.addrs = addrs;
}
public void setPhones(Set<String> phones) {
this.phones = phones;
}
public void setNames(List<String> names) {
this.names = names;
}
}
<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="peopleBean" class="com.powernode.spring6.bean.People">
<property name="names">
<list>
<value>铁锤value>
<value>张三value>
<value>张三value>
<value>张三value>
<value>狼value>
list>
property>
<property name="phones">
<set>
<value>110value>
<value>110value>
<value>120value>
<value>120value>
<value>119value>
<value>119value>
set>
property>
<property name="addrs">
<map>
<entry key="1" value="北京大兴区"/>
<entry key="2" value="上海浦东区"/>
<entry key="3" value="深圳宝安区"/>
map>
property>
<property name="properties">
<props>
<prop key="driver">com.mysql.cj.jdbc.Driverprop>
<prop key="url">jdbc:mysql://localhost:3306/spring6prop>
<prop key="username">rootprop>
<prop key="password">123456prop>
props>
property>
bean>
beans>
@Test
public void testCollection(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-collection.xml");
People peopleBean = applicationContext.getBean("peopleBean", People.class);
System.out.println(peopleBean);
}
注入空字符串使用:
注入null使用:
我们先来看一下,怎么注入空字符串。
com.powernode.spring6.bean.Vip
package com.powernode.spring6.bean;
/**
* @author shanglinsong
* @version 1.0
* @className com.powernode.spring6.bean.Vip
* @date 2022/11/9
* @since 1.0
*/
public class Vip {
private String email;
@Override
public String toString() {
return "Vip{" +
"email='" + email + '\'' +
'}';
}
public void setEmail(String email) {
this.email = email;
}
}
<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="vipBean" class="com.powernode.spring6.bean.Vip">
<property name="email">
<value/>
property>
bean>
beans>
@Test
public void testNull(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-null.xml");
Vip vipBean = applicationContext.getBean("vipBean", Vip.class);
System.out.println(vipBean);
}
怎么注入null呢?
XML中有5个特殊字符,分别是:<、>、'、"、&
解决方案包括两种:
5个特殊字符对应的转义字符分别是:
特殊字符 | 转义字符 |
---|---|
> | > |
< | < |
’ | ' |
" | " |
& | & |
先使用转义字符来代替:
package com.powernode.spring6.bean;
/**
* @author shanglinsong
* @version 1.0
* @className com.powernode.spring6.bean.Math
* @date 2022/11/9
* @since 1.0
*/
public class Math {
private String result;
@Override
public String toString() {
return "Math{" +
"result='" + result + '\'' +
'}';
}
public void setResult(String result) {
this.result = result;
}
}
<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="mathBean" class="com.powernode.spring6.bean.Math">
<property name="result" value="2 < 3"/>
bean>
beans>
@Test
public void testSpecial(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-special.xml");
Math mathBean = applicationContext.getBean("mathBean", Math.class);
System.out.println(mathBean);
我们再来使用CDATA方式:
<bean id="mathBean" class="com.powernode.spring6.bean.Math">
<property name="result">
<value>value>
property>
bean>
注意:使用CDATA时,不能使用value属性,只能使用value标签。
目的:简化配置。
使用p命名空间注入的前提条件包括两个:
第一:在XML头部信息中添加p命名空间的配置信息:xmlns:p=“http://www.springframework.org/schema/p”
第二:p命名空间注入是基于setter方法的,所以需要对应的属性提供setter方法。
package com.powernode.spring6.bean;
/**
* @author shanglinsong
* @version 1.0
* @className com.powernode.spring6.bean.Customer
* @date 2022/11/9
* @since 1.0
*/
public class Customer {
private String name;
private int age;
@Override
public String toString() {
return "Customer{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
}
<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="customerBean" class="com.powernode.spring6.bean.Customer" p:name="zhangsan" p:age="20"/>
beans>
@Test
public void testP(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-p.xml");
Customer customerBean = applicationContext.getBean("customerBean", Customer.class);
System.out.println(customerBean);
}
把setter方法去掉:
所以p命名空间实际上是对set注入的简化。
c命名空间是简化构造方法注入的。
使用c命名空间的两个前提条件:
第一:需要在xml配置文件头部添加信息:xmlns:c=“http://www.springframework.org/schema/c”
第二:需要提供构造方法。
package com.powernode.spring6.bean;
/**
* @author shanglinsong
* @version 1.0
* @className com.powernode.spring6.bean.MyTime
* @date 2022/11/9
* @since 1.0
*/
public class MyTime {
private int year;
private int month;
private int day;
@Override
public String toString() {
return "MyTime{" +
"year=" + year +
", month=" + month +
", day=" + day +
'}';
}
public MyTime(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
}
<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="myTimeBean" class="com.powernode.spring6.bean.MyTime" c:_0="2008" c:_1="8" c:_2="8"/>
beans>
@Test
public void testC(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-c.xml");
MyTime myTimeBean = applicationContext.getBean("myTimeBean", MyTime.class);
System.out.println(myTimeBean);
}
把构造方法注释掉:
所以,c命名空间是依靠构造方法的。
注意:不管是p命名空间还是c命名空间,注入的时候都可以注入简单类型以及非简单类型。
使用util命名空间可以让配置复用。
使用util命名空间的前提是:在spring配置文件头部添加配置信息。如下:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:util="http://www.springframework.org/schema/util"
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
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
beans>
com.powernode.spring6.bean.MyDataSource1
package com.powernode.spring6.bean;
import java.util.Properties;
/**
* @author shanglinsong
* @version 1.0
* @className com.powernode.spring6.bean.MyDataSource1
* @date 2022/11/9
* @since 1.0
*/
public class MyDataSource1 {
private Properties properties;
@Override
public String toString() {
return "MyDataSource1{" +
"properties=" + properties +
'}';
}
public void setProperties(Properties properties) {
this.properties = properties;
}
}
com.powernode.spring6.bean.MyDataSource2
package com.powernode.spring6.bean;
import java.util.Properties;
/**
* @author shanglinsong
* @version 1.0
* @className com.powernode.spring6.bean.MyDataSource2
* @date 2022/11/9
* @since 1.0
*/
public class MyDataSource2 {
private Properties properties;
@Override
public String toString() {
return "MyDataSource2{" +
"properties=" + properties +
'}';
}
public void setProperties(Properties properties) {
this.properties = properties;
}
}
spring-util.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:util="http://www.springframework.org/schema/util"
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
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<util:properties id="prop">
<prop key="driver">com.mysql.cj.jdbc.Driverprop>
<prop key="url">jdbc:mysql://localhost:3306/spring6prop>
<prop key="username">rootprop>
<prop key="password">000000prop>
util:properties>
<bean id="myDataSource1" class="com.powernode.spring6.bean.MyDataSource1">
<property name="properties">
<props>
<prop key="driver">com.mysql.cj.jdbc.Driverprop>
<prop key="url">jdbc:mysql://localhost:3306/spring6prop>
<prop key="username">rootprop>
<prop key="password">000000prop>
props>
property>
bean>
<bean id="myDataSource2" class="com.powernode.spring6.bean.MyDataSource2">
<property name="properties" ref="prop" />
bean>
beans>
测试程序
@Test
public void testUtil(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-util.xml");
MyDataSource1 myDataSource1 = applicationContext.getBean("myDataSource1", MyDataSource1.class);
System.out.println(myDataSource1);
MyDataSource2 myDataSource2 = applicationContext.getBean("myDataSource2", MyDataSource2.class);
System.out.println(myDataSource2);
}
测试结果
com.powernode.spring6.dao.UserDao
package com.powernode.spring6.dao;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author shanglinsong
* @version 1.0
* @className com.powernode.spring6.dao.UserDao
* @date 2022/11/8
* @since 1.0
*/
public class UserDao {
private static final Logger logger = LoggerFactory.getLogger(UserDao.class);
public void insert(){
// System.out.println("正在保存用户数据。。。");
logger.info("数据库正在保存用户信息。。。");
}
}
com.powernode.spring6.service.UserService
package com.powernode.spring6.service;
import com.powernode.spring6.dao.UserDao;
import com.powernode.spring6.dao.VipDao;
/**
* @author shanglinsong
* @version 1.0
* @className com.powernode.spring6.service.UserService
* @date 2022/11/8
* @since 1.0
*/
public class UserService {
private UserDao userDao;
// 这个set方法非常关键
public void setAaa(UserDao userDao) {
this.userDao = userDao;
}
/**
* 保存用户信息到数据库
*/
public void saveUser(){
userDao.insert();
}
}
spring-autowire.xml
<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="userService" class="com.powernode.spring6.service.UserService" autowire="byName"/>
<bean id="aaa" class="com.powernode.spring6.dao.UserDao"/>
beans>
这个配置起到关键作用:
测试程序
@Test
public void testAutowireByName(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-autowire.xml");
UserService userService = applicationContext.getBean("userService", UserService.class);
userService.saveUser();
}
测试结果
我们来测试一下,byName装配是和属性名有关还是和set方法名有关系:
package com.powernode.spring6.service;
import com.powernode.spring6.dao.UserDao;
import com.powernode.spring6.dao.VipDao;
/**
* @author shanglinsong
* @version 1.0
* @className com.powernode.spring6.service.UserService
* @date 2022/11/8
* @since 1.0
*/
public class UserService {
private UserDao userDao;
// 这个set方法非常关键
/*public void setAaa(UserDao userDao) {
this.userDao = userDao;
}*/
// 修改set方法名
public void setDao(UserDao userDao) {
this.userDao = userDao;
}
/**
* 保存用户信息到数据库
*/
public void saveUser(){
userDao.insert();
}
}
通过测试得知,aaa属性并没有赋值成功。也就是并没有装配成功。
我们将spring配置文件修改以下:
<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="userService" class="com.powernode.spring6.service.UserService" autowire="byName"/>
<bean id="dao" class="com.powernode.spring6.dao.UserDao"/>
beans>
测试结果
这说明,如果根据名称装配(byName),底层会调用set方法进行注入。
例如:setAge() 对应的名字是age,setPassword()对应的名字是password,setEmail()对应的名字是email。
com.powernode.spring6.dao.AccountDao
package com.powernode.spring6.dao;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author shanglinsong
* @version 1.0
* @className com.powernode.spring6.dao.AccountDao
* @date 2022/11/9
* @since 1.0
*/
public class AccountDao {
private static final Logger logger = LoggerFactory.getLogger(AccountDao.class);
public void insert(){
logger.info("正在保护账户信息。。。");
}
}
com.powernode.spring6.service.AccountService
package com.powernode.spring6.service;
import com.powernode.spring6.dao.AccountDao;
/**
* @author shanglinsong
* @version 1.0
* @className com.powernode.spring6.service.AccountService
* @date 2022/11/9
* @since 1.0
*/
public class AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
public void save(){
accountDao.insert();
}
}
spring-autowire.xml
<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="accountService" class="com.powernode.spring6.service.AccountService" autowire="byType"/>
<bean class="com.powernode.spring6.dao.AccountDao"/>
beans>
测试程序
@Test
public void testAutowireByType(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-autowire.xml");
AccountService accountService = applicationContext.getBean("accountService", AccountService.class);
accountService.save();
}
测试结果
我们把AccountService中的set方法注释掉,再执行:
可以看到无论是byName还是byType,在装配的时候都是基于set方法的。所以set方法是必须要提供的。提供构造方法是不行的,大家可以测试一下。这里就不再赘述。
如果byType,根据类型装配时,如果配置文件中有两个类型一样的bean会出现什么问题呢?
测试结果说明了,当byType进行自动装配的时候,配置文件中某种类型的Bean必须是唯一的,不能出现多个。
我们都知道编写数据源的时候是需要连接数据库的信息的,例如:driver url username password等信息。这些信息可以单独写到一个属性配置文件中吗,这样用户修改起来会更加的方便。
第一步:写一个数据源类,提供相关属性。
package com.powernode.spring6.bean;
import javax.sql.DataSource;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.logging.Logger;
/**
* @author shanglinsong
* @version 1.0
* @className com.powernode.spring6.bean.MyDataSource
* @date 2022/11/9
* @since 1.0
*/
public class MyDataSource implements DataSource {
private String driver;
private String url;
private String username;
private String password;
public void setDriver(String driver) {
this.driver = driver;
}
public void setUrl(String url) {
this.url = url;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "MyDataSource{" +
"driver='" + driver + '\'' +
", url='" + url + '\'' +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
@Override
public Connection getConnection() throws SQLException {
return null;
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
return null;
}
@Override
public PrintWriter getLogWriter() throws SQLException {
return null;
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
}
@Override
public int getLoginTimeout() throws SQLException {
return 0;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
return null;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
}
第二步:在类路径下新建jdbc.properties文件,并配置信息。
# 不加jdbc. , username的root会自动定位到Administrator
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring6
jdbc.username=root
jdbc.password=000000
第三步:在spring配置文件中引入context命名空间。
spring-properties.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
beans>
第四步:在spring中配置使用jdbc.properties文件。
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder location="jdbc.properties"/>
<bean id="dataSource" class="com.powernode.spring6.bean.MyDataSource">
<property name="driver" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
bean>
beans>
测试程序
@Test
public void testProperties(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-properties.xml");
MyDataSource dataSource = applicationContext.getBean("dataSource", MyDataSource.class);
System.out.println(dataSource);
}
测试结果
默认情况下,Spring的IoC容器创建的Bean对象是单例的。来测试一下:
package com.powernode.spring6.beans;
/**
* @author shanglinsong
* @version 1.0
* @className com.powernode.spring6.beans.SpringBean
* @date 2022/11/14
* @since 1.0
*/
public class SpringBean {
}
<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="sb" class="com.powernode.spring6.beans.SpringBean"/>
beans>
package com.powernode.spring6.test;
import com.powernode.spring6.beans.SpringBean;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* @author shanglinsong
* @version 1.0
* @className com.powernode.spring6.test.SpringScopeTest
* @date 2022/11/14
* @since 1.0
*/
public class SpringScopeTest {
@Test
public void testScope(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-scope.xml");
SpringBean sb1 = applicationContext.getBean("sb", SpringBean.class);
System.out.println(sb1);
SpringBean sb2 = applicationContext.getBean("sb", SpringBean.class);
System.out.println(sb2);
}
}
这个对象在什么时候创建的呢?可以为SpringBean提供一个无参数构造方法,测试一下,如下:
package com.powernode.spring6.beans;
/**
* @author shanglinsong
* @version 1.0
* @className com.powernode.spring6.beans.SpringBean
* @date 2022/11/14
* @since 1.0
*/
public class SpringBean {
public SpringBean() {
System.out.println("SpringBean的无参数构造方法执行。");
}
}
@Test
public void testScope(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-scope.xml");
}
如果想让Spring的Bean对象以多例的形式存在,可以在bean标签中指定scope属性的值为:prototype,这样Spring会在每一次执行getBean()方法的时候创建Bean对象,调用几次则创建几次。
<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="sb" class="com.powernode.spring6.beans.SpringBean"/>
beans>
@Test
public void testScope(){
/**
* 1. Spring默认情况下是如何管理这个Bean的:
* 默认情况下Bean是单例的。(单例:singleton)
* 在Spring上下文初始化的时候实例化。
* 每一次调用getBean()方法的时候,都返回那个单例的对象。
*
* 2. 当将bean的scope属性设置为prototype:
* bean是多例的。
* spring上下文初始化的时候,并不会初始化这些prototype的bean。
* 每一次调用getBean()方法的时候,实例化该bean对象。
* prototype翻译为:原型。
*/
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-scope.xml");
SpringBean sb1 = applicationContext.getBean("sb", SpringBean.class);
System.out.println(sb1);
SpringBean sb2 = applicationContext.getBean("sb", SpringBean.class);
System.out.println(sb2);
}
我们可以把测试代码中的getBean()方法所在行代码注释掉:
scope如果没有配置,它的默认值是什么呢?
scope属性的值不止两个,它一共包括8个选项:
接下来咱们自定义一个Scope,线程级别的Scope,在同一个线程中,获取的Bean都是同一个。跨线程则是不同的对象:(以下内容作为了解)
第一步:自定义Scope。(实现Scope接口)
第二步:将自定义的Scope注册到Spring容器中。
spring-scope.xml
<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 class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="myThread">
<bean class="org.springframework.context.support.SimpleThreadScope"/>
entry>
map>
property>
bean>
<bean id="sb" class="com.powernode.spring6.beans.SpringBean" scope="myThread"/>
beans>
@Test
public void testCustomScope(){
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-scope.xml");
SpringBean sb1 = applicationContext.getBean("sb", SpringBean.class);
System.out.println(sb1);
SpringBean sb2 = applicationContext.getBean("sb", SpringBean.class);
System.out.println(sb2);
// 启动线程
new Thread(new Runnable() {
@Override
public void run() {
SpringBean sb3 = applicationContext.getBean("sb", SpringBean.class);
System.out.println(sb3);
SpringBean sb4 = applicationContext.getBean("sb", SpringBean.class);
System.out.println(sb4);
}
}).start();
}