zoukankan      html  css  js  c++  java
  • 第一天:Spring 框架简介和IOC详解

    Spring 框架简介和IOC详解

    (一)Spring框架简介

    1Spring 简介

    Spring 框架是一个轻量级的解决方案,可以一站式地构建企业级应用。它是为了解决 企业应用开发的复杂性而创建的。Spring 使用基本的 JavaBean 来完成以前只可能由 EJB 完成的事情。然而,Spring 的用途不仅限于服务器端的开发。从简单性、可测试性和松 耦合的角度而言,任何 Java 应用都可以从 Spring 中受益。

    目的:解决企业应用开发的复杂性

    功能:使用基本的 JavaBean 代替 EJB,并提供了更多的企业应用功能

    范围:任何 Java 应用

    简单来说,Spring 是一个轻量级的控制反转 (IoC) 面向切面 (AOP) 的容器框架。

    2Spring 的优势

    (1)降低了 J2EE 开发难度。

    (2)面向对象的设计比任何实现技术(比如 J2EE)都重要。

    (3)面向接口编程,而不是针对类编程。Spring 将使用接口的复杂度降低到零。(面

    向接口编程有哪些复杂度?)解耦!!!!!

    (4)使用 Spring 构建的应用程序易于单元测试。

    (5)JavaBean 提供了应用程序配置的最好方法。

    (6) Java 中,已检查异常(Checked exception)被过度使用。框架不应该迫使你

    捕获不能恢复的异常。

    3Spring 的体系结构

     

    如果作为一个整体,这些模块为你提供了开发企业应用所需的一切。但你不必将应用

    完全基于 Spring 框架。你可以自由地挑选适合你的应用的模块而忽略其余的模块。

    就像你所 看到的,所有的 Spring 模块都是在核心容器之上构建的。容器定义了 Bean

    是如何创建、配置和管理的——更多的 Spring 细节。当你配置你的应用时,你会潜在地

    使用这些类。但是作为一名开发者,你最可能对影响容器所提供的服务的其它模块感兴趣。

    这些模块将会为你提供用于构建应用服务的框架。

    (二) Spring IOC 详解

    1Spring IOC简介

    SpringIOCIOC Inversion of Control 的缩写,多数书籍翻译成控制反转,还有些书籍翻译成为控制反向或者控制倒置

    1996 年,Michael Mattson 在一篇有关探讨面向对象框架的文章中,首先提出了 IOC

    这个概念。对于面向对象设计及编程的基本思想,前面我们已经讲了很多了,不再赘述,

    简单来说就是把复杂系统分解成相互合作的对象,这些对象类通过封装以后,内部实现对

    外部是透明的,从而降低了解决问题的复杂度,而且可以灵活地被重用和扩展。。IOC 理论

    提出的观点大体是这样的:借助于第三方实现具有依 赖关系的对象之间的解耦 如下

     

    大家看到了吧,由于引进了中间位置的“第三方”,也就是IOC容器,使得A、B、C、D这4个对象没有了耦合关系,齿轮之间的传动全部依靠“第三方”了, 全部对象的控制权全部上缴给“第三方”IOC容器,所以,IOC容器成了整个系统的关键核心,它起到了一种类似“粘合剂”的作用,把系统中的所有对象粘合 在一起发挥作用,如果没有这个“粘合剂”,对象与对象之间会彼此失去联系,这就是有人把IOC容器比喻成“粘合剂”的由来。
    我们再来做个试验:把上图中间的IOC容器拿掉,然后再来看看这套系统:

     

    我们现在看到的画面,就是我们要实现整个系统所需要完成的全部内容。这时候,ABCD4个对象之间已经没有了耦合关系,彼此毫无联系,这样的话, 当你在实现A的时候,根本无须再去考虑BCD了,对象之间的依赖关系已经降低到了最低程度。所以,如果真能实现IOC容器,对于系统开发而言,这将是 一件多么美好的事情,参与开发的每一成员只要实现自己的类就可以了,跟别人没有任何关系!

    2Spring入门案例

    让我们使用Spring,看看用了它之后是如何创建Book

    1) 导入坐标

    <dependencies>
        <!-- 导入jar包:spring的相关的包,实现ioc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>4.3.26.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

    </dependencies>

    2) 创建User类 提供set get 方法

    public class User {

       private String name;

       private Integer age;

    }

    3)在src下面创建application.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 https://www.Springframework.org/schema/beans/Spring-beans.xsd">

    <bean id="book" class="com.offcn.Spring.User"/>

    </beans>

    4)编码测试

    @Test

    public void fun1() {

    //1. 创建容器对象,相对于 src 下的路径

    ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");

    //2. 向容器”user 对象

    User u=(User) ac.getBean("user");

    //3. 打印 user 对象

    System.out.println(u);

    }

    5)总结:

    SpringIOC容器工作原理

     

    a.Spring的初始化的时候,会读取配置文件中的诸多bean

    b.根据beanclass的值寻找对应的Class字节码文件,

    c.通过反射技术,创建出一个个对象,

    d.创建的对象会被存放到内部的一个Map结构中,等待被使用当我们需要使用具体的对象时就无须自己创建,而是直接从SpringIOC容器中取。

    3Spring IOC原理

    1 编写一个Book,并在APP中创建它

    public class Book {

    private String name;

    private Float price;

    private Date publishDate;

     }

    public class App {

    public static void main(String[] args) {

    //创建一本书

    Book book = new Book();

    }

    }

    代码问题:当前APP类和Book类耦合度太高

     

    2使用工厂解耦合

    实现一个BookFactory负责生产书,APP只需要调用它的方法,就能拿到一本书。

    //工厂

    public class BookFactory {

    //创建书

    public static Book getBook() {

    Book book = new Book();

    return book;

    }

    }

    public class App {

    public static void main(String[] args) {

    //调用工厂产生一本书

    Book book = BookFactory.getBook();

    }

    }

    思考:这个工厂只能生产Book,是不是太弱了,能不能造一个能生产多种东西的工厂出来呢?

     

    3工厂升级

    实现一个BeanFactory,通过传递给它参数的形式告诉它产生什么,它就生产什么

    public class BeanFactory {

    public static Object getBean(String name) {

    try {

    //可以读取properties文件,拿到键值对

    ResourceBundle bundle = ResourceBundle.getBundle("product");

    String className = bundle.getString(name);

    //使用反射创建对象

    Class<?> clazz = Class.forName(className);

    Object instance = clazz.newInstance();

    return instance;

    } catch (Exception e) {

    throw new RuntimeException("读取配置文件失败");

    }

    }

    }

     

    public class App {

    public static void main(String[] args) {

    //调用工厂产生一本书

    Book book = (Book) BeanFactory.getBean("book");

    }

    }

     

    思考:现在的工厂是每次调用的时候才产生对象,那能不能提前创建好对象存起来,用的时候直接拿呢?

     

    4 优化后的工厂

    public class BeanFactory {

    //准备一个集合,用来存储创建的对象

    public static Map<String, Object> map = new HashMap<>();

    static {

    try {

    //可以读取properties文件,拿到键值对

    ResourceBundle bundle = ResourceBundle.getBundle("product");

    Enumeration<String> keys = bundle.getKeys();

    while (keys.hasMoreElements()) {

    String key = keys.nextElement();

    String className = bundle.getString(key);

    //使用反射创建对象

    Class<?> clazz = Class.forName(className);

    Object instance = clazz.newInstance();

    map.put(key, instance);

    }

    } catch (Exception e) {

    throw new RuntimeException("读取配置文件失败");

            }

    }

    public static Object getBean(String name) {

    return map.get(name);

    }

    }

     

    最后这个BeanFactory可以认为就是一个简单的SpringIOC容器。 通过一系列推导,我们发现: 对象的创建操作确实是由程序员在类中主动new反转到了在BeanFactory中创建。也就是实现了控制反转

     

    4Spring API

    1BeanFactory

     

    BeanFactorySpring "心脏"

    BeanFactoryIOC 容器的核心接口,它定义了IOC的基本功能。

    Spring使用它来配置文档,管理bean的加载,实例化并维护bean之间的依赖关系,负责bean的声明周期。

     

    2ApplicationContext

     

    ApplicationContextBeanFactory派生而来,可以比喻为Spring的躯体。

    ApplicationContextBeanFactory的基础上添加了很多功能:

    支持了aop功能和web应用 MessageSource, 提供国际化的消息访问

    通过配置来实现BeanFactory中很多编码才能实现的功能

    ApplicationContext的常用实现类 ClassPathXmlApplicationContext: classpath目录读取配置文件

    FileSystemXmlApplicationContext:从文件系统或者url中读取配置文件 AnnotationConfigApplicationContext:当我们使用注解配置容器对象时,需要使用此类来创建 Spring 容器。它用来读取注解。

     

    3两者区别:

    beanFactory主要是面向Spring框架的基础设施,也就是供Spring自身内部调用, Applicationcontext 主要面向Spring的使用者。

    BeanFactroy在第一次使用到某个Bean(调用getBean()),才对该Bean进行加载实例化, 而ApplicationContext是在容器启动时,一次性创建并加载了所有的Bean

    二、Spring Bean详解

    (一)Bean的实例化

    1、使用默认无参构造函数

    它会根据默认无参构造函数来创建类对象。如果 bean 中没有默认无参构造函数,将会创建失败

    <bean id="book" class="com.offcn.Spring.User"></bean>

     

    2、使用工厂实例化对象

    Spring中还可以通过工厂模式来创建对象

     

    工厂模式:用来产生工具

    静态工厂:不产生工厂的实例,直接调用工厂的静态方法创建对象。

    实例化工厂:先产生工厂的实例,再调用工厂实例的方法创建对象。

     

    使用工厂初始化对象,又可以根据工厂的性质分为使用静态工厂和实例化工厂,但是都很简单。

    public class BookFactory {

    public static Book getBook1() {

    return new Book();

    }

    public Book getBook2() {

            return new Book();

        }

    }

     

    <!--

    使用静态工厂创建对象:直接调用工厂类的静态方法

    id 属性:指定 bean id,用于从容器中获取

    class 属性:指定静态工厂的全限定类名

    factory-method 属性:指定生产对象的静态方法

    -->

    <bean id="book1" class="com.offcn.Spring.BookFactory" factory-method="getBook1" />

     

    <!--

    使用实例化工厂创建对象:先创建工厂对象,再调用工厂对象的方法

    factory-bean 属性:用于指定实例工厂 bean id

    factory-method 属性:用于指定实例工厂中创建对象的方法。

     -->

    <bean id="bookFactory" class="com.offcn.Spring.BookFactory" />

    <bean id="book2" factory-bean="bookFactory" factory-method="getBook2" />

     

    (二) Bean的作用范围和生命周期

    <bean id="book" class="com.offcn.Spring.BookFactory" scope="singleton"></bean>

     

    所谓Bean的作用范围其实就是指Spring给我们创建出的对象的存活范围。

    在配置文件中通过beanscope属性指定,有五个取值:

    singleton(默认) 创建出的实例为单例模式,IOC只创建一次,然后一直存在

    prototype 创建出的实例为多例模式,每次获取bean的时候,IOC都给我们重新创建新对象

    request(web) web项目中,Spring 创建一个 Bean 的对象,将对象存入到 request 域中. session (web) web项目中,Spring 创建一个 Bean 的对象,将对象存入到 session 域中. globalSession (用于分布式web开发) 创建的实例绑定全局session对象

    (三)Bean的生命周期中的两个特殊方法

    在这里所谓的Bean的生命周期其实指的是Bean创建到销毁的这么一段时间。

    Spring中可以通过配置的形式,指定bean在创建后和销毁前要调用的方法。

     

    <!--通过init-method指定创建后要进行的动作,通过destroy-method指定销毁前要进行的动作-->

    <bean id="book" class="com.offcn.Spring.Book" init-method="init" destroy-method="destory" />

     

    注意:

    由于Bean的生命周期由Spring管理, Spring在没有关闭其IoC容器前,将不销毁所管理的Bean, 因此必须 将其手动关闭才可以销毁Spring所控制的Bean实例。

    ApplicationContext并没有提供关闭方法,要使用其子类ClassPathXmlApplicationContext进行关闭。

    销毁操作只能用于单例对象,即scopesingletion的对象,非单例对象的内存回收交由垃圾回收机制管理

     

    三、Spring 依赖注入和配置文件模块化

    (一) 依赖注入的概念

    2004 年,Martin Fowler 探讨了同一个问题,既然 IOC 是控制反转,那么到底是

    些方面的控制被反转了呢?,经过详细地分析和论证后,他得出了答案:获得依赖对

    象的过程被反转了。控制被反转之后,获得依赖对象的过程由自身管理变为了由 IOC

    器主动注入。于是,他给控制反转取了一个更合适的名字叫做依赖 注入(Dependency

    Injection。他的这个答案,实际上给出了实现 IOC 的方法:注入。所谓依赖注入,

    就是由 IOC 容器在运行期间,动态地将某种依赖关系注入到对象之中。

    所以,依赖注入 (DI) 和控制反转 (IOC) 是从不同的角度的描述的同一件事情,就是

    指通过引入 IOC 容器,利用依赖关系注入的方式,实现对象之间的解耦。

     

    (二) 多种数据类型的注入

    1、通过构造函数注入

    1)构建User类的有参构造方法

    public User(String name, Float price, Date publishDate) {

    this.name = name;

    this.price = price;

    this.publishDate = publishDate;

     }

    2)在配置文件applicationContext.xml中添加配置

    <bean id="date" class="java.util.Date" />

    <bean id="user" class="com.offcn.Spring.User">

     <!--

    index:指定参数在构造函数参数列表的索引位置,从0开始

    name:指定参数在构造函数中的名称

    type:指定参数在构造函数中的数据类型,可以通过反射拿到,不需要关系

    value:它能赋的值是基本数据类型和 String 类型

    ref:它能赋的值是其他 bean 类型,也就是说,必须得是在配置文件中配置过的 bean -->

    <constructor-arg name="name" value="向日葵" />

    <constructor-arg name="price" value="1" />

    <constructor-arg name="publishDate" ref="date" />

    </bean>

    (3)测试代码

    @Test

    public void fun1() {

    //1. 创建容器对象,相对于 src 下的路径

    ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");

    //2. 向容器”user 对象

    User u=(User) ac.getBean("user");

    //3. 打印 user 对象

    System.out.println(u);

    }

     

    2、通过setter属性注入

    1)在User类型中,给属性提供setter方法

    public class User{

        private String name;

        private Double price;

        private Date publishDate;

    }

    2)在配置文件applicationContext.xml中添加配置

    <bean id="user" class="com.offcn.Spring.User">

    <property name="name" value="向日葵" />

    <property name="price" value="1" />

    <property name="publishDate" ref="date" />

    </bean>

    (4)测试代码

    @Test

    public void fun1() {

    //1. 创建容器对象,相对于 src 下的路径

    ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");

    //2. 向容器”user 对象

    User u=(User) ac.getBean("user");

    //3. 打印 user 对象

    System.out.println(u);

    }

       

     

    3、注入集合属性

    顾名思义,就是给类中的集合成员传值,它用的也是 set方法注入的方式,只不过变量的数据类型都是集合。 我们 这里介绍注入数组、ListSetMapProperties

     

    <bean id="book" class="com.offcn.Spring.Book">

    <!--List-->

    <property name="list">

    <list>

    <value>1</value>

    <value>2</value>

    </list>

        </property>

    <!--Set-->

    <property name="set">

    <set>

    <value>3</value>

    <value>4</value>

    </set>

    </property>

    <!--数组-->

    <property name="array">

    <array>

    <value>5</value>

    <value>6</value>

    </array>

    </property>

    <!--Map-->

    <property name="map">

    <map>

    <entry key="7" value="7-1" />

          <entry key="8" value="8-1" />

    </map>

    </property>

    <!--Properties-->

    <property name="properties">

    <props>

    <prop key="9">9-1</prop>

    <prop key="10">10-1</prop>

    </props>

    </property>

    </bean>

    (三)配置文件模块化

    我们现在的配置都集中配在了一个applicationContext.xml文件中,当开发人员过多时, 如果所有bean都配 置到同一个配置文件中,会使这个文件巨大,而且也不方便维护。 针对这个问题,Spring提供了多配置文件的方式,也就是所谓的配置文件模块化。 配置文件模块化有两种形式:

    1、并列的多个配置文件 直接编写多个配置文件,比如说beans1.xmlbeans2.xml......, 然后在创建ApplicationContext的时候,直接 传入多个配置文件。

    ApplicationContext act = new ClassPathXmlApplicationContext("beans1.xml","beans2.xml","...");

     

    2主从配置文件 先陪一个主配置文件,然后在里面导入其它的配置文件。

    <import resource="beans1.xml" />

    <import resource="beans2.xml" />

     

    注意:

    同一个xml文件中不能出现相同名称的bean,如果出现会报错

    多个xml文件如果出现相同名称的bean,不会报错,但是后加载的会覆盖前加载的bean,所以企业开发中尽 量保证bean的名称是唯一的。

     

    四、Spring 整合Jdbc

    (一) Spring整合jdbc原理

    Spring提供了ioc容器,管理jdbc操作数据库的过程中需要的数据库连接对象,同时Spring提供了整合jdbc操作数据库的工具类JdbcDaoSupport 和模板工具 JdbcTemplate,在JdbcTemplate中提供了大量的操作数据库的方式通用户使用。所以我们只需要获取模板工具类然后调用方法就可以完成Jdbc的操作了。

    (二) 使用Spring整合jdbc完成对象的CRUD操作

    1、添加依赖

    <dependencies>

    <dependency>

    <groupId>org.Springframework</groupId>

    <artifactId>Spring-context</artifactId>

    <version>5.1.5.RELEASE</version>

    </dependency>

    <dependency>

    <groupId>MySQL</groupId>

    <artifactId>MySQL-connector-java</artifactId>

    <version>5.1.6</version>

    </dependency>

    <dependency>

        <groupId>com.mchange</groupId>

        <artifactId>c3p0</artifactId>

        <version>0.9.5.2</version>

    </dependency>

    <dependency>

    <groupId>junit</groupId>

    <artifactId>junit</artifactId>

    <version>4.12</version>

    </dependency>

    </dependencies>

     

    2、创建对应的数据库表

    create table user(

     id int(11) primary key auto_increment,

     name varchar(32) not null,

     birthday date not null,

     age int(11) not null

    );

     

    3、书写配置文件

    <?xml version="1.0" encoding="UTF-8"?>

    <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xmlns="http://www.Springframework.org/schema/beans" xmlns:context="http://

    www.Springframework.org/schema/context" xsi:schemaLocation="http://www.

    Springframework.org/schema/beans http://www.Springframework.org/schema/beans/

    Spring-beans-4.2.xsd http://www.Springframework.org/schema/context http://

    www.Springframework.org/schema/context/Spring-context-4.2.xsd ">

    <!-- 1. 将连接池放入 Spring 容器中 -->

    <bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">

     <property name="jdbcUrl" value="jdbc:MySQL:///Spring"></property>

     <property name="driverClass" value="com.MySQL.jdbc.Driver"></property>

     <property name="user" value="root"></property>

     <property name="password" value="root"></property>

    </bean>

    <!-- 2. JDBCTemplate 放入 Spring 容器中 -->

    <!--JDBCTemplate 需要 datasource 连接池 -->

    <bean name="jdbcTemplate" class="org.Springframework.jdbc.core.

    JdbcTemplate">

     <property name="dataSource" ref="dataSource"></property>

    </bean>

    <!-- 3. UserDao 放入 Spring 容器中 -->

    <!--UserDaoIml 需要 jdbctemplate-->

    <bean name="userDao" class="com.offcn.template.UserDaoIml">

     <property name="jTemplate" ref="jdbcTemplate"></property>

    </bean>

    </beans>

    注意 :Spring 提供了一个工具类 JDBCDaoSupport 中定义了获取 JDBCTemplate 工具类

    的的方法,如果 dao 层实现类选择继承 JDBCDaoSupport 工具类,可以简化配置为

    去除 JDBCTemplate bean 配置

    dao 层实现类的配置中采用 setter 注入我们的数据源对象即可

     

    4、书写 Dao的代码

    public class UserDaoIml implements UserDao{

         // 并且给 jTemplate 设置 setter 方法

         private JdbcTemplate jTemplate;

     @Override

     public void save(User u) {

         String sql="insert into t_user values(null,?)";

         jTemplate.update(sql, u.getName());

     }

     @Override

     public void delete(Integer id) {

         String sql="delete from t_user where id=?";

         jTemplate.update(sql, id);

     }

     @Override

     public void update(User u) {

         String sql="update t_user set name=? where id=?";

         jTemplate.update(sql, u.getName(),u.getId());

     }

     @Override

     public User getById(Integer id) {

       String sql="select * from t_user where id=?";

       return jTemplate.queryForObject(sql, new RowMapper<User>()

    {

         @Override

         public User mapRow(ResultSet rs, int arg1) throws SQLException {

         User user=new User();

         user.setId(rs.getInt("id"));

         user.setName(rs.getString("name"));

         return user;

      }}, id);

     }

     @Override

     public int getTotalCount() {

         String sql="select count(*) from t_user";

         Integer count = jTemplate.queryForObject(sql, Integer.class);

         return count;

     }

     @Override

     public List<User> getAll() {

         String sql="select * from t_user";

     return jTemplate.query(sql,new RowMapper<User>() {

     @Override

     public User mapRow(ResultSet rs, int arg1) throws SQLException {

     User user=new User();

     user.setId(rs.getInt("id"));

     user.setName(rs.getString("name"));

     return user;

     }});

     }

     public void setjTemplate(JdbcTemplate jTemplate) {

       this.jTemplate = jTemplate;

     }

    }

    5、测试代码

    ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");

    //2. 向容器”user 对象

    UserDao udao=(UserDao) ac.getBean("userDao");

    使用 dao 对象调用接口中的方法进行 CRUD 操作

    udao.getById(1)

     

  • 相关阅读:
    Ajax技术的工作原理
    《统计学习方法》第七章,支持向量机
    《西瓜书》第四章,决策树3
    OpenCL 矩阵乘法
    MPI 集合通信函数 MPI_Reduce(),MPI_Allreduce(),MPI_Bcast(),MPI_Scatter(),MPI_Gather(),MPI_Allgather(),MPI_Scan(),MPI_Reduce_Scatter()
    有关CUBLAS中的矩阵乘法函数
    TensorFlow 笔记04
    TensorFlow 笔记03-TensoeFlow 和 TensorRT 调试的一些方法
    TensorFlow 笔记02-mnist 的 tensorRT 实现,从 .npz 文件中加载参数进行推理
    TensorFlow 笔记01-mnist 的两种 tensorFlow 实现,保存和加载模型的三种方法
  • 原文地址:https://www.cnblogs.com/masterhxh/p/13826911.html
Copyright © 2011-2022 走看看