spring
工厂模式解耦 (将表现层调用Dao、Dao调用Service都用指定id和全类名存入配置文件
通过工厂的静态方法从配置文件得到全类名、通过反射获得对象)
配置文件加载------在静态代码块中
获取全类名并反射得到对象-----在工厂方法中
将三层架构的代码,解耦
1. 将需要new的实体类的全类名,存入配置文件bean.properties中
2. 创建一个工厂,静态代码块中配置文件加载,工厂方法中通过传入的bean名从配置文件中得到全类名,通过反射返回实例对象
存在问题:每次getBean都是重新创建,导致如果多次getBean每次得到的对象都不相同,
此处因将反射创建对象也放入静态代码块中,将生成的对象,放入Map,和beanPath组成键值对,getBean只负责根据beanName从Map中取对象
3. 在调用处,使用工厂的工程方法,传入bean名称指定的id,返回一个实例对象
工厂模式解耦:任然存在问题
存在多例问题:
多次打印得到的业务层对象,不唯一
由于没有类成员,不会出现线程安全问题
在Dao、Service层中都不存在方法改变类属性的情况,因此,就不需要每次获取实现类都重新创建,完全可以再次使用
多例:通过循环输出IAccountService对象,查看不是同一个对象
单例:一次创建对象,供多次使用,提高效率,不用每次都创建
配置文件加载、获取所有key、通过每个key得到全类名、再通过全类名反射得到对象,再将key和对象存入Map-----------都在静态代码块中
工厂方法只负责通过参数获取bean名称指定id,返回对象
存在问题:每次getBean都是重新创建,导致如果多次getBean每次得到的对象都不相同,
此处因将反射创建对象也放入静态代码块中,将生成的对象,放入Map,和beanPath组成键值对,getBean只负责根据beanName从Map中取对象
配置文件加载,读取配置文件得到实例化对象,都放入静态代码块中,并存入Map容器,只执行一次,再次调用直接从容器中取
通过bean的名称----配置文件中的key,从容器中取出实例化对象---------------使得自始至终都使用一个对象
IOC 控制反转
用户不需要知道如何得到对象的(即控制创建对象的权限反转给了工厂)--- 控制反转
简单意思就是 表现层调用Dao层、Dao层调用Service层,原来是通过new实现类对象,自主找需要的实现类(Dao、Service)
当使用工厂得到实现类,本身的调用类就失去了自主控制调用实现类的权利,这种控制权发生转移的方式,就叫控制反转
IOC:削减程序的耦合(降低代码的依赖关系)
基于xml的IOC开发环境
创建bean.xml配置文件,设置相应 的id 指向实现类全类名
spring 将工厂解耦的配置文件替换为XML文件,通过容器读取XML文件
再通过容器的方法 getBean( ) ,传入bean指定的id参数,返回相应的对象
---用户不需要知道如何得到对象的(即控制创建对象的权限反转给了容器)
ApplicationContext的三个实现类
使用实现类ClassPathXmlApplicationContext获得ApplicationContext核心容器
ClassPathXmlApplicationContext 默认加载classpath路径下的文件,只需指明对应文件的classpath路径下的配置文件名字即可。FileSystemXmlApplicationContext : 该容器从 XML 文件中加载已被定义的 bean。在这里,你需要提供给构造器 XML 文件的完整路径。
容器就将 读取配置文件,获取bean名称及对应的全类名,并反射得到实例对象封装到Map的过程 全部放入核心容器中
容器中存放的是key-value: bean指定id、对应的对象
根据传入的id 获取 对应的全类名对象 ----两种方法
ac.getBean("id1")
ac.getBean("id2",对应全类名接口的class文件)
获取容器的两种方法:
ApplicationContext及其子实现类 (官方推荐)
BeanFactory及其子实现类+Resource(淘汰)
![](https://i-blog.csdnimg.cn/blog_migrate/1549b4797d2b29cc3b089be21664cd79.png)
![](https://i-blog.csdnimg.cn/blog_migrate/67bd5bc0e12b1ece3219d0eeca56e188.png)
![](https://i-blog.csdnimg.cn/blog_migrate/79a911ef112511f0706dc3a534114c4f.png)
核心容器两个接口引发的问题
![](https://i-blog.csdnimg.cn/blog_migrate/157f85309230cacff84b9f95e99f3752.png)
spring中bean的细节,创建Bean对象的方法
默认构造函数、普通工厂方法、工厂静态方法--------------都只是在bean.xml中<beans>配置
构造函数得到bean的配置
先构造函数得到指定工厂bean,再传入该工厂bean,通过工厂方法返回需要的对象
在构造函数得到bean的基础上,传入的是工厂接口,加入工厂静态方法得到指定bean
容器创建的bean对象,一般是单例的--------只构造一次
bean作用范围
![](https://i-blog.csdnimg.cn/blog_migrate/73e928f7d8b8e53556ecca23083835fc.png)
global session
bean对象的生命周期
![](https://i-blog.csdnimg.cn/blog_migrate/d7b2f596a2451f4950289ae26711bd70.png)
多例对象,容器销毁,也不消失
当为单例对象时,容器销毁,对象消失------------配置文件加载完,立刻创建对象(立刻执行类似applicationContext)
当改为多例对象时,即使容器被销毁了,对象还存在-----------当需要使用对象时,才创建对象(延时类似BeanFactory)
依赖注入
![](https://i-blog.csdnimg.cn/blog_migrate/e5477703a335cafc3dc9fb8eb6f570d4.png)
构造方法注入(一般不用------限制了类的创建(有参数,不好构造类))
![](https://i-blog.csdnimg.cn/blog_migrate/3c2ebca8d4fd1985153f76f2d25100d4.png)
![](https://i-blog.csdnimg.cn/blog_migrate/80be084b1c5f5190d6ef000172761273.png)
![](https://i-blog.csdnimg.cn/blog_migrate/5f835eeeb5b9b87597c2d89b15f7ed0a.png)
set方法注入(常用)
![](https://i-blog.csdnimg.cn/blog_migrate/3eebb06e00f92bf8053d005eb39c27a4.png)
![](https://i-blog.csdnimg.cn/blog_migrate/f402a14d3c3c3c158511c8b98af312b7.png)
![](https://i-blog.csdnimg.cn/blog_migrate/2e2aebe1a02e1482f3702c5a42ba74a8.png)
![](https://i-blog.csdnimg.cn/blog_migrate/47a447807015a95815f0bdb3a6e4d93a.png)
复杂类型的注入---注入集合数据
![](https://i-blog.csdnimg.cn/blog_migrate/45b175253c518f6dc94dde96622d2596.png)
结构相同,标签可以互换
list、array、set
map、props
<map>-----<entry(key、value)> <map>-----<entry(key)>----<value>值
<props>-----<prop(key)>
<property >的name中必须写set方法名称,还必须是首字母小写
曾经XML的配置:
* <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"
* scope="" init-method="" destroy-method="">
* <property name="" value="" | ref=""></property>
* </bean>
*
注解
* 用于创建对象的
* 他们的作用就和在XML配置文件中编写一个<bean>标签实现的功能是一样的
* Component:
* 作用:用于把当前类对象存入spring容器中
* 属性:
* value:用于指定bean的id。当我们不写时,它的默认值是当前类名,且首字母改小写。
* Controller:一般用在表现层
* Service:一般用在业务层
* Repository:一般用在持久层
* 以上三个注解他们的作用和属性与Component是一模一样。
* 他们三个是spring框架为我们提供明确的三层使用的注解,使我们的三层对象更加清晰
* 用于注入数据的
* 他们的作用就和在xml配置文件中的bean标签中写一个<property>标签的作用是一样的
* Autowired:
* 作用:自动按照类型注入。只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功
* 如果ioc容器中没有任何bean的类型和要注入的变量类型匹配,则报错。
* 如果Ioc容器中有多个类型匹配时:
* 出现位置:
* 可以是变量上,也可以是方法上
* 细节:
* 在使用注解注入时,set方法就不是必须的了。
* Qualifier:
* 作用:在按照类中注入的基础之上再按照名称注入。它在给类成员注入时不能单独使用。但是在给方法参数注入时可以
* 属性:
* value:用于指定注入bean的id。
* Resource
* 作用:直接按照bean的id注入。它可以独立使用
* 属性:
* name:用于指定bean的id。
* 以上三个注入都只能注入其他bean类型的数据,而基本类型和String类型无法使用上述注解实现。
* 另外,集合类型的注入只能通过XML来实现。
* Value
* 作用:用于注入基本类型和String类型的数据
* 属性:
* value:用于指定数据的值。它可以使用spring中SpEL(也就是spring的el表达式)
* SpEL的写法:${表达式}
* 用于改变作用范围的
* 他们的作用就和在bean标签中使用scope属性实现的功能是一样的
* Scope
* 作用:用于指定bean的作用范围
* 属性:
* value:指定范围的取值。常用取值:singleton prototype
* 和生命周期相关 了解
* 他们的作用就和在bean标签中使用init-method和destroy-methode的作用是一样的
* PreDestroy
* 作用:用于指定销毁方法
* PostConstruct
* 作用:用于指定初始化方法
创建对象的注解
Component: 将当前类对象存入容器
Controller: 表现层
Service: 业务层
Repository: 持久层
数据注入的注解
Bean类型的数据注入
Autowired 数据注入(方法、变量) 按数据类型注入
Qualifier---value 设置名称,按该名称注入,给类成员注入,不能单独使用,需要和Autowired,注入方法可以单独使用
Resource---value 按设置的名称id注入,可以单独使用
集合类型的注入只能通过xml来注入
基本类型和String类型的数据注入
value---value 可以使用spEL
用于改变作用范围
scope---value (和bean标签中的scope相同)
给一个类添加注解之前,需要在bean.xml中导入context的名称空间,在指定包中扫描注解--------因为还是要通过读取bean.xml
<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">
并添加创建容器时,需要spring扫描的包,告诉spring所需的标签是一个context的名称空间和约束
<!--告知spring在创建容器时要扫描的包,配置所需要的标签不是在beans的约束中,而是一个名称为context名称空间和约束中-->
<context:component-scan base-package="com.itheima"></context:component-scan>
@component ---- 当不属于三层的时候,用来注入类
也可以给注解compoent,加上属性value用于指定bean的id
Controller 表现层
Service 业务层
Repository 持久层
@Autowired 注解注入时,set就不再是必须的了
(注入方法、变量)
@Qualifier(value="key变量名称")
注入类成员变量时,需要和Autowired一起使用,注入方法可以单独使用
@Resource(value="key变量名称")
按设置的名称id注入,可以单独使用
先比较数据类型和容器中value,再比较变量名称
根据数据类型注入,直接跳过key查找对象,有相同的就返回(包括接口类型实现类)
如果value中存在相同的两个对象或者接口类型相同的实现类,就会用变量名称来对比key,如果有相对应的key,就返回value
没有就报错
容器中,先存入key---accountService value---IAccountService实现类对象
再存入IAccountDao实现类 key 分别为 ----accountDao1、----accountDao2
当通过数据类型查找时,容器中value存在两个对应的对象(都是IAccountDao的实现类),再通过变量名称查找,没有key对应accountDao,报错
当同时存在两个同一个Dao的实现类,并且变量名称accountDao不等于任何一个注解,就会报错
@Value(value="")
可以${表达式} :spEL 用于注入基本类型数据、string类型数据
@Scope (value="singleton/prototype")
指定bean作用范围
@PreDestroy
指定销毁方法
@PostConstruct
指定初始化方法
基于XML的IOC案例
环境配置
pom.xml :spring-context、commons-dbutils、mysql、c3p0、junit
bean.xml: ds是QueryRunner有参构造的参数---传入过来的dataSource数据源
之前获取,都是通过连接池得到
基于注解的IOC案例
bean.xml:::: 在 xml的基础上改动
serviceImpl和DaoImpl加上注解,其中变量也加上
去除bean.xml,使用SpringConfiguration.java 自定义配置文件类
spring新注解 :为了替代bean.xml ----- 指定配置类、指定扫描包、方法返回值存容器、导入其他配置类、指定配置文件
* 该类是一个配置类,它的作用和bean.xml是一样的
* spring中的新注解
* Configuration
* 作用:指定当前类是一个配置类
* 细节:当配置类作为AnnotationConfigApplicationContext对象创建的参数时,该注解可以不写。
* ComponentScan
* 作用:用于通过注解指定spring在创建容器时要扫描的包
* 属性:
* value:它和basePackages的作用是一样的,都是用于指定创建容器时要扫描的包。
* 我们使用此注解就等同于在xml中配置了:
* <context:component-scan base-package="com.itheima"></context:component-scan>
* Bean
* 作用:用于把当前方法的返回值作为bean对象存入spring的ioc容器中
* 属性:
* name:用于指定bean的id。当不写时,默认值是当前方法的名称
* 细节:
* 当我们使用注解配置方法时,如果方法有参数,spring框架会去容器中查找有没有可用的bean对象。
* 查找的方式和Autowired注解的作用是一样的
* Import
* 作用:用于导入其他的配置类
* 属性:
* value:用于指定其他配置类的字节码。
* 当我们使用Import的注解之后,有Import注解的类就父配置类,而导入的都是子配置类
* PropertySource
* 作用:用于指定properties文件的位置
* 属性:
* value:指定文件的名称和路径。
* 关键字:classpath,表示类路径下
@Configuration 指定当前类是一个配置类
@ComponentScan 指定使用注解要扫描的包
注解如果属性有且只有一个值,并且是数组类型的,可以去掉大括号和属性名 ----其中basePackages==value
@Bean 把当前方法的返回值作为bean对象存入spring的ioc容器
如果方法带参数,spring会去容器中查找可用的bean对象(先通过参数类型,再通过参数名称)等同于@Autowried
其中QueryRunner对应XML中,自定义一个方法,参数为dataSource,返回值为QueryRunner,此处的参数dataSource对应的是中的runner,runner没有实际的意义只用来赋值,因为要赋值,@Autowried会先通过数据类型查找,再通过参数dataSource找,对应的数据源id,如果没有或有多个,报错
用配置类替换bean.xml后,创建容器时,使用AnnotationConfigApplicationContext(传入配置文件类.class)
![](https://i-blog.csdnimg.cn/blog_migrate/5256c2d36d07bebb4e7709f75ab8303d.png)
bean.xml后,此时通过容器得到的对象,都默认是单例对象,可以scope改为多例
@Improt 用于导入其他的配置类 有Import注解的类就父配置类,而导入的都是子配置类
重新创建一个配置类,将数据源对的创建和QueryRunner的注入都存入,SpringConfiguration作为一个主配置文件,存一些公共配置
也可同时导入多个配置类的class文件,但达不到,其他配置类和主配置类的主从关系
将bean.xml完全用注解替代,需要新建配置类
总结:半注解,半XML的方式----是最合适的
最后,可见纯注解,当获取连接时,使用第三方的类,是比较复杂的,使用纯XML,注入时配置也比较复杂
所有,可以采用---------半注解,半XML的方式
当需要调用的类对象是自己创建的,可以使用注解直接注入,因为只需类名上方写上注解就可以存容器中
当是第三方的类时,可以使用XML配置,因为XML配置更简洁的将第三方的类导入容器
例:
当ServiceImpl调用Dao时,依赖注入的对象就可以通过注解存入到容器
当DaoImpl调用QueryRunner时,依赖注入的对象就可以使用XML配置,会方便很多
当一个对象在容器中存在多个实现类时,当需要用方法区返回实现类时,可以通过@Qualifier@Bean修饰参数,准确的
@Qualifier的另一种用法:修饰@Bean修饰的方法的参数------用于准确决定方法返回值对象
当@Qualifier用在@Bean修饰的方法参数中,修饰参数时,----------用来决定返回值的对象
由于@Bean修饰的方法带有参数时,默认包含一个@Autowried来修饰参数,此时加上一个@Qualifier,就更加准确的决定参数的取值,在容器中更准确的找到------------更准确的决定方法返回值
注入变量时,通过参数类型参数名dataSource到容器中找,如果有多个,则需要通过@Qualifier标示,找哪一个
原理:因为@Bean中有Autowired的功能
spring整合Junit
注解的
XML的
注意
当整合了Junit后,测试类中,获取service对象,可以通过注解注入,不需要容器