一般来说我们创建对象都是通过new的方式来创建对象的示例的,这可以说是主动创建。
在三层架构中通过这种方式创建对象层与层之间耦合性高,UserDao依赖UserDaoImpl,当实现类改变时,我们就要重新编写代码创建该实现类。
//UserDao依赖UserDaoImpl
UserDao userDao=new UserDaoImpl();
使用IOC(控制反转)可以降低程序的耦合
(解耦),即降低代码中的依赖关系。
IOC的核心思想是使用工厂模式通过读取配置文件,得到全限定类名,通过反射创建对象。再进一步优化,提供一个容器对象,使用静态代码块,把所有创建的对象存放在容器中。
读取配置文件 (beans.properties)
userDao=com.mycode.dao.impl.UserDaoImpl
工厂对象(BeanFactory )只要一读取配置文件,就会创建配置文件配置了的所有对象,并存放在容器中
public class BeanFactory {
//创建一个Properties,存储配置文件信息
private static Properties props = new Properties();
//存储所有需要创建对象实例的容器对象
private static Map<String,Object> beans = new HashMap<String,Object>();
static {
try {
//加载配置文件数据,获得文件数据的IO流对象
InputStream is = BeanFactory.class.getClassLoader().getResourceAsStream("beans.properties");
//将IO流对象载入到props中
props.load(is);
//获取所有key
for (Object key : props.keySet()) {
//循环key,根据key获取类的全限定名
String clazz = (String) props.get(key);
//创建对象实例
Object instance = Class.forName(clazz).newInstance();
//将对象实例存入到Map
beans.put(key.toString(),instance);
}
} catch (Exception e) {
e.printStackTrace();
}
}
//根据key获取容器中存放的实例对象
public static <T> T getBean(String key){
return (T) beans.get(key);
}
}
这时我们要创建对象则可以直接通过工厂对象提供的方法来获取相应实例对象,这种被动接收获取对象的思想就是IOC(控制反转),由原来通过new主动创建对象变为通过工厂被动接受对象(把创建对象的控制权给第三方)。引申到Spring,IOC是spring框架两大核心之一,使用Spring框架即把创建对象的控制权给了spring框架,让它帮我们创建对象,给SpringIOC容器管理,我们只要在配置文件中配置要创建的对象即可.(通过 Spring 提供的 IoC 容器,可以将对象间的依赖关系交由 Spring 进行控制,避免硬编码所造成的过度程序耦合)。
//UserDao userDao=new UserDaoImpl();
UserDao userDao= BeanFactory.getBean("userDao");
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-contextartifactId>
<version>5.0.2.RELEASEversion>
dependency>
dependencies>
在类的根路径下创建一个任意名称的xml文件(beans.xml),让spring管理资源,在配置文件中配置service和dao
<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.mycode.service.impl.UserServiceImpl">bean>
<bean id="userDao" class="com.mycode.dao.impl.UserDaoImpl">bean>
beans>
测试 通过Spring提供的工厂对象的方法获取相应示例对象
@Test
public void SpringIOCTest(){
//使用ApplicationContext接口(Spring容器接口),指定要加载的核心配置文件,获取spring容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
//根据bean的id获取对象
UserService userService = (UserService) applicationContext.getBean("userService");
System.out.println(userService);
//com.mycode.service.impl.UserServiceImpl@1877ab81
}
BeanFactory是Spring容器中的顶层接口
ApplicationContext是BeanFactory的子接口
BeanFactory和ApplicationContext的区别:
BeanFactory
:按需创建,即什么使用什么时候创建对象
ApplicationContext
:读取配置文件,就会创建对象
ApplicationContext接口的实现类:
ClassPathXmlApplicationContext
:从类的根路径下加载配置文件FileSystemXmlApplicationContext
: 从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。AnnotationConfigApplicationContext
:当我们使用注解配置容器对象时,使用此类来创建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.mycode.service.impl.UserServiceImpl" scope="singleton" init-method="initMethod" destroy-method="destroyMethod" >bean>
beans>
配置了init-method
属性与destroy-method
属性要在对应类中增加相应
public class UserServiceImpl implements UserService {
//初始化时调用
public void initMethod(){
System.out.println("UserServiceImpl已经初始化了");
}
//销毁时调用
public void destroyMethod(){
System.out.println("UserServiceImpl 要被销毁了");
}
}
①构造器实例化bean(使用默认无参构造函数)
<bean id="userService" class="com.mycode.service.impl.UserServiceImpl" >bean>
②spring管理静态工厂(使用静态工厂的方法创建对象)
public class StaticFactory {
//创建对象的静态方法
public static UserServiceImpl createUserService(){
return new UserServiceImpl();
}
}
<bean id="userService"
class="com.mycode.factory.StaticFactory"
factory-method="createUserService">bean>
③spring管理实例工厂(使用实例工厂的方法创建对象)
public class BeanFactory {
//创建对象的方法
public UserServiceImpl createUserService(){
return new UserServiceImpl();
}
}
<bean id="instancFactory" class="com.mycode.factory.BeanFactory">bean>
<bean id="accountService"
factory-bean="instancFactory"
factory-method="createUserService">bean>
依赖注入(DI),spring框架核心IoC的具体实现,给Bean的属性赋值(依赖被注入到对象中)
public class UserServiceImpl implements UserService {
private String name;
private Integer age;
private Date birthday;
//类中提供需要注入成员的set方法
public void setName(String name) {
System.out.println("调用了set方法,传入的值是:"+name);
this.name = name;
}
public void setAge(Integer age) {
System.out.println("调用了set方法,传入的值是:"+age);
this.age = age;
}
public void setBirthday(Date birthday) {
System.out.println("调用了set方法,传入的值是:"+birthday);
this.birthday = birthday;
}
@Override
public String toString() {
return "UserServiceImpl{" +
"name='" + name + '\'' +
", age=" + age +
", birthday=" + birthday +
'}';
}
}
xml配置
<bean id="userService" class="com.mycode.service.impl.UserServiceImpl">
<property name="age" value="18">property>
<property name="name" value="张三三">property>
<property name="birthday" ref="userdate">property>
bean>
<bean id="userdate" class="java.util.Date">bean>
测试:
public class IOCTest {
@Test
public void SpringIOCTest(){
//使用ApplicationContext接口(Spring容器接口),指定要加载的核心配置文件,获取spring容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
//根据bean的id获取对象
UserService userService1 = (UserService) applicationContext.getBean("userService");
System.out.println(userService1);
}
}
<bean id="userService3" class="com.mycode.service.impl.UserServiceImpl"
p:name="李四四"
p:age="19"
p:birthday-ref="userdate"
/>
<bean id="userdate" class="java.util.Date">bean>
使用类中的相应构造函数给成员变量赋值。赋值的操作是通过配置的方式,让spring框架来帮我们注入。
public class UserServiceImpl implements UserService {
private String name;
private Integer age;
private Date birthday;
public UserServiceImpl(String name, Integer age, Date birthday) {
System.out.println("构造函数注入");
this.name = name;
this.age = age;
this.birthday = birthday;
}
}
①根据构造函数中参数的名字注入
<bean id="userService4" class="com.mycode.service.impl.UserServiceImpl">
<constructor-arg name="name" value="王五五_根据构造函数中参数的名字注入"/>
<constructor-arg name="age" value="21"/>
<constructor-arg name="birthday" ref="userdate"/>
bean>
②根据下标注入
<bean id="userService5" class="com.mycode.service.impl.UserServiceImpl">
<constructor-arg index="0" value="王五五_根据下标注入"/>
<constructor-arg index="1" value="20"/>
<constructor-arg index="2" ref="userdate"/>
bean>
③根据构造函数中参数的类型注入
<!--根据构造函数中参数的类型注入-->
<bean id="userService6" class="com.mycode.service.impl.UserServiceImpl">
<constructor-arg type="java.lang.String" value="王五五_根据构造函数中参数的类型注入" />
<constructor-arg type="java.lang.Integer" value="23" />
<constructor-arg type="java.util.Date" ref="userdate" />
</bean>
public class UserDaoImpl implements UserDao {
private String[] myStrs;
private List<String> myList;
private Set<String> mySet;
private Map<String,String> myMap;
private Properties myProps;
public void setMyStrs(String[] myStrs) {
this.myStrs = myStrs;
}
public void setMyList(List<String> myList) {
this.myList = myList;
}
public void setMySet(Set<String> mySet) {
this.mySet = mySet;
}
public void setMyMap(Map<String, String> myMap) {
this.myMap = myMap;
}
public void setMyProps(Properties myProps) {
this.myProps = myProps;
}
@Override
public String toString() {
return "UserDaoImpl{" +
"myStrs=" + Arrays.toString(myStrs) +
", myList=" + myList +
", mySet=" + mySet +
", myMap=" + myMap +
", myProps=" + myProps +
'}';
}
}
xml配置
<bean id="userDaoImpl" class="com.mycode.dao.impl.UserDaoImpl">
<property name="myStrs">
<array>
<value>array1value>
<value>array2value>
<value>array3value>
array>
property>
<property name="myList">
<list>
<value>list1value>
<value>list2value>
<value>list3value>
list>
property>
<property name="mySet">
<set>
<value>set1value>
<value>set2value>
<value>set3value>
set>
property>
<property name="myMap">
<map>
<entry key="map1" value="map1value" />
<entry key="map2" value="map2value" />
<entry key="map3" value="map3value" />
map>
property>
<property name="myProps">
<props>
<prop key="prop1">prop1valueprop>
<prop key="prop2">prop2valueprop>
<prop key="prop3">prop3valueprop>
props>
property>
bean>
测试:
@Test
public void SpringIOCTest(){
//使用ApplicationContext接口(Spring容器接口),指定要加载的核心配置文件,获取spring容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
//根据bean的id获取对象
UserDao UserDao = (UserDao) applicationContext.getBean("userDaoImpl");
System.out.println(UserDao);
}
}
把配置文件与Java关联起来可以这样理解:
创建Bean
<bean id="usertService" class=" com.mycode.service.impl.UsertServiceImpl">
UsertService usertService= new com.mycode.service.impl.UsertServiceImpl()
set方法注入
<property name="name" value="张三三" />
usertService.setName("张三三")
构造函数注入
<constructor-arg name="name" value="张三三" />
new UsertServiceImpl("张三三");