Spring注解配置
从 Spring 2.5 开始就可以使用注解来配置依赖注入。使用注解的方式使我们无需在XML中配置一个Bean引用,更加简单和方便。
首先要引入context名称空间:
xmlns:context="http://www.springframework.org/schema/context"
声明context命名空间后,即可通过context命名空间的component-scan 的 base-package属性指定一个需要扫描的基类包,Spring容器将会扫描这个基类包里的所有类,并从类的注解信息中获取Bean的定义信息。若希望仅扫描特定的类而非基包下所有的类,那么可以使用resource-pattern属性过滤出特定的类 。< context:include-filter > 表示要包含的目标类,而 < context:exclude-filter > 表示要抛出在外的目标类。一个< context:component-scan >下可以拥有若干个< context:exclude-filter >和< context:include-filter >元素,这两个元素均支持多种类型的过滤表达式。
applicationContext.xml配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd ">
<!-- 指定扫描pers.zhang.bean报下的所有类中的注解.
注意:扫描包时.会扫描指定报下的所有子孙包
-->
<context:component-scan base-package="pers.zhang.bean"></context:component-scan>
</beans>
创建一个User类:
package pers.zhang.bean;
public class User {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public void init(){
System.out.println("我是初始化方法!");
}
public void destory(){
System.out.println("我是销毁方法!");
}
@Override
public String toString() {
return "User [name=" + name + ", age=" + age + ", car=" + car + "]";
}
}
@Component
@Component 作用在类上,相当于 xml 中的 < bean id="..." name="..." class="..." / >。@Component(str) 可以填写一个参数,相当于 name 属性。
同时,Spring 中还提供了@Component 的三个衍生注解:
- @Controller: 用于Controller层
- @Service: 用于业务层
- @Repository: 用于持久层
这三个注解是为了让注解本身的用途更加清晰,功能上目前来讲是一致的。
@Scope
@Scope 作用于类上,用于指定Bean的作用范围,取值有如下几个:
- singleton(默认): Spring IOC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id(name)与该bean定义相匹配,则只会返回bean的同一实例,一个容器对应一个bean。
- prototype: 表示多例,每一次请求(将其注入到另一个bean中,或者以程序的方式调用容器的 getBean()方法)都会产生一个新的bean实例,相当与一个new的操作。
- request: 表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP request内有效。
- session: 表示作用域表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP session内有效。
- global session: 作用域类似于标准的HTTP Session作用域,不过它仅仅在基于portlet的web应用中才有意义。Portlet规范定义了全局Session的概念,它被所有构成某个 portlet web应用的各种不同的portlet所共享。在global session作用域中定义的bean被限定于全局portlet Session的生命周期范围内。如果你在web中使用global session作用域来标识bean,那么web会自动当成session类型来使用。
@PostConstruct 和 @PreDestroy
@PostConstruct 用于在依赖关系注入完成之后需要执行的方法上,以执行任何初始化。相当于 init-method。
@PreDestroy 用于在对象销毁之前需要执行的方法上,以执行释放资源等操作。相当于 destroy-method。
package pers.zhang.bean;
@Repository("user")
@Scope(scopeName="singleton")
public class User {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@PostConstruct
public void init(){
System.out.println("我是初始化方法!");
}
@PreDestroy
public void destory(){
System.out.println("我是销毁方法!");
}
@Override
public String toString() {
return "User [name=" + name + ", age=" + age + ", car=" + car + "]";
}
}
测试方法:
package pers.zhang.test;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import pers.zhang.bean.User;
public class Demo {
@Test
public void fun1(){
//1 创建容器对象
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
//2 向容器"要"user对象
User u1 = (User) ac.getBean("user");
User u2 = (User) ac.getBean("user");
System.out.println(u1==u2);
//3 打印user对象
System.out.println(u1);
ac.close();
}
}
运行JUnit测试输出:
我是初始化方法!
true
User [name=null, age=null, car=null]
我是销毁方法!
@Value
@Value 一般作用在属性上或 set 方法上,用于值的注入。@Value的作用位置不同,其实现原理也不同:
- 作用在属性上: 使用反射技术,操作Field对字段直接赋值,但是破坏了对象的封装性。
- 作用在set方法上: 使用set方法赋值,但是不够直观,不够一目了然。
package pers.zhang.bean;
@Repository("user")
@Scope(scopeName="singleton")
public class User {
private String name;
@Value("18")
private Integer age;
public String getName() {
return name;
}
@Value("tom")
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@PostConstruct
public void init(){
System.out.println("我是初始化方法!");
}
@PreDestroy
public void destory(){
System.out.println("我是销毁方法!");
}
@Override
public String toString() {
return "User [name=" + name + ", age=" + age + ", car=" + car + "]";
}
}
测试方法:
package pers.zhang.test;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import pers.zhang.bean.User;
public class Demo {
@Test
public void fun1(){
//1 创建容器对象
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
//2 向容器"要"user对象
User u1 = (User) ac.getBean("user");
User u2 = (User) ac.getBean("user");
System.out.println(u1==u2);
//3 打印user对象
System.out.println(u1);
ac.close();
}
}
运行JUnit测试输出:
我是初始化方法!
true
User [name=tom, age=18, car=null]
我是销毁方法!
@Autowired 和 @Qualifier
@Autowired ,它可以对类成员变量、方法及构造函数进行标注,完成自动装配的工作。 通过 @Autowired的使用来消除 set ,get方法。
但需要注意的是,@Autowired是通过类型来进行匹配的。如果Spring容器中有多个同类型的对象,使用@Autowired装配得到的结果将不确定。
这时就需要使用 @Qualifier,限定描述符能根据名字进行注入,更能进行更细粒度的控制如何选择注入对象。
创建一个Car类,并使用注解配置一个实例:
package pers.zhang.bean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Repository("car1")
public class Car {
@Value("car1:二手捷达")
private String name;
@Value("呕吐绿")
private String color;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
@Override
public String toString() {
return "Car [name=" + name + ", color=" + color + "]";
}
}
在applicationContext.xml中再配置一个Car的实例:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd ">
<!-- 指定扫描cn.itcast.bean报下的所有类中的注解.
注意:扫描包时.会扫描指定报下的所有子孙包
-->
<context:component-scan base-package="pers.zhang.bean"></context:component-scan>
<bean name="car2" class="pers.zhang.bean.Car" >
<property name="name" value="car2:9手夏利" ></property>
<property name="color" value="屎黄色" ></property>
</bean>
</beans>
修改User类如下:
package pers.zhang.bean;
@Repository("user")
@Scope(scopeName="singleton")
public class User {
@Value("tom")
private String name;
@Value("18")
private Integer age;
@Autowired //自动装配,问题:如果匹配多个类型一致的对象.将无法选择具体注入哪一个对象.
@Qualifier("car2")//使用@Qualifier注解告诉spring容器自动装配哪个名称的对象
private Car car;
public Car getCar() {
return car;
}
public void setCar(Car car) {
this.car = car;
}
public String getName() {
return name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@PostConstruct
public void init(){
System.out.println("我是初始化方法!");
}
@PreDestroy
public void destory(){
System.out.println("我是销毁方法!");
}
@Override
public String toString() {
return "User [name=" + name + ", age=" + age + ", car=" + car + "]";
}
}
测试方法:
package pers.zhang.test;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import pers.zhang.bean.User;
public class Demo {
@Test
public void fun1(){
//1 创建容器对象
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
//2 向容器"要"user对象
User u1 = (User) ac.getBean("user");
User u2 = (User) ac.getBean("user");
System.out.println(u1==u2);
//3 打印user对象
System.out.println(u1);
ac.close();
}
}
运行JUnit测试输出:
我是初始化方法!
true
User [name=tom, age=18, car=Car [name=car2:9手夏利, color=屎黄色]]
我是销毁方法!
@Resource
@Resource,手动注入,默认是按照byName自动注入。
@Resource有两个重要的属性,name和type:
- name: name属性解析为bean的名字.
- type: type属性解析为bean的类型。
如果使用name属性,则使用byName的自动注入策略;而使用type属性则使用byType自动注入策略;如果既不指定name也不指定type属性,这时通过反射机制使用byName自动注入策略。
@Resource装配的顺序:
- 如果同时指定了name和type属性,则从spring上下问中找到唯一匹配的bean进行装配,如果没有找到,则会抛出异常。
- 如果指定了name属性,则从spring上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常。
- 如果指定type属性,则从spring上下文中找到类型匹配的唯一bean进行装配,找不到或者是找到多个,则抛出异常。
- 如果既没有指定name属性,也没有指定type属性,则默认是按照byName的方式进行装配;如果没有匹配,则返回一个原始的类型进行装配,如果匹配则自动装配。
package pers.zhang.bean;
@Repository("user")
@Scope(scopeName="singleton")
public class User {
@Value("tom")
private String name;
@Value("18")
private Integer age;
@Resource(name="car1")//手动注入
private Car car;
public Car getCar() {
return car;
}
public void setCar(Car car) {
this.car = car;
}
public String getName() {
return name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@PostConstruct
public void init(){
System.out.println("我是初始化方法!");
}
@PreDestroy
public void destory(){
System.out.println("我是销毁方法!");
}
@Override
public String toString() {
return "User [name=" + name + ", age=" + age + ", car=" + car + "]";
}
}
测试方法:
package pers.zhang.test;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import pers.zhang.bean.User;
public class Demo {
@Test
public void fun1(){
//1 创建容器对象
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
//2 向容器"要"user对象
User u1 = (User) ac.getBean("user");
User u2 = (User) ac.getBean("user");
System.out.println(u1==u2);
//3 打印user对象
System.out.println(u1);
ac.close();
}
}
运行JUnit测试输出:
我是初始化方法!
true
User [name=tom, age=18, car=Car [name=car1:2手捷达, color=呕吐绿]]
我是销毁方法!