本文章适用于第12周spring课程内容的补充学习。
主要内容来源于视频课程的笔记:https://www.bilibili.com/video/BV1kR4y1b7Qc/?p=22&share_source=copy_web&vd_source=97444cc205d3d3ef369bafb60cf90ca0
一、概述
1.Spring是什么?
Spring是一款主流的Java EE轻量级开源框架,Spring的用途不仅局限于服务器端的开发。从简单性,可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。Spring框架除了自己提供功能外还能提供整合其他技术和框架的能力。
这次学习的Spring版本为Spring6。
2.Spring的狭义与广义
广义上的Spring: Spring技术栈
广义上的Spring泛指以Spring Framework为核心的Spring技术栈。Spring现在已经不是一个单纯的应用框架,而是而是逐渐发展为一个由多个不同子模块组成的成熟技术,例如Spring Framework, Spring MVC, Spring Boot, Spring Cloud, Spring Data等,其中Spring Framework是其他子项目的基础。
狭义上的Spring: Spring Framework
狭义上的spring特指Spring Framework, 通常我们将它称为Spring框架。
Spring框架是一个分层的、面向切面的Java应用程序的一站式轻量级解决方案,它是Spring技术栈 的核心与基础,是为了解决企业级应用开发的复杂性而创建的。
Spring有两个最核心的模块:IoC与AOP
IoC: Inverse of Control的缩写,意为“控制反转”,指把创建对象的过程交给Spring进行管理
AOP: Aspect Oriented Programming的简写,意为 "面向切面编程"。AOP用来封装多个类的公共行为,将那些与业务无关,却为业务模块所共同调用的逻辑封装起来,减少系统的重复代码,降低模块间的耦合度。另外, AOP还解决一些系统层面上的问题,比如日志,事务,权限等。
3.Spring Framework的特点
(1)非侵入式:使用Spring Framework开发应用程序时,Spring对应用程序本身的结构影响非常小。对领域模型可以做到零污染;对功能型组件也只需要使用几个简单的注解进行标记,完全不会破坏原有结构。,反而能将组件结构进一步简化。这就使得基于Spring Framework开发应用程序时结构清晰、简洁优雅。
(2)控制反转:Ioc -- Inversion of Control,反转资源获取方向。把自己创建资源、向环境索取资源变成环境将资源准备好,我们享受资源注入。
(3)面向切面编程:AOP -- Aspect Oriented Programming,在不修改源代码的基础上增强功能。
(4)容器:Spring IOC是一个容器,因为他包含并管理组件对象的生命周期。组件享受到了容器化的管理,替程序员屏蔽了组件创建过程中的大量细节,极大地降低了使用门槛,提高了开发效率。
(5)组件化:Spring实现了使用简单的配件组合成一个复杂的应用。在Spring中可以使用XML和Java注解组合这些对象。这使我们可以基于一个一个功能明确、边界清晰的组件有条不紊的搭建超大型应用系统。
(6)一站式:在IOC和AOP的基础上可以整合各种企业应用的开源框架和优秀的第三方类库。而且Spring旗下的项目已经覆盖了广泛领域,很多方面的功能性需求可以在Spring Framework的基础上全部使用Spring来实现。
4.Spring模块组成
这些是对各模块的简单介绍,在后续的学习中会逐步了解它们。
(1)Spring Core: 核心容器
- spring-core: IOC和DI的基本实现
- spring-beans: BeanFactory和Bean的装配管理
- spring-context:Spring context上下文,即IOC容器
- spring-expression:Spring表达式语言
(2)Spring AOP:
- spring-aop:面向切面编程的应用模块,整合ASM, CGlib, JDK Proxy
- spring-aspects:集成Aspectctj,AOP应用框架
- spring-instrucment:动态Class Loading模块
(3)Spring Data Access:
- spring-jdbc: Spring对JDBC的封装,用于简化jdbc操作
- spring-orm: java对象与数据库数据的映射框架
- spring-oxm: 对象与xml文件的映射框架
- spring-jms: spring对java Message Service(Java消息服务)的封装,用于服务之间的通信
(4)Spring Web:
- spring-web:最基础的web支持,建立于spring-context之上,通过Servlet或listener来初始化IOC容器
- spring-webmvc: 实现web mvc
- spring-websocket:与前端的全双工通信
- spring-webflux: spring5.0提供的,用于取代传统java servlet,非阻塞式Reactive Web框架,异步,非阻塞,事件驱动的服务。
(5)Spring Message:
- spring-messaging: spring4.0提供的,为spring集成一些基础的报文传送服务。
(6)Spring Test:
- spring-test:集成测试支持,主要是对Junit的封装。
二、Spring开发入门
现在创建一个maven项目,进行Spring入门案例的测试
Spring: 6.0.2
JDK: 17
IDEA: 2023 社区版
1.入门案例开发步骤
这部分讲解如何创建并使用bean
文档结构:(这是一个Maven项目)
(1)引入Spring相关依赖
在pom.xml配置文件中添加依赖:
<dependencies>
<!--context-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.2</version>
</dependency>
<!--junit-->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.6.3</version>
<scope>test</scope>
</dependency>
</dependencies>
(2)创建类,定义属性和方法
创建User类,这个类用来测试Bean,它的对象将由Spring进行创建。
package com.test1.demo;
public class User {
public void add(){
System.out.println("add");
}
}
(3)按照Spring的要求来创建配置文件(.xml)
在resource文件夹下创建配置文件bean.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-3.0.xsd">
<!--完成User对象创建
bean标签
id属性:唯一标识
class:创建对象所在类的全路径 (包名称+类名称)-->
<bean id="user" class="com.test1.demo.User"> </bean>
</beans>
解析:
每个<bean>标签都应该由一个唯一的id,对其进行标识。
class属性值为创建对象所在类的全路径,即包名称+类名称,用来指定一个类。
(4)在xml文件中配置相关信息
相关信息即为<bean>标签所指定的信息(类),每一个bean标签对应着一个类。
除了<bean>标签外,我们以后还将会配置其他标签及其属性,实现不同的功能。
(5)进行最终测试
在主包中创建testUser类进行测试:
package com.test1.demo;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestUser {
@Test
public void testUserObject(){
//加载spring的配置文件,进行对象的创建
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
//获取创建的对象
User user = (User) context.getBean("user");
System.out.println(user);
//使用对象调用方法进行测试
user.add();
}
}
解析:
ApplicationContext对象context加载spring的配置文件,来创建文件中指定的类的对象。
调用context的getBean(String id)方法,获取它加载的类对象。这个方法的参数是在<bean>标签中设置好的id属性,它能够标识一个被记录的类。
测试结果:
可以看到,Spring通过反射创建了一个User对象,并能够调用它的add()方法。
2.如何使用反射创建对象?
下面的内容讲解,为什么我们通过调用context的getBean()方法,就可以得到指定的类对象。
(1) 类对象被创建的过程
- 加载bean.xml文件
- 对xml文件进行解析操作
- 获取xml文件中bean标签中的属性值 -- id属性和class属性
- 使用反射根据类的全路径创建对象(原理)
Class clazz = Class.forName(class属性值) User user = (User)clazz.getDeclaredConstructor().newInstance();
可以发现,创建对象context时,它的构造函数通过反射技术,对xml文件中指定的类一股脑地创建出了它们的对象,让我们得以通过getBeans()获取到这些对象。
(2)创建的对象放在哪里?
在Map<String, BeanDefinition> beanDefinitionMap变量中,对这些对象进行存储。
Key:唯一标识(id)
Value:类的定义(描述信息)
在DefaultListableBeanFactory.class中有这个变量的定义:(源码)
类的描述信息(Value)有哪些呢?
打开BeanDefiniton,可以看到:
其中,beanClassName, Scope等都是类的描述信息。
我们通常把BeanDefinition接口叫为:Bean的定义对象。
三、整合Log4j2日记框架
在之前的学习中,我们大致了解了如何构建一个Spring项目。同时在spring的广义定义上,我们知道,它还可以整合其他框架,实现更复杂的功能。在接下来的学习中,我们将使用spring整合Log4j2日记框架进行学习。
1.Log4j2日志概述
在项目开发中,日志非常重要,不管是记录运行情况还是定位线上问题,都离不开对日志的分析。日志记录了系统行为的时间、地点、状态等相关信息,能够帮助我们了解并监控系统状态,在发生错误或者接近某种危险状态的时候能够及时提醒我们处理,同时在系统产生问题的时候,能够帮助我们快速地定位、诊断并解决问题。
Apache Log4j2是一个开源的日志记录组件,使用非常广泛。在工程中易用方法替代了System.out等打印语句。
Log4j2由几个重要的组件构成:
(1)日志信息的优先级:下面的信息优先级由低到高依次排列
TRACE: 追踪,是最低的日志级别,相当于追踪程序的执行
DEBUG: 调试,一般在开发中,都将其设置为最低的日志级别
INFO: 信息,输出重要的信息,使用较多
WARN: 警告,输出警告的信息
ERROR: 错误,输出错误信息
FATAL: 严重错误
这些级别分别用来指定这条信息的重要程度,级别高的信息会自动屏蔽级别低的日志信息。
(2)日志信息的输出目的地:日志信息的输出目的地指定了将日志打印到控制台还是文件中。
(3)日志信息的输出格式:日志信息的输出格式控制了日志信息的显示内容。
2.整合日志的操作
文档结构:
(1)引入Log4j2的依赖
在pom.xml中,引入对Log4j2框架的依赖(类似于springFrame的依赖)
<!--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>
(2)加入日志配置文件
在resource包中,创建Log4j2.xml配置文件,用来控制日志的输出
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<loggers>
<!-- TRACE: 追踪,是最低的日志级别,相当于追踪程序的执行-->
<!-- DEBUG: 调试,一般在开发中,都将其设置为最低的日志级别-->
<!-- INFO: 信息,输出重要的信息,使用较多-->
<!-- WARN: 警告,输出警告的信息-->
<!-- ERROR: 错误,输出错误信息-->
<!-- FATAL: 严重错误-->
<!--日志信息的级别-->
<root level="DEBUG">
<!--日志输出的信息,对应着下面三个name-->
<appender-ref ref="spring6log"/>
<appender-ref ref="RollingFile"/>
<appender-ref ref="log"/>
</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>
<!--文件会打印出所有信息,这个log每次运行程序都会自动清空,由append属性决定,适合-->
<File name="log" fileName="d:/C_Java/testSpring/Spring6/info/test.log" append="false">
<PatternLayout pattern="%d{HH:mm:ss SSS} %-5level %class{36} %L %M - %msg%xEx%n"/>
</File>
<!--这个会打印出所有信息,
每次大小超过size,
这个大小为size的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,
作为存档-->
<RollingFile name="RollingFile" fileName="d:/C_Java/testSpring/Spring6/info/app.log"
filePattern="log/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.log.gz">
<PatternLayout pattern="%d{yyyy-MM-dd 'at' HH:mm:ss z} %-5level %class{36} %L %M - %msg%xEx%n"/>
<SizeBasedTriggeringPolicy size="50MB"/>
<!--DefaultRolloverStrategy属性如不设置
则默认最多同一文件夹下有7个文件,这里设置了20-->
<DefaultRolloverStrategy max="20"/>
</RollingFile>
</appenders>
</configuration>
解析:
log4j2.xml被标签分为两个部分,一是<loggers>标签,另一个是<appenders>标签。
<loggers>标签中的内容:
<root>:每个配置都应该具有这个标签,它的属性level表示日志信息的级别。
<appender-ref>: 是root的子节点,用来指定该日志输出到哪个appender。
<appenders>标签中的内容:
<console>:用于定义输出到控制台的Appender。
<File>: 用于定义输出到指定位置的文件的Appender。
<RollingFile>: 定义指定方式触发新的Appender。
对于这些标签的使用及它们的属性,可以参考博客:
log4j2的使用及配置详解_log4j2 additivity-CSDN博客
(3)对日志输出进行测试
依旧使用TestUser类进行测试:
可以看到,新增了三条控制台输出。同时,在你之前设置的文件夹下,可以看到新增了test.log和app.log两篇日志。它们分别对应着三条日志信息的输出。
3.补充说明
(1)Appender的使用方法
Log4j2使用Appender将日志数据写到各种目标位置(控制台、文件、多种数据库API、远程套接字服务器等)。
Appender可以通过特定的Appender插件名或appender元素来配置,每个Appender都要有一个name属性,用来指定区别于其他appender的唯一标识。这个标识的值在Logger的appender-ref中被引用(见上面的loggers标签),从而将Appender配置到该Logger中。
(2)常用的Appender--ConsoleAppender
ConsoleAppender会将输出写入System.out或者System.err中。必须提供一个Layout来格式化LogEvent。简单地来说,这个标签(console)将日志信息输出到控制台。
(3)常用的Appender--RollingFileAppender
RollingFileAppender会自动输出到filename参数指定的文件中,并且需要指定TriggeringPolicy和RolloverStrategy。其中TriggeringPolicy决定是否生成新的日志文件,RolloverStrategy决定如何生成新的日志文件。
4.手动书写日志
在程序中,我们也可以直接手动书写日志,而不使用xml文件进行配置。
接下来在testUser测试类中进行手动书写日志的测试。
//创建Logger对象
private Logger logger = LoggerFactory.getLogger(TestUser.class);
@Test
public void testUserObject(){
//加载spring的配置文件,进行对象的创建
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
//获取创建的对象
User user = (User) context.getBean("user");
System.out.println(user);
//使用对象调用方法进行测试
user.add();
//手动写日志
logger.info("###执行调用成功");
}
输出:
四、容器 IoC
1.概述
IoC是Inversion of Control的简写,译为“控制反转”,它不是一门技术,而是一种设计思想,是一个重要的面向对象编程法则,能够指导我们设计出松耦合、更优良的程序。
Spring通过IoC容器来管理所有Java对象的实例化和初始化,控制对象与对象之间的依赖关系。我们将由IoC容器管理的Java对象称为Spring Bean,它与使用关键字new创建的Java对象没有任何区别。
IoC容器是Spring框架中最重要的核心组件之一,它贯穿了Spring从诞生到成长的整个过程。
(1)控制反转
控制反转是一种思想,目的是降低程序耦合度,提高程序扩展力。
控制反转,反转的是什么?将对象的创建权力交出去,交给第三方容器负责;将对象和对象之间的维护权交出去,交给第三方容器负责。
控制反转这种思想如何实现?DI(Dependency Injection): 依赖注入。
(2)依赖注入
依赖注入实现了控制反转的思想。
指Spring创建对象的过程中,将对象依赖属性通过配置进行注入。
依赖注入常见的实现方式:
set注入
构造注入
Bean管理:Bean对象的创建,以及Bean对象中属性的赋值(或者叫Bean对象之间关系的维护)
(3)IoC容器在Spring的实现
Spring的IoC容器就是IoC思想的一个落地的产品实现。IoC容器中管理的组件也叫做bean。在创建bean之前,首先要创建IoC容器。Spring提供了IoC容器的两种创建方式:
BeanFactory
这是IoC容器的基本实现,使用Spring内部使用的接口。面向Spring本身,不提供给开发者使用。
ApplicationContext (主要)
BeanFactory的子接口,提供了更多的高级特性。面向Spring的使用者,几乎所有场合都使用ApplicationContext而不是底层的BeanFactory。
Application的主要实现类:
ClassPathXmlApplicationContext | 通过读取类路径下的xml格式的配置文件创建IoC容器对象 |
FileSystemXmlApplicationContext | 通过文件系统路径读取xml格式的配置文件创建IoC容器对象 |
ConfigurableApplicationContext | ApplicationContext的子接口,包含一些扩展方法refresh()和close(),让Application具有活动,关闭和刷新上下文功能 |
WebApplicationContext | 专门为web应用准备,基于web环境创建IoC容器对象,并将对象引入存入ServletContext域中 |
(4)IoC容器如何创建Bean
xml配置文件
| Bean定义信息
| BeanDefinition
V
抽象 BeanDefinitionReader
|
|
V BeanFactory工厂+反射
IoC容器 | Bean定义信息--> 实例化 |
| 初始化 |
| 最终对象 |
类中:context.getBean("类名") 获取最终的类对象
2.基于XML管理Bean
在上面的学习中,我们对bean的创建和获取方法有了一定认识:
创建实体类--->在xml文件中将这个类设置为bean--->在要调用这个类时,使用getBean()方法
(1)搭建子模块test-xml
项目结构:
这个模块用来测试使用xml管理bean,为了方便,可以将依赖添加到父模块的pom.xml中。
- 创建User类(将通过这个类进行创建bean的测试)
package com.test.demo1;
public class User {
private String name;
private Integer age;
public void run(){
System.out.println("run");
}
}
(2)实验一:获取bean
方式1:根据 id获取
由于id属性指定了bean的唯一标识,所以根据bean标签的id属性可以精确地获取到一个组件对象。上个实验中我们使用的就是这种方式。
将上个实验中的bean.xml和resource.xml配置文件复制到这个项目中,再次进行测试。
方式2:根据类型获取 (见2)
方式3:根据id和类型获取 (见3)
public class TestUser {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
//1.根据id获取bean
User user = (User) context.getBean("user");
System.out.println("根据id获取bean: " + user);
//2.根据类型获取bean
User user2 =context.getBean(User.class);
System.out.println("根据类型获取bean: " + user2);
//3.根据id+类型获取bean
User user3 = context.getBean("user", User.class);
System.out.println("根据id和类型获取bean: " + user3);
}
}
注意:当根据类型获取bean的时候,要求IoC容器中指定类型的bean有且只能有一个。
当IoC容器中一共配置了两个bean:
<bean id="user1" class="com.test.demo1.User"> </bean>
<bean id="user2" class="com.test.demo1.User"> </bean>
则根据类型获取时会抛出异常:
No qualifying bean of type 'com.test.demo1.User' available: expected single matching bean but found 2: user,user2
扩展知识:
如果组件类实现了接口,根据接口类型可以获取bean吗?
可以,前提是bean唯一
如果一个接口有多个实现类,这些实现类都配置了bean,根据接口类型可以获取bean吗
不行,因为bean不唯一
如图,UserDao接口有两个实现,且这两个实现类均配置了bean:
<bean id="userDaoImpl" class="com.test.demo1.bean.UserDaoImpl"> </bean>
<bean id="userDaoImpl" class="com.test.demo1.bean.UserDaoImpl2"> </bean>
这时,再通过接口类型获取bean,则会报错。
UserDao userDao = context.getBean(UserDao.class);
System.out.println("根据接口类型获取Bean: " + userDao);
userDao.run();
结论:
根据类型来获取bean时,在满足bean唯一性的前提下,其实只是看:对象inatanceof指定的类型 的返回结果,只要返回的是true就可以认定为和类型匹配,能够获取到。
(3)实验二:依赖注入之setter注入
- 首先创建一个实体类Book来测试setter注入,这个类有两个成员变量bname和author。
public class Book {
private String bname;
private String author;
public void setBname(String bname) {
this.bname = bname;
}
public void setAuthor(String author) {
this.author = author;
}
//设置toString方法
@Override
public String toString() {
return "Book{" +
"bname='" + bname + '\'' +
", author='" + author + '\'' +
'}';
}
}
- 接下来编写配置文件bean-di.xml:
<!--set方法注入-->
<bean id="book" class="com.test.demo1.di.Book">
<property name="bname" value="Java"></property>
<property name="author" value="hwh"></property>
</bean>
现在,读取配置文件时,加载<bean "book">时,就会调用set方法,对context中的对象进行赋值。
- 创建测试类TestBook:
public class TestBook {
@Test
public void test1(){
ApplicationContext context = new ClassPathXmlApplicationContext("bean-di.xml");
Book book = context.getBean("book", Book.class);
System.out.println(book);
}
}
运行结果:
可以看到,context中的book对象已经被赋值。
(4)实验三:依赖注入之构造器注入
区别:类中要有有参数的构造函数。
- 在配置文件bean-di.xml中添加构造器注入的方式:
<!--构造器注入-->
<bean id="book2" class="com.test.demo1.di.Book">
<constructor-arg name="bname" value="Spring"> </constructor-arg>
<constructor-arg name="author" value="hwh"> </constructor-arg>
</bean>
其中,constructor-arg表示类的有参构造的参数,name为参数名称,value为参数值。
注意:
如果你只在Book类中设置了有参构造,而没有添加无参构造,那么当你创建ApplicationContext对象context = new ClassPathXmlApplication("xxx.xml"); 时,会报错。
原因我们之前学习过,context解析xml文件,创建类对象并将它们存入map中。所以此时所有在xml文件中被标记的类都将被创建出对象。而之前设置的类对象"book"是无参构造出的对象,此时Book类中没有无参构造函数,创建这个对象时就会报错。
构造器注入和setter注入最直观的区别:标签的区别
setter注入在bean中使用<property>来指定变量名和值。
构造器注入在bean中使用<constructor-arg>来指定变量名和值。
(5)实验四:特殊值处理
- 字面量赋值
什么是字面量?
eg. int a = 10;
声明一个变量a,初始化为10,此时a就不代表字母a了,而是作为一个变量的名字。当我们引用a时,实际上拿到的值是10。
如果a是带引号的'a',那么它就不是一个变量,而是代表a这个字母本身,这就是字面量。所以字面量没有引申含义,就是我们看到的数据本身(不代表变量)。
<bean id="book2" class="com.test.demo1.di.Book">
//此时,bname和Spring都不代表任何变量,只是表示字符串本身
<constructor-arg name="bname" value="Spring"> </constructor-arg>
<constructor-arg name="author" value="hwh"> </constructor-arg>
</bean>
- 空值
如果某个bean中的一个属性为空值,如何进行设置?不能写value = "null"。如图所示,在<constructir-arg>的属性中只指定name值,在标签中添加<null/>。
<bean id="book3" class="com.test.demo1.di.Book">
<constructor-arg name="bname" value="Mybatis"> </constructor-arg>
<constructor-arg name="author">
<null/>
</constructor-arg>
</bean>
- xml实体
"<"小于号在XML文档中用来定义标签的开始,不能随意使用。
解决方案:使用XML实体代替。
<bean id="book3" class="com.test.demo1.di.Book">
<constructor-arg name="bname" value="Mybatis"> </constructor-arg>
<!--<表示< >表示> -->
<constructor-arg name="author" value="<>"> </constructor-arg>
</bean>
- CDATA节(另一个解决方案)
CDATA表示纯文本数据,当XML解析器看到CDATA节就知道这里是纯文本,就不会当作XML标签属性来解析。
<bean id="book3" class="com.test.demo1.di.Book">
<constructor-arg name="bname" value="Mybatis"> </constructor-arg>
<constructor-arg name="author">
<!-- <![CDATA[]]> 在[]中书写你的文本-->
<value><![CDATA[a < b]]></value>
</constructor-arg>
</bean>
(6)实验五:为对象类属性赋值
首先,先创建一个新的文件夹,添加两个类:员工类Emp和部门类Dept:
并完成这两个类属性的配置:
package com.test.demo1.ditest;
//部门类
public class Dept {
private String Dname;
//setter
......
public void info(){
System.out.println("部门名称: " + Dname);
}
}
package com.test.demo1.ditest;
//员工类
public class Emp {
private String Ename;
private Integer age;
//创建另一个类的对象
Dept dept;
//setter
......
public void work(){
dept.info();
System.out.println("Emp is working");
}
}
记得如果使用setter方式注入,要添加set方法。
新建test.xml来并进行配置:
方式一:引用外部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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!-- 第一种方式:引入外部bean
1.创建两个类对象dept和emp
2.在emp的bean标签里,使用property引入dept的bean-->
<!--部门类-->
<bean id="dept" class="com.test.demo1.ditest.Dept">
<property name="dname" value="安保部"> </property>
</bean>
<!--员工类-->
<bean id="emp" class="com.test.demo1.ditest.Emp">
<!--普通属性注入-->
<property name="ename" value="tom"> </property>
<property name="age" value="40"> </property>
<!--注入对象类型属性-->
<property name="dept" ref="dept"> </property>
</bean>
</beans>
如图,首先创建dept类的bean,再将其作为一个成员变量储存再emp对象中。
注意:如何区别deft这个值是对象还是普通的String值?使用ref=xxx来表示传入的值是类对象。
接下来创建一个测试类Test5进行测试:
public class Test5 {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("test.xml");
//员工对象
Emp emp = context.getBean("emp", Emp.class);
emp.work();
}
}
执行效果:
方法二:内部bean
<!--第二种方式 内部bean注入-->
<bean id="emp2" class="com.test.demo1.ditest.Emp">
<property name="ename" value="jack"> </property>
<property name="age" value="30"> </property>
<property name="dept">
<!--在emp2的dept属性值下,配置一个bean-->
<bean id="dept2" class="com.test.demo1.ditest.Dept">
<property name="dname" value="财务部"> </property>
</bean>
</property>
</bean>
方法三:级联赋值
<!--第三种方式 级联赋值-->
<bean id="dept3" class="com.test.demo1.ditest.Dept"> </bean>
<bean id="emp3" class="com.test.demo1.ditest.Emp">
<property name="ename" value="jerry"> </property>
<property name="age" value="34"> </property>
<property name="dept" ref="dept3"> </property>
<property name="dept.dname" value="技术研发部"> </property>
</bean>
注意:使用这种方法配置时,应该在Emp类中创建getDept方法,否则Bean无法通过get方法获取到emp对象中的dept属性,自然也无法为这个属性进行赋值(级联赋值)。对于一般的属性(不是类对象的),Bean直接调用set方法就可以对它们进行赋值。
简单地说,如果对类赋值,可以进行级联赋值,但是要通过get方法先获取这个成员类的对象。
(7)实验六:数组属性赋值
在类中,属性可能是数组,拥有多个不同的值,接下来学习bean如何给这些属性赋值。
首先在Emp类中新增int[]类型的属性hobbies和set方法:
private String[] hobbies;
public void setHobbies(int[] hobbies) {
this.hobbies = hobbies;
}
//print()方法用于观察配置情况
public void pring(){
for (String hobby : this.hobbies){
System.out.println(hobby);
}
}
然后再配置bean-diarray.xml文件:
<bean id="emp" class="com.test.demo1.ditest.Emp">
<!--普通属性-->
<property name="ename" value="张三"> </property>
<property name="age" value="31"> </property>
<!--对象类型属性-->
<property name="dept">
<!--内部bean-->
<bean id="dept1" class="com.test.demo1.ditest.Dept">
<property name="dname" value="技术部"> </property>
</bean>
</property>
<!--数组类型属性-->
<property name="hobbies">
<array>
<value>抽烟</value>
<value>喝酒</value>
<value>烫头</value>
</array>
</property>
</bean>
注意:配置数组的各个元素时,在property标签下设置array标签并分别赋值即可。
(8)实验七:为集合类型属性赋值
为List属性赋值
在Dept类中添加List<Emp>类型的属性,以及get和set方法,并设置输出的函数:
private List<Emp> employees;
//getter and setter
......
public void info(){
System.out.println("部门名称: " + Dname);
for (Emp emp : employees){
System.out.println(emp.toString());
}
}
<!--创建三个Emp类的bean-->
<bean id="emp1" class="com.test.demo1.ditest.Emp">
<property name="ename" value="张三"> </property>
<property name="age" value="23"> </property>
</bean>
<bean id="emp2" class="com.test.demo1.ditest.Emp">
<property name="ename" value="李四"> </property>
<property name="age" value="33"> </property>
</bean>
<bean id="emp3" class="com.test.demo1.ditest.Emp">
<property name="ename" value="王五"> </property>
<property name="age" value="43"> </property>
</bean>
<!--数组类型属性-->
<bean id="dept1" class="com.test.demo1.ditest.Dept">
<property name="dname" value="技术部"> </property>
<property name="employees">
<list>
<ref bean="emp1"/>
<ref bean="emp2"/>
<ref bean="emp3"/>
</list>
</property>
</bean>
与数组的赋值很相似,List容器赋值所用的标签是<List>,值得注意的是,你为它赋的值是自定义类,索引<List>内部的标签应该使用<ref bean="xxx">而非<value="xxx">。
为Map属性赋值
现在假设我们拥有两个类:Student和Teacher,对应着师生关系,一门课程对应着一个老师。
创建Student类,其中拥有一个Map类型的属性。
package com.test.demo1.dimap;
import java.util.Map;
public class Student {
private int sid;
private String snmae;
private Map<String, Teacher> teacherMap;
public void run(){
System.out.println("Student name: " + snmae + " Student id: " + sid);
System.out.println(teacherMap);
}
//getter and setter
.......
}
编写xml文件来创建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-3.0.xsd">
<!--
1.创建两个类的对象
2.注入普通类型的属性
3.在学生bean中注入Map类型的属性-->
<bean id="teacher" class="com.test.demo1.dimap.Teacher">
<!--注入普通类型的属性-->
<property name="teacherId" value="100"> </property>
<property name="teacherName" value="王老师"> </property>
</bean>
<bean id="student" class="com.test.demo1.dimap.Student">
<!--注入普通类型的属性-->
<property name="sid" value="001"> </property>
<property name="sname" value="张三"> </property>
<property name="teacherMap">
<!--说明: 首先,使用<map>标签注入map类型的值
然后在一个<entry>标签中设置一个键值对
<key>中表示建的值,在<value>标签中为这个键进行赋值
<key>外是value(值)的值,由于它是Teacher类型,使用<ref>标签赋值-->
<map>
<entry>
<key>
<value>10010</value>
</key>
<ref bean="teacher"/>
</entry>
</map>
</property>
</bean>
</beans>
引用集合类型bean
现在在Student类中新增一种属性List<Lesson>,以表示该学生要上的课,其中Lesson是自定义类
private List<Lesson> lessonList;
配置xml文件:
<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-3.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd">
<!--
1.创建三个对象
2.注入普通类型属性
3.使用util:类型 定义
4.在学生的bean引入util:类型定义bean,完成list、map类型注入-->
<bean id="lesson" class="com.test.demo1.dimap.Lesson">
<property name="lessonName" value="java开发"> </property>
</bean>
<bean id="lesson2" class="com.test.demo1.dimap.Lesson">
<property name="lessonName" value="前端开发"> </property>
</bean>
<bean id="teacher" class="com.test.demo1.dimap.Teacher">
<property name="teacherName" value="王老师"> </property>
<property name="teacherId" value="00001"> </property>
</bean>
<bean id="teacher2" class="com.test.demo1.dimap.Teacher">
<property name="teacherName" value="刘老师"> </property>
<property name="teacherId" value="00002"> </property>
</bean>
<bean id="student" class="com.test.demo1.dimap.Student">
<property name="sid" value="10000"> </property>
<property name="sname" value="张三"> </property>
<!--注入list、map类型-->
<property name="lessonList" ref="lessonList"> </property>
<property name="teacherMap" ref="teacherMap"> </property>
</bean>
<!--这是一个名为lessonList,储存两个Lesson类对象的List数组-->
<util:list id="lessonList">
<ref bean="lesson"/>
<ref bean="lesson2"/>
</util:list>
<!--这是一个名为teacherMap,储存一个<String,Teacher>键值对的Map-->
<util:map id="teacherMap">
<entry>
<key>
<value>10010</value>
</key>
<ref bean="teacher"/>
</entry>
</util:map>
</beans>
3.值得注意的问题:
(1)在使用setter方式进行注入的时候,你通常使用<property>的name属性来指定你要进行赋值的成员变量,Bean会调用相应的setter方法对其进行赋值。此时,需要注意:你的setter方法应该被命名为:set+变量名,否则这个方法将无法被识别。
(2)当你使用接口类型的值来创建bean时,应该确保它的实现类只有一个。
(3)Ioc容器如何创建和管理bean。
(4)使用<util>标签时应当在在<beans>标签中添加:
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation=""中添加:
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
在后续的内容中将继续介绍Ioc容器管理bean的方法,全注解开发以及AOP相关概念。