Spring中的依赖注入(2)

一、背景

实现控制反转(Ioc)的方式有很多种,依赖注入(DI)是实现控制反转的一种方法.

1.有数据层dao包和服务层service包

分别在其中定义了相应的接口.

1.1接口UserDao

在UserDao接口中定义了一个getUserName方法

package dao; /** * @Author Yiang37 * @Date 2020/4/30 20:16 * Description: */ public interface UserDao { void getUserName(); }

它有两个实现类UserDaoImplByMysql与UserDaoImplByOracle

UserDaoImplByMysql代码如下

package dao.impl; import dao.UserDao; /** * @Class: UserDaoImplByMysql * @Author: Yiang37 * @Date: 2020/4/30 20:19 * @Description: */ public class UserDaoImplByMysql implements UserDao { public void getUserName() { System.out.println("我是Mysql的getUserName"); } }

UserDaoImplByOracle代码如下

package dao.impl; import dao.UserDao; /** * @Class: UserDaoImpl * @Author: Yiang37 * @Date: 2020/4/30 20:17 * @Description: */ public class UserDaoImplByOracle implements UserDao { public void getUserName() { System.out.println("我是Oracle的getUserName"); } }

1.2接口UserService

在UserService接口中定义了一个outUserName()方法

package service; /** * @Class: UserService * @Author: Yiang37 * @Date: 2020/4/30 20:21 * @Description: */ public interface UserService { void outUserName(); }

它有一个实现类UserServiceImpl

UserServiceImpl代码如下

package service.impl; import dao.UserDao; import dao.impl.UserDaoImplByMysql; import service.UserService; /** * @Class: UserService * @Author: Yiang37 * @Date: 2020/4/30 20:21 * @Description: */ public class UserServiceImpl implements UserService { UserDao userDao = new UserDaoImplByMysql(); public void outUserName() { userDao.getUserName(); } }

2.使用service完成操作

import service.UserService; import service.impl.UserServiceImpl; /** * @Class: Test1 * @Author: Yiang37 * @Date: 2020/4/30 20:23 * @Description: */ public class Test1 { public static void main(String[] args) { UserService userService = new UserServiceImpl(); userService.outUserName(); } }

可以看到程序输出

我是Mysql的getUserName

二、分析

1.修改显示的值

在上面的内容中我们看到userService.outUserName()在执行后输出了

我是Mysql的getUserName

为什么会是这个结果?这还得回到UserServiceImpl的代码中

1. public class UserServiceImpl implements UserService { 2. 3. UserDao userDao = new UserDaoImplByMysql(); 4. 5. public void outUserName() { 6. userDao.getUserName(); 7. } 8. }

我们看到在代码的第6行调用了userDao.getUserName();
而这个userDao是谁呢?
在代码的第3行指定了

UserDao userDao = new UserDaoImplByMysql();

现在问题来了,我们想修改为UserDaoImplByOracle()来实现?
可以更改上面userDao的具体实现类

UserDao userDao = new UserDaoImplByOracle();

程序输出了我们想要的结果

我是Oracle的getUserName

通过上面的方式,虽然能达到结果,但是我们得修改掉原来的代码
在调用userDao时,我们就得明确的指明它的实现类.

2.改进

我们是否可以每次为userService动态的指定其中的userDao呢?
修改我们的代码,变成下面这样.

public class UserServiceImpl implements UserService { //成员变量UserDao private UserDao userDao; //UserDao属性的set方法 public void setUserDao(UserDao userDao) { this.userDao = userDao; } public void outUserName() { userDao.getUserName(); } }

现在我们再修改测试类

public class Test1 { public static void main(String[] args) { UserService userService = new UserServiceImpl(); ((UserServiceImpl)userService).setUserDao(new UserDaoImplByMysql()); userService.outUserName(); } }

注:
UserService userService = new UserServiceImpl();
左侧是UserService类型,是找不到setUserDao()方法的.
所以在setUserDao()方法之前进行了类型转换.
( (UserServiceImpl) userService).xx

当创建时指明类型为UserServiceImpl时才有这个方法
"多态相关知识"

现在我们通过set方法为其注入了UserDaoImplByMysql对象,再次运行.

我是Mysql的getUserName

为其set进不同的值,例如.

((UserServiceImpl)userService).setUserDao(new UserDaoImplByOracle());

同样能成功输出想要的内容

我是Oracle的getUserName

3、对比

一开始是程序主动设置userDao对象,现在变成了程序被动的接收userDao对象.
我们可以看到,通过后者,无需更改UserServiceImpl中的任何内容.
只是在使用通过set注入,即可实现想要的效果.

当写了新的userDao时,例如.

public class UserDaoImplBySqlServer implements UserDao { public void getUserName() { System.out.println("我是SqlServer的getUserName"); } }

然后在使用时set进不同的内容

((UserServiceImpl)userService).setUserDao(new UserDaoImplBySqlServer());

输出值即可改变

我是SqlServer的getUserName

通过上面的方式,写代码时不用再去管理对象的创建了.
例如UserServiceImpl中的userDao对象我们不用显示的指明,而是可以从外部动态的(被动的)接收.

我还没工作啊我设想一下,controller层调用service层吧.
我们是不是可以直接在controller中写判断之类的,然后根据情况动态的set进去业务对象,实现不同的业务逻辑.
这样控制权就交给了用户,用户来选择执行哪个业务方法?

控制反转,现在是不是反转了.
这就是Ioc的原型.

三、Spring中的Ioc

1.xml文件

创建一个pojo包,里面创建一个User类,包含一个成员变量username.
为其设置上set方法和toString方法.

package pojo; /** * @Class: User * @Author: Yiang37 * @Date: 2020/4/30 22:34 * @Description: */ public class User { private String username; //set方法 public void setUsername(String username) { this.username = username; } @Override public String toString() { return "User{" + "username='" + username + '\'' + '}'; } }

在xml文件中声明一个对应的bean.

<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- id对应bean的名字 class对应类的位置--> <bean id="user" class="pojo.User"> <!-- name对应成员变量username --> <!-- value是我们要赋的值 --> <property name="username" value="yang"> </property> </bean> </beans>

编写测试类.

import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext; import pojo.User; /** * @Class: Test2 * @Author: Yiang37 * @Date: 2020/4/30 22:38 * @Description: */ public class Test2 { public static void main(String[] args) { // 参数是具体的文件位置 ApplicationContext context =new FileSystemXmlApplicationContext("./web/WEB-INF/applicationContext.xml"); //与xml中的bean.id对应 User tUser = (User) context.getBean("user"); System.out.println(tUser.toString()); } }

运行后,可以看到username成功注入"yang".

User{username='yang'}

tUser对象是谁创建的?
Spring创建的

tUser对象的属性怎么设置的?
Spring容器设置的

总结

<!-- Java创建对象 User user = new User(); xx类 bean名字 = new xx类() --> <bean id="bean名字" class="xx类"> <property name="属性名字" value="需要传入的参数"> </property> </bean> <!-- 外层<bean></bean>标签相当于创建类 内层<property></<property>标签相当于给属性赋值 -->

控制反转,把控制权转交给了Spring容器(<beans></beans>包裹).
我们使用的时候从Spring容器中拿就可以了,不用我们new对象.
即使用了Spring框架后,对象是由Spring创建的.

Spring通过依赖注入的方式完成了控制反转.
set方式注入

在User中注释掉set方法

public class User { private String username; //set方法 // public void setUsername(String username) { // this.username = username; // } @Override public String toString() { return "User{" + "username='" + username + '\'' + '}'; } }

可以看到xml中报错.

扩展
在xml中注释掉property.

<bean id="user" class="pojo.User"> <!-- <property name="username" value="yang">--> <!-- </property>--> </bean>

图示位置没有图标显示

现在取消注释

<bean id="user" class="pojo.User"> <property name="username" value="yang"> </property> </bean>

图示位置出现Spring标志

点击可以跳转到相应的位置

同理,类前方也会有Spring的标志

看到这些标志就说明已经被Spring托管了.

下面前面提到的UserServiceImpl进行配置

<bean id="daoImplByMysql" class="dao.impl.UserDaoImplByMysql"> </bean> <bean id="daoImplByOracle" class="dao.impl.UserDaoImplByOracle"> </bean> <bean id="userServiceImpl" class="service.impl.UserServiceImpl"> <!-- value注入值 ref注入bean,对应beanId即可 --> <property name="userDao" ref="daoImplByMysql"> </property> </bean>

测试类

import org.springframework.context.ApplicationContext; import org.springframework.context.support.FileSystemXmlApplicationContext; import service.impl.UserServiceImpl; /** * @Class: Test3 * @Author: Yiang37 * @Date: 2020/4/30 23:24 * @Description: */ public class Test3 { public static void main(String[] args) { //拿到Spring容器 ApplicationContext applicationContext = new FileSystemXmlApplicationContext("./web/WEB-INF/applicationContext.xml"); UserServiceImpl userServiceImpl = (UserServiceImpl) applicationContext.getBean("userServiceImpl"); userServiceImpl.outUserName(); } }

输出结果

我是Mysql的getUserName

还记得前面是在Java代码中set进去值吗?

((UserServiceImpl)userService).setUserDao(new UserDaoImplBySqlServer());

现在都不用在Java代码写这个东西了,交给Spring后,直接修改xml文件,指明ref后,即可达到目的.
我们只管写代码,完了在xml做好配置就好了.

Spring的Ioc:对象由Spring来创建、管理、装配.

2.Ioc如何创建对象

在上文中,我们构建bean后,通过property为其set进去了值.

<bean id="user" class="pojo.User"> <property name="username" value="yang"> </property> </bean>

即我们了解到<bean></bean>标签可以完成对象的创建.
现在是先构造,再进行property为其set进去值.
那么是否和类一样,存在构造函数的方式呢?
注释掉<property></property>标签,现在我们研究一下SpringBean如何实现构造函数.
现在情况如下.

  • 类User
public class User { private String username; private int age; @Override @Override public String toString() { return "User{" + "username='" + username + '\'' + ", age=" + age + '}'; } }

注意此时我们关注的是构造函数,所以没有写出username属性的set方法.

  • xml文件
<bean id="user" class="pojo.User"> </bean>
  • 测试类
public class Test2 { public static void main(String[] args) { ApplicationContext context =new FileSystemXmlApplicationContext("./web/WEB-INF/applicationContext.xml"); User tUser = (User) context.getBean("user"); System.out.println(tUser.toString()); } }

显示写出无参构造函数

public class User { private String username; private int age; public User() { System.out.println("我是无参构造函数"); } @Override public String toString() { return "User{" + "username='" + username + '\'' + ", age=" + age + '}'; } }

运行测试类,输出.

我是无参构造函数 User{username='null', age=0}

得知,默认执行无参构造函数.
查阅Spring文档,在1.4.1节可以找到有参构造的三种书写方式.
增加有参构造函数.

public class User { private String username; private int age; public User() { System.out.println("我是无参构造函数"); } public User(String username, int age) { this.username = username; this.age = age; } @Override public String toString() { return "User{" + "username='" + username + '\'' + ", age=" + age + '}'; } }

(1)构造函数参数类型匹配
xml文件

<bean id="user" class="pojo.User"> //上面构造形参第一个是String类型,第二个是int,这里我特意把int放在了前面. //是根据参数类型匹配 <constructor-arg type="int" value="22"/> <constructor-arg type="java.lang.String" value="yang"/> </bean>

指明了构造函数,无参函数"我是无参构造函数"不再输出,结果如下.

User{username='yang', age=22}

(2)构造函数参数索引(0开始)
xml文件

<bean id="user" class="pojo.User"> //构造函数参数是String类型,结果还能自动转换. <constructor-arg index="0" value="7500000"/> <constructor-arg index="1" value="42"/> </bean>

结果

User{username='7500000', age=42}

尝试把第二个形参值,即下标1处的int赋值为字符串"abc",很智能,直接报错.

(3)构造函数参数名称
xml文件

<bean id="user" class="pojo.User"> <constructor-arg name="username" value="666"/> <constructor-arg name="age" value="42"/> </bean>

确实智能,直接提示参数名字了.

结果

User{username='666', age=42}

个人觉得用参数名字比较方便吧(参数类型可能重复,参数下标也不好数.)
在注册bean的时候就会给我们创建对象

<bean id="user" class="pojo.User"> <constructor-arg name="username" value="666"/> <constructor-arg name="age" value="42"/> </bean> //增加一个无参函数构建的bean <bean id="otherUser" class="pojo.User"> </bean>

测试类用的bean是user,这个otherUser没有用.

public class Test2 { public static void main(String[] args) { ApplicationContext context =new FileSystemXmlApplicationContext("./web/WEB-INF/applicationContext.xml"); //拿到的bean名字叫user,名字叫otherUser的bean没使用. User tUser = (User) context.getBean("user"); System.out.println(tUser.toString()); } }

结果第一行显示otherUser对象也会被创建.

我是无参构造函数 User{username='666', age=42}

无论是否使用,Spring容器在启动时就会创建完成注册的bean对象,等需要用了自己取就好.
例如:不管你家有没有打野位置,野怪都会生成,想打了去打就好了.
修改测试类,获取同一个bean.

public class Test2 { public static void main(String[] args) { ApplicationContext context =new FileSystemXmlApplicationContext("./web/WEB-INF/applicationContext.xml"); User tUser = (User) context.getBean("user"); User tUser2 = (User) context.getBean("user"); System.out.println(tUser.toString()); System.out.println(tUser2.toString()); System.out.println(tUser == tUser2); } }

结果显示true,说明这个bean的实例只有一份.
总结

<beans></beans>标签包裹着我们的<bean></bean>标签.
beans就相当于Spring容器,bean就是容器里的东西.

无论用不用,Spring容器在启动时就会将注册的bean创建,需要了获取即可.

Sring实现控制反转有无参构造后set方式注入直接通过构造函数注入这两种常见方式(依赖注入).

3.依赖注入

  • 依赖:bean对象的创建依赖于bean容器
  • 注入:bean对象的属性通过bean容器注入

__EOF__

本文作者羊37
本文链接https://www.cnblogs.com/yang37/p/12811265.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   羊37  阅读(325)  评论(0编辑  收藏  举报
编辑推荐:
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示