spring学习笔记(4)依赖注入详解

常用的注入方式有四种:
1. 属性注入
2. 构造方法注入
3. 工厂方法注入
4. 注解注入
下面先定义我们后面用到的POJO类:

package test;

public class User {
    private String name;
    private String gender;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getGender() {
        return gender;
    }
    public void setGender(String gender) {
        this.gender = gender;
    }
    @Override
    public String toString() {
        return "User [name=" + name + ", gender=" + gender + "]";
    }
}

属性注入

属性注入(set方法注入)对Bean有两点要求:
1. 提供一个默认的构造函数
2. 为需要注入的属性提供set方法
配置示例如下:
xml文件配置

    <bean id="user" class="test.User">
        <property name="name" value="zeng"></property>
        <property name="gender" value="male"></property>
    </bean>

测试函数

public static void main(String args[]){
    ApplicationContext atc = new ClassPathXmlApplicationContext("spring.xml");
    User user = (User) atc.getBean("user");
    System.out.println(user);//print User [name=zeng, gender=male]
}

而如果我们把POJO中的getName方法去掉,结果不变,但setName方法删掉,我们再运行程序,会看到如下报错信息:

Exception in thread “main” org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘user’ defined in class path resource [spring.xml]: Error setting property values; nested exception is org.springframework.beans.NotWritablePropertyException: Invalid property ‘name’ of bean class [test.User]: Bean property ‘name’ is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?
显然,我们的属性注入,set方法是必不可少的

2. 构造方法注入

1. 按类型匹配入参

先在User POJO中新增带参构造方法和age属性

    private Integer age;
    get and set ...

    public User(String name, Integer age) {
    this.name = name;
    this.age = age;
    }
    //这里需特别注意的是,我们定义了带参的构造函数,JVM就不会因为没有定义构造函数为我们创建默认的构造函数了,因此这里我们需要自己重新定义

在xml文件定义Bean

<bean id="user1" class="test.User">
    <constructor-arg type="java.lang.String" value="zeng1" ></constructor-arg>
    <constructor-arg type="java.lang.Integer" value="20"></constructor-arg>
</bean>

调用测试函数

public static void main(String args[]){
    ApplicationContext atc = new ClassPathXmlApplicationContext("spring.xml");
    User user = (User) atc.getBean("user1");
    System.out.println(user.getName() + "——" + user.getAge());
    //print zeng1——20
}

这时候,我们再稍微修改下xml文件

<bean id="user1" class="test.User">
    <constructor-arg type="java.lang.String" value="male1"></constructor-arg>
    <constructor-arg type="java.lang.Integer" value="12" ></constructor-arg>
    <constructor-arg type="java.lang.String" value="zeng"></constructor-arg>
</bean>

同时对应User POJO类新定义构造方法

public User(String name, String gender, Integer age) {
    super();
    this.name = name;
    this.gender = gender;
    this.age = age;
}

最后修改测试函数并运行

public static void main(String args[]){
    ApplicationContext atc = new ClassPathXmlApplicationContext("spring.xml");
    User user = (User) atc.getBean("user1");
    System.out.println(user.getName() + "——" + user.getAge() +"——" +user.getGender());
    //print male1——12——zeng
}

通过这个有趣的试验,我们发现,当出现重复的类型时,构造函数会根据的定义顺序和构造函数的参数顺序对应入参。即使中间插入了其他类型参数,容器也只会对相同类型的进行顺序注入,下面可辅助验证这个特点:
假如我们去掉中间<constructor-arg type="java.lang.Integer" value="12" ></constructor-arg>,就会打印male1——null——zeng

2. 按索引匹配入参:

在前面第5点建立了三参数构造函数的基础上,我们在xml文件上编写

<bean id="user2" class="test.User">
   <constructor-arg index="0" value="male2"></constructor-arg>
   <constructor-arg index="2" value="12" ></constructor-arg>
   <constructor-arg index="1" value="zeng2"></constructor-arg>
</bean>

运行测试函数

public static void main(String args[]){
    ApplicationContext atc = new ClassPathXmlApplicationContext("spring.xml");
    User user = (User) atc.getBean("user2");
    System.out.println(user.getName() + "——" + user.getAge() +"——" +user.getGender());
    //print male2——12——zeng2
}

注意索引为3的value必须为Integer(与构造函数对应,否则会报错:Could not resolve matching constructor (hint: specify index/type/name arguments for simple parameters to avoid type ambiguities))
注意到我这里是name=“male2”和gender=”zeng2”,即参数值注入是严格根据index来的,且index从0开始算起
关于构造函数注入的优点:
1. 保证一些主要的属性在Bean实例化时就配置好
2. 不需要为每个方法提供Setter方法,减少了类的方法个数,同时还能更好的封装变量,避免外界的错误调用Setter方法
关于构造函数注入的缺点:
1. 如果一个类的属性众多,且我们需要针对不同的属性堆构造特定的构造方法,那么构造函数的数量就会很多(否则需制定特点属性为null,这样可读性也差),而且构造函数本身会变得很庞大臃肿。同时我们还要考虑到配置文件的构造函数参数匹配的歧义问题
2. 构造函数不利于类的继承和扩展,因为子类需要引用到父类复杂的构造函数

3. 联合使用类型和参数索引

此方法能更有效地避免歧义产生

3. 工厂方法注入

1. 非静态工厂方法

因为工厂方法非静态,要调用改方法必须先初始化对象

先定义工厂类

    package test;

    public class UserFactory {
        public User createUser(){
           User user = new User("zeng3","male3",20);
           return user;
        }
    }

进行Bean配置

<bean id="userFactory" class="test.UserFactory" />
<bean id="user3" factory-bean="userFactory" factory-method="createUser"></bean>

调用测试函数

    public static void main(String args[]){
        ApplicationContext atc = new ClassPathXmlApplicationContext("spring.xml");
        User user = (User) atc.getBean("user3");
        System.out.println(user.getName() +" ——" +user.getGender()+ "——" + user.getAge() );
        //print zeng3——male3——20
    }

2. 静态工厂方法

无须另外创建对象,先将createUser方法给为静态,然后将bean配置为:
<bean id="user3" class="test.UserFactory" factory-method="createUser"></bean>
调用测试函数即可得到与上次相同的结果。

这里需要注意的是,静态方法先定义工厂Bean,再配置在id为user3的bean中,而id=user3的bean没有定义class属性。因为它的类的归属在工厂类里判决,而静态工厂方法中,没有了userFactory bean的定义(即不用初始化),但在user3中定义了一个class,这个class指向了工厂类

4. 注解注入

1. 对类成员变量注解

使用@Autowire实现自动注入,格式如:

@Component
public class IdCard{
    .....
}

public class User{
    //Autowired默认按类型注入,@required 表明如果找不到对应的bean则为null,但如果设定为true(也是默认值),则要求一定要找到匹配的bean,否则会抛出异常。
    //Qualifier常用于容器有一个以上相同类型的Bean,通过指定名字来指定唯一的Bean
    @Autowired(required = false )
    @Qualifier("idCard")
    private IdCard idCard;//也可以将IdCard配置在xml文件中注入
    .....
}

2. 对类成员方法进行注解

@Autowired
public void init(@Qualifier(“usar1")User user1,@Qualifier("user2")User user2){
    this.user1 = user1;
    this.user2 = user2;
}
时间: 2024-10-14 00:16:08

spring学习笔记(4)依赖注入详解的相关文章

AngularJS学习笔记之依赖注入详解_AngularJS

     最近在看AngularJS权威指南,由于各种各样的原因(主要是因为我没有money,好讨厌的有木有......),于是我选择了网上下载电子版的(因为它不要钱,哈哈...),字体也蛮清晰的,总体效果还不错.但是,当我看到左上角的总页码的时候,479页....479....479....俺的小心脏被击穿了二分之一有木有啊,上半身都石化了有木有啊,那种特别想学但是看到页码又不想学的纠结的心情比和女朋友吵架了还复杂有木有啊,我平常看的电子书百位数都不大于3的好伐! 哎,原谅我吧,我应该多看几本

JavaScript中的依赖注入详解

 这篇文章主要介绍了JavaScript中的依赖注入详解,本文讲解了requirejs/AMD方法.反射(reflection)方法等内容,需要的朋友可以参考下     计算机编程的世界其实就是一个将简单的部分不断抽象,并将这些抽象组织起来的过程.JavaScript也不例外,在我们使用JavaScript编写应用时,我们是不是都会使用到别人编写的代码,例如一些著名的开源库或者框架.随着我们项目的增长,我们需要依赖的模块变得越来越多,这个时候,如何有效的组织这些模块就成了一个非常重要的问题.依赖

Spring学习笔记之依赖的注解(2)

Spring学习笔记之依赖的注解(2) 1.0 注解,不能单独存在,是Java中的一种类型 1.1 写注解 1.2 注解反射 2.0 spring的注解 spring的 @Controller@Component@Service//更多典型化注解,但是@Controller@Service建议使用 @service("personService")可以代替set get 方法,@Resource(name=personDao) @Autowired//按照类型匹配 @Qualifier

面向对象编程依赖注入详解_java

说说依赖注入 在面向对象编程中,我们经常处理处理的问题就是解耦,程序的耦合性越低表明这个程序的可读性以及可维护性越高.控制反转(Inversion of Control或IoC)就是常用的面向对象编程的设计原则,使用这个原则我们可以降低耦合性.其中依赖注入是控制反转最常用的实现. 什么是依赖 依赖是程序中常见的现象,比如类Car中用到了GasEnergy类的实例energy,通常的做法就是在Car类中显式地创建GasEnergy类的实例,并赋值给energy.如下面的代码 interface E

AngularJS 依赖注入详解及示例代码_AngularJS

依赖注入是一个在组件中给出的替代了硬的组件内的编码它们的依赖关系的软件设计模式.这减轻一个组成部分,从定位的依赖,依赖配置.这有助于使组件可重用,维护和测试. AngularJS提供了一个至高无上的依赖注入机制.它提供了一个可注入彼此依赖下列核心组件. 值 工厂 服务 提供者 常值 值 值是简单的JavaScript对象,它是用来将值传递过程中的配置相位控制器. //define a module var mainApp = angular.module("mainApp", []);

AngularJS $injector 依赖注入详解_AngularJS

推断式注入 这种注入方式,需要在保证参数名称与服务名称相同.如果代码要经过压缩等操作,就会导致注入失败. app.controller("myCtrl1", function($scope,hello1,hello2){ $scope.hello = function(){ hello1.hello(); hello2.hello(); } }); 标记式注入 这种注入方式,需要设置一个依赖数组,数组内是依赖的服务名字,在函数参数中,可以随意设置参数名称,但是必须保证顺序的一致性. v

Javascript技术栈中的四种依赖注入详解_javascript技巧

作为面向对象编程中实现控制反转(Inversion of Control,下文称IoC)最常见的技术手段之一,依赖注入(Dependency Injection,下文称DI)可谓在OOP编程中大行其道经久不衰.比如在J2EE中,就有大名鼎鼎的执牛耳者Spring.Javascript社区中自然也不乏一些积极的尝试,广为人知的AngularJS很大程度上就是基于DI实现的.遗憾的是,作为一款缺少反射机制.不支持Annotation语法的动态语言,Javascript长期以来都没有属于自己的Spri

Spring学习(三) AOP详解

上次的博文深入浅出Spring(二) IoC详解中,我为大家简单介绍了一下Spring框架核心内容中的IoC,接下来我们继续讲解另一个核心AOP(Aspect Oriented Programming),即面向切面编程. 1.OOP回顾 在介绍AOP之前先来回顾一下大家都比较熟悉的OOP(Object Oriented Programming).OOP主要是为了实现编程的重用性.灵活性和扩展性.它的几个特征分别是继承.封装.多态和抽象.OOP重点体现在编程架构,强调的是类之间的层次关系. 2.O

Spring学习(二) IoC详解

上次的博客深入浅出Spring(一)Spring概述中,我给大家简单介绍了一下Spring相关概念.重点是这么一句:Spring是为了解决企业应用开发的复杂性而创建的一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架.在这句话中重点有两个,一个是IoC,另一个是AOP.今天我们讲第一个IoC. IoC概念 控制反转(Inversion of Control)是一个重要的面向对象编程的法则来削减计算机程序的耦合问题. 它还有一个名字叫做依赖注入(Dependency Injection)