spinrg重温--重新认识ioc

本文的主要讲解是关于spring里面的ioc发展以及对于ioc的理解
学习任何知识点都不应该是直接去扣它的细节,而是应该先去从脉络上去熟悉相关知识点。


什么是ioc?
从spring容器的创建到销毁,每个bean都会有自己独立的生命周期。而ioc容器主要是用于配置,定位,实力化这些个bean的相关信息。个人的认识里面,ioc更多的是一种思想,原先我们创建一个bean的时候,都需要手动的去对这个bean进行初始化,然后各种内部依赖的bean都需要额外去注入,这就会导致每个对象内部引入的对象信息都需要手动去完善和维护,代码耦合性也较高。而spring的ioc容器里面,将这部分的工作全部都封装在了其框架的内部,从而使得原先的对象引入从主动引入变为了被动引入,更加透明化,降低耦合程度。

ioc改变了什么?
主动式注入对象,引入某些对象字段的时候我们并不需要去关心这个对象是如何实现的,有些类似于响应式编程的味道。
主动地完善了依赖信息的注入,更加地降低了代码的耦合程度等。
在理解ioc容器的基本知识之后,推荐还可以去阅读下国外大牛对于ioc容器的理解博文,下边是对应的链接地址:
https://martinfowler.com/articles/injection.html
当然martinfowler在提出ioc和di的理解之前,已经有了spring容器的出现。

除了spring之外,还有哪些依赖注入的容器?
在martin大神的文章中还有介绍来同类型的这些容器注入管理框架,其中举例说明了spring容器,picocontainer框架等。
相关连的github地址如下所示:
https://github.com/picocontainer/picocontainer

Ioc和DI怎么理解?
下边我列举了一段维基百科上关于ioc的知识介绍:

In object-oriented programming, there are several basic techniques to implement inversion of control. These are:

Using a service locator pattern
Using dependency injection, for example
	Constructor injection
	Parameter injection
	Setter injection
	Interface injection
Using a contextualized lookup
Using template method design pattern
Using strategy design pattern
In an original article by Martin Fowler,[9] the first three different techniques are discussed. In a description about inversion of control types,[10] the last one is mentioned. Often the contextualized lookup will be accomplished using a service locator

ioc的实现策略有哪些?
在这段描述里面,我简单介绍下这几种实现ioc的策略分别是哪些:
第一种是服务定位模式
这种方式主要是javaee里定位具体bean的一种技术手段,例如说java内部的jndi技术,关于jndi方面的技术可以去自行阅读下以下链接内容:
https://www.cnblogs.com/zengda/p/4767036.html

第二类是依赖注入 DI
这类型方面的主要实现可以参考以下内容:

setter注入
构造器注入
静态工厂
动态工厂

这部分我整理了相应的代码来进行总结
1.什么是setter注入?

首先是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.xsd">

    <!--  这里面对于user对象需要设置无参数构造函数  -->
    <bean class="com.think.spring.bean.User" id="user" />

    <bean class="com.think.spring.dao.UserDaoImpl" id="userDao"></bean>

    <bean class="com.think.spring.service.UserServiceImpl" id="userService">
        <property name="userDao" ref="userDao"></property>
    </bean>
</beans>

然后是对应的容器调用部分:

package com.think.spring;

import com.think.spring.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @Author idea
 * @Date created in 2:36 下午 2020/4/19
 */
public class ApplicationTest {

    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
        UserService userService = (UserService) applicationContext.getBean("userService");
        System.out.println(userService.findOne());
    }
}

接下来是对应服务的实现具体细节方面,它们分别是:

package com.think.spring.service;

import com.think.spring.bean.User;
import com.think.spring.dao.UserDao;

/**
 * @Author idea
 * @Date created in 3:35 下午 2020/4/19
 */
public class UserServiceImpl implements UserService{

    private UserDao userDao;

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public User findOne() {
        return userDao.findOne();
    }
}


package com.think.spring.service;

import com.think.spring.bean.User;

/**
 * @Author idea
 * @Date created in 3:35 下午 2020/4/19
 */
public interface UserService {
    /**
     * find one obj
     * @return
     */
    User findOne();
}

package com.think.spring.dao;

import com.think.spring.bean.User;

/**
 * @Author idea
 * @Date created in 3:36 下午 2020/4/19
 */
public interface UserDao {

    /**
     * findOne
     *
     * @return
     */
    User findOne();
}

package com.think.spring.dao;

import com.think.spring.bean.User;

/**
 * @Author idea
 * @Date created in 3:37 下午 2020/4/19
 */
public class UserDaoImpl implements UserDao {

    @Override
    public User findOne() {
        System.out.println("this is findOne");
        return new User(1,"idea");
    }
}

package com.think.spring.bean;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

/**
 * 创建用于测试的user对象
 *
 * @Author idea
 * @Date created in 2:38 下午 2020/4/19
 */
@Data
public class User implements Serializable {
    private static final long serialVersionUID = -6841693635661644101L;

    private Integer id;

    private String username;

    public User(Integer id, String username) {
        this.id = id;
        this.username = username;
        System.out.println("this is init...");
    }

    public User() {
        System.out.println("this is no args init...");
    }
}


通过这个简单的案例我们可以很好的理解来什么是构造器注入,那么基于setter注入的方式又是如何的呢?

2.构造器注入
其实所谓的构造器注入主要是差别是在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.xsd">

    <!--  这里面对于user对象需要设置无参数构造函数  -->
    <bean class="com.think.spring.bean.User" id="user" />

    <bean class="com.think.spring.dao.UserDaoImpl" id="userDao"></bean>

    <bean class="com.think.spring.service.UserServiceImpl" id="userService">
<!--        <property name="userDao" ref="userDao"></property>-->
        <constructor-arg index="0" type="com.think.spring.dao.UserDao" ref="userDao"> </constructor-arg>
    </bean>
</beans>

然后对应对实现类对构造函数也有部分细节变化


   public UserServiceImpl(UserDao userDao) {
        this.userDao = userDao;
    }

当然在我们对dao做属性注入对时候,可以设置对应对type或者index来区分构造函数里对参数信息。

3.参数注入–静态工厂
关于参数注入这块我对于其的理解更多是偏向于静态工厂的方式来初始化bean。
举个例子来说明:

package com.think.spring.dao;

import com.think.spring.bean.User;

import java.util.HashMap;
import java.util.Map;

/**
 * @Author idea
 * @Date created in 4:01 下午 2020/4/19
 */
public class UserFactory {

    static Map<String, User> userMap = new HashMap<>(3);
    static {
      userMap.put("1",new User(1,"user_01"));
      userMap.put("2",new User(2,"user_02"));
      userMap.put("3",new User(3,"user_03"));
    }

    public static User getUserFromMap(String id){
        return userMap.get(id);
    }

    public static User getDynamicUser(int id){
        return new User(id,"user_0"+id);
    }

}

这里有一个工厂专门用于生成user这个bean,然后我们希望通过spring容器能够指定生成对应的bean对象,这个时候可以考虑使用factory-method这个属性,并且在xml里面做些修改调整:(注意 静态工厂在spring容器里可以不做初始化操作,因此如果xml里面没有配置静态工厂的bean相关属性也是可以的)



    <bean id="staticFactoryUser01" class="com.think.spring.dao.UserFactory" factory-method="getUserFromMap">
        <constructor-arg value="1"></constructor-arg>
    </bean>

    <bean id="staticFactoryUser02" class="com.think.spring.dao.UserFactory" factory-method="getUserFromMap">
        <constructor-arg value="2"></constructor-arg>
    </bean>

    <bean id="staticFactoryUser03" class="com.think.spring.dao.UserFactory" factory-method="getUserFromMap">
        <constructor-arg value="3"></constructor-arg>
    </bean>
	
	对应的测试类如下:
	public class ApplicationTest {

    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
        for (int i=1;i<=3;i++){
            User user = (User) applicationContext.getBean("staticFactoryUser0"+i);
            System.out.println(user.toString());
        }
    }
	}

然后我们在容器里面取bean的时候就可以根据指定的标示来加载不同的bean对象了。

4.动态工厂
了解了静态工厂之后,对于动态工厂也就比较好理解了,动态工厂在调用具体对象的时候,需要先创建对象本身,然后再进行进一步的调用,代码案例如下所示:

 public  User getDynamicUser(int id){
        System.out.println("需要先创建对象本身,然后再来调用对象信息");
        return new User(id,"user_0"+id);
    }


xml案例如下:
 <bean id="userFactory" class="com.think.spring.dao.UserFactory" ></bean>

    <bean id="dyFactoryUser" class="com.think.spring.dao.UserFactory"  factory-bean="userFactory" factory-method="getDynamicUser">
        <constructor-arg value="1"></constructor-arg>
    </bean>

动态工厂和静态工厂是两个长相相似的工厂类,但是其存在的目的却有所不同,静态工厂在调用对应方法的时候不需要实例化对应的对象,在高并发场景中比较能节省内存消耗,而对于动态工厂而言,调用对应的函数时候需要先对该对象作出具体的实现,比较消耗内存空间。
上边说到的这四类是目前主流的di实现的策略方式,但是并不是说ioc就只有这些实现策略类。下边我们继续来深入挖掘ioc还有哪些实现策略。

java内部有个叫做beancontext的上下文,专门用于管理javabean,不过个人功力有限,目前对这块领域的知识还不是太熟悉。

模版模式的实现,例如说传统的jdbctemplate这块的实现,内部使用了callback的机制,也是有涉及部分关于ioc的设计灵感。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章