spring IOC和AOP的底层实现

spring IOC和AOP的底层实现

https://juejin.im/post/5bf51d4c5188256d9832b0d3

IOC

2、spring核心主要两部分:
(1)aop:面向切面编程:扩展功能不是修改源代码实现
(2)Ioc:控制反转,比如有一个类,在类里面有方法(不是静态的方法,创建类的对象,使用对象调用方法,创建类对象的过程,需要new出来对象。
现在对象的创建不是通过new方式实现,而是交给spring配置创建对象)


1、创建对象

(1)直接new

public class User{
public void add(){

}
}

//在servlet调用User类里面的方法

User user =new User();
user.add()

缺点:如果类名改了,servlet里面也要跟着改。总之,两个类之间耦合度太强。

(2)使用工厂方法

spring IOC和AOP的底层实现

降低了Servlet和service的耦合,但servlet和工厂又产生了耦合

(3)Spring IOC

配置文件




类里面的实现
public class UserService{
public void add(){}
}

public class UserServlet{
private UserServlet userServlet;
public void UserService(UserServlet UserServlet){
this.userServlet=userServlet;

}

}

这样,userServlet就可以直接调用UserService类里面的方法啦

2、IOC底层原理使用技术

(1)xml配置文件

(2)dom4j解析xml文件

(3)工厂设计模式

(4)反射

那么这几种技术是怎么用到IOC里面的呢?

第一步:创建xml配置文件,配置要创建对象类

第二步:创建工厂类,使用dom4j解析配置文件+反射

spring IOC和AOP的底层实现

如果类的路径或类名发生了变化,只需更改配置文件即可,降低了类之间的耦合度。


总体上说,IOC 是一种可以帮助我们解耦各业务对象间依赖关系的对象绑定方式,那么Spring 提供了两种容器类型来提供支持 IOC方式。这两种类型是:

  • BeanFactory: 基础类型的IOC容器,提供完整的IOC服务支持
  • ApplicationContext: ApplicationContext是在 BeanFactory的基础之上构建的,是相对高级的容器实现,除了拥有BeanFactory的所有支持,ApplicationContext提供了其他高级特性。

BeanFactory

BeanFactory的介绍

BeanFactory 是基础类型IoC容器,提供完整的IoC服务支持。如果没有特殊指定,默认采用延迟初始化策略(lazy-load)只有当客户端对象需要访问容器中的某个受管对象的时候,才对该受管对象进行初始化以及依赖注入工作

BeanFactory,就是生产 Java Bean 的工厂,作为Spring 提供的基本的IoC容器,BeanFactory 帮助完成 业务对象的注册和对象间依赖关系的绑定

实际上,BeanFactory只是一个接口,它负责定义如何访问容器内管理的Bean的方法,各个BeanFactory的具体实现类负责具体Bean的注册以及管理工作

从上图可以看出,BeanFactory有三个直接子类:

  • ListableBeanFactory: 通过继承该接口可以列出所有的Bean,也可以只列出与预期类型相对应的bean
  • HierarchicalBeanFactory: 支持分层bean的管理,使BeanFactory支持双亲IOC容器的管理功能
  • AutowireCapableBeanFactory: 可以填充不受Spring 控制的 Bean

ApplicationContext

ApplicationContext的介绍

ApplicationContext是在BeanFactory的基础上构建的,是相对比较高级的容器实现,除了拥有 BeanFactory的所有支持,ApplicationContext还提供了其他高级特性,比如:

  • 统一资源加载策略
  • 国际化信息支持
  • 容器内部事件发布机制

在ApplicationContext 容器启动之后,默认全部初始化并绑定完成,所以,对于BeanFactory来说,ApplicationContext 往往要求更多的系统资源

对象注入方式

我们都知道IOC容器一般有两种对象注入方式:基于XML配置文件 与 基于注解驱动的方式。下面就分别从这两个角度来看如何使用 ApplicationContext

基于 XML 配置文件

  1. 定义个实体类
public class User {
    private Integer id;

    private String username;

    public User(Integer id, String username) {
        this.id = id;
        this.username = username;
    }

    public User() {
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }
}
  1. 设置一个XML配置文件,声明 User Bean
<?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="user" class="com.pjmike.spring.domain.User">
        <constructor-arg name="id" value="1"/>
        <constructor-arg name="username" value="pjmike"/>
    </bean>
</beans>

基于注解方式

  1. 声明一个配置类
@Configuration
public class UserConfiguration {
    @Bean(name = "user")
    public User user() {
        User user = new User();
        user.setUsername("pj");
        return user;
    }
}

从上面的两个例子可以看出基于XML和基于注解注入Bean 的方式是不一样的,基于XML的应用上下文ClassPathXmlApplicationContext需要设置配置路径,基于注解的应用上下文AnnotationConfigApplicationContext需要注册一个配置Bean,但它们相同的一步就是必须要调用 refresh()方法,该方法可以看做是IOC容器的启动方法,它会做很多操作,比如完成配置的解析、各种BeanFactoryPostProcessor和BeanPostProcessor的注册、国际化配置的初始化、web内置容器的构造等等,不调用它,容器就无法启动。这里只是简要说明,更加详细的介绍会在后面的文章介绍。

现在是springboot盛行的阶段,基于XML配置文件的方式已经逐步被基于注解的方式所取代,如今的项目中,更多的使用 注解的方式。

AOP

https://juejin.im/post/591d8c8ba22b9d00585007dd

例如,我们会对API进行抽象成四个模块:Controller,Service,Gateway,Command.这很好地解决了业务级别的开发,但对于系统级别的开发我们很难聚焦。比如、对于每一个模块需要进行打日志、代码监控、异常处理。以打日志为例,我只能将日志代码嵌套在各个对象上,而无法关注日志本身,而这种现象又偏离了OOP思想。

spring IOC和AOP的底层实现

为了能够更好地将系统级别的代码抽离出来,去掉与对象的耦合,就产生了面向AOP(面向切面)。如上图所示,OOP属于一种横向扩展,AOP是一种纵向扩展。AOP依托于OOP,进一步将系统级别的代码抽象出来,进行纵向排列,实现低耦合。

1.2 AOP 的家庭成员

1.2.1 PointCut

即在哪个地方进行切入,它可以指定某一个点,也可以指定多个点。
比如类A的methord函数,当然一般的AOP与语言(AOL)会采用多用方式来定义PointCut,比如说利用正则表达式,可以同时指定多个类的多个函数。

1.2.2 Advice

在切入点干什么,指定在PointCut地方做什么事情(增强),打日志、执行缓存、处理异常等等。

1.2.3 Advisor/Aspect

PointCut + Advice 形成了切面Aspect,这个概念本身即代表切面的所有元素。但到这一地步并不是完整的,因为还不知道如何将切面植入到代码中,解决此问题的技术就是PROXY

1.2.4 Proxy

Proxy 即代理,其不能算做AOP的家庭成员,更相当于一个管理部门,它管理 了AOP的如何融入OOP。之所以将其放在这里,是因为Aspect虽然是面向切面核心思想的重要组成部分,但其思想的践行者却是Proxy,也是实现AOP的难点与核心据在。

AOP的技术实现Proxy

AOP仅仅是一种思想,那为了让这种思想发光,必然脱离语言本身的技术支持,Java在实现该技术时就是采用的代理Proxy,那我们就去了解一下,如何通过代理实现面向切面