一、依赖注入DI
1、概念
依赖:指Bean对象的创建依赖于容器 . Bean对象的依赖资源 .
注入:指Bean对象所依赖的资源 , 由容器来设置和装配
依赖注入就是当一个bean实例引用到了另外一个bean实例时spring容器帮助我们创建依赖bean实例并注入(传递)到另一个bean中,如上述案例中的AccountService依赖于AccountDao,Spring容器会在创建AccountService的实现类和AccountDao的实现类后,把AccountDao的实现类注入AccountService实例中,下面分别介绍setter注入和构造函数注入。
2、注入方式
(1)、构造器注入
构造器注入即被注入的属性需要有set方法, Setter注入支持简单类型和引用类型,Setter注入时在bean实例创建完成后执行的。
构造器注入依赖于构造方法实现,采用反射的方式,通过使用构造方法来完成注入,构造器注入在元素里声明属性
四种常见的构造器注入的方式:无参构造、索引匹配、类型匹配、名称匹配
定义一个JavaBean对象,为其提供构造方法
spring配置文件context.xml
测试
在配置文件中配置该类的bean,并配置构造器,在配置构造器中用到了<constructor-arg>节点,该节点有四个属性:
index是索引,指定注入的属性,从0开始,如:0代表personDao,1代表str属性;
type是指该属性所对应的类型,如Persondao对应的是com.aptech.dao.PersonDAO;
ref 是指引用的依赖对象;
value 当注入的不是依赖对象,而是基本数据类型时,就用value;
(2)、set注入
Set方式注入可以给指定Bean注入属性值或者依赖的对象,属性注入使用元素, 使用 name 属性指定 Bean 的属性名称,value 属性或 子节点指定属性值.
定义一个JavaBean并赋予其Setter方法
注入各种数据类型
测试
3、注解的注入
autowire byName (按名称自动装配),会自动在容器上下文中查找,和自己对象set方法后面的值对应的beand id
使用的时需要保证所有的bean id唯一,并且这个bean需要和自动注入的属性的set方法的值一致
4、扩展的注入
(1)、常量的注入
<!-- 第一种 普通值注入 value-->
<property name="name" value="张三"/>
(2)、set方式注入
<property name="aid" value="22"></property>
(3)、构造方法注入
构造方法里的参数顺序
<constructor-arg value="值"/>
构造方法里参数的索引位置
<constructor-arg index="索引" value="值"/>
(4)、Bean注入
<!-- 第二种 Bean注入 ref-->
<property name="address" ref="address"/>
(5)、数组注入
<!-- 第三种 数组注入 -->
<property name="books">
<!-- 数组 -->
<array>
<value>红楼梦</value>
<value>水浒传</value>
<value>西游记</value>
<value>三国演义</value>
</array>
</property>
(6)、List注入
<!-- 第四种 List注入 -->
<property name="hobbys">
<list>
<value>唱歌</value>
<value>阅读</value>
<value>观影</value>
</list>
</property>
(7)、Map注入
<!-- 第五种 Map注入 -->
<property name="card">
<map>
<entry key="省份证" value="123456789"/>
<entry key="银行卡" value="987654321"/>
</map>
</property>
(8)、ull注入
<!-- 第七种 Null注入 -->
<property name="wife">
<null/>
</property>
(9)、Properties注入
<!-- 第八种 Properties注入 -->
<property name="info">
<props>
<prop key="学号">20201501</prop>
<prop key="姓名">"王浩"</prop>
<prop key="性别">"男"</prop>
</props>
</property>
(10)、p命令注入
注意:这里没有有参构造器
User类
public class User {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "user{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
userbeans.xml
<!-- p命名空间注入,可以直接注入属性的值:property-->
<bean id="user" class="com.User" p:name="张三" p:age="18"/>
测试类
public class mytest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml");
User user = context.getBean("user",User.class);
System.out.println(user);
}
}
(11)、c命名注入
注意:这里有有参构造器
User类
public class User {
private String name;
private int age;
public User(){
}
public User(String name,int age){
this.name=name;
this.age=age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "user{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
userbeans.xml
<!-- c命名空间注入,可以通过构造器注入:constructs-->
<bean id="user2" class="com.User" c:name="李四" c:age="20"/>
测试类
public class mytest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml");
User user = context.getBean("user2",User.class);
System.out.println(user);
}
}
5、Bean的自动装配
Spring中bean有三种装配机制,分别是:
在xml中显式配置;
在java中显式配置;
隐式的bean发现机制和自动装配
Spring的自动装配的两个操作:
组件扫描(component scanning):spring会自动发现应用上下文中所创建的bean;
自动装配(autowiring):spring自动满足bean之间的依赖,也就是我们说的IoC/DI;
Cat类
Dog类
People类
beans.xml
mytest
(1)、byName
autowire byName (按名称自动装配),会自动在容器上下文中查找,和自己对象set方法后面的值对应的beand id
使用的时需要保证所有的bean id唯一,并且这个bean需要和自动注入的属性的set方法的值一致
当一个bean节点带有 autowire byName的属性时:
将查找其类中所有的set方法名,例如setCat,获得将set去掉并且首字母小写的字符串,即cat。
去spring容器中寻找是否有此字符串名称id的对象。
如果有,就取出注入;如果没有,就报空指针异常。
(2)、byType
autowire byType (按类型自动装配),会自动在容器上下文中查找,和自己对象属性类型相同bean
使用的的时候,需要保证所有bean的class唯一,并且这个bean需要和自动注入的属性的类型一致
7、使用注解
在spring配置文件中引入context文件头
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
开启属性注解支持
<context:annotation-config/>
(1)、@Autowired
@Autowired是按类型自动转配的,不支持id匹配。
需要导入 spring-aop的包
将People中set方法去掉,使用@Autowired注解
beans.xml
(2)、@Qualifier
@Autowired是根据类型自动装配的,加上@Qualifier则可以根据byName的方式自动装配
@Qualifier不能单独使用。
beams.xml
People
(3)、@Resource
maven配置porm.xml中未导入java注释需要的包
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>jsr250-api</artifactId>
<version>1.0</version>
</dependency>
@Resource如有指定的name属性,先按该属性进行byName方式查找装配;
其次再进行默认的byName方式进行装配;
如果以上都不成功,则按byType的方式自动装配。
都不成功,则报异常。
People
beans.xml
8、使用注解开发
(1)、属性注入
User类
可以不用提供set方法,直接在直接名上添加@value(“值”)
@Component("user")
// 相当于配置文件中 <bean id="user" class="当前注解的类"/>
public class User {
@Value("秦疆")
// 相当于配置文件中 <property name="name" value="秦疆"/>
public String name;
}
如果提供了set方法,在set方法上添加@value(“值”);
@Component("user")
public class User {
public String name;
@Value("秦疆")
public void setName(String name) {
this.name = name;
}
}
(2)、衍生注解
@Component三个衍生注解
为了更好的进行分层,Spring可以使用其它三个注解,功能一样,目前使用哪一个功能都一样。
@Controller:controller层
@Service:service层
@Repository:dao层
写上这些注解,就相当于将这个类交给Spring管理装配
(3)、作用域
所谓Bean的作用域是指spring容器创建Bean后的生存周期即由创建到销毁的整个过程。
singleton(单例模式) |
在spring IoC容器仅存在一个Bean实例,Bean以单例方式存在,bean作用域范围的默认值。 |
prototype(原型模式) |
每次从容器中调用Bean时,都返回一个新的实例,即每次调用getBean()时,相当于执行newXxxBean()。 |
request |
每次HTTP请求都会创建一个新的Bean,该作用域仅适用于web的Spring WebApplicationContext环境。 |
session |
同一个HTTP Session共享一个Bean,不同Session使用不同的Bean。该作用域仅适用于web的Spring WebApplicationContext环境。 |
application |
限定一个Bean的作用域为ServletContext的生命周期。该作用域仅适用于web的Spring WebApplicationContext环境。 |
singleton
Spring默认所有Bean其作用域都是Singleton。在这样的作用域下,每一个Bean的实例只会被创建一次,而且Spring容器在整个应用程序生存期中都可以使用该实例。因此之前的代码中spring容器创建Bean后,通过代码获取的bean,无论多少次,都是同一个Bean的实例。我们可使用<bean>标签的scope属性来指定一个Bean的作用域,如下:
prototype
prototype,它代表每次获取Bean实例时都会新创建一个实例对象,类似new操作符
区别
singleton:默认的,Spring会采用单例模式创建这个对象。关闭工厂 ,所有的对象都会销毁。
prototype:多例模式。关闭工厂 ,所有的对象不会销毁。内部的垃圾回收机制会回收
(4)、生命周期
Bean实例生命周期的执行过程如下:
Spring对bean进行实例化,默认bean是单例;
Spring对bean进行依赖注入;
如果bean实现了BeanNameAware接口,Spring将bean的名称传给setBeanName()方法;
如果bean实现了BeanFactoryAware接口,Spring将调用setBeanFactory()方法,将BeanFactory实例传进来;
如果bean实现了ApplicationContextAware接口,它的setApplicationContext()方法将被调用,将应用上下文的引用传入到bean中;
如果bean实现了BeanPostProcessor接口,它的postProcessBeforeInitialization()方法将被调用;
如果bean中有方法添加了@PostConstruct注解,那么该方法将被调用;
如果bean实现了InitializingBean接口,spring将调用它的afterPropertiesSet()接口方法,类似的如果bean使用了init-method属性声明了初始化方法,该方法也会被调用;
如果在xml文件中通过<bean>标签的init-method元素指定了初始化方法,那么该方法将被调用;
如果bean实现了BeanPostProcessor接口,它的postProcessAfterInitialization()接口方法将被调用;
此时bean已经准备就绪,可以被应用程序使用了,他们将一直驻留在应用上下文中,直到该应用上下文被销毁;
如果bean中有方法添加了@PreDestroy注解,那么该方法将被调用;
若bean实现了DisposableBean接口,spring将调用它的distroy()接口方法。同样的,如果bean使用了destroy-method属性声明了销毁方法,则该方法被调用;