DI注解
@Autowired
看看之前开发的案例, 两个bean(bean1和bean2), bean1依赖bean2
bean代码:
public class Bean1 {
private Bean2 b2;
public void setB2(Bean2 b2) {
this.b2 = b2;
}
@Override
public String toString() {
return "Bean1 [b2=" + b2 + "]";
}
}
class Bean2{
}
配置文件中这样配:
<bean id="bean1" class="_03_anno._1_start.Bean1" />
<bean id="bean2" class="_03_anno._1_start.Bean2" />
这时打印一个bean1对象, 其中的bean2属性是空的(很好理解, 因为没有给bean1注入bean2):
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:_03_anno/_1_start/AppTest.xml")
public class App {
@Autowired
private Bean1 b1;
@Test
public void testBean1(){
System.out.println(b1);
}
}
之前开发时是在bean元素里配置property元素, 然后再注入. 现在使用@AutoWired
简单些, 直接在属性上面或者属性的setter方法上面配置@AutoWired
即可
package _03_anno._1_start;
import org.springframework.beans.factory.annotation.Autowired;
public class Bean1 {
@Autowired
private Bean2 b2;
@Override
public String toString() {
return "Bean1 [b2=" + b2 + "]";
}
}
class Bean2{
}
在属性上配置时甚至可以不要setter方法
注意: 最好还是在配置文件中配上<context:annotation-config>
, 表示DI注解解析器
<context:annotation-config />
<bean id="bean1" class="_03_anno._1_start.Bean1" />
<bean id="bean2" class="_03_anno._1_start.Bean2" />
说明:
-
@AutoWired
注解默认按照类型去找, 如果找不到这个类则报错. 可以通过@AutoWired(required=false)
来解决这个问题 -
因为
@AutoWired
注解默认按照类型去找, 如果一个类型的bean配置有多个对象(如下面情况)则报错
<bean id="bean1_01" class="_03_anno._1_start.Bean1" />
<bean id="bean1_02" class="_03_anno._1_start.Bean1" />
<bean id="bean2" class="_03_anno._1_start.Bean2" />
使用@Qualifier(bean.id")
的方式可以按照类型去寻找对象, 找到多个再按id去寻找(如果找不到则报错)
@Autowired
@Qualifier("bean1_01")
private Bean1 b1;
@Test
public void testBean1(){
System.out.println(b1);
}
@Resource
@Resource
注解和@AutoWired
功能其实是一样的, 且都需要配置<context:annotation-config />
.
不同点:
-
@AutoWired
注解是spring提供的,@Resource
注解是JavaEE官方提供的 -
@AutoWired
先按照类型寻找bean, 再按照名称或者id;@Resource
则先按照名称, 再按照类型
@Value
@AutoWired
注解用于给对象属性注入值, 而**@Value
注解用于给常量注入值**, 相当于bean的property属性. 下面演示:
bean:
package _03_anno._2_value;
import org.springframework.beans.factory.annotation.Value;
public class ValueBean {
private int port;
@Override
public String toString() {
return "ValueBean [port=" + port + "]";
}
}
配置文件:
<bean id="valueBeanId" class="_03_anno._2_value.ValueBean" />
测试代码:
@Autowired
private ValueBean valueBean;
@Test
public void testValueBean(){
System.out.println(valueBean);
}
现在打印出来肯定是空的(port=8080), 因为没有注入值, 之前我们是在配置文件中添加<property>
元素, 在其中设置value属性, 现在使用注解方式:
@Value("8080")
private int port;
@Override
public String toString() {
return "ValueBean [port=" + port + "]";
}
这样就能输出"port=8080"
然而这样做显然没有什么用, 因为我还不如直接写private int port = 8080;
, 但为了降低耦合度, 可以将值写在properties文件中, 让spring加载配置文件即可
新建service.properties文件:
service.port=8080
此时的xml文件需要加载service.properties文件:
<context:annotation-config />
<context:property-placeholder location="classpath:_03_anno/_2_value/service.properties" />
<bean id="valueBeanId" class="_03_anno._2_value.ValueBean" />
现在的bean应该这样写(解析properties文件):
@Value("${service.port}")
private int port;
@Override
public String toString() {
return "ValueBean [port=" + port + "]";
}
测试代码不变, 此时就能正常输出port=8080
IoC注解
@Component
之前我们让spring帮我们管理bean使用的是xml配置方式, 但这样有点繁琐, 使用注解可以简单很多
bean
package _03_anno._3_component;
import org.springframework.stereotype.Component;
@Component("componentBeanId")
public class ComponentBean {
}
@Component
注解即代表控制反转, 括号里的value值相当于xml配置中的id属性值. 因为贴在哪个类上就代表管理哪个类, 所以不需要类似xml中的class属性
但这样还不够. 类似使用注入注解需要在xm文件中配置"DI注解解析器", 使用IoC注解需要在xm文件中配置"IoC注解解析器", 用于解析IoC注解(即@Component
)
<context:component-scan base-package="_03_anno._3_component" />
base-package
属性代表包名, 表示从哪个包下去扫描组件注解
bean组件版型
其实除开@Component
注解另外还有3个组件, 但这4个组件功能是一样的, 下面简单说下区别:
-
@Component
: 泛指组件, 当不知道如何归类时使用这个 -
@Repository
: Dao组件 -
@Service
: 业务层组件 -
@Controller
: 控制层组件
优先使用下面3个.
作用域和生命周期配置
之前配置bean的作用域(单例还是多例)和生命周期方法是在xml中配置的, 如下:
bean:
package _03_anno._4_lifecycle;
public class Bean1 {
public Bean1(){
System.out.println("Bean1 constructor");
}
public void open(){
System.out.println("Bean1.open()");
}
public void doWork(){
System.out.println("Bean1.doWork()");
}
public void destroy(){
System.out.println("Bean1.destroy()");
}
}
配置文件:
<bean id="bean1" class="_03_anno._4_lifecycle.Bean1"
scope="prototype" init-method="open" destroy-method="destroy" />
现在使用注解配置, scope属性对应@Scope
, init-method属性对应@PostConstruct
, destroy-method属性对应PreDestroy
, 如下演示:
bean:
package _03_anno._4_lifecycle;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
@Scope("prototype")
public class Bean1 {
public Bean1(){
System.out.println("Bean1 constructor");
}
public void open(){
System.out.println("Bean1.open()");
}
public void doWork(){
System.out.println("Bean1.doWork()");
}
public void destroy(){
System.out.println("Bean1.destroy()");
}
}
配置文件中需添加<context:component-scan base-package="" >