spring 框架(核心思想、IOC 控制反转、依赖注入、管理连接池)

分层开发

     1、表现层

  •        直接和用户打交道,大部分跟界面有关(html,jsp,servlet)

     2、服务层

  •        指业务逻辑,业务逻辑由一个到多个基本的增删改查组成

    3、 持久层(数据访问层)

  •        将数据永久的保存,jdbc,mybatis

spring 框架

  •  将其他框架进行整合,便于开发,提高程序的扩展性
  •  声明式的事务管理

  就是指不需要编写代码进行事务控制了,可以 xml 配置文件,或是用注解实现事务控制
    编程式的事务管理如下:
             connection.setAutoCommit(false);
             connection.commit();
             connection.rollback();

spring 框架的核心思想

1) IOC inversion of controll (控制反转)

    servlet java 类

    public class MyServlet extends HttpServlet {

        service
        doGet
        doPost
    }
    // 并不需要自己创建它的实例对象, tomcat 是servlet的运行环境,由 tomcat 来创建 MyServlet 的实例对象, 负责调用 servlet 中的方法
    new MyServlet();
    .service

    // tomcat 容器

    所谓的控制反转,就是把对象的一些控制权(对象的创建、一些方法的调用)都交给容器来完成。

    对刚才的例子,就是把 servlet 控制权交给了 tomcat 容器
    以后可以把很多对象的控制权交给 spring 容器来管理,包括对象的创建、对象的生命周期、对象的个数、对象的依赖关系

2)AOP aspect oriented programming (面向切面编程)
3)OOP object oriented programming (面向对象编程)

spring 中 IOC(控制反转)

1. 添加spring依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>4.3.22.RELEASE</version>
</dependency>

2. 编写spring的配置文件,提供一个 xml 的配置文件,放在 main/resources 下

<?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">

    <!-- spring 的配置  如:<bean> 标签-->
</beans>

3. 编写一个java 类交给spring容器管理,使用了一个 bean 标签把某个类交给spring容器管理

<!-- id 管理这个Java类的名字(随便起,不过一般是让Java类的首字母小写,UserService类 id=“userService”)class 包名.类名 管理的java类->

<bean id="userService" class="service.UserService"></bean>

4. 调用方法(以userService类为例)

 1. 创建 spring 容器

 类路径(对maven项目 java 和 resources)  application 应用程序  context 容器,也就是放在 main/resources 下spring.xml文件写入
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");

2.获取容器中的对象  

  • 方法一  根据 id 获取容器中的对象

类型 对象 = (类型)context.getBean(" bean 中的 id 名");

UserService service = (UserService) context.getBean("userService");//需要强制转换为你需要的对象类型,因为它返回的是object类型

  • 方法二  根据 类型 获取容器中的对象  

类型 对象 = context.getBean(类名.class);

UserService service = context.getBean(UserService.class);//不需要强制转换,比较方便

 原来要调用方法的手段:
        UserService service = new UserService();

 3. 使用对象 

对象.方法名();
service.insert(new User());

spring 容器控制反转都能控制哪些方面

1 对象的个数
默认情况下,spring 中的每个 bean 标签只会创建一个对象(单例),可以通过配置来实现多例子

<!-- 其中  prototype是多例, singleton是单例(默认值) -->
<bean scope="prototype|singleton">

2 对象的生命周期方法

  1.  对于单例对象来讲,容器一创建,就会创建这些单例对象,并且随后就调用他们的初始化方法, 并且容器 close 时,会调用他们的销毁方法
  2.  而对于多例来讲,每次使用多例时,就会创建新的对象,并调用他们的初始化方法,多例对象不会调用销毁方法
  • 配置初始化

<bean init-method="初始化方法名">

  • 配置销毁

<bean destroy-method="销毁方法名">

3 控制懒惰初始化

默认是false(非懒惰), 改为 true 表示(懒惰)

<bean lazy-init="true">

<!-- 全局配置 -->
<beans default-lazy-init="true">

4 对象之间的依赖关系

例如,管理 service 和 dao 之间的依赖关系

<bean id="userService">
    <!-- property 标签用来给对象的属性赋值 name="属性名,一般与引用id的名字一样" ref="引用id" -->
    <property name="userDao" ref="userDao"/>
</bean>

<bean id="userDao">
</bean>

降低耦合度:层与层之间,要依赖接口,而不要依赖于具体实现;配合 spring 的控制反转真正实现低耦合

依赖注入

1 依赖注入的两种方式

DI (dependency inject) - 建立对象之间依赖关系的过程和方式。

把userService 需要的 userDao 注入给 userService 的属性

常见的方式:
 set 方法注入,spring调用set方法来完成对属性的赋值,因此要给属性提供一个 公共的 setter 方法

<property name="属性名" ref="引用id"/>  //写在<bean>标签之间

 构造方法注入,spring调用构造构造方法完成对属性的赋值,index是构造方法的次序,从0开始

<constructor-arg index="下标" ref="引用id"/>

2 简化注入的办法
 1)autowired 自动织入 - 要么根据名字匹配, 要么根据类型匹配

<!-- 根据属性名字查找容器中的bean,找到了就进行依赖注入, 可以唯一确定容器中的bean -->
<bean autowire="byName">
</bean>

<!-- 根据属性类型查找容器中的bean,找到了就进行依赖注入, 如果容器中有多个类型相同的bean, byType就不适用了 -->
<bean autowire="byType">
</bean>

 2)注解方式的注入
@Autowired 可以加在要注入的属性上,也可以加在属性对应的set方法或构造方法上,底层根据 byType 进行匹配注入

配置:1、在配置好的spring的文件里的beans中写上

xmlns:context="http://www.springframework.org/schema/context"

           2、在xsi:schemaLocation的双引号里加上

http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd

           3、最后在<beans>标签之间加上,才可以启用一些注解,如:@Autowired

<context:annotation-config></context:annotation-config>

3 值注入
两种标签的方式(都是写在<bean>标签之间的):

方法一  <property name="属性名" value="值" />

<!-- index="构造方法的参数下标" ref="对象id,也就是注入的接口"-->

方法二  <constructor-arg index="下标" value="值"/> 

注解值注入:

方法一 @Value("值") -- 不推荐直接给值,直接写在属性上赋值
方法二 @Value("${key}") -- 引用外部配置文件(*.properties)中的值, 根据这个key 到 properties 文件中找相应的值

需要在spring.xml 中配置此 properties 文件:

<context:property-placeholder location="classpath:文件的位置"> 也就是放在resource包下的 properties 文件

a.properties 文件里的内容

order.name=订单
order.price=5000

用 @Value("${key}") 注入值

import org.springframework.beans.factory.annotation.Value;

public class OrderService {
    // @Value("${key}") 注入值
    @Value("${order.price}")
    private int price;

    @Value("${order.name}")
    private String name;

    public void print() {
        System.out.println("name:" + name);
        System.out.println("price:" + price);
    }
}

测试类 

import org.springframework.context.support.ClassPathXmlApplicationContext;
import service.OrderService;

public class TestOrderService {

    public static void main(String[] args) {
        //获得管理容器
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
        //从管理容器中获得对象
        OrderService orderService = context.getBean(OrderService.class);
        //调用方法
        orderService.print();

    }
}

结果

name:订单
price:5000

实际例子

1 spring 管理连接池(以阿里的 Druid 为例)

spring.xml中连接池的配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       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
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"
       >
       <!--destroy-method 关闭方式-->
       <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
           <!--连接接口和数据库,以下的所有name都是规定好的,value根据需求改-->
           <property name="url" value="jdbc:mysql://localhost:3306/test"/>
           <!--驱动-->
           <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
           <!--数据库用户名-->
           <property name="username" value="root"/>
           <!--数据库密码-->
           <property name="password" value="root"/>
           <!--设定连接池的最大连接数-->
           <property name="maxActive" value="10"/>
           <!--最小连接数-->
           <property name="minIdle" value="2"/>
       </bean>
</beans>

获取连接

import org.springframework.context.support.ClassPathXmlApplicationContext;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

public class TestDataSource {
    public static void main(String[] args) throws SQLException {
        // 没有用 spring 之前的代码
        /*DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl("jdbc:mysql://localhost:3306/test");
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUsername("root");
        dataSource.setPassword("root");

        dataSource.setMaxActive(10);
        dataSource.setMinIdle(2);
        dataSource.close()
        */
        //创建 spring 容器,把classpath(resource包)下的spring.xml文件放入
        ClassPathXmlApplicationContext context= new ClassPathXmlApplicationContext("spring.xml");
        //获得对象
        DataSource dataSource = context.getBean(DataSource.class);
        //获得连接
        Connection connection = dataSource.getConnection();
        //输出连接
        System.out.println(connection);
        //查看结束时是否调用close方法
        System.out.println(connection.isClosed());
    }
}

结果

com.mysql.jdbc.JDBC4Connection@38bc8ab5
false

2、.在spring之前,可以使用工厂方法+接口+配置文件的方式实现解耦合
spring中的做法

<!-- 启用 @Autowired 等注解 ,在spring.xml中配置-->

<context:annotation-config></context:annotation-config>

@Autowired
private UserDao userDao; // 被动获取,依赖注入

工厂方法的做法

private UserDao userDao = UserDaoFactory.getUserDao(); // 主动获取, 一般不管依赖关系

代码如下:

创建 config.properties 文件,填写如下

dao.UserDao=dao.impl.UserDaoFile

接口

public interface UserDao {

    public void insert();

    public void update();

}

实现接口

public class UserDaoFile implements UserDao {
    public void insert() {
        System.out.println(" UserDaoDatabase 文件的实现 insert");
    }

    public void update() {
        System.out.println(" UserDaoDatabase 文件的实现 update");

    }
}
public class UserDaoDatabase implements UserDao {
    public void insert() {
        System.out.println(" UserDaoDatabase 数据库的实现 insert");
    }

    public void update() {
        System.out.println(" UserDaoDatabase 数据库的实现 update");

    }
}

创建工厂对象

import java.io.FileInputStream;
import java.util.Properties;

// 用户dao 的工厂类, 负责创建 UserDao对象
public class UserDaoFactory {

    // 工厂方法模式 - factory-method 解耦了 service 和 dao 层
    // 1. 利用了接口
    // 2. 工厂方法
    // 3. 把变化的部分放入配置文件
    public static UserDao getUserDao() {
        try {
            Properties props = new Properties();
            //加载config.properties文件
            props.load(new FileInputStream("config.properties"));
            //输出加载文件得到的键(dao.UserDao)的值(dao.impl.UserDaoFile)
            System.out.println(props.get("dao.UserDao"));
            //获得字符串形式的类名
            String className = props.getProperty("dao.UserDao");
            System.out.println(className);

            // 根据字符串类名,反射得到类对象
            Class c = Class.forName(className);
            // 根据类对象,创建新的实例对象
            UserDao userDao = (UserDao)c.newInstance();

            return userDao;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

调用方法

public class UserService {

    // 创建对象的职责被分给了工厂类
    private UserDao userDao = UserDaoFactory.getUserDao();

    public void service() {
        userDao.insert();
        userDao.update();
    }
}

测试类

public class TestUserService {

    public static void main(String[] args) {
        UserService userService  = new UserService();
        userService.service();
    }
}

结果

dao.impl.UserDaoFile
dao.impl.UserDaoFile
 UserDaoDatabase 文件的实现 insert
 UserDaoDatabase 文件的实现 update

只要改动 config.properties 文件里 dao.UserDao 等于号后面的值,改成 dao.UserDao=dao.impl.UserDaoDatabase name调用的就是 UserDaoDatabase 中的两个方法了,下面输出的就不是文件实现,而是数据库实现了,这样代码明显比用标签注入的多。

总结

熟练应用框架,可以使代码量变少,并且便于管理项目的各个模块,使项目分层清楚,但是框架的学习不是一天就可以搞定,需要花费时间去搞定它,尤其是它还会更新新的功能,经常去它的官网查看最新功能,可以让自己该框架更加了解。

猜你喜欢

转载自blog.csdn.net/grey_mouse/article/details/87401298