目录
Ioc是什么
IOC(inversion of control)控制反转,Spring反向控制应用程序所需要的外部资源,将控制的外部资源都放在spring容器中(ioc容器),开发者可以不用去管对象的创建和释放,全部都由spring代为完成。由我们之前主动去创建资源变为被动的由spring分配资源
简单的入门案例
- 第一步 配置spring的maven坐标
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
</dependencies>
- 第二步 编写我们的服务层对象,这里是创建了一个UserService接口和一个UserServiceImpl实现类
package com.jjs.service;
interface UserService {
public void save();
}
package com.jjs.service;
public class UserServiceImpl implements UserService {
public void save() {
System.out.println("已保存");
}
}
- 第三步 编写spring配置文件applicationContext.xml,将我们的服务层对象作为spring的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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--创建spring控制的资源-->
<!--id可以随便取,但是为了规范最好和接口名一致-->
<!--我们要创建的是实现类,class写的是实现了的包带类名-->
<bean id="userService" class="com.jjs.service.UserServiceImpl"></bean>
</beans>
- 第四步 编写业务层实现类,加载配置文件,获取spring管理的资源
import com.jjs.service.UserServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class UserAPP {
@Autowired
UserServiceImpl userService;
public static void main(String[] args) {
//加载配置文件
ApplicationContext applicationContext =new ClassPathXmlApplicationContext("applicationContext.xml");
//获取资源
UserServiceImpl userService = (UserServiceImpl)applicationContext.getBean("userService");
userService.save();
}
}
- 整个文件结构:
IOC配置
bean
- 名称:bean
- 类型:标签
- 归属:beans
- 作用:定义spring中的资源,受此标签定义的资源将受spring的控制
- 范例:
<bean id="userService" name="user1,user2" class="com.jjs.service.UserServiceImpl"></bean>
- 其中id为bean的唯一标识,可以通过id获取bean,可以随意取,一般取为接口名称
- class 为bean的类型
- name 为bean名称,可以通过name获取bean,配合多人使用的时候取别名
bean的scop属性
bean的scop属性主要有两种
-
singleton:标记scop为singleton的bean为单例bean,即便显示创建了多个对象,这些对象都同一个,对象会在spring容器加载之后就创建出来
例如:
配置bean的scope为singleton
<bean id="userService" scope="singleton" class="com.jjs.service.UserServiceImpl"></bean>
通过spring创建多个对象,打印地址并比较
import com.jjs.service.UserServiceImpl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class UserAPP { @Autowired UserServiceImpl userService; public static void main(String[] args) { //加载配置文件 ApplicationContext applicationContext =new ClassPathXmlApplicationContext("applicationContext.xml"); //获取资源 UserServiceImpl userService1 = (UserServiceImpl)applicationContext.getBean("userService"); UserServiceImpl userService2 = (UserServiceImpl)applicationContext.getBean("userService"); System.out.println(userService1); System.out.println(userService2); System.out.println(userService1==userService2); } }
-
prototype:标记scope为prototype的bean为非单例,创建了几个对象就会有几个不同的对象,创建对象属于惰性创建,只有在显示创建的时候spring才会创建出对象
例如:
配置bean的scope为prototype
<bean id="userService" scope="prototype" class="com.jjs.service.UserServiceImpl"></bean>
通过spring创建多个对象,打印地址并比较
import com.jjs.service.UserServiceImpl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class UserAPP { @Autowired UserServiceImpl userService; public static void main(String[] args) { //加载配置文件 ApplicationContext applicationContext =new ClassPathXmlApplicationContext("applicationContext.xml"); //获取资源 UserServiceImpl userService1 = (UserServiceImpl)applicationContext.getBean("userService"); UserServiceImpl userService2 = (UserServiceImpl)applicationContext.getBean("userService"); System.out.println(userService1); System.out.println(userService2); System.out.println(userService1==userService2); } }
bean的初始化方法以及销毁方法属性
初始化:通过标签init-method可以将指定方法作为bean的初始化方法:
使用方法:先在类中创建一个方法作为初始化方法,当bean载入容器之后会调用方法
package com.jjs.service; public class UserServiceImpl implements UserService { public void save() { System.out.println("已保存"); } void init(){ System.out.println("初始化"); } }
在spring配置文件中声明初始化方法,init-method中填写方法名
<bean id="userService" scope="singleton" init-method="init" class="com.jjs.service.UserServiceImpl"></bean>
值得说明的是,单例bean中的初始化方法只会调用一次,因为都是同一个对象
二多例bean中的初始化方法会根据要获取对象的数量调用。
销毁:通过标签destroy-method,当bean销毁的时候会调用销毁方法
使用方法与初始化基本一致,修改spring配置文件
<bean id="userService" scope="singleton" init-method="init" destroy-method="" class="com.jjs.service.UserServiceImpl"></bean>
值得说明的是,非单例的bean销毁不由spring管理,所以不能通过spring对单例bean进行销毁
工厂创建bean对象
静态工厂
- 先创建一个工厂类,用于返回UserServiceIml对象,方法为静态方法
package com.jjs;
import com.jjs.service.UserServiceImpl;
public class BeanFactory {
public static UserServiceImpl getService(){
return new UserServiceImpl();
}
}
- 在spring配置文件中将工厂类声明为bean,factory-method放的是返回service对象的方法
<bean id="userService" class="com.jjs.BeanFactory" factory-method="getService"></bean>
- 然后通过id获取bean对象即可
动态工厂
- 先创建一个工厂类,用于返回UserServiceIml对象,此时不是静态方法了
package com.jjs;
import com.jjs.service.UserServiceImpl;
public class BeanFactory {
public UserServiceImpl getService(){
return new UserServiceImpl();
}
}
- 编写spring的配置文件,先创建一个工厂类的bean,然后再创建一个用于获取资源的bean,再这个bean中要表明工厂类的bean(factory-bean),与工厂类的方法(factory-method)。
<bean id="beanFactory" class="com.jjs.BeanFactory"></bean>
<bean id="userService" factory-bean="beanFactory" factory-method="getService"></bean>
DI
DI也叫依赖注入,应用程序运行依赖的资源由spring提供,资源进入程序的方式成为注入
set注入
现在要将UserDao注入到UserService中 过程如下:
- 1、首先创建一个UserDao的实现类继承UserDao接口
package com.jjs.dao;
public interface UserDao {
void save();
}
package com.jjs.dao;
public class UserDaoImpl implements UserDao {
public void save() {
System.out.println("dao已保存数据");
}
}
- 2、然后再UserServiceImpl中通过set注入UserDao
package com.jjs.service;
import com.jjs.dao.UserDao;
public class UserServiceImpl implements UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void save() {
userDao.save();
}
}
- 3、然后进入spring的配置文件中先将UserDaoImpl声明为bean
<bean id="useDao" class="com.jjs.dao.UserDaoImpl"></bean>
- 4、然后声明id为userdao的这个bean已经注入到了UserService中
<bean id="userService" scope="singleton" class="com.jjs.service.UserServiceImpl">
<property name="userDao" ref="useDao"></property>
</bean>
需要注意的是property标签中,ref中写的是需要注入的bean的id,而name是set方法中的名称
例如,这里就是userDao,首字符小写
如果我们要注入的是基本类型或者字符串、Integer等包装类呢?如下代码,一样也是先添加set方法
package com.jjs.service;
import com.jjs.dao.UserDao;
public class UserServiceImpl implements UserService {
private int a ;
public void setA(int a) {
this.a = a;
}
public void save() {
System.out.println(a);
}
}
然后在spring配置文件中声明,但是这里不使用ref了,ref是给引用对象使用的,在这里基本类型,字符串,包装类都当作非引用类型使用,通过value赋值
<bean id="userService" scope="singleton" class="com.jjs.service.UserServiceImpl">
<property name="a" value="10"></property>
</bean>
构造器注入
首先先在UserServiceImpl中创建要注入的数据或者对象声明为私有成员变量,a为基本类型,userDao为引用类型
package com.jjs.service;
import com.jjs.dao.UserDao;
public class UserServiceImpl implements UserService {
private UserDao userDao;
private int a ;
//创建构造方法
public UserServiceImpl(UserDao userDao, int a) {
this.userDao = userDao;
this.a = a;
}
public void save() {
userDao.save();
System.out.println(a);
}
}
然后修改配置文件,在useService中声明constructor-arg ,将构造方法的成员变量声明,对于引用类型使用ref,对于基本类型,字符串、包装类使用value赋值。
此外constructor-arg标签还有两个属性,一个是type,和index,这两个属性的作用和name是一样的,都是为了标明在为哪个参数赋值,但是由于很鸡肋,就不多说了
<bean id="userService" name="service" class="com.jjs.service.UserServiceImpl" scope="singleton">
<constructor-arg name="userDao" ref="useDao"></constructor-arg>
<constructor-arg name="a" value="10"></constructor-arg>
</bean>
<bean id="useDao" class="com.jjs.dao.UserDaoImpl"></bean>
集合的注入
这里只摆上数组、list集合、map、properties、set 这五种集合的xml的配置方法,注入方式使用的是set方法注入
<bean id="useDao" class="com.jjs.dao.UserDaoImpl">
<!--数组-->
<property name="array">
<array>
<value>1100</value>
<value>1200</value>
</array>
</property>
<!--list集合-->
<property name="list">
<list>
<value>20</value>
<value>张三</value>
</list>
</property>
<!--map-->
<property name="map">
<map>
<entry key="name" value="张三"></entry>
<entry key="age" value="18"></entry>
</map>
</property>
<!--properties-->
<property name="properties">
<props>
<prop key="username">王五</prop>
<prop key="password">123654</prop>
</props>
</property>
<!--Set集合-->
<property name="set">
<set>
<value>50</value>
<value>100</value>
</set>
</property>
</bean>
读取外部properties文件
第一步:准备一个外部properties文件
第二步:先创建接收数据的变量
package com.jjs.dao;
public class UserDaoImpl implements UserDao {
private String username;
private String password;
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public void save() {
System.out.println("用户名:"+username+" 密码:"+password);
}
}
第三步:配置xml的context命名空间、加载配置文件并获取数据
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!--加载配置文件-->
<context:property-placeholder location="uu.properties"></context:property-placeholder>
<!--获取数据-->
<bean id="useDao" class="com.jjs.dao.UserDaoImpl">
<property name="username" value="${username}"></property>
<property name="password" value="${password}"></property>
</bean>
</beans>
团队配置Import
多人共同研发的时候可能会有多个xml配置文件的产生,或者多个模块也会可能出现多个xml配置,
这时候可以选定一个主配置,然后将从配置导入到主配置中
<import resource=“config.xml"/>
bean覆盖问题
Spring容器中的bean定义冲突问题
-
同id的bean,后定义的覆盖先定义的(位于不同配置文件中)
-
导入配置文件可以理解为将导入的配置文件复制粘贴到对应位置
-
导入配置文件的顺序与位置不同可能会导致最终程序运行结果不同