Spring【依赖注入】就是这么简单

前言

在Spring的第二篇中主要讲解了Spring Core模块的使用IOC容器创建对象的问题,Spring Core模块主要是解决对象的创建和对象之间的依赖关系,因此本博文主要讲解如何使用IOC容器来解决对象之间的依赖关系

回顾以前对象依赖

我们来看一下我们以前关于对象依赖,是怎么的历程

直接new对象

  • 在最开始,我们是直接new对象给serice的userDao属性赋值...

class  UserService{
    UserDao userDao = new UserDao();
}

写DaoFactory,用字符串来维护依赖关系

  • 后来,我们发现service层紧紧耦合了dao层。我们就写了DaoFactory,在service层只要通过字符串就能够创建对应的dao层的对象了。

  • DaoFactory


public class DaoFactory {

    private static final DaoFactory factory = new DaoFactory();
    private DaoFactory(){}

    public static DaoFactory getInstance(){
        return factory;
    }

    public <T> T createDao(String className,Class<T> clazz){
        try{
            T t = (T) Class.forName(className).newInstance();
            return t;
        }catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}
  • serivce
    private CategoryDao categoryDao = DaoFactory.getInstance().createDao("zhongfucheng.dao.impl.CategoryDAOImpl", CategoryDao.class);

    private BookDao bookDao = DaoFactory.getInstance().createDao("zhongfucheng.dao.impl.BookDaoImpl", BookDao.class);

    private UserDao userDao = DaoFactory.getInstance().createDao("zhongfucheng.dao.impl.UserDaoImpl", UserDao.class);

    private OrderDao orderDao = DaoFactory.getInstance().createDao("zhongfucheng.dao.impl.OrderDaoImpl", OrderDao.class);

DaoFactory读取配置文件

  • 再后来,我们发现要修改Dao的实现类,还是得修改service层的源代码呀..于是我们就在DaoFactory中读取关于daoImpl的配置文件,根据配置文件来创建对象,这样一来,创建的是哪个daoImpl对service层就是透明的

  • DaoFactory

    
    

public class DaoFactory {

private  UserDao userdao = null;

private DaoFactory(){
    try{
        InputStream in = DaoFactory.class.getClassLoader().getResourceAsStream("dao.properties");
        Properties prop = new Properties();
        prop.load(in);

        String daoClassName = prop.getProperty("userdao");
        userdao = (UserDao)Class.forName(daoClassName).newInstance();

    }catch (Exception e) {
        throw new RuntimeException(e);
    }
}

private static final DaoFactory instance = new DaoFactory();

public static DaoFactory getInstance(){
    return instance;
}

public UserDao createUserDao(){
    return userdao;
}

}


*   service

UserDao dao = DaoFactory.getInstance().createUserDao();

* * *

# Spring依赖注入

通过上面的历程,我们可以清晰地发现:**对象之间的依赖关系,其实就是给对象上的属性赋值!因为对象上有其他对象的变量,因此存在了依赖**...

Spring提供了好几种的方式来给属性赋值

*   **1) 通过构造函数**
*   **2) 通过set方法给属性注入值**
*   3) p名称空间
*   4)自动装配(了解)
*   **5) 注解**

## 搭建测试环境

*   **UserService中使用userDao变量来维护与Dao层之间的依赖关系**
*   **UserAction中使用userService变量来维护与Service层之间的依赖关系**

*   **userDao**

public class UserDao {

public void save() {
    System.out.println("DB:保存用户");
}

}


*   **userService**

public class UserService {

private UserDao userDao; 

public void save() {
    userDao.save();
}

}


*   **userAnction**

public class UserAction {

private UserService userService;

public String execute() {
    userService.save();
    return null;
}

}


## 构造函数给属性赋值

其实我们在讲解**创建带参数的构造函数的时候已经讲过了**...我们还是来回顾一下呗..

我们**测试service和dao的依赖关系就好了**....在**serice中加入一个构造函数,参数就是userDao**

public UserService(UserDao userDao) {
    this.userDao = userDao;

    //看看有没有拿到userDao
    System.out.println(userDao);
}

**applicationContext.xml配置文件**

<!--创建userDao对象-->
<bean id="userDao" class="UserDao"/>

<!--创建userService对象-->
<bean id="userService" class="UserService">
    <!--要想在userService层中能够引用到userDao,就必须先创建userDao对象-->
    <constructor-arg index="0" name="userDao" type="UserDao" ref="userDao"></constructor-arg>
</bean>

*   **测试:可以成功获取到userDao对象**

    // 创建容器对象
    ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");

    //得到service对象
    UserService userService = (UserService) ac.getBean("userService");

![这里写图片描述](http://upload-images.jianshu.io/upload_images/5291509-0e5334c5fae77ced?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

* * *

## 通过set方法给属性注入值

我们这里也是测试service和dao层的依赖关系就好了...**在service层通过set方法来把userDao注入到UserService中**

*   **为UserService添加set方法**

public class UserService {

private UserDao userDao;

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

    //看看有没有拿到userDao
    System.out.println(userDao);
}

public void save() {
    userDao.save();
}

}


**applicationContext.xml配置文件:通过property节点来给属性赋值**

*   **引用类型使用ref属性**
*   **基本类型使用value属性**

<!--创建userDao对象-->
<bean id="userDao" class="UserDao"/>

<!--创建userService对象-->
<bean id="userService" class="UserService">
    <property name="userDao" ref="userDao"/>
</bean>

*   测试:

![这里写图片描述](http://upload-images.jianshu.io/upload_images/5291509-cbb8c984d8f972d5?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

* * *

### **内部Bean**

我们刚才是**先创建userDao对象,再由userService对userDao对象进行引用**...我们还有另一种思维:**先创建userService,发现userService需要userDao的属性,再创建userDao**...我们来看看这种思维方式是怎么配置的:

**applicationContext.xml配置文件:property节点内置bean节点**

<!--
    1.创建userService,看到有userDao这个属性
    2.而userDao这个属性又是一个对象
    3.在property属性下又内置了一个bean
    4.创建userDao
-->
<bean id="userService" class="UserService">
    <property name="userDao">
        <bean id="userDao" class="UserDao"/>
    </property>
</bean>

*   测试

![这里写图片描述](http://upload-images.jianshu.io/upload_images/5291509-e46436d038bbcfaa?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

我们发现这种思维方式和服务器访问的执行顺序是一样的,但是**如果userDao要多次被其他service使用的话,就要多次配置了**...

## p 名称空间注入属性值

p名称控件这种方式**其实就是set方法的一种优化,优化了配置而已**...p名称空间这个内容**需要在Spring3版本以上才能使用**...我们来看看:

**applicationContext.xml配置文件:使用p名称空间**

<bean id="userDao" class="UserDao"/>

<!--不用写property节点了,直接使用p名称空间-->
<bean id="userService" class="UserService" p:userDao-ref="userDao"/>

*   测试

![这里写图片描述](http://upload-images.jianshu.io/upload_images/5291509-8809e286a74edfa2?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

* * *

## 自动装配

**Spring还提供了自动装配的功能,能够非常简化我们的配置**

自动装载默认是不打开的,自动装配常用的可分为两种:

*   **根据名字来装配**
*   **根据类型类装配**

### **XML配置根据名字**

**applicationContext.xml配置文件:使用自动装配,根据名字**

<bean id="userDao" class="UserDao"/>

<!--
    1.通过名字来自动装配
    2.发现userService中有个叫userDao的属性
    3.看看IOC容器中没有叫userDao的对象
    4.如果有,就装配进去
-->
<bean id="userService" class="UserService" autowire="byName"/>

*   测试

![这里写图片描述](http://upload-images.jianshu.io/upload_images/5291509-e9b68024f9918086?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

* * *

### **XML配置根据类型**

**applicationContext.xml配置文件:使用自动装配,根据类型**

值得注意的是:**如果使用了根据类型来自动装配,那么在IOC容器中只能有一个这样的类型,否则就会报错!**

<bean id="userDao" class="UserDao"/>

<!--
    1.通过名字来自动装配
    2.发现userService中有个叫userDao的属性
    3.看看IOC容器UserDao类型的对象
    4.如果有,就装配进去
-->
<bean id="userService" class="UserService" autowire="byType"/>

*   测试:

![这里写图片描述](http://upload-images.jianshu.io/upload_images/5291509-10c03ae01ac287cb?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

* * *

我们这只是测试两个对象之间的依赖关系,如果我们有很多对象,我们也可以**使用默认自动分配**

![这里写图片描述](http://upload-images.jianshu.io/upload_images/5291509-f7200a5cec2f914a?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

* * *

### **使用注解来实现自动装配**

@Autowired注解来实现自动装配:

*   **可以在构造器上修饰**
*   **也可以在setter方法上修饰**
*   **来自java的@Inject的和@AutoWired有相同的功能**

**如果没有匹配到bean,又为了避免异常的出现,我们可以使用required属性上设置为false。【谨慎对待】**

*   测试代码

    ```
    @Component
    public class UserService {

    private UserDao userDao ;

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

顺利拿到userDao的引用

![这里写图片描述](http://upload-images.jianshu.io/upload_images/5291509-af76b035d108ffd9?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

## 使用JavaConfig配置类实现对象依赖

在

*   **第一种(测试不出来)**

import org.springframework.context.annotation.Bean;

@org.springframework.context.annotation.Configuration
public class Configuration {

@Bean()
public UserDao userDao() {

    return new UserDao();
}

@Bean
public UserService userService() {

    //直接调用@bean的方法
    return new UserService(userDao());
}

}


* * *

*   **第二种(测试不出来)**

import org.springframework.context.annotation.Bean;

@org.springframework.context.annotation.Configuration
public class Configuration {

@Bean()
public UserDao userDao() {

    return new UserDao();
}

@Bean
public UserService userService(UserDao userDao) {

    //通过构造函数依赖注入
    return new UserService(userDao);
}

}


![这里写图片描述](http://upload-images.jianshu.io/upload_images/5291509-eb95a3d8738d2a2f?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

* * *

*   **如果我直接通过构造器传入的话,那么报错了**

import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.context.annotation.Bean;

@org.springframework.context.annotation.Configuration
public class Configuration {

@Bean()
public UserDao userDao() {

    return new UserDao();
}

@Bean(autowire = Autowire.BY_TYPE)
public UserService userService(UserDao userDao) {

    return new UserService(userDao);
}

}


![这里写图片描述](http://upload-images.jianshu.io/upload_images/5291509-df03b9373e3b9cd1?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

* * *

*   **我测试中只有通过这种方法才能拿到userDao的引用。**

public class Configuration {

@Bean()
public UserDao userDao() {

    return new UserDao();
}

@Bean(autowire = Autowire.BY_TYPE)
public UserService userService() {

    return new UserService(userDao());
}

}


![这里写图片描述](http://upload-images.jianshu.io/upload_images/5291509-59c94c88cad2e86c?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

* * *

当然了,**最简单直观的方法**还有一种:**在UserService中加入setUser()方法,那么只要set进去就行了**..

*   **UserService**

public class UserService {

private UserDao userDao ;

public UserService() {
}

public UserService(UserDao userDao) {

}

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

}


*   Config

import org.springframework.context.annotation.Bean;

@org.springframework.context.annotation.Configuration
public class Config1 {

@Bean(name = "userDao")
public UserDao userDao() {

    return new UserDao();
}

@Bean(name="userService")
public UserService userService() {

    UserService userService = new UserService();

    userService.setUserDao(userDao());

    return userService;
}

}


![这里写图片描述](http://upload-images.jianshu.io/upload_images/5291509-884626d48815890e?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

* * *

# 最后

扩展阅读:

*   [https://zhuanlan.zhihu.com/p/29426019](https://zhuanlan.zhihu.com/p/29426019)

> 如果文章有错的地方欢迎指正,大家互相交流。习惯在微信看技术文章,想要获取更多的Java资源的同学,可以**关注微信公众号:Java3y**
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容