目录
IOC概念
IOC思想
Spring管理对象
集成依赖
spring的配置文件(Applicationcontext.xml)
创建实体类User
Spring对Bean的实例化方式
基于配置形式
1.通过无参构造函数实例化
2.通过静态工厂方式实例化
3.通过普通工厂方式实例化Bean
基于注解形式
Spring中依赖注入方式
基于xml配置实现依赖注入
1.有参构造函数
2.setter方式注入
基于注解形式注入依赖
IOC概念
IOC(Inversion Of Control) 控制反转,spring中提供了IOC容器,对象的创建交给外部容器完成,这个就是控制反转,spring中使用控制反转来实现对象在不同程序中的使用,容器存在的必要性就是管理对象。
通过购买图书的场景来分析使用java实例,使用相关对象是需要new创建并持有,缺点:
- 实例化一个组件比较困难,例如:bookservice和userservice要创建DataSource实例,实际上需要读取配置,即config,通过config实例才创建出DataSourceservice,没有必要bookservice和UserService分别创建DataSourceservice,可以共享ataSourceservice,谁负责来创建DataSourceservice实例,也不好处理。
- 很多组件需要销毁来释放资源,例如datasouce,如果采用共享,如何确保使用方能正确的销毁。
- 随着映入到的业务组件越来越多,对组件的共享管理也就越来越复杂。
可以提供容器,来管理所有的组件(对象),所有的对象的创建,销毁也好都交给容器,谁需要使用对象,在容器中获取该对象的使用权,哪一个对象需要,就将使用的对象注入到该对象中,对于使用方而言,只需要关注使用即可,对象的生命周期的管理就交给IOC容器进行管理,IOC容器负责实例化所有的组件,需要进行依赖的对象都交给容器管理。
IOC思想
对象的创建交给外部容器完成,这个就是控制反转,spring中使用控制反转来实现对象在不同程序中的使用,控制反转解决对象处理的问题,将对象交给容器进行创建。
依赖注入(DI):dendendency injection
对象与对象之间的依赖问题即依赖注入 AOP->DI
Spring使用依赖注入来实现对象之间的依赖关系
在对象创建之后,对象的关系处理就是依赖注入
无论对象的创建,处理对象之间的依赖关系,对象创建的时间还有对象创建的数量,都只需在spring的IOC容器上配置对象的信息即可
IOC最简单的方式就是采用XML方式来进行管理:
<beans>
数据源交给容器管理
<bean id="datasource" class="DataSourceService" >
<bean id="userservice" class="UserService">
<property name="datasource" ref="datasource"/>
</bean>
...
</beans>
在BuyService中使用获取用户信息,通过容器:context 提供的方式来获取用户的实例context.getName("userservice"),就会在容器中找id=“userservice”,进而获取该对象实例进行使用。
Spring IOC容器中接口的继承关系,ApplicationContext是BeanFactory子接口实现之一,
BeanFactory是IOC容器定义的最底层的接口。
ApplicationContext是高级实现之一,对BeanFactory的接口做了很多的扩展,大部分情况ApplicationContext使用。
ApplicationContext接口常见的实现类:
- ClassPathXmlApplicationContext(读取classpath中的资源)
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationcontext.xml");
- FileSystemXmlApplicationContext(读取系统指定的资源文件)
FileSystemXmlApplicationContext applicationContext1 = new FileSystemXmlApplicationContext("d://test.xml");
- XmlWebApplicationContext(读取web环境下的资源文件)
XmlWebApplicationContext applicationcontext = new XmlWebApplicationContext();
Spring管理对象
集成依赖
添加依赖的时候要注意版本问题,我的IDEA为2020.2,maven为3.6.3,因此spring的依赖添加的是5.2.8版本
<?xml version="1.0" encoding="UTF-8"?>
<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.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>springTest</artifactId>
<version>1.0-SNAPSHOT</version>
<name>springTest</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.2.8.RELEASE</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
</dependencies>
<build>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
<!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.7.1</version>
</plugin>
<plugin>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.0.0</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
spring的配置文件(Applicationcontext.xml)
需要容器管理的对象在该xml文件中进行配置。
声明如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
</beans>
创建实体类User
public class User {
private Integer id;
private String name;
}
使用bean标签类管理对象 id:属性标识对象,不可重复,class属性代表要管理的对象的全路径
<bean id="user" class="org.example.bean.User"/>
通过容器获取对象
//获取IOC容器,通过读取classpath路径下的spring的配置文件
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationcontext.xml");
//在IOC容器获取需要的对象实例
User user = (User) applicationContext.getBean("user");
System.out.println(user);
原理:获取User对象时,new User() ,将对象的全路径配置在容器中,容器创建对象通过->反射 ->IOC容器底层拿到类的全路径,然后可以通过反射的技术来创建实例。
Spring对Bean的实例化方式
基于配置形式
1.通过无参构造函数实例化
<!--通过无参构造来实例化Bean-->
<bean id="user" class="org.example.bean.User" scope="singleton"/>
spring容器对上面的类的实例化采用的是无参构造函数来实例化bean
注:如果不指定无参构造函数,会生成一个默认的无参构造函数,如果指定了有参构造函数,(就不会生成默认的无参构造函数),就必须显性的写明一个无参构造函数,否则会报错
2.通过静态工厂方式实例化
首先创建静态工厂类来获取User对象
public class staticFactory {
public static User getUser(){
return new User();
}
}
spring容器管理配置如下:
<!--通过静态工厂类获取User对象-->
<bean id="user1" class="org.example.factory.staticFactory" factory-method="getUser"></bean>
静态工厂方式获取对象是 class属性工厂类的全路径,factory-method属性指在工厂类中指定的方法来获取需要的对象
获取结果:
3.通过普通工厂方式实例化Bean
创建一个普通工厂来管理user
/**
* 普通工厂
*/
public class commonFactory {
public User getUser(){
return new User();
}
}
spring容器配置如下:
<!--通过普通工厂类获取User对象-->
<!--先获取工厂实例-->
<bean id="factory" class="org.example.factory.commonFactory"/>
<!--指定工厂类的方法-->
<bean id="user2" factory-bean="factory" factory-method="getUser"/>
首先需要获取工厂实例,通过普通工厂实例获取user对象时,factory-bean属性指的是容器管理的工厂对象,factory-method属性指的是工厂中获取user对象的方法
获取结果:
基于注解形式
基于注解形式比xml方式更加简单,开发更快。
创建了一个新的配置文件spring-config.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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!--开启扫描注解:指定的包路径下的类,会扫描的到类的方法,属性上-->
<context:component-scan base-package="org.example.bean"/>
<!--只扫描类中属性的注解(不建议使用)-->
<!--<context:annotation-config></context:annotation-config>-->
</beans>
只需在需要管理的类上添加注解即可
@Component("user")
//注解中user=XML配置形式中bean id属性
public class User {
private Integer id;
private String name;
}
这里使用的注解@Component
@Component:通用注解
@Repository:对DAO层实现类进行标注
@Service:对Service层业务逻辑层进行标注
@Controller:对Controller的实现类进行标注
四个注解可以通用,功能一样,可以互换,使用不同注解主要是区分被注解的类处在不同的业务层,使逻辑更加清晰。
Spring中依赖注入方式
bean的实例化说明的是IOC的问题,创建对象之后对象中的属性如何赋值问题,即依赖注入问题(DI),主要有两种依赖注入方式:
- xml配置方式进行依赖注入
- 注解形式进行依赖注入
基于xml配置实现依赖注入
1.有参构造函数
该种注入方式要求对应的实体类中必须提供有参构造函数,如果没有构造函数,在配置文件中就会报错,无法通过编译。
public class User {
private Integer id;
private String name;
/**
* 有参构造函数
*/
public User(Integer id, String name) {
this.id = id;
this.name = name;
}
spring容器的配置如下:
<!--依赖注入:有参构造函数-->
<bean id="user3" class="org.example.bean.User">
<!--
注入属性
name属性:给定类的属性名称
value属性:对指定属性名称赋值 ,直接获取的是String类型的值
ref属性:对指定属性进行赋值 指定的是一个容器管理的对象
-->
<constructor-arg name="id" value="1"></constructor-arg>
<constructor-arg name="name" value="tom"></constructor-arg>
</bean>
通过有参构造函数完成依赖注入时,使用constructor-arg标签进行属性赋值,为方便查看,在实体类中改写toString函数进行打印
2.setter方式注入
该方式需要为User类的属性设置setter方法
public class User {
private Integer id;
private String name;
public User() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
容器中的配置信息如下:
<!--依赖注入:setter方式-->
<bean id="user4" class="org.example.bean.User">
<property name="id" value="2"></property>
<property name="name" value="jack"></property>
</bean>
setter方式的依赖注入:使用property标签进行赋值
赋值类型除了常见的String,Integer这种,还可以赋值给自定义类型,自定义类型无论是set方法还是有参构造均可,对自定义类型赋值时使用ref。
<bean id="user3" class="org.example.bean.User">
<constructor-arg name="book" ref="book"/>
</bean>
注入集合类型:数组、Set、List、Map的个形式集合都可以注入,处理集合存在以下标签:
以map类型为例,进行介绍
package org.example.bean;
import org.springframework.stereotype.Component;
import java.util.Map;
/**
* Description :
* Created by Resumebb
* Date :2021/4/2
*/
@Component("user")
public class User {
private Integer id;
private String name;
private Map<Integer, String> map;
public User(Integer id, String name) {
this.id = id;
this.name = name;
}
public User(){}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", map=" + map +
'}';
}
public Map<Integer, String> getMap() {
return map;
}
public void setMap(Map<Integer, String> map) {
this.map = map;
}
}
spring容器中的配置:
<bean id="user5" class="org.example.bean.User">
<property name="id" value="3"></property>
<property name="name" value="rose"></property>
<property name="map">
<map>
<entry key="1" value="a"></entry>
<entry key="2" value="b"></entry>
<entry key="3" value="c"></entry>
</map>
</property>
</bean>
基于注解形式注入依赖
@Value("3")//注入普通类型属性
@Resource //注入对象类型
@Autowired //注入对象类型,默认是按照类型注解
依赖的注入都是在类的属性上。
Resource与Autowired注解区别
Spring中,@Resource和@Autowired都是做bean的注入时使用。使用过程中,有时候@Resource 和 @Autowired可以替换使用;有时,则不可以。
- 共同点
@Resource和@Autowired都可以作为注入属性的修饰,在接口仅有单一实现类时,两个注解的修饰效果相同,可以互相替换,不影响使用。
- 不同点
@Resource是Java自己的注解,@Resource有两个属性是比较重要的,分是name和type;Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不指定name也不指定type属性,这时将通过反射机制使用byName自动注入策略。
@Autowired是spring的注解,是spring2.5版本引入的,Autowired只根据type进行注入,不会去匹配name。如果涉及到type无法辨别注入对象时,那需要依赖@Qualifier或@Primary注解一起来修饰。