spring01

Spring框架

今日内容

  • 能够描述spring框架
  • 能够理解spring的IOC的容器
  • 能够编写spring的IOC的入门案例
  • 能够说出spring的bean标签的配置
  • 能够理解Bean的实例化方法
  • 能够理解Bean的属性注入方法
  • 能够理解复杂类型的属性注入

Spring框架介绍

什么是spring

  • Spring是一个一站式/full-stack(全栈)的框架。意思是使用Spring框架既可以开发web层、也可以开发service层也可以开发dao层

  • 本质:Spring就是一个对象的容器,因为在这个容器中有可以处理请求的对象,所以可以用来写web层,因为有service层对象,所以可以用写service层,因为有dao层对象,所以可以用来操作数据库。

  • Spring的开放性和包容性非常强,无缝整合第三方优秀框架,不会重复造轮子。

  • Spring的框架结构

    注意:Spring是一个模块化的框架,Spring功能很多很强大,但是并不要求所有的功能我们都得用上,结构图中每一个圆角矩形都对应一个jar,我们使用什么模块什么功能引入对应模块即可。

Spring的发展历程

  • 1997年IBM提出了EJB的思想
  • 1998年,SUN制定开发标准规范EJB1.0
  • 1999年,EJB1.1发布
  • 2001年,EJB2.0发布
  • 2003年,EJB2.1发布
  • 2006年,EJB3.0发布
  • Rod Johnson(spring之父)
    • Expert One-to-One J2EE Design and Development(2002)
      阐述了J2EE使用EJB开发设计的优点及解决方案
      Expert One-to-One J2EE Development without EJB(2004)
      阐述了J2EE开发不使用EJB的解决方式(Spring雏形)
  • 2017年9月份发布了spring的最新版本spring 5.0通用版(GA)

Spring优势

  • 方便解耦,简化开发
    通过Spring提供的IoC容器,可以将对象间的依赖关系交由Spring进行控制,避免硬编码所造成的过度程序耦合。用户也不必再为单例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用
  • AOP编程的支持
    通过Spring的AOP功能,方便进行面向切面的编程,许多不容易用传统OOP实现的功能可以通过AOP轻松应付。
  • 声明式事务的支持
    可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务的管理,提高开发效率和质量。
  • 方便程序的测试 junit 4.12
    可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可做的事情。
  • 方便集成各种优秀框架
    Spring可以降低各种框架的使用难度,提供了对各种优秀框架(Struts、Hibernate、Hessian、Quartz等)的直接支持。
  • 降低JavaEE API的使用难度
    Spring对JavaEE API(如JDBC、JavaMail、远程调用等)进行了薄薄的封装层,使这些API的使用难度大为降低。
  • Java源码是经典学习范例
    Spring的源代码设计精妙、结构清晰、匠心独用,处处体现着大师对Java设计模式灵活运用以及对Java技术的高深造诣。它的源代码无意是Java技术的最佳实践的范例。

IOC的概念和作用

注意:IOC和AOP不是从Spring开始才有的,在Spring之前更趋向于理论化,在Spring这才发扬光大的。

  • IOC的概念

    IOC:inverse of control (反转控制/控制反转),我们之前在一个java类中通过new的方式去引入外部资源(其他类等),这叫正向;现在Spring框架帮我们去new,你什么时候需要你去问Spring框架要,这就是反转(创建对象权利的一个反转),我们丧失了一个权利(主动创建对象的权利),但是我们拥有了一个福利(我们不用考虑对象的创建、销毁等问题)。

  • IOC的作用:IOC解决程序耦合问题

    • 关于程序耦合(有一个认识即可)
    • 耦合:耦合度的意思,程序当中:为了实现一个业务功能,不同的模块之间互相调用就会产生耦合
    • 注意:耦合是不能避免的,耦合尽可能去降低
    • “高内聚、低耦合”,内聚度过高不利于代码复用,内聚度往往通过增加方法来完成(麻烦自己、方便别人)。

程序的耦合

耦合性(Coupling),也叫耦合度,是对模块间关联程度的度量。耦合的强弱取决于模块间接口的复杂性、调用模块的方式以及通过界面传送数据的多少。模块间的耦合度是指模块之间的依赖关系,包括控制关系、调用关系、数据传递关系。模块间联系越多,其耦合性越强,同时表明其独立性越差( 降低耦合性,可以提高其独立性)。耦合性存在于各个领域,而非软件设计中独有的,但是我们只讨论软件工程中的耦合。 在软件工程中,耦合指的就是就是对象之间的依赖性。对象之间的耦合越高,维护成本越高。因此对象的设计应使类和构件之间的耦合最小。软件设计中通常用耦合度和内聚度作为衡量模块独立程度的标准。划分模块的一个准则就是高内聚低耦合。 它有如下

  • 分类:
    • (1) 内容耦合。当一个模块直接修改或操作另一个模块的数据时,或一个模块不通过正常入口而转入另一个模块时,这样的耦合被称为内容耦合。内容耦合是最高程度的耦合,应该避免使用之。
    • (2) 公共耦合。两个或两个以上的模块共同引用一个全局数据项,这种耦合被称为公共耦合。在具有大量公共耦合的结构中,确定究竟是哪个模块给全局变量赋了一个特定的值是十分困难的。
    • (3) 外部耦合 。一组模块都访问同一全局简单变量而不是同一全局数据结构,而且不是通过参数表传递该全局变量的信息,则称之为外部耦合。
    • (4) 控制耦合 。一个模块通过接口向另一个模块传递一个控制信号,接受信号的模块根据信号值而进行适当的动作,这种耦合被称为控制耦合。
    • (5) 标记耦合 。若一个模块A通过接口向两个模块B和C传递一个公共参数,那么称模块B和C之间存在一个标记耦合。
    • (6) 数据耦合。模块之间通过参数来传递数据,那么被称为数据耦合。数据耦合是最低的一种耦合形式,系统中一般都存在这种类型的耦合,因为为了完成一些有意义的功能,往往需要将某些模块的输出数据作为另一些模块的输入数据。
    • (7) 非直接耦合 。两个模块之间没有直接关系,它们之间的联系完全是通过主模块的控制和调用来实现的。z
  • 总结: 耦合是影响软件复杂程度和设计质量的一个重要因素,在设计上我们应采用以下原则:如果模块间必须存在耦合,就尽量使用数据耦合,少用控制耦合,限制公共耦合的范围,尽量避免使用内容耦合。 内聚与耦合 内聚标志一个模块内各个元素彼此结合的紧密程度,它是信息隐蔽和局部化概念的自然扩展。内聚是从功能角度来度量模块内的联系,一个好的内聚模块应当恰好做一件事。它描述的是模块内的功能联系。耦合是软件结构中各模块之间相互连接的一种度量,耦合强弱取决于模块间接口的复杂程度、进入或访问一个模块的点以及通过接口的数据。 程序讲究的是低耦合,高内聚。就是同一个模块内的各个元素之间要高度紧密,但是各个模块之间的相互依存度却要不那么紧密。

自定义IOC实现程序解耦合

需求:开发dao层和service层模拟保存账户(Account),不具体操作数据库

  • pojo对象
public class Account {
    private Integer id;
    //账户名
    private String name;
    //账户金额
    private Double money;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Double getMoney() {
        return money;
    }

    public void setMoney(Double money) {
        this.money = money;
    }
}
  • service层
public interface AccountService {
    public void saveAccount(Account account);
}
public class AccountServiceImpl implements AccountService {
    @Override
    public void saveAccount(Account account) {
        AccountDao accountDao = (AccountDao) BeanFactory.getInstance("accountDao");
        accountDao.saveAccount(account);
    }
}
  • dao层
public interface AccountDao {
     void saveAccount(Account account);
}
public class AccountDaoImpl implements AccountDao {
    @Override
    public void saveAccount(Account account) {
        System.out.println("模拟保存账户");
    }
}
  • bean工厂
/**
 *  bean工厂
 *    读取xml信息
 *    反射创建对象
 *    存储到集合Map (对象容器)
 *
 *    提供一个方法,根据id获取对象
 */
public class BeanFactory {
    private static  Map<String,Object> map = new HashMap<String, Object>();

    /**
     *  读取xml配置文件
     *  创建对象,存储Map集合
     */
    static {
        InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");
        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(in);
            Element rootElement = document.getRootElement();
            List<Element> elementList = rootElement.selectNodes("//bean");
            if(elementList != null && elementList.size() >0 ){
                for(Element element : elementList){
                    String id = element.attributeValue("id");
                    String className = element.attributeValue("class");
                    Class clazz = Class.forName(className);
                    Object obj = clazz.newInstance();
                    map.put(id,obj);
                }
            }
        }catch (Exception ex){
            ex.printStackTrace();
        }
        try {
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     *  根据id返回对象
     */
    public static  Object getInstance(String id){
        return map.get(id);
    }
}
  • beans.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!--
   存储需要实例化的对象
   id 唯一标识
   class 类全限定名
-->
<beans>
    <bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl"></bean>
    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"></bean>
</beans>

SpringIOC的使用

  • applicationContext.xml
<?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">
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"></bean>
<bean id="accountDao" class="com.itheima.dao.impl.AccountDaoImpl"></bean>
</beans>
  • 测试类
/**
 * 测试springIOC容器
 */
@Test
public void testIOC(){
   // ApplicationContext对象容器
   ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
   // getBean方法获取对象
    AccountService service = (AccountService) context.getBean("accountService");
    AccountDao dao = (AccountDao)context.getBean("accountDao");
    /*
      接口class获取实现类对象,接口只有一个实现类
      context.getBean(AccountService.class);
    */
    /*
      接口class获取实现类对象
      id唯一标识
       context.getBean("accountService",AccountService.class);
    */
    System.out.println(service);
    System.out.println(dao);
    }
}

ApplicationContext和BeanFactory的区别

  • ApplicationContext:立即加载模式,在Spring启动的时候会立即实例化对象,等待使用,推荐使用

  • BeanFactory:使用到对象的时候(getBean等)才去实例化对象(延迟加载模式),不推荐的

  • 测试延迟加载

/**
 * 测试BeanFactory延迟加载
 */
@Test
public void testBeanFactory(){
    // 加载跟目录下的配置文件
    ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
    Resource res = resolver.getResource("applicationContext.xml");
    // 创建Bean工厂类 注意 XmlBeanFactory已经过期!
    BeanFactory factory = new XmlBeanFactory(res);
    AccountDao accountDAO = factory.getBean("accountDao", AccountDao.class);
    AccountService accountService = factory.getBean("accountService", AccountService.class);
    System.out.println(accountDAO);
    System.out.println(accountService);
}

Bean标签

  • id:标识符
  • class:实例化类的全限定类名,供反射实例化使用
  • scope:对象的使用范围
  • singleton:默认值,单例,项目全局内存中只有一个对象,一般在使用Spring的时候保持默认值即可
  • prototype:原型的意思,在这里是多例,每引用或者getBean一次,Spring框架就返回一个新的对象,比如SqlSession这个特殊的对象
  • request:这种配置针对web应用,每一个request范围内只对应一个对象
  • session:这种配置针对web应用,每一次会话该bean只有一个对象
  • global session:针对的是portlet环境,等同于servlet下的session
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"  scope="prototype">
</bean>
/**
 * 测试scope属性
 * 对象使用范围
 */
@Test
public void testBeanScope(){
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    // getBean方法获取对象
    AccountService service = (AccountService) context.getBean("accountService");
    AccountService service1 = (AccountService) context.getBean("accountService");
    System.out.println(service);
    System.out.println(service1);
}

Bean标签的生命周期

  • init-method指定初始化时执行的方法
  • destory-method指定销毁时执行的方法
    • 销毁执行方法,scope配置为单例子
    • 关闭方法在ApplicationContext接口实现类ClassPathXmlApplicationContext中定义
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl" scope="singleton" init-method="init" destroy-method="destroy">
</bean>
public AccountServiceImpl(){
    System.out.println("我是构造方法");
}

public void init(){
    System.out.println("我是初始化方法");
}

public void destroy(){
    System.out.println("我是销毁方法");
}
/**
 * 测试对象生命周期
 */
@Test
public void testBeanLife(){
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    // getBean方法获取对象
    AccountService service = (AccountService) context.getBean("accountService");
    System.out.println(service);
    context.close();
}

Bean标签的创建对象方式

  • 创建对象:通过class配置的全限定类名创建对象,框架使用反射技术.

  • 自己创建对象:加入到IOC容器中

    • 静态创建

      /**
       * 静态工厂
       * 获取接口实现类对象
       */
      public class StaticFactory {
          public static AccountService createAccountService(){
              return new AccountServiceImpl();
          }
      }
      
      <!--对象产生方式:静态工厂 -->
      <bean id="accountService" class="com.itheima.factory.StaticFactory" factory-method="createAccountService"></bean>
      
    • 实例化创建

      public class InstanceFactory {
          public  AccountService createAccountService(){
              return new AccountServiceImpl();
          }
      }
      
      <!-- 配置实例化的工厂对象-->
      <bean id="instanceFactory" class="com.itheima.factory.InstanceFactory">
      </bean>
      <!-- 配置工厂创建的对象 factory-bean和id相同-->
      <bean id="accountService"  factory-bean="instanceFactory" factory-method="createAccountService">
      </bean>
      

依赖注入(DI)

Dependance Inject(依赖注入的意思)

IOC和DI的区别

IOC和DI是一个事情,只是描述的角度不同(面试题)

Set方法注入,也叫设值注入,这是常用和推荐的用法

  • bean子标签property配置
    • name:set方法名,去掉set
    • ref:创建JavaBean对象的id属性值
<!--
   向service层中的成员AccountDao注入对象
-->
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
    <!--
       name:属性配置方法名
       ref:配置创建JavaBean标签的id属性值
    -->
    <property name="accountDao" ref="accountDao"></property>
</bean>
  • service层
private AccountDao accountDao;

public void setAccountDao(AccountDao accountDao) {
    this.accountDao = accountDao;
}

@Override
public void saveAccount(Account account) {
    accountDao.saveAccount(account);
}
  • 测试set方法注入
/**
 * 测试set方法依赖注入
 */
@Test
public void testDISet(){
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    AccountService service = (AccountService) context.getBean("accountService");
    service.saveAccount(new Account());
}

简单数据类型注入

  • AccountServiceImpl添加成员
private Integer id;
private Double money;
private String name;

public void setId(Integer id) {
    this.id = id;
}

public void setMoney(Double money) {
    this.money = money;
}

public void setName(String name) {
    this.name = name;
}
  • property配置
<property name="id" value="123"></property>
<property name="money" value="10.99"></property>
<property name="name" value="张三"></property>

构造方法注入

  • AccountServiceImpl添加构造方法
private AccountDao accountDao;
private Integer id;
private Double money;
private String name;


public AccountServiceImpl(AccountDao accountDao, Integer id, Double money, String name){
    this.accountDao = accountDao;
    this.id = id;
    this.money = money;
    this.name = name;
}
  • property配置
<constructor-arg name="accountDao" ref="accountDao"></constructor-arg>
<constructor-arg name="id" value="99"></constructor-arg>
<constructor-arg name="money" value="8.55"></constructor-arg>
<constructor-arg name="name" value="李四"></constructor-arg>

复杂对象注入

  • AccountServiceImpl添加复杂数据类型
private String[] myStrs;
private List<String> myList;
private Set<String> mySet;
private Map<String,String> myMap;
private Properties myPro;

public void setMyStrs(String[] myStrs) {
    this.myStrs = myStrs;
}

public void setMyList(List<String> myList) {
    this.myList = myList;
}

public void setMySet(Set<String> mySet) {
    this.mySet = mySet;
}

public void setMyMap(Map<String, String> myMap) {
    this.myMap = myMap;
}

public void setMyPro(Properties myPro) {
    this.myPro = myPro;
}
  • property标签配置
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
    <!--
       name:属性配置方法名
       ref:配置创建JavaBean标签的id属性值
    -->
    <property name="accountDao" ref="accountDao"></property>
    <property name="id" value="123"></property>
    <property name="money" value="10.99"></property>
    <property name="name" value="张三"></property>

    <!-- 注入数组 -->
    <property name="myStrs">
        <array>
            <value>strs1</value>
            <value>strs2</value>
            <value>strs3</value>
        </array>
    </property>

    <!-- 注入List集合 -->
    <property name="myList">
        <list>
            <value>list1</value>
            <value>list2</value>
            <value>list3</value>
        </list>
    </property>

    <!-- 注入Set集合 -->
    <property name="mySet">
        <set>
            <value>set1</value>
            <value>set2</value>
            <value>set3</value>
        </set>
    </property>

    <!-- 注入Map集合 -->
    <property name="myMap">
        <map>
            <entry key="map1" value="mapvalue1"></entry>
            <entry key="map2" value="mapvalue2"></entry>
            <entry key="map3" value="mapvalue3"></entry>
        </map>
    </property>

    <!-- 注入properties集合 -->
    <property name="myPro">
        <props>
            <prop key="pro1">provalue1</prop>
            <prop key="pro2">provalue2</prop>
            <prop key="pro3">provalue3</prop>
        </props>
    </property>

猜你喜欢

转载自blog.csdn.net/qq_35670694/article/details/89136298