底层源码分析 Spring 的核心功能和执行流程(二)

共 8457字,需浏览 17分钟

 ·

2021-07-29 06:59

谈一谈你对 IoC 和 DI 的理解

IoC(Inversion of Control,翻译为“控制反转”)不是一个具体的技术,而是一种设计思想。与传统控制流相比,IoC 会颠倒控制流,在传统的编程中需要开发者自行创建并销毁对象,而在 IoC 中会把这些操作交给框架来处理,这样开发者就不用关注具体的实现细节了,拿来直接用就可以了,这就是控制反转。

IoC 很好的体现出了面向对象的设计法则之一——好莱坞法则:“别找我们,我们找你”。即由 IoC 容器帮对象找到相应的依赖对象并注入,而不是由对象主动去找。

DI(Dependency Injection,翻译为“依赖注入”)表示组件间的依赖关系交由容器在运行期自动生成,也就是说,由容器动态的将某个依赖关系注入到组件之中,这样就能提升组件的重用频率。通过依赖注入机制,我们只需要通过简单的配置,就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心资源来自哪里、由谁实现等问题。

Spring IoC 有哪些优势

  1. 使用更方便,拿来即用,无需显式的创建和销毁的过程;

  2. 可以很容易提供众多服务,比如事务管理、消息服务等;

  3. 提供了单例模式的支持;

  4. 提供了 AOP 抽象,利用它很容易实现权限拦截、运行期监控等功能;

  5. 更符合面向对象的设计法则;

  6. 低侵入式设计,代码的污染极低,降低了业务对象替换的复杂性。

IoC 的注入方式有哪些

  1. 构造方法注入:构造方法注入主要是依赖于构造方法去实现,构造方法可以是有参的也可以是无参的,我们平时 new 对象时就是通过类的构造方法来创建类对象的,每个类对象默认会有一个无参的构造方法,Spring 通过构造方法注入

  2. Setter 注入:Setter 方法注入的方式是目前 Spring 主流的注入方式,它可以利用 Java Bean 规范所定义的 Setter/Getter 方法来完成注入,可读性和灵活性都很高,它不需要使用声明式构造方法,而是使用 Setter 注入直接设置相关的值

  3. 接口注入:接口注入方式是比较古老的注入方式,因为它需要被依赖的对象实现不必要的接口,带有侵入性,因此现在已经被完全舍弃了

谈一谈你对 AOP 的理解

AOP(
Aspect-OrientedProgramming,面向切面编程)可以说是 OOP(Object-Oriented Programing,面向对象编程)的补充和完善,OOP 引入封装、继承和多态性等概念来建立一种公共对象处理的能力,当我们需要处理公共行为的时候,OOP 就会显得无能为力,而 AOP 的出现正好解决了这个问题。比如统一的日志处理模块、授权验证模块等都可以使用 AOP 很轻松的处理。

Spring AOP 目前提供了三种配置方式

  1. 基于 Java API 的方式

  2. 基于 @AspectJ(Java)注解的方式;

  3. 基于 XML <aop /> 标签的方式

基于 Java API 的方式

此配置方式需要实现相关的接口,例如 MethodBeforeAdvice 和 AfterReturningAdvice,并且在 XML 配置中定义相应的规则即可实现。

我们先来定义一个实体类,代码如下:

package org.springframework.beans;





public class Person {

   public Person findPerson() {

      Person person = new Person(1"JDK");

      System.out.println("findPerson 被执行");

      return person;

   }

   public Person() {

   }

   public Person(Integer id, String name) {

      this.id = id;

      this.name = name;

   }

   private Integer id;

   private String name;

   // 忽略 Getter、Setter 方法

}

再定义一个 advice 类,用于对拦截方法的调用之前和调用之后进行相关的业务处理,实现代码如下:

import org.springframework.aop.AfterReturningAdvice;

import org.springframework.aop.MethodBeforeAdvice;



import java.lang.reflect.Method;



public class MyAdvice implements MethodBeforeAdviceAfterReturningAdvice {

   @Override

   public void before(Method method, Object[] args, Object target) throws Throwable {

      System.out.println("准备执行方法: " + method.getName());

   }



   @Override

   public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {

      System.out.println(method.getName() + " 方法执行结束");

   }
}

然后需要在 application.xml 文件中配置相应的拦截规则,配置如下:

<!-- 定义 advisor -->

<bean id="myAdvice" class="org.springframework.advice.MyAdvice"></bean>

<!-- 配置规则,拦截方法名称为 find* -->

<bean class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">

    <property name="advice" ref="myAdvice"></property>

    <property name="pattern" value="org.springframework.beans.*.find.*"></property>

</bean>



<!-- 定义 DefaultAdvisorAutoProxyCreator 使所有的 advisor 配置自动生效 -->

<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"></bean>

基于 @AspectJ 注解的方式

首先需要在项目中添加 aspectjweaver 的 jar 包,配置如下:

<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->

<dependency>

    <groupId>org.aspectj</groupId>

    <artifactId>aspectjweaver</artifactId>

    <version>1.9.5</version>

</dependency>

此 jar 包来自于 AspectJ,因为 Spring 使用了 AspectJ 提供的一些注解,因此需要添加此 jar 包。之后,我们需要开启 @AspectJ 的注解,开启方式有两种。

可以在 application.xml 配置如下代码中开启 @AspectJ 的注解:

<aop:aspectj-autoproxy/>

也可以使用 @EnableAspectJAutoProxy 注解开启,代码如下:

@Configuration

@EnableAspectJAutoProxy

public class AppConfig {

}

之后我们需要声明拦截器的类和拦截方法,以及配置相应的拦截规则,代码如下:

import org.aspectj.lang.annotation.After;

import org.aspectj.lang.annotation.Aspect;

import org.aspectj.lang.annotation.Before;

import org.aspectj.lang.annotation.Pointcut;



@Aspect

public class MyAspectJ {



   // 配置拦截类 Person

   @Pointcut("execution(* org.springframework.beans.Person.*(..))")

   public void pointCut() {

   }



   @Before("pointCut()")

   public void doBefore() {

      System.out.println("执行 doBefore 方法");

   }



   @After("pointCut()")

   public void doAfter() {

      System.out.println("执行 doAfter 方法");

然后我们只需要在 application.xml 配置中添加注解类,配置如下:

<bean class="org.springframework.advice.MyAspectJ"/>

基于 XML <aop /> 标签的方式

基于 XML 的方式与基于注解的方式类似,只是无需使用注解,把相关信息配置到 application.xml 中即可,配置如下:

<!-- 拦截处理类 -->

<bean id="myPointcut" class="org.springframework.advice.MyPointcut"></bean>

<aop:config>

    <!-- 拦截规则配置 -->

    <aop:pointcut id="pointcutConfig"

                    expression="execution(* org.springframework.beans.Person.*(..))"/>


    <!-- 拦截方法配置 -->

    <aop:aspect ref="myPointcut">

        <aop:before method="doBefore" pointcut-ref="pointcutConfig"/>

        <aop:after method="doAfter" pointcut-ref="pointcutConfig"/>

    </aop:aspect>

</aop:config>

之后,添加一个普通的类来进行拦截业务的处理,实现代码如下:

public class MyPointcut {

   public void doBefore() {

      System.out.println("执行 doBefore 方法");

   }

   public void doAfter() {

      System.out.println("执行 doAfter 方法");

   }

}

Spring AOP 的原理

Spring AOP 的原理其实很简单,它其实就是一个动态代理,我们在调用 getBean() 方法的时候返回的其实是代理类的实例,而这个代理类在 Spring 中使用的是 JDK Proxy 或 CgLib 实现的,它的核心代码在 DefaultAopProxyFactory#createAopProxy(...) 中,源码如下:

public class DefaultAopProxyFactory implements AopProxyFactorySerializable {



@Override

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {

if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {

Class<?> targetClass = config.getTargetClass();

if (targetClass == null) {

throw new AopConfigException("TargetSource cannot determine target class: " +

"Either an interface or a target is required for proxy creation.");

}

            // 判断目标类是否为接口

if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {

                // 是接口使用 jdk 的代理

return new JdkDynamicAopProxy(config);

}

            // 其他情况使用 CgLib 代理

return new ObjenesisCglibAopProxy(config);

}

else {

return new JdkDynamicAopProxy(config);

}

}

    // 忽略其他代码

}


浏览 22
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报