Spring 4.1.6(三)

9 篇文章 0 订阅
4 篇文章 0 订阅

Dependency Injection(依赖注入)

对于一个application 来说,需要组织很多的class,让他们相互独立,便于重用、方便测试。依赖注入就可以实现。

SpellChecker 是一个空class。

public class TextEditor {
	private SpellChecker sc;

	public TextEditor() {
		sc = new SpellChecker();
	}
}

从代码中,我们可以看出TextEditor 是依赖于SpellChecker ,TextEditor 在创建的时候,必须要有SpellChecker ,这时是实际上,是通过TextEditor 去控制SpellChecker ,写死在TextEditor 构造器中,假如这时我们要依赖一个其他的class,就要手动该代码,这很痛苦,那么,我们看下面这种:

public class TextEditor {
	private SpellChecker sc;

	public TextEditor(SpellChecker sc) {
		this.sc = sc;
	}
}

在TextEditor 的构造器中,传入SpellChecker 对象,然后在赋值。这时,就很灵活,就不是由TextEditor 去控制死了,而是交给Spring 容器去控制。这时如果传入不同对象,我们只需要让它继承共同的父类,或者实现共同的接口即可。

依赖注入的两种形式
  1. 通过构造器

在前面的例子,我们是通过set方法来演示了依赖注入,下面我们看看如何通过构造器去注入:

public class SpellChecker {
    public void spellCheck() {
    	System.out.println("i am the inner texteditor");
    }
}
public class TextEditor {
	private SpellChecker sc;
	private String name;
	public TextEditor(SpellChecker sc, String name) {
		super();
		this.sc = sc;
		this.name = name;
	}

	public void spellCheck() {
		sc.spellCheck();
	}

}
        <bean name="texteditor" class="test.TextEditor">
        <constructor-arg name="name" value="hello world"></constructor-arg>//通过构造器传参,达到注入
	<constructor-arg name="sc" ref="spellchecker"></constructor-arg>//注入的是一个引用对象
	</bean>
	<bean name="spellchecker" class="test.SpellChecker"></bean>

使用构造器传参设置的时候,和你Bean 里面提供的带参的构造器参数顺序,无所谓。

  1. 通过set方法
注入内部Bean

内部Bean 简而言之就和Java 的内部类差不多。就是在一个外部Bean中在依赖一个Bean。

public class SpellChecker {
    public void spellCheck() {
    	System.out.println("i am the inner texteditor");
    }
}
public class TextEditor {
	private SpellChecker sc;

	public SpellChecker getSc() {
		return sc;
	}

	public void setSc(SpellChecker sc) {
		this.sc = sc;
	}
	public void spellCheck() {
		sc.spellCheck();
	}
	
}

这里通过set 把SpellChecker 对象注入到TextEditor 。

<bean name="texteditor" class="test.TextEditor">
 <property name="sc">
 <bean name="spellchecker" class="test.SpellChecker"></bean>
 </property>
 </bean>
 

这里实际和之前配置property 差不多,只不过这时不是简单的属性值,而是一个Bean。

运行之后,会调用SpellChecker 的方法。

注入集合

之前无论是注入一个基本属性,还是一个Bean,注入的value 都是一个,如果是多个值,比如Java 的集合类型。Spring 提供了四种集合注入的属性。

ElementDescription
list允许注入一系列list 的值,可以重复
set允许注入一系列set的值,不可以重复
map允许注入键值对的集合,键和值可以是任何类型
Property允许注入键值对集合,但是必须键和值都是String 类型
public class InjectCollection {
	private List list;
	private Set set;
	private Map<Integer,String> map;
	private Properties prop;
	}

通过set注入,set、get省略,自行补充。

 <bean name="injectcollection" class="test.InjectCollection">
    <property name="list">
     <list>
        <value>list1</value>
        <value>list2</value>
     </list>
    </property>
    <property name="set">
    <set>
    <value>set1</value>
    <value>set2</value>
    </set>
    </property>
    <property name="map">
    <map>
    <entry key="1" value="map1"></entry>
    <entry key="2" value="map2"></entry>
    </map>
    </property>
    <property name="prop">
     <props>
      <prop key="one">one</prop>
      <prop key="two">two</prop>
     </props>
    </property>
 </bean>

这里的集合配置,实际上是作为属性的value,去配置的。

这配置没啥好说,就是规定你按照写即可。

上述的配置,集合的值全部是基本类型,假如集合的值全部是引用类型,我们又该如何处理,加个引用呗!

 <bean name="injectcollection" class="test.InjectCollection">
    <property name="list">
     <list>
     <ref bean="address1"></ref>//List 的直接引用Bean 的id
     </list>
    </property>
    <property name="set">
    <set>
     <ref bean="address1"></ref>
    </set>
    </property>
    <property name="map">
    <map>
    <entry key-ref="address1" value-ref="address2"></entry>//map 的键和值都可以是引用类型
    </map>
    </property>
 </bean>

为了方便,这里引用的Bean ,我就不定义了。properties 因为规定了键和值类型,就不存在引用类型。

注入空和null值
 <property   name="message" value="" ></property>

等同于setXXX("")

 <property   name="message"  ><null></null></property>

等同于setXXX(null)

自动装载

自动装载,装载的对象是引用类型对象,不是普通类型的对象。

知道我们的依赖注入,都是通过set、构造函数,去手动装载,现在,我们可以通过自动装载减少相应的代码。

但是自动也也有不好的,之前手动注入,你设置注入,最多没有值,但是现在,它会跟着相应的策略去自己寻找,自动装载。

自动装载的模式
ModeDescription
no该Bean 不采用任何装配模式,你显式定义你的装配
byName根据你的属性名去自动装配,它会根据你xml定义的属性名和你Bean 定义的属性名,一致自动装配
byType根据的属性的类型去自动装配,它会根据XML配置文件中查看属性,然后,如果属性的类型与配置文件中的一个bean名称匹配,则会尝试匹配并连接属性。 如果存在多个这样的bean,则抛出异常。
constructor应用于构造函数,根据构造函数参数的数据类型,进行byType模式的自动装配。
autodetect第一次采用constructor 去自动装载,如果不行,采用 byType
  1. byname

spring 容器会根据xml 配置自动装载的属性,到相应的Bean 中找名字一致的属性,如果找到,就装配成功;否则,无法注入该属性。

public class SpellChecker {
    public void spellCheck() {
    	System.out.println("i am the inner texteditor");
    }
}
public class TextEditor {
	private SpellChecker sc;

	public void spellCheck() {
		sc.spellCheck();
	}
	
}

基于属性注入,set、get方法省略,自行补充

<bean name="texteditor" class="test.TextEditor" autowire="byName">//同时,你这里
 <property name="sc" ref="spellchecker">//这里我们不是采用在texteditor Bean 内部配置SpellCheckerBean,而是直接让使用去引用另外的Bean。
 </property>
 </bean>
  <bean name="spellchecker" class="test.SpellChecker"></bean>

运行结果和之前的一致。

  1. bytype

如果设置该自动装载模式,Spring 容器会通过Bean 中该属性的类型,去自动加载。但是你还是可以在xml 配置给基本类型去注入值。

public class SpellChecker {
    public void spellCheck() {
    	System.out.println("i am the inner texteditor");
    }
}
public class TextEditor {
	private SpellChecker sc;
	private String name;//属性注入,set、get 方法省略,自行补充。

	public void spellCheck() {
		sc.spellCheck();
	}

}
<bean name="texteditor" class="test.TextEditor" autowire="byType">//这里由于设置装载模式byType 会自动扫描该Bean 的中的依赖类型,并自动装载。
		<property name="name" value="hello"></property>
		//<property name="sc"  ></property>引用对象的属性就没有必要显式设置。
	</bean>
	<bean name="spellchecker" class="test.SpellChecker"></bean>
  1. constructor

使用该模式,一定注意你的基于构造注入,才有效,如果你是set注入,那还搞个毛毛。

该模式和bytype 很类似,但是应用于构造器上的,它会根据构造器的参数的类型,去自动装配。

public class SpellChecker {
    public void spellCheck() {
    	System.out.println("i am the inner texteditor");
    }
}
public class TextEditor {
	private SpellChecker sc;
	private String name;
	public TextEditor(SpellChecker sc, String name) {
		super();
		this.sc = sc;
		this.name = name;
	}

	public void spellCheck() {
		sc.spellCheck();
	}

}
    <bean name="texteditor" class="test.TextEditor" autowire="constructor">
    <constructor-arg name="name" value="hello world"></constructor-arg>//这里和butype 一致,会自动装载SpellChecker 类型,无需你显式设置,是需要传入基本类型的name和value。
    </bean>
   <bean name="spellchecker" class="test.SpellChecker"></bean>
  1. autodetect
自动装载的缺点
  1. 自动装载的设置会被 and 重写。
  2. 只能装载基础类型。
  3. 自动装载没有显式装载准确,自动装载匹配会出现异常。
依赖注入注解

注解注入的好处,在于会覆盖在xml 装载的设置,为Spring 自动装配,我们需要在Beans.xml添加如下设置:

           <context:annotation-config/>

如果在你的Beans,没有该标签,请在xml 头添加:

	xmlns:context="http://www.springframework.org/schema/context"
	http://www.springframework.org/schema/context
    	http://www.springframework.org/schema/context/spring-context-3.1.xsd

AnnotationDescription
@Required该注解主要用于在属性的set方法,它表明受影响的 bean 属性在配置时必须放在 XML 配置文件中,否则容器就会抛出一个 BeanInitializationException 异常
@Autowired该注解主要用在set、构造器、属性和其他任何方法上的自动装载
@Qualifier配合@Autowired使用,使装载Bean 更准确,具体看例子
JSR-250 AnnotationsSpring 对 JSR-250 Annotations的支持
  1. @Required

该注解是应用在Bean 的属性的set方法上的。

public class Student {
	private String name;
	private int age;
	public String getName() {
		return name;
	}
	@Required
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	@Required
	public void setAge(int age) {
		this.age = age;
	}
	

}

<bean name="student" class="test.Student">
    <property name="name" value="lao"></property>
    <property name="age" value="11"></property>
 </bean>

一旦在Bean 的属性中,设置了该注解,相应的必须在Beans.xml 中设置属性,否则会出现如下的错误:

Caused by: org.springframework.beans.factory.BeanInitializationException: Property ‘xxx’ is required for bean 'student’

		Student s = (Student) context.getBean("student");
		System.out.println(s.getAge()+s.getName());
  1. @Autowired

该注解可以用于在set 方法、构造函数、等任意的方法上,均可以实现自动装载。它实现是根据byType 这种方法去自动注入的。

@Autowired on Setter Methods

public class SpellChecker {
    public void spellCheck() {
    	System.out.println("i am the inner texteditor");
    }
}
public class TextEditor {
	private SpellChecker sc;
	private String name;

	public TextEditor() {
		super();
		// TODO Auto-generated constructor stub
	}

	public SpellChecker getSc() {
		return sc;
	}

	@Autowired
	public void setSc(SpellChecker sc) {
		this.sc = sc;
	}

	public String getName() {
		return name;
	}

	@Autowired
	public void setName(String name) {
		this.name = name;
	}

	public void spellCheck() {
		sc.spellCheck();
	}

}

       <bean name="texteditor" class="test.TextEditor">
	<property name="name" value="122"></property>
	</bean>
	<bean name="spellchecker" class="test.SpellChecker"></bean>

这里有一点,你需要注意,你在前面的Bean 中给name 也设置该注解,如果你在不通过标签去给它设置相应的值,它就会默认去自动注入,去找String 类型,然后蒙蔽找不到,就报错了。

@Autowired on Properties

该注解设置在属性上,可以省去一系列的set方法,所以基于这种方法,我们不可以在配置文件中,设置,它会提示你没有set方法,这一步,容器会自动帮我们设置值去处理。

public class TextEditor {
	@Autowired
	private SpellChecker sc;
	private String name;

	public TextEditor() {
		super();
		// TODO Auto-generated constructor stub
	}

	public SpellChecker getSc() {
		return sc;
	}


	public String getName() {
		return name;
	}

	public void spellCheck() {
		sc.spellCheck();
	}

}

SpellChecker类不变。

	<bean name="texteditor" class="test.TextEditor">
	</bean>
	<bean name="spellchecker" class="test.SpellChecker"></bean>

@Autowired on Constructors

该注解应用于构造器上

public class TextEditor {
	private SpellChecker sc;
	private String name;
    @Autowired
	public TextEditor(SpellChecker sc, String name) {
		super();
		this.sc = sc;
		this.name = name;
	}

	public void spellCheck() {
		sc.spellCheck();
	}

}

SpellChecker 类不变。

	<bean name="texteditor" class="test.TextEditor">
	<constructor-arg name="name" value="lao"/>
	</bean>
	<bean name="spellchecker" class="test.SpellChecker"></bean>

这里需要注意一下,因为该注解的构造器,包含两个属性,由于name 是String 类型,不需要自动装载了,所以,你在这配置 文件 给出相应的值,由于该注入是基于构造器注入,所以用标签。(上面是基于set方法注入,所以用)

@Autowired with (required = false) option

该注解有一个参数,可以设置是否强制自动装载。

public class Student {
private String name;
private int age;
public String getName() {
return name;
}
@Autowired
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
@Autowired(required=false)
public void setAge(int age) {
this.age = age;
}

}


SpellChecker 类不变。

<bean name="texteditor" class="test.TextEditor">

</bean>
<bean name="spellchecker" class="test.SpellChecker"></bean>

```

如果这里没有设置age 上注解为false,这里会报错。这样就可以控制@Autowired 是否装载。该注解的required 默认值是false。

  1. @Qualifier

该注解一般和@Autowired 一起,使用,实现自动装载,但是该属性,可以指定如果有多个复合的Bean类型,可以指定装载的Bean 的name,使得装载更精确。


public class Student {
	private String name;
	private int age;
         //set、get方法省略,请自行补充
	}
public class ProFile {
   @Autowired
   @Qualifier("stu1")
   private Student student;
   public void getInformation() {
	   System.out.println(student.getAge()+student.getName());
   }
}
<bean name="student" class="test.Student">
    <property name="name" value="lao"></property>
    <property name="age" value="11"></property>
 </bean>
 
  <bean name="stu1" class="test.Student">
    <property name="name" value="lao11"></property>
    <property name="age" value="1111"></property>
 </bean>
 <bean name="profile" class="test.ProFile"></bean>
``

 4. JSR-250 Annotations

这里面主要涉及到这三个注解@PostConstruct, @PreDestroy and @Resource。

public class Student {
private String name;
private int age;
//set、get方法自行补充
}


public class ProFile {
@Autowired
@Resource(name=“student”)
private Student student;
public void getInformation() {
System.out.println(student.getAge()+student.getName());
}
@PostConstruct
public void init1() {
System.out.println(“bean is start lalalal”);
}
@PreDestroy
public void destory1() {
System.out.println(“bean is over kukukuk”);
}
}


```

通过运行,我们可以看出@PostConstruct 是和我们之前说的,在Bean 实例初始化之前,执行的方法;@PreDestroy,在Bean 销毁之前,执行的方法。@Resource 和 @Qualifier很类似,可以指定某一个Bean 类型,达到精确装载。

Spring 常用注解

其实是用Spring 注解配置Bean 和在xml 中配置,是起相同的作用,你只需要二者取其一即可,另外,在学习的时候,也要对比着学习。当然,借助注解,你可省去对xml 的操作。

@Configuration

该注解应用于某一个类,则说明该类是被Spring 容器认为是Bean 定义的源头。

@Bean

该注解应用于某一个方法,则说明该方法返回的是一个对象bean,并该bean 会被注册到Spring context 中,以便使用。

使用例子

    public class HelloWord {
	private String message;
    
	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}
}

@Configurable
public class BeanConfig {
	@Bean
    public HelloWord getHelloWorld() {
    	 return new HelloWord();
    }
}

这一步就很类似于xml中:

 <bean id="helloworld" class="test.HelloWord" >
 <property   name="message"  ><null></null></property>
 </bean>
ApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class);//这里要注意获取Spring 上下文的时候,和之前的不一样了,是通过AnnotationConfigApplicationContext
	    HelloWord h1 =  (HelloWord) context.getBean(HelloWord.class);//因为我们在那个Bean 类中获取Bean,是根据返回类型,没有像xml 中有name,可以直接根据name。
	    h1.setMessage("hello annotation ");
	    System.out.println(h1.getMessage());
通过注解的方法去注入Bean 依赖

我们上面的例子只是简单示例,如何通过注解的方式,注入一个Bean,但是如果Bean 中还依赖其他Bean,那该如何处理:

public class HelloWord {
	private String message;
	private Student s;

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}
	
	
	public HelloWord(Student s) {
		super();
		this.s = s;
	}

	public Student getS() {
		return s;
	}

	public void setS(Student s) {
		this.s = s;
	}

	public void showStudent() {
		System.out.println("student "+s.getName()+s.getAge());
	}
	
}
public class Student {
	private String name;
	private int age;
	//自行补充相应的set、get方法
	}
ApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class);
	    HelloWord h1 =  (HelloWord) context.getBean(HelloWord.class);
	    h1.setMessage("hello annotation ");
	    System.out.println(h1.getMessage());
	    h1.getS().setAge(11);
	    h1.getS().setName("lao");
	    h1.showStudent();
@Configurable
public class BeanConfig {
	@Bean
    public HelloWord getHelloWorld() {
    	 return new HelloWord(getStudent());//**这里实际上是通过构造器注入的Student 对象**
    }
	@Bean
	public Student getStudent() {
		return new Student();
	}
	
}

上面我们是通过构造器去注入,那么如何使用set注入,一点小变化:

在HelloWord 中,提供Student 的set、get方法。

@Configurable
public class BeanConfig {
	@Bean
    public HelloWord getHelloWorld() {
		HelloWord hw = new HelloWord();
		hw.setS(getStudent());
    	        return hw;
    }
	@Bean
	public Student getStudent() {
		return new Student();
	}
	
}

@import

该注解主要用于在一个Bean 定义的配置文件中,引入其他Bean 定义的class,可以在配置中,直击获取其他Bean 配置的Bean 的实例。

@Bean

该注解中,可以指定Bean 在初始化之前执行的方法和在销毁之前执行的方法。

	@Bean(initMethod="init",destroyMethod="destory")//这里填入对应Bean 中的方法名,方法必须申明在对应的Bean 类中。

@Scope

该注解可以指定Bean 的模式,默认是单例模式。该注解和@Bean 一起使用

	@Scope("prototype")

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值