万字解析 | 深入浅出Spring 框架,原来以前白学了(精华帖收藏)

本文深入浅出地介绍了Spring框架,从为什么要使用Spring开始,逐步讲解了Spring的IOC(控制反转)和DI(依赖注入)原理,包括bean的生命周期、后置处理和代理设计模式。接着详细阐述了AOP(面向切面编程)的概念、底层实现原理,如JDK动态代理和Cglib动态代理。文章还探讨了Spring中的各种注解,如@Component、@Autowired、@Resource等,以及如何通过注解实现AOP。通过实例,读者可以全面理解Spring框架的精髓所在。
摘要由CSDN通过智能技术生成

目录

1. 为啥要用 Spring

2. Spring 简介

3. 环境搭建

4. IOC

4.1 IOC 简介

4.2 Spring 核心内容描述

4.3 IOC 优点

5. DI

5.1 set 注入

5.2 构造注入

5.3 注入总结

6. Bean 的生命周期

6.1 创建阶段

6.2 初始化阶段

6.3 销毁阶段

7. Bean 的后置处理

8. 代理设计模式

8.1 为啥要用代理设计模式?

8.2 代理设计模式

8.3 静态代理

8.4 动态代理

8.5 动态代理实现原理

9. AOP

9.1 AOP 概念

9.2 AOP 底层实现原理

9.2.1 JDK 动态代理

9.2.2 Cglib 动态代理

9.2.3 Spring 如何创建代理对象?

9.3 基于注解开发 AOP

9.4 切入点表达式

10. Spring 相关注解

10.1 创建对象相关注解

10.1.1 @Component

10.1.2 @Component 衍生注解

10.1.3 @Scope

10.1.4 生命周期相关注解

10.2 注入相关注解

10.2.1 @Autowired

10.2.2 @Resource

10.2.3 案例

10.3 Spring 配置文件相关注解

10.3.1 @Configuration

10.3.2 @Bean

10.3.3 @ComponentScan

11. 注解小案例


1. 为啥要用 Spring

张三是一个编程小白,他每次在 service 层写代码都要自己 new 一堆 Dao 接口的实现类。

public class ProjectServiceImpl implements ProjectService {
    UserDao userDao = new UserDaoImpl();
    ProjectSectionDao projectSessionDao = new ProjectSessionDaoImpl();
    ProjectDao projectDao = new ProjectDaoImpl();
    SupplyDao supplyDao = new SupplyDaoImpl();
    .......   
}
复制代码

有一天正 new 着对象,张三心想:"我这一个 service 都需要 new 好多 Dao ,那如果有一堆 service ,那我不得花费好长时间?"

"有没有一个工具类或者什么框架能帮我管理这些对象?我只需要配置一下,需要的时候它就能自动帮我 new 个对象出来?"

张三陷入了深深的沉思之中。

张三的室友李四也是一个编程小白。

李四呢想给自己的小项目增加一个功能:记录方法执行的时间。结果他脑子一热竟然给所有的方法都增加了一堆打印方法:

System.out.println("项目开始执行");
// 开始时间
long start = System.currentTimeMillis();

// 业务代码

// 结束时间
long end = System.currentTimeMillis();
// 计算执行时间
System.out.printf("执行时间:%d 毫秒.", (end - start));
复制代码

过了半个小时,李四终于给项目中所有的方法都复制粘贴上了打印语句。他长舒一口气:"我真是个大聪明!"

张三看了一眼李四的代码,连连鼓掌:"妙啊!咱们宿舍的技术大神!"

旁边的王五实在忍不住了,对张三说:"妙个屁!最近的 Spring 框架课你俩是不是都没去?光顾着打游戏了?我都替你俩答了三次到了!"

李四问王五:"这个Spring 框架学了有用吗?"

王五:"不仅能解决张三说的管理对象的问题,还能帮你解决记录日志的问题。配置完 Spring ,你只需要定义一个切面类,根本不需要在一堆类上面复制粘贴一堆代码。"

张三摸摸后脑勺笑着说:"原来 Spring 框架那么好用,我以后再也不逃课了。我这就去翻课本学习 Spring 框架去。"

2. Spring 简介

Spring 是一个轻量级的 Java 开发框架。Spring 的核心是控制反转(IOC)和面向切面编程(AOP)。

Spring 主要有如下优点:

1.解耦

2.支持面向切面编程

3.便于集成其他框架

3. 环境搭建

1.创建 Maven 项目

File -> New -> Project -> Maven

2.引入依赖

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.16.RELEASE</version>
    </dependency>
</dependencies>
复制代码

3.创建接口和实现类

UserService

public interface UserService {
    void print();
}
复制代码

UserServiceImpl

public class UserServiceImpl implements  UserService{
    @Override
    public void print() {
        System.out.println("hello world");
    }
}
复制代码

4.创建配置文件

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="userService" class="com.xxl.service.impl.UserServiceImpl"/>
</beans>
复制代码

5.测试

@Test
public void testSpring(){
    // 1、获取工厂
    ApplicationContext act = new ClassPathXmlApplicationContext("/applicationContext.xml");
    // 2、通过工厂类获得对象
    UserService userService = (UserService)act.getBean("userService");
    // 3.调用方法
    userService.print();
}
复制代码

测试结果:

4. IOC

4.1 IOC 简介

IOC,全称 Inversion of Control,意思是控制反转。它是 Spring 框架中的一种思想。

控制反转就是将对象的控制权从程序中的代码转移到了 Spring 的工厂,通过 Spring 的工厂完成对象的创建以及赋值。

也就是说之前是我们自己 new 对象、给对象中的成员变量赋值。现在是让 Spring 来帮助我们创建对象、给成员变量赋值。

4.2 Spring 核心内容描述

1.配置文件

Spring 的配置文件可以放到项目中的任意一个地方,也可以随意命名,但是建议使用:applicationContext.xml。

你可以将这个配置文件看成一个装有一堆 bean 标签的容器。

2.bean 标签

Spring 工厂创建的对象,叫做 bean,所以一个 bean 标签代表一个对象。

<bean id="userService" class="com.xxl.service.impl.UserServiceImpl"/>
复制代码

bean 标签中必须要有 class 属性,它的值是一个类的全限定名(包名+类名)。

除了 class 属性,bean 标签还可以设置 id 、name 、scope属性。

id:

id 必须以字母开头,相当于这个 bean 的身份证号,是唯一的。

如果这个 bean 只使用一次,id 可以省略不写。

如果这个 bean 需要被其他 bean 引用,或者这个 bean 要使用很多次,则必须要有 id 属性。

如果只配置 class 属性,Spring 框架会给每一个 bean 配置一个默认的 id:"全限定名#1"。

例如:

com.xxl.service.impl.UserServiceImpl#1
复制代码

name:

name 相当于这个 bean 的别名,它可以配置多个,例如:

<bean id="user" name="aa,bb,cc" class="com.xxl.model.User"/>
复制代码

scope:

scope 属性可以控制简单对象的创建次数,它有两个值:

1.singleton:每次只会创建唯一⼀个简单对象,默认值。

2.prototype:每⼀次都会创建新的对象。

例如:

<bean id="user" class="com.xxl.model.User" scope="singleton"/>
复制代码

3.ApplicationContext

ApplicationContext 是 Spring 的工厂,主要用来创建对象。

Spring 通过读取配置文件创建工厂。

因为 Spring 的工厂会占用大量内存,所以一个程序一般只会创建一个工厂对象。

4.工厂常用方法

1.根据 id 获取对象

UserService userService = (UserService)act.getBean("userService");
复制代码

2.根据 id 和类名获取对象

UserService userService = (UserService)act.getBean("userService",UserService.class);
复制代码

3.只根据类名获取对象

UserService userService = (UserService)act.getBean(UserService.class);
复制代码

4.获取配置文件中所有 bean 标签的 id 值

String[] beanDefinitionNames = act.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
    System.out.println(beanDefinitionName);
}
复制代码

结果:

5.判断是否存在指定 id 或者 name 的 bean

act.containsBean("userService")
复制代码

6.判断是否存在指定 id 的 bean,只能用来判断 id

act.containsBeanDefinition("userService")
复制代码

5.创建对象

Spring 是如何创建对象的呢?

工厂和反射

首先说下反射,我们可以通过一个类的全限定名获取 Class 对象,然后再通过 Class 实例化一个对象:

Class serviceClass = Class.forName("com.xxl.service.impl.UserServiceImpl");
UserService userService = (UserService)serviceClass.newInstance();
复制代码

Spring 配置文件中 bean 标签的 id 和类的全限定名一一对应,所以 Spring 工厂的 getBean 方法其实就是先根据 bean 的 id 获取该类的全限定名,然后再利用反射根据类的全限定名创建对象并返回。

4.3 IOC 优点

解耦

说起解耦之前先说下耦合:耦合是指代码之间的关联性太强,我如果改了这一段代码,可能会影响到一堆代码。

那创建对象哪里有耦合了?其实就是new关键字带来的耦合。

如果你发现一个接口的实现类需要修改,你需要手动改动程序中的代码,比如修改 new 关键字后面的实现类,这样可能会影响到其他的代码。

但是使用了 Spring 之后,我们只需要修改配置文件中 bean 标签的 class 属性对应的类的全限定名,不用修改程序中的代码,这样就做到了解耦。

解耦就是解除不同代码之间的关联性、依赖性。

5. DI

DI 全称 Dependency Injection,意思是依赖注入,它是 IOC 的具体实现。

依赖就是说我需要你,比如 Service 层依赖 Dao 层,注入就是赋值

依赖注入:使用 Spring 的工厂和配置文件为一个类的成员变量赋值。

没有使用 Spring 的依赖注入我们是这样赋值的:

User user = new User();
user.setName("张三");
复制代码

如果设置有误,就需要手动修改代码,代码耦合度较高,而依赖注入的出现就是为了解耦。

Spring 的依赖注入包含两种方式:

5.1 set 注入

set 注入:Spring 调用 Set 方法通过配置文件为成员变量赋值。

1.创建对象,为属性添加 set/get 方法

public class User {
    private String name;
    private int age;

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
复制代码

2.修改配置文件

<bean id="user" class="com.xxl.model.User">
  <property name="name" value="知否君" />
  <property name="age" value="18" />
</bean>
复制代码

3.测试

// 1、获取工厂
ApplicationContext act = new ClassPathXmlApplicationContext("/applicationContext.xml");
// 2、通过工厂类获得对象
User user = (User)act.getBean("user");
System.out.println("姓名:"+user.getName());
System.out.println("性别:"+user.getAge());
复制代码

测试结果:

从上面的例子可以看出 Set 注入就是在 property 标签中为属性赋值。spring 可以为 JDK 内置的数据类型进行赋值,也可以为用户自定义的数据类型进行赋值。

5.1.1 JDK 内置数据类型

1.基本类型

<property name="name" value="知否君" />
<property name="age" value="18" />
复制代码

2.List 集合

<property name="phones">
	<list>
		<value>15799999918</value>
		<value>15788888819</value>
		<value>15766666620</value>
	</list>
</property>
复制代码

3.Set 集合

<property name="phones">
	<set>
		<value>15799999918</value>
		<value>15788888819</value>
		<value>15766666620</value>
	</set>
</property>
复制代码

4.Map 集合

<property name="mapInfo">
    <map>
        <entry>
            <key><value>name</value></key>
            <value>知否君</value>
        </entry>
        <entry>
            <key><value>age</value></key>
            <value>23</value>
        </entry>
    </map>
</property>
复制代码

5.数组

<property name="phones">
    <list>
      	<value>15799999918</value>
        <value>15788888819</value>
        <value>15766666620</value>
    </list>
</property>
复制代码

6.Properites

<property name="prop">
    <props>
        <prop key="key1">value1</prop>
        <prop key="key2">value2</prop>
    </props>
</property>
复制代码

5.1.2 用户自定义数据类型

1.为成员变量添加 set/get 方法

public class UserServiceImpl implements UserService {
    
    private UserDao userDao;

    public UserDao getUserDao() {
        return userDao;
    }

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
    @Override
    public void print() {
        userDao.print();
    }
}
复制代码

2.bean 标签使用 ref 属性

<bean id="userDao" class="com.xxl.dao.impl.UserDaoImpl" />
<bean id="userService" class="com.xxl.service.impl.UserServiceImpl">
   <property name="userDao" ref="userDao"/>
</bean>
复制代码

3.测试

@Test
public void testSpring(){
    // 1、获取工厂
    ApplicationContext act = new ClassPathXmlApplicationContext("/applicationContext.xml");
    // 2、通过工厂类获得对象
    UserService userService = (UserService)act.getBean("userService");
    // 3.调用方法
    userService.print();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值