依赖注入方式&自动装配
# 依赖注入方式
![[Pasted image 20220611193259.png]]
# setter注入
其实在先前的说明中,不难发现,所谓的依赖也能用于间接实现有参构造,我们可以通过applicationContext.xml
中的property
标签实现。 继续使用上节中的applicationContext.xml
数据作为例子
public class UserServiceImpl implements UserService{
UserDao ud;
public int age;
public String name;
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public void setUd(UserDao ud) {
this.ud = ud;
}
@Override
public void save() {
System.out.println(this.name+this.age);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<bean id="userServiceImpl" class="com.project.service.impl.UserServiceImpl">
<property name="ud" ref="userDaoImpl"/>
<property name="age" value="10"/>
<property name="name" value="LamKaKuan"/>
</bean>
2
3
4
5
我们可以为UserServiceImpl
类配置两个简单类型的变量,然后通过property标签填入值,你能看到引用类型用ref
,简单类型用value
# 构造器注入
说白了就是有参构造器而已,但不能像以往一样直接一个有参构造器就完事儿了,需要在配置文件中说明才行。
public class UserServiceImpl implements UserService{
UserDao ud;
public UserServiceImpl(UserDao ud){
this.ud=ud;
}
@Override
public void save() {
System.out.println("hello");
}
}
2
3
4
5
6
7
8
9
10
11
12
<bean id="userServiceImpl" class="com.project.service.impl.UserServiceImpl">
<constructor-arg name="ud" ref="userDaoImpl"/>
</bean>
2
3
不难发现,其实和setter的区别就是多了个有参构造器以及内嵌了新的constructor-arg
标签,name属性填写的是有参构造器中参数的名称,ref则是引用类型
这种方式很明显违背了Spring的初衷(解耦合),当你更改参数名时也必须更改配置文件
于是作者又更新了新属性
<bean id="userServiceImpl" class="com.project.service.impl.UserServiceImpl">
<constructor-arg index="0" ref="userDaoImpl"/>
<constructor-arg index="1" value="10"/>
</bean>
2
3
4
为constructor-arg
增加一个index作为识别顺序,从而解决耦合,但老实说这没卵用,我换了参数顺序你不就寄了吗
# 选择
![[Pasted image 20220611195459.png]]
# 依赖自动装配
- IoC容器根据Bean所依赖的资源在容器中自动寻找并注入到Bean中的过程
使用自动装配时,总共两步
- 需要被自动装配的变量必须已写有setter方法
- bean标签中写有autowire=bytype
# 选项
autowire有以下几个值可选:
- byType - 根据类型去判断(同时多个候选则无法使用)
- byName - 根据变量名判断
# byType
<bean id="userDaoImpl" class="com.project.dao.impl.UserDaoImpl"/>
<bean id="userDaoImpl2" class="com.project.dao.impl.UserDaoImpl"/>
<bean id="userServiceImpl" class="com.project.service.impl.UserServiceImpl" autowire="byType"/>
2
3
public class UserServiceImpl implements UserService{
UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void save() {
System.out.println("hello");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
假设此时有两个userDao的实现类,对于UserServiceImpl而言就无法判断该装配哪一个了。
# byName
虽然两个实现类很少见,但也并非不存在,真遇到这种问题该如何解决呢? byName选项就是根据名字进行判断
<bean id="userDao" class="com.project.dao.impl.UserDaoImpl"/>
<bean id="userDao2" class="com.project.dao.impl.UserDaoImpl"/>
<bean id="userServiceImpl" class="com.project.service.impl.UserServiceImpl" autowire="byName"/>
2
3
public class UserServiceImpl implements UserService{
UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void save() {
System.out.println("hello");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
那么此时,就会自动装配id为userDao
的。这个名字缘由是根据setUserDao
函数去除set
并将首字母小写得出的。
# 依赖自动装配特征
![[Pasted image 20220612004323.png]]
# 第三方数据管理
以C3P0为例
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.cj.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql:///user_info"/>
<property name="user" value="root"/>
<property name="password" value="Lam1134312514"/>
</bean>
2
3
4
5
6
其实创建第三方的类也就是在class填写对应类的位置而已。
# Properties
上面第三方例子中不难发现一个问题,我们又把参数写死了,这导致耦合度很高。 因此我们应该利用Properties将数据抽出来单独放置
首先这是resources/jdbc.properties
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql:///user_info
jdbc.username=root
jdbc.password=root
2
3
4
- 开辟命名空间
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
2
3
4
5
6
这是文件头!!!
使用context空间加载properties文件 在
<beans>
标签下添加<context:property-placeholder location="jdbc.properties"/>
使用properties中的变量
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
2
3
4
5
6
# 注意
在定义properties文件时你可能注意到了一个问题,为什么我们定义的是jdbc.username
而不直接是username
呢?
建议在使用此类方法时都加上前缀,因为谁也不知道系统变量中是否恰好有个变量与你重名,系统变量的优先级远高于你的properties变量优先级!
当然,如果你头铁打死不加前缀,那你也能在<context>
标签内添加属性system-properties-mode="Never"
,这个属性告知系统忽视原有的系统变量
# 小技巧
倘若你需要加载多个properties配置文件,有两种方式
- 加载所有properties:
<context:property-placeholder location="*.properties"/>
- 加载特定properties:
<context:property-placeholder location="jdbc1.properties,jdbc2.properties"/>
但以上两种方式只能读自己项目的properties文件,如果你想读取第三方jar中的properties,你需要使用<context:property-placeholder location="classpath*:*.properties"/>
![[Pasted image 20220612142220.png]]