Spring两大特性IOC和AOP究竟是什么东西

首先,在学习之前,我们先了解一下什么是Spring

1、Spring简介

顾名思义,Spring : 春天 —>给软件行业带来了春天。

2002年,Rod Jahnson首次推出了Spring框架雏形interface21框架。

2004年3月24日,Spring框架以interface21框架为基础,经过重新设计,发布了1.0正式版。

很难想象Rod Johnson的学历 , 他是悉尼大学的博士,然而他的专业不是计算机,而是音乐学。

Spring理念 : 使现有技术更加实用 . 本身就是一个大杂烩 , 整合现有的框架技术

<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.10.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-orm</artifactId>
    <version>5.2.10.RELEASE</version>
</dependency>

1.2、优点

1、Spring是一个开源免费的框架 , 容器 .

2、Spring是一个轻量级的框架 , 非侵入式的 .

3、控制反转 IOC , 面向切面 Aop

4、对事物的支持 , 对框架的支持

一句话概括:

Spring是一个轻量级的控制反转(IOC)和面向切面(AOP)的容器(框架)。

1.3、组成

Spring 框架是一个分层架构,由 7 个定义良好的模块组成。Spring 模块构建在核心容器之上,核心容器定义了创建、配置和管理 bean 的方式 .

å¨è¿éæå¥å¾çæè¿°

组成 Spring 框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现。每个模块的功能如下:

  • 核心容器:核心容器提供 Spring 框架的基本功能。核心容器的主要组件是 BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转(IOC) 模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。
  • Spring 上下文:Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。
  • Spring AOP:通过配置管理特性,Spring AOP 模块直接将面向切面的编程功能 , 集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理任何支持 AOP的对象。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖组件,就可以将声明性事务管理集成到应用程序中。
  • Spring DAO:JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。
  • Spring ORM:Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括 JDO、Hibernate 和 iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。
  • Spring Web 模块:Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以,Spring 框架支持与 Jakarta Struts 的集成。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
  • Spring MVC 框架:MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI。

粗略介绍完Spring,我们就不得不着重学习一下它最重要的两个思想,也就是上文中提到过的控制反转(IOC)面向切面(AOP)

2、控制反转 Inversion Of Control(IOC)

2.1.分析实现

在学习Spring之前

我们创建对象一般有两种方法:一个是new,一个是反射,并且用什么创建什么。

那么能不能有一个很简便的方法让我们用的时候省去了创建的步骤,直接拿来用呢?

就像我们现实中找对象一样,找一个对象需要花费时间,金钱,还有肾,才可能拥有一个对象,那么能不能真的有一天国家开始分发对象,谁用谁去领呢?别担心,Spring替我们实现了这个梦想!!!

以前,所有东西都是由程序去进行控制创建 , 而现在是由我们自行控制创建对象 , 把主动权交给了调用者 . 程序不用去管怎么创建,怎么实现了 . 它只负责提供一个接口 .

这种思想 , 从本质上解决了问题 , 我们程序员不再去管理对象的创建了 , 更多的去关注业务的实现 . 耦合性大大降低 . 这也就是IOC的原型 !

也是Spring最大的特点之一,说白了就像是工厂模式一样,把创建对象的任务交付给工厂,从而降低耦合性,其实Spring中创建Bean,就是用的工厂,叫做BeanFactory

2.2、IOC本质

实质上,控制反转IOC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IOC的一种方法,也有人认为DI只是IOC的另一种说法。没有IOC的程序中 , 我们使用面向对象编程 , 对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。

控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IOC容器,其实现方法是依赖注入(Dependency Injection,DI)。

那么说白了,他就是创建对象的,并且给他创建的对象起了个名字叫做Bean,这就是IOC最浅显的意思和道理。

那它究竟是怎么具体创建出来这个叫做Bean的对象的呢?

这就涉及到Spring这里很常见的一个面试题叫做:Bean的生命周期。也就是一个Bean从无到有的过程。

我们去源码里面看一看它!

2.3、SpringBean的生命周期

就像上文中的例子一样,国家的确给你创建出来了对象,你也可以去领,那么国家对象管理局是怎么创建对象的呢?我们是不是也要了解一下,不能拿来主义呢?

下面我们详细介绍一下国家对象管理局创建对象的过程:

1、配置文件

既然要创建对象,那么就要告诉Spring你要创建什么对象,就需要配置文件,Spring中配置对象的属性文件一般有两种形式:XML文件和注解。

推荐大家要用就用一种,免得显得你的对象很low,上身穿着貂,下面大红裤衩,不伦不类。

他们的具体实现方式大家一定都知道了。

一、Xml文件配置:

<bean name="Car" class="com.dyit.entity.Car">
		<property name="brand" value="奔驰G"/>
		<property name="color" value="黑色"/>
		<property name="price" value="2000000.00"/>
</bean>

二、注解配置

<context:component-scan base-package="com.dyit.entity"/>
@Component
public class Car {
    private String brand;
    private String color;
    private Double price;
    private Company company;

    public void run(){
        System.out.println("小火车呜呜呜");
    }
}

然后通过以下代码,运行同一种接果:

public void Test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:bean.xml");
        Car car = context.getBean("Car", Car.class);
        car.run();
}

这就是一个最简单的IOC实例,在这个过程没有出现new吧!正式代码中我们只是用了一个Get方法,就获取到了对象,因此看得出来,创建对象全部在

ApplicationContext context = new ClassPathXmlApplicationContext("classpath:bean.xml")

这一行代码中完成,具体怎么操作我们不如点进去看看。

进入源码我们可以看见这样两段代码:

this.setConfigLocations(configLocations);
if (refresh) {
    this.refresh();
}

其中,setConfigLocations就是用来设置我们的配置文件位置的,也就是我们传进去的参数“...Xml”,而这里面这个

refresh()

才是完成Bean创建的核心方法!

在这个方法中,实现了以下步骤,用来创建一个Bean对象:

步骤一、构建工厂加载Bean定义信息

在读取配置文件,创建对象之前,我们自然要构建一个工厂,不然对象谁来创建?

所以第二步,自然而然就是创建工厂,在refresh方法中,有这样一行代码:

ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();

在这个方法中,有一个这样的方法:

protected DefaultListableBeanFactory createBeanFactory() {
        return new DefaultListableBeanFactory(this.getInternalParentBeanFactory());
}

这就是创建工厂的代步骤,看的出来我们创建工厂的工具类叫做DefaultListableBeanFactory而不是BeanFactory,一定注意!!!

同时还有一段代码叫做:

this.loadBeanDefinitions(beanFactory);

这是用来加载Bean的定义信息的,在这一步就会扫描我们的配置文件,完成Bean定义信息的加载,并不实例化。

Bean对象在Spring底层中时放在Map集合中的,这一步就明确了这个Map集合(我一般叫它单例池)的大小,也就是说将来放多少对象进去。

然后紧接着在创建完成之后,会将Bean工厂丢进这样一行代码中:
 

 this.prepareBeanFactory(beanFactory);

这里面有一大堆Set方法,用来定义Bean工厂的基本信息,我们今天今天讲的时IOC我也就没去看。

步骤二、执行postProcessBeanFactory方法

这个方法是一个空的方法,是留给我们去复写使用的,叫做Bean工厂的处理器,起码我现在的水平,对我而言没多大卵用。执行BeanFactoryPostProcessors一系列方法。

步骤三、执行invokeBeanFactoryPostProcessors方法

然后还有一大堆注册监听等过程,不在赘述

this.registerBeanPostProcessors(beanFactory);
this.initMessageSource();
this.initApplicationEventMulticaster();
this.onRefresh();
this.registerListeners();

步骤五、执行finishBeanFactoryInitialization(beanFactory)方法

这行代码,我只能说重中之重!!!他的注解叫做“实例化所有懒加载的单例对象”,注意实例化三个字,也就是说,前面做了那么多工作,就是为了在这里创建对象出来。具体怎么做呢?

1、这个方法中有一个方法叫做:

beanFactory.preInstantiateSingletons();

在这个里面创建,这个方法中有这样一串代码:

 List<String> beanNames = new ArrayList(this.beanDefinitionNames);

注意这个词:beanDefinitionNames,这个东西叫做Bean的定义信息,是在上面第二步构建工厂的时候,加载好的,现在这个方法中拿到了这些定义信息,然后放在一个List集合中,在进行迭代实例化。

2、迭代的时候。利用preInstantiateSingletons方法里面一个叫做getBean的方法里面的doGetBean方法来实例化。这里有一个常识,Java里面凡是带有“do”字样的方法,一般就是干实事的方法。

3、dogetBean方法这里有一个叫做createBean的方法,里面还有一个doCreatBean方法。

doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)

再一次看的“do”,说明真的要干实事了!(实例化)

4、这个doCreateBean里还有一个方法(是不是懵逼的一批?),里面这个方法叫做creatBeanInstance,顾名思义:创建Bean的单例对象。

他是这么操作的:

doCreateBean(){
    BeanWrapper instanceWrapper = null;

    if (instanceWrapper == null) {
        instanceWrapper = this.createBeanInstance(beanName, mbd, args);
    }
}

注意: 这里这里有一个很小的细节,方法中并没有直接返回Bean类型,而是重新创建了一个包装类叫做BeanWrapper用来存放返回的Bean的实例对象。

这个类再后面的AOP中,大展宏图!!!

5、包装类创建好之后,createBeanInstance里面会有一段这样的代码:

Constructor<?>[] ctors = this.determineConstructorsFromBeanPostProcessors(beanClass, beanName);

在这里,先通过反射得到我们要创建对象的类,然后再返回当前类的构造器(构造方法)的一个集合,他返回了一个构造器的集合,所以我们现在有构造器了。

6、拿到构造器之后,会进入一个这个方法之中:

instantiateBean();

注意:这个方法的返回值是BeanWrapper类型的。

7、这个方法中有一个返回方法:

return BeanUtils.instantiateClass(constructorToUse, new Object[0])

8、他的里面有一行这样的代码:

return ctor.newInstance(argsWithDefaultValues);

这行代码我们再熟悉不过,这TMD不就是反射嘛!!!这不就创建出对象了!!!我终于成功了!!!

终于完了???

当然还没有.............

到了这一步,我们的确以及完成了对象的实例化,但是他是个空壳呀!就像上面我们说的向国家身前对象,国家给你创建对象总得有标准嘛!不能人人一样,比如张三喜欢腿长的,李四喜欢胸大的。。。

众口难调也得调!所以下一步就是属性填充!

属性填充:

9、我们再次回到doCreateBeanC这个方法下面,现在我们已经执行完了creatBeanInstance方法,接下来进入一个这个方法:

this.populateBean(beanName, mbd, instanceWrapper);

populate英文单词,你们比我强的人都知道,这个单词的意思就是:填充!

填充当然很简单,一大堆set方法呗!

10、然后,就是紧跟着这行代码的一个代码:

exposedObject = this.initializeBean(beanName, exposedObject, mbd);

这里执行的,是一系列BeanPostProcessor,什么是BeanProCessor呢?这个就是Spring的另一个重要概念AOP所涉及到的了,我们待会说。

走到这里。终于,从上面preInstantiateSingletons方法中的List中实例化了一个对象出来,接下来Spring继续迭代,如法炮制。

步骤六:执行finishRefresh()完成refresh

随后。我们从这么多方法中一个个将实例化的对象一个个返回,我们就完成了所有对象的实例化!!!然后放进我们第一步创建的单例池中,放进IOC容器。

这,就是SpringIOC的原理的源码详解。

这么梳理下来,我都有点晕,所以我们画一个框图,来表示以下

3、面向切面 Aspect-Oriented Programming (AOP)

面向切面编程(AOP)就是纵向的编程。比如业务A和业务B现在需要一个相同的操作,传统方法我们可能需要在A、B中都加入相关操作代码,而应用AOP就可以只写一遍代码,A、B共用这段代码。并且,当A、B需要增加新的操作时,可以在不改动原代码的情况下,灵活添加新的业务逻辑实现。

在实际开发中,比如商品查询、促销查询等业务,都需要记录日志、异常处理等操作,AOP把所有共用代码都剥离出来,单独放置到某个类中进行集中管理,在具体运行时,由容器进行动态织入这些公共代码。

 AOP主要一般应用于签名验签、参数校验、日志记录、事务控制、权限控制、性能统计、异常处理等。

3.1、涉及名词

切面(ASPECT):横切关注点 被模块化 的特殊对象。即,它是一个类。

通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。

目标(Target):被通知对象。

代理(Proxy):向目标对象应用通知之后创建的对象。

切入点(PointCut):切面通知 执行的 “地点”的定义。

连接点(JointPoint):与切入点匹配的执行点。

SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice,即 Aop 在 不改变原有代码的情况下 , 去增加新的功能 .

3.2、实现

使用注解实现

第一步:编写一个注解实现的增强类

package com.kuang.config;
 
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
 
@Aspect
public class AnnotationPointcut {
    @Before("execution(* com.kuang.service.UserServiceImpl.*(..))")
    public void before(){
        System.out.println("---------方法执行前---------");
    }
 
    @After("execution(* com.kuang.service.UserServiceImpl.*(..))")
    public void after(){
        System.out.println("---------方法执行后---------");
    }
 
    @Around("execution(* com.kuang.service.UserServiceImpl.*(..))")
    public void around(ProceedingJoinPoint jp) throws Throwable {
        System.out.println("环绕前");
        System.out.println("签名:"+jp.getSignature());
        //执行目标方法proceed
        Object proceed = jp.proceed();
        System.out.println("环绕后");
        System.out.println(proceed);
    }
}

第二步:在Spring配置文件中,注册bean,并增加支持注解的配置

<!--第三种方式:注解实现-->
<bean id="annotationPointcut" class="com.kuang.config.AnnotationPointcut"/>
<aop:aspectj-autoproxy/>

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值