依赖注入
全局知识要点回顾
配置业务层Service
-
接口
-
public interface UserService { // save方法--调用dao层的save方法 public void save(); }
-
实现类
-
// 表示我们的业务层 public class UserServiceImpl implements UserService { // 调用到dao层的save方法 public void save() { ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml"); // 通过getBean方法获取到我们刚刚在容器中创建好的组件 UserImpl userdao = (UserImpl) app.getBean("Userdao"); userdao.save(); } }
-
将我们的UserService注入到配置文件当中
-
<!--Service层的对象--> <bean id="UserService" class="com.waves.service.impl.UserServiceImpl"></bean>
1.2、模拟Controller控制层,模拟web环境下的后端响应
// 模拟控制层--响应web开发(目前是没得的)
public class UserController {
public static void main(String[] args) {
// 加载配置文件
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
// 获取我们注入到配置文件的UserService对象
UserService userService = (UserService) app.getBean("UserService");
userService.save();
}
}
- 启动–响应结果
2、Spring配置–依赖注入(Bean依赖注入分析)
2.1、分析部分
-
依赖注入(Dependency Injection):它是Spring框架核心IOC的具体实现
-
在我们编写程序的时候,通过控制反转,将对象的创建权交给了Spring来帮我们进行管理和创建,但是我们写代码的,不可能出现没有依赖的情况,例如我们的控制层,依赖于业务层,业务层,依赖于我们的持久层,持续套娃,IOC解耦只是为了降低他们的依赖关系,并不会消除,也就是说,业务层仍旧会调用持久层的方法,而控制层仍旧会调用业务层的方法
-
那种业务层和持久层的依赖关系,使用Spring之后,就让Spring来维护了,简单的说就是坐等框架把持久层的对象传入业务层,而不用我们自己去调用
-
刚刚那坨代码有大问题,就是没有依赖注入,我表达不出来,但是可以很明显的看到,这个程序运行完了以后,加载配置文件的方法被执行了两次,相当的麻烦,如果我能够在Service层,将Dao层的对象获取到,我是不是就可以不用在Service层就直接加载配置文件了?
2.2、对象依赖注入的两种方法(set方法or有参构造)
-
set方法
-
有参构造
-
我们主要将set方法,在UserService层创建一个私有的UserDao对象,通过Set的方法将对象直接注入到我们Service创建的那个Userdao对象中
-
// 创建一个UserDao的对象 private UserDao userdao; // 将这个对象传递到我们本身创建的这个对象中 public void setUserdao(UserDao userdao) { this.userdao = userdao; }
-
实现UserService中的save方法
-
// 调用到dao层的save方法 public void save() { // 直接用我们创建好的这个对象来调用Dao层的save()方法 userdao.save(); }
-
问题–你知道,但是Spring不知道,那该怎么办?
-
我们需要稍稍改造一下我们刚开始在容器中创建的Service对象
-
property属性设置,name,是set方法后面那坨东西的名字,并且,第一个字母必须是小写
-
例如setUserdao,那么我写这个name的时候就必须要写成name=“userdao”
-
ref:对象的引用,你要把哪个注入到我的Service对象中?—Userdao这个对象撒~
-
<!--Service层的对象--> <bean id="UserService" class="com.waves.service.impl.UserServiceImpl"> <property name="userdao" ref="Userdao"></property> </bean>
-
我现在通过Service实现类中的SetUserdao的方法,将我的Userdao对象注入到我的Service对象中
-
现在我们来测试下
-
-
Very Deep!这就是依赖注入的一个体现
2.3、加深印象
-
如果我通过new 对象的方式来创建这个业务层的对象,会发生什么?
-
public static void main(String[] args) { /*// 加载配置文件 ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml"); // 获取我们注入到配置文件的UserService对象 UserService userService = (UserService) app.getBean("UserService"); userService.save();*/ UserService userService = new UserServiceImpl(); userService.save(); }
-
为什么会空指针异常,因为我new的这个对象并不是从容器当中获取的,而只有容器中的那个UserService的对象,他被UserDao对象注入进去了,我们new的这个对象是木得滴~
3、Spring配置–依赖注入(set方法注入简便的方式)
3.1、引入P命名空间
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
3.2、修改注入的方式
-
如果我们注入的是一个对象的话,那就直接p:userdao-ref=“方法名”(userdao)
-
<bean id="UserService" class="com.waves.service.impl.UserServiceImpl" p:userdao-ref="Userdao"/>
-
测试一下
-
-
Very Deep!但是我们用的多的还是property的属性注入,只有需要设置很多属性的时候,才会用这个,了解一下
4、Spring配置–依赖注入(有参构造)
4.1、创建有参构造
//依赖注入--有参构造
public UserServiceImpl(UserDao userdao) {
this.userdao = userdao;
}
4.2、配置xml文件
-
告诉Spring我是通过有参构造的方式进行依赖注入
-
构造构造–Contructor-arg(参数)–这里放的是我们的构造方法中的参数名
-
ref-你要引用的对象是谁–要把哪个对象注入进来
-
<!-- 有参构造的依赖注入方式 --> <bean id="UserService" class="com.waves.service.impl.UserServiceImpl"> <!-- 还是在内部写 --> <constructor-arg name="userdao" ref="Userdao"></constructor-arg> </bean>
-
测试
-
5、Spring配置–依赖注入(Bean依赖注入的数据类型–普通数据)
上面的操作,都是注入的引用Bean,除了对象的引用可以注入进去,普通的数据类型,集合等,都可以在容器当中注入
5.1、在我们的UserDaoImpl实现类中,设置两个参数,并注入到我们的Bean中
-
要设置好set方法哦,不然怎么注入呢?
-
public class UserImpl implements UserDao { private String name; private Integer age; public void setName(String name) { this.name = name; } public void setAge(Integer age) { this.age = age; } }
-
在Bean中注入我们设置的两个属性值
-
设置姓名为张三,年龄为18
-
<bean id="Userdao" class="com.waves.dao.impl.UserImpl" init-method="init" destroy-method="destory"> <property name="name" value="zhangsan"></property> <property name="age" value="18"></property> </bean>
-
测试
-
6、Spring配置–依赖注入(复杂数据类型)
6.1、在UserDao中新增三个复杂类型的数据,设置好set方法
// Bean数据类型的注入--复杂数据
private List<String> strList;
private Map<String, User> userMap;
private Properties properties;
public void setStrList(List<String> strList) {
this.strList = strList;
}
public void setUserMap(Map<String, User> userMap) {
this.userMap = userMap;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
6.2、List的注入
<bean id="Userdao" class="com.waves.dao.impl.UserImpl">
<!-- 属性的注入-开头肯定是property -->
<property name="strList">
<!-- 这个List是一个复杂的数据类型 -->
<list>
<!--且泛型为String- 使用value-->
<value>aaa</value>
<value>bbb</value>
<value>ccc</value>
<value>ddd</value>
</list>
</property>
</bean>
打印一下结果
6.3、Map的注入
-
因为我这里的Map,内部含了一个User的对象,如果我要对这个user设置值,那么这个User的对象必须要在这个容器当中,这样才能被我引用
-
将User注册到容器当中
-
<!-- 注册User到容器当中 --> <bean id="User1" class="com.waves.unity.User"> <!-- 将User的值注入 --> <property name="name" value="律师罗翔"></property> <property name="hobby" value="审判张三"></property> </bean> <!-- 注册User到容器当中 --> <bean id="User2" class="com.waves.unity.User"> <!-- 将User的值注入 --> <property name="name" value="法外狂徒罗翔"></property> <property name="hobby" value="狡辩"></property> </bean>
-
开始将User注入到map的集合当中
<property name="userMap">
<!-- 他是map类型,内部的注入方式肯定是map -->
<map>
<entry key="user1" value-ref="User1"></entry>
<entry key="user2" value-ref="User2"></entry>
</map>
</property>
- 测试,User那个是因为我们重写了User类当中的toString方法
6.4、properties的注入
他是一个字符串类型的键值对,应该是
<property name="properties">
<!-- 外部写参数类型 -->
<props>
<!-- 一堆一堆的写 -->
<prop key="p1">ppp</prop>
<prop key="p2">hhh</prop>
<prop key="p3">ttt</prop>
<prop key="p4">rrr</prop>
</props>
</property>
- 测试
7、Spring配置文件-import导入文件和知识要点–(分模块开发)
7.1、实际开发中,Spring的配置文件内容会非常多
-
因为设计到控制层-业务层-持久层(dao层)这些模块的开发
-
这些模块不可能放在一个配置文件当中
-
需要相应的去设计每个模块的配置文件
-
如果在项目启动的时候,我们去加载这些配置文件,是不是就会有许多这样的
-
配置文件加载?
-
例如我在这里添加了几个逻辑层的配置文件
-
-
项目运行的时候,我是不是要先加载这些配置文件?
-
因为我需要获取到容器当中,这些配置类所创建的对象
-
ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml"); ClassPathXmlApplicationContext app1 = new ClassPathXmlApplicationContext("applicationContext-controller.xml"); ClassPathXmlApplicationContext app2 = new ClassPathXmlApplicationContext("applicationContext-service.xml"); ClassPathXmlApplicationContext app3 = new ClassPathXmlApplicationContext("applicationContext-user.xml");
7.2、如何解决?----import导入
-
将其他配置文件导入到主配置文件当中,这个配置文件启动的时候带动其他的配置文件一起加载
-
-
导入两个配置试试水
-
<import resource="applicationContext-controller.xml"></import> <import resource="applicationContext-user.xml"></import>
8、Spring配置文件–相关的API1
8.1、ApplicationContext的继承体系–图解
8.2、ApplicationContext的实现类(目前三个)
8.2.1、ClassPathXmlApplicationContext()–使用的最多
他是从类的更路径下加载配置文件–resources下配置文件
8.2.2、FileSystemXmlApplicationContext()–较少,不易改变
这个是从磁盘的路径上加载配置文件,配置文件可以再磁盘的任意位置上
举个例子–二者的对比
// 这个是我们Spring程序的入口函数,参数为我们创建好的那个配置文件xml
ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
// 磁盘加载--内容的跟原路径,死路径
ApplicationContext app1 = new FileSystemXmlApplicationContext("D:\\Spring\\Spring快速入门\\src\\main\\resources\\applicationContext.xml");
8.2.3、AnnotationConfigApplicationContext–注解开发
当使用注解配置容器对象的时候,需要使用这个类来创建Spring的容器,让他来读取注解
9、Spring配置文件–相关API2–getBean
9.1、两个不同类型的接收参数(id和class)
- getBean接口有两个方式参数类型不相同
- 之前案例中用的是普遍以id的方式进行调用
- 第二个需要的参数是–对象的类型
9.2、二者的不同之处
-
第一个getBean,他可以根据id来获取自己想要获取的那个对象,但是获取的时候需要类型强转
-
而第二个getBean,他可以直接通过那个类的对象来获取
-
打印结果为
-
-
public static void main(String[] args) { // 这个是我们Spring程序的入口函数,参数为我们创建好的那个配置文件xml ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml"); // 我要获取我那个user的对象,我已经注释掉了之前容器中创建的第二个user对象 User user1 = app.getBean(User.class); System.out.println(user1); }
- 如果容器中该对象的实例创建超过两次以上,那么**getBean(class)**会报错
9.3、总结
- 在Spring的IOC容器当中,如果我们创建的Bean,他的类型在这个xml配置文件(IOC)容器当中的类型是唯一的
- 那么我们通过上下文对象(ApplicationContext)的getBean去获取到这个组件
- 获取这个组件的方式可以是传入类类型来获取,否则就老老实实使用其他方式来获取