SSM----Spring 工厂解耦、IoC控制反转、依赖注入、基于注解的IoC配置、spring新注解

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(淘汰)

核心容器两个接口引发的问题




spring中bean的细节,创建Bean对象的方法

             默认构造函数、普通工厂方法、工厂静态方法--------------都只是在bean.xml中<beans>配置



构造函数得到bean的配置

先构造函数得到指定工厂bean,再传入该工厂bean,通过工厂方法返回需要的对象

在构造函数得到bean的基础上,传入的是工厂接口,加入工厂静态方法得到指定bean


容器创建的bean对象,一般是单例的--------只构造一次



bean作用范围

global session


bean对象的生命周期

多例对象,容器销毁,也不消失

当为单例对象时,容器销毁,对象消失------------配置文件加载完,立刻创建对象(立刻执行类似applicationContext)


当改为多例对象时,即使容器被销毁了,对象还存在-----------当需要使用对象时,才创建对象(延时类似BeanFactory)

 

依赖注入



构造方法注入一般不用------限制了类的创建(有参数,不好构造类))

set方法注入(常用


复杂类型的注入---注入集合数据

结构相同,标签可以互换

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)

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对象,可以通过注解注入,不需要容器
 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值