【超详细】Spring 核心知识点全解析(IOC+AOP)

一、Spring 概述

1.1 Spring 介绍

Spring 是轻量级 Java EE 应用开源框架(全栈式开发框架,官网: http://spring.io ),由 Rod Johnson 创建,旨在解决企业级编程开发的复杂性。

1.2 Spring 的优点

  1. IOC:解决传统 Web 开发中硬编码所造成的程序耦合(控制反转)
  2. AOP:在运行期间不修改源代码对程序进行增强(切面编程)
  3. 粘合剂:除自身功能外,还提供整合其他技术和框架的能力

1.3 Spring 的体系结构

Spring 框架按功能分为五大模块:

模块分类 核心功能
Core Container(核心容器) 提供 IOC 和依赖注入特性,是框架最基础部分
AOP、Aspects、Instrumentation(检测)、Messaging(消息处理) 提供面向切面编程实现、检测、消息处理等
Data Access/Integration(数据访问与集成) 简化持久层操作
Web 提供 Spring MVC 框架及与 Servlet、WebSocket 的集成
Test 方便程序测试

1.4 Spring 的发展历程

  • 1997 年 IBM 提出了 EJB 的思想
  • 1998 年,SUN 制定开发标准规范 EJB1.0
  • 1999 年,EJB1.1 发布
  • 2001 年,EJB2.0 发布
  • 2003 年,EJB2.1 发布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 雏形)
  • 2006 年,EJB3.0 发布
  • 2017 年 9 月发布了 Spring 的最新版本 Spring5.0 通用版
  • ......

二、Spring IOC(重点)

2.1 程序的耦合

  • 耦合:对象之间的依赖关系,耦合越高,维护成本越高

  • 传统开发耦合案例:没有引入 IOC 容器时系统的 Web 层、业务层、持久层存在耦合

    /**

    • 持久层实现类
      */
      public class UserDaoImpl implements UserDao {

      @Override
      public void addUser(){
      System.out.println("insert into tb_user......");
      }
      }

    /**

    • 业务层实现类
      */
      public class UserServiceImpl implements UserService {
      //硬编码:此处有依赖关系
      private UserDao userDao = new UserDaoImpl();

      public void addUser(){
      userDao.addUser();
      }
      }

    /**

    • 模拟表现层
      */
      public class Client {
      public static void main(String[] args) {
      //硬编码:此处有依赖关系
      UserService userService = new UserServiceImpl();
      userService.addUser();
      }
      }
  • 问题分析:service 层依赖 dao 层实现类,若修改 dao 实现类或缺失 dao 实现类,编译失败

  • IOC (工厂模式) 解耦 步骤

    1. 把所有的 dao 和 service 对象使用配置文件配置起来
    2. 当服务器启动时读取配置文件
    3. 把这些对象通过反射创建 出来并保存在容器(Map)中
    4. 在使用的时候,直接从工厂拿

2.2 工厂模式的 IOC 解决程序耦合

2.2.1 什么是 IOC

  • IOC (Inverse of Control) 即控制反转:正转是自己创建依赖对象;反转是由IOC 工厂来创建依赖对象;

  • 传统方式:主动 new 创建依赖对象。

  • IOC 方式:被动从 IOC 工厂获取依赖对象。

    这种被动接收的方式获取对象的思想就是控制反转,它是 spring 框架的核心之一。

2.2.2 工厂模式的 IOC 解耦

  • 案例一

    /**

    • bean工厂
      */
      public class BeanFactory_v1 {

      /**

      • 获得UserServiceImpl对象
      • @return
        */
        public static UserService getUserService(){
        return new UserServiceImpl();
        }

      /**

      • 获得UserDaoImpl对象
      • @return
        */
        public static UserDao getUserDao(){
        return new UserDaoImpl();
        }
        }

问题:我们在开发中会有很多个 service 和 dao,此时工厂类就要添加无数个方法。

  • 案例二

配置文件(bean.properties):

properties

复制代码
#1、配置要使用的dao和service
UserDao=com.hg.dao.UserDaoImpl
UserService=com.hg.service.UserServiceImpl

工厂类:

复制代码
/**
 * bean工厂
 */
public class BeanFactory_v2 {

    private static Properties prop = new Properties();

    /**
     * 根据全类名获取bean对象
     * @param beanName
     * @return
     * @throws ClassNotFoundException
     */
    public static Object getBean(String beanName) {
        try {
            //不能使用:web工程发布后没有src目录
            //InputStream is = new FileInputStream("src/bean.properties");
            InputStream is = 
            BeanFactory_v2.class.getClassLoader()
                .getResourceAsStream("bean.properties");
            prop.load(is);
            return Class.forName(prop.getProperty(beanName)).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static void main(String[] args) {
        System.out.println(prop.get("UserService"));
        System.out.println(getBean("UserService"));
    }
}

业务层实现类:

复制代码
/**
 * 业务层实现类
 */
public class UserServiceImpl implements UserService {
    private UserDao userDao = (UserDao) BeanFactory.getBean("UserDao");

    public void addUser(){
      userDao.addUser();
    }
}

测试类:

运行

复制代码
/**
 * 模拟表现层
 */
public class Client {
    public static void main(String[] args) {
        //直接引用接口实现类
      for (int i = 0; i < 5; i++) {
            UserService userService = 
              (UserService)BeanFactory_v2.getBean("UserService");
            System.out.println(userService);
        }
    }
}

问题

  1. 每次都会创建新的对象
  2. 程序运行时才创建对象 (读取配置文件)
  • 案例三

    package com.hg.factory;

    import java.io.InputStream;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Properties;
    import java.util.Set;

    /**

    • bean工厂
      */
      public class BeanFactory_v3 {

      //定义一个容器,用于存放对象
      private static Map<String, Object> beans = new HashMap<>();

      /**

      • 加载配置文件
        */
        static {
        try {
        //2、读取配置文件
        //不能使用:web工程发布后没有src目录
        //InputStream is = new FileInputStream("src/bean.properties");
        InputStream is =
        BeanFactory_v3.class.getClassLoader()
        .getResourceAsStream("bean.properties");

        复制代码
         //3、通过反射创建对象,把对象存到容器中
         Properties prop = new Properties();
         prop.load(is);
         Set<Map.Entry<Object, Object>> entrySet = prop.entrySet();
         for (Map.Entry<Object, Object> entry : entrySet) {
             String key = entry.getKey().toString();
             String beanName = entry.getValue().toString();
             Object value = Class.forName(beanName).newInstance();
             beans.put(key, value);
         }

        } catch (Exception e) {
        e.printStackTrace();
        }
        }

      /**

      • 4、在使用的时候,直接从工厂拿
      • @param beanName
      • @return
        */
        public static Object getBean(String beanName) {
        try {
        return beans.get(beanName);
        } catch (Exception e) {
        e.printStackTrace();
        }
        return null;
        }

      public static void main(String[] args) {
      System.out.println(getBean("UserService"));
      }
      }

2.3 Spring 的 IOC 解决程序耦合

2.3.1 创建工程

2.3.1.1 pom.xml

xml

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.hg</groupId>
    <artifactId>Spring_IOC_Xml</artifactId>
    <version>1.0-SNAPSHOT</version>
    
    <properties>
        <!-- 项目源码及编译输出的编码 -->
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <!-- 项目编译JDK版本 -->
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    
    <dependencies>
        <!-- Spring常用依赖 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.8.RELEASE</version>
        </dependency>
    </dependencies>
</project>

注意:Jar 包彼此存在依赖,只需引入最外层 Jar 即可由 Maven 自动将相关依赖 Jar 引入到项目中。

Spring 常用功能的 Jar 包依赖关系

核心容器模块说明

  • spring-beans + spring-core:提供 IOC/DI,BeanFactory(延迟加载)
  • spring-context:扩展 BeanFactory,ApplicationContext(立即加载)
  • spring-expression:Spring 表达式语言
2.3.1.2 dao
复制代码
/**
 * 持久层实现类
 */
public class UserDaoImpl implements UserDao {

    @Override
    public void addUser(){
        System.out.println("insert into tb_user......");
    }
}
2.3.1.3 service
复制代码
/**
 * 业务层实现类
 */
public class UserServiceImpl implements UserService {
    //此处有依赖关系
    private UserDao userDao = new UserDaoImpl();
    
	@Override
    public void addUser(){
        userDao.addUser();
    }
}

2.3.2 IOC

2.3.2.1 applicationContext.xml

xml

XML 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!--1、注意:要导入schema约束-->
<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">
    
    <!--
		2、<bean>标签作用:通过反射创建对象并交给spring的ioc容器去管理
       		id:给对象在容器中提供一个唯一标识。用于获取对象	
		   	class:指定类的全限定类名。用于反射创建对象。默认情况下调用无参构造函数
	-->
    <bean id="userDao" class="com.hg.dao.UserDaoImpl"></bean>
    <bean id="userService" class="com.hg.service.UserServiceImpl"></bean>
</beans>

注意:命名无限制,约定俗成命名有:spring-context.xml、applicationContext.xml、beans.xml

2.3.2.2 测试

运行

复制代码
/**
 * 模拟表现层
 */
public class Client {
    public static void main(String[] args) {
        //1.使用ApplicationContext接口,就是在获取spring容器
        ApplicationContext ac = new 
            ClassPathXmlApplicationContext("applicationContext.xml");
        //2.根据bean的id获取对象
        UserDao userDao = (UserDao) ac.getBean("userDao");
        System.out.println(userDao);

        UserService userService = (UserService) ac.getBean("userService");
        System.out.println(userService);
        userService.addUser();
    }
}
  • 问题:service 层仍然耦合

2.3.3 DI

概述:DI(Dependency Injection)依赖注入:将依赖对象(userDao)从容器中拿出来赋值给调用者(userService)

2.3.3.1 构造函数注入

使用类中的构造函数,给成员变量赋值。注意,赋值的操作不是我们自己做的,而是通过配置的方式,让 spring 框架来为我们注入。具体代码如下:

复制代码
/**
 * 业务层实现类
 */
public class UserServiceImpl implements UserService {

    private UserDao userDao;
    private String name;
    private Integer age;

    public UserServiceImpl(UserDao userDao, String name, Integer age) {
        this.userDao = userDao;
        this.name = name;
        this.age = age;
    }

    public void addUser(){
        System.out.println(name+","+age);
        userDao.addUser();
    }
}

xml

XML 复制代码
​
<?xml version="1.0" encoding="UTF-8"?>
<!--1、注意:要导入schema约束-->
<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">
    <!--2、把对象交给spring来创建-->
    <bean id="userDao" class="com.hg.dao.UserDaoImpl"></bean>
    <bean id="userService" class="com.hg.service.UserServiceImpl">
        <!--
               要求:类中需要提供一个对应参数列表的构造函数。
               标签:constructor-arg
                       ==给谁赋值:==
				           index:指定参数在构造函数参数列表的索引位置
				           name:指定参数在构造函数中的名称
				       ==赋什么值:==
				           value:它能赋的值是基本数据类型和String类型
				           ref:它能赋的值是其他bean类型,也就是说,必须得是在配置文件中配置过的bean
        -->
        <constructor-arg name="userDao" ref="userDao"></constructor-arg>
        <constructor-arg name="name" value="张三"></constructor-arg>
        <constructor-arg index="2" value="18"></constructor-arg>
    </bean>
</beans>

​
2.3.3.2 set 方法注入

顾名思义,就是在类中提供需要注入成员的 set 方法。具体代码如下:

复制代码
/**
 * 业务层实现类
 */
public class UserServiceImpl implements UserService {

    private UserDao userDao;
    private String name;
    private Integer age;

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

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

    public void setAge(Integer age) {
        this.age = age;
    }

    public void addUser(){
        System.out.println(name+","+age);
        userDao.addUser();
    }
}

xml

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!--1、注意:要导入schema约束-->
<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">
    <!--2、把对象交给spring来创建-->
    <bean id="userDao" class="com.hg.dao.UserDaoImpl"></bean>
    <bean id="userService" class="com.hg.service.UserServiceImpl">
        <!--
               要求:必须提供set方法
               标签:property
                       ==给谁赋值:==
				           name:找的是类中set方法后面的部分
				       ==赋什么值:==
				           value:它能赋的值是基本数据类型和String类型
				           ref:它能赋的值是其他bean类型,也就是说,必须得是在配置文件中配置过的bean
        -->
        <property name="userDao" ref="userDao"></property>
        <property name="name" value="张三"></property>
        <property name="age" value="18"></property>
    </bean>
</beans>
2.3.3.3 自动注入

不用在配置中 指定为哪个属性赋值,由 spring 自动根据某个 "原则" ,在工厂中查找一个 bean 并为属性注入值。具体代码如下:

复制代码
/**
 * 业务层实现类
 */
public class UserServiceImpl implements UserService {

    private UserDao userDao;

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public void addUser(){
        userDao.addUser();
    }
}

xml

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!--1、注意:要导入schema约束-->
<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">
    <!--2、把对象交给spring来创建-->
    <bean id="userDao" class="com.hg.dao.UserDaoImpl"></bean>
        <!--autowire="byType":按照类型自动注入值-->
    <bean id="userService" class="com.hg.service.UserServiceImpl" autowire="byType">
    </bean>
</beans>
未完待续...
相关推荐
李白的粉2 小时前
基于springboot的阿博图书馆管理系统
java·spring boot·后端·毕业设计·课程设计·源代码·图书馆管理系统
缘空如是2 小时前
基础工具包之pdf操作
java·pdf·搜索和水印
ren049182 小时前
Spring Framework、SpringBoot、Mybatis、Freemarker
spring boot·spring·mybatis
wsss_fan2 小时前
IntelliJ IDEA 无法识别 Maven SNAPSHOT 依赖,但 Maven 编译正常
pycharm·maven·intellij-idea
tsyjjOvO2 小时前
Spring 核心知识点全解析(IOC+AOP)
java·后端·spring
absunique2 小时前
Spring boot 3.3.1 官方文档 中文
java·数据库·spring boot
召田最帅boy2 小时前
Spring Boot博客系统集成AI智能摘要功能实战
人工智能·spring boot·后端
96772 小时前
spring boot 终端运行指令以及这个查询端口是否被占用,以及释放端口的命令
java·spring boot·后端
XiYang-DING2 小时前
【Java SE】Java中的static关键字总结
java·开发语言