实现Spring底层机制-01

主要实现:初始化IOC容器+依赖注入+BeanPostProcessor机制+AOP

前面我们实际上已经使用代码简单实现了:

  1. Spring XML 注入 bean (Spring基本介绍02)
  2. Spring 注解方式注入 bean (Spring管理Bean-IOC-04)
  3. Spring AOP 动态代理实现 (AOP-01)

1.引出问题

1.1原生Spring如何实现依赖注入、singleton和prototype

例子

1.创建新的Maven项目:

2.在pom.xml文件中添加 spring 开发的基本包:

<dependencies>
<!--加入 spring 开发的基本包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.8</version>
</dependency>
<!--加入spring开发切面编程需要的包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.8</version>
</dependency>
</dependencies>

3.src/main/java/ 目录下创建包 com/li/component,在该包下分别创建UserDao.java、UserService.java、UserAction.java

UserDao:

package com.li.component;

import org.springframework.stereotype.Component;

/**
* @author 李
* @version 1.0
*/
//也可以使用 @Repository
@Component
public class UserDao { public void hi() {
System.out.println("UserDao-hi()---");
}
}

UserService:

package com.li.component;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; /**
* @author 李
* @version 1.0
*/
//也可以使用 @Service
@Component
public class UserService {
//定义属性
//也可以使用 @Resource
@Autowired
private UserDao userDao; public void m1() {
userDao.hi();
}
}

UserAction:

package com.li.component;

import org.springframework.stereotype.Component;

/**
* @author 李
* @version 1.0
* 一个 Controller
*/
//也可以使用 @Controller
@Component
public class UserAction {
}

4.在 src/main/resources 目录下创建 spring 的容器文件 beans.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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd"> <!--配置自动扫描的包,同时引入对应的名称空间-->
<!--说明:
1.如果是普通的java项目,beans.xml 放在src 目录下即可
2.如果是maven项目,beans.xml文件就要放在 src/main/resources 目录下-->
<context:component-scan base-package="com.li.component"/> </beans>

5.测试类中获取配置的bean,并输出对象的地址值

package com.li;

import com.li.component.UserAction;
import com.li.component.UserDao;
import com.li.component.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; /**
* @author 李
* @version 1.0
*/
public class AppMain {
public static void main(String[] args) {
//测试是否可以得到spring容器中的bean
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml"); UserAction userAction = (UserAction) ioc.getBean("userAction");
UserAction userAction2 = (UserAction) ioc.getBean("userAction"); System.out.println("userAction=" + userAction);
System.out.println("userAction2=" + userAction2);
}
}

可以看到通过“userAction”名称获取的对象的地址值相同,这说明它们实际上是同一个对象

在默认情况下,我们配置的@Component,@Controller,@Service,@Repository 是单例的,即spring的ioc容器只会创建一个bean实例

6.如果我们希望将一个类配置为多例的,怎么办呢?

只需要在对应的类头部添加 @Scope(value = "prototype"),表示以多实例的形式返回该类的bean对象

package com.li.component;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component; @Component
@Scope(value = "prototype")
public class UserAction {
}

现在我们重新运行测试类,可以看到通过“userAction”名称获取的对象的地址值不相同,这说明它们是不同的对象。

7.我们在测试类中获取userService对象,并调用m1方法

package com.li;

import com.li.component.UserAction;
import com.li.component.UserDao;
import com.li.component.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; /**
* @author 李
* @version 1.0
*/
public class AppMain {
public static void main(String[] args) {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
UserService userService = (UserService) ioc.getBean("userService"); System.out.println("userService=" + userService);
//测试依赖注入
System.out.print("userService对象调用m1()=");
userService.m1(); }
}

输出如下,成功获取到userService对象,并且调用m1方法成功。这说明UserService类中的userDao属性成功通过@AutoWired 注解装配。


问题一:spring底层是如何通过注解来完成多例或者单例对象的创建的?

问题二:Spring容器如何实现依赖注入?

1.2原生Spring如何实现BeanPostProcessor

BeanPosecessor详见Spring管理Bean-IOC-03-2.16后置处理器

  1. 后置处理器会在 bean 初始化方法调用前 和 初始化方法调用后 被调用
  2. 后置处理器对象会作用在容器配置文件的所有bean对象中(即使bean对象没有初始化方法)

例子

1.创建一个后置处理器:

package com.li.process;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component; /**
* @author 李
* @version 1.0
* 一个后置处理器
*/
@Component
public class MyBeanPostProcessor implements BeanPostProcessor { //在 Bean的 init初始化方法前被调用
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessBeforeInitialization 被调用 " + beanName + " bean= " + bean.getClass());
return bean;
} //在 Bean的 init初始化方法后被调用
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessAfterInitialization 被调用 " + beanName + " bean= " + bean.getClass());
return bean;
}
}

要使用后置处理器,需要进行配置,配置的方式有两种:(1)在xml容器文件中进行配置(2)添加注解

使用注解时,还要保证扫描的范围要覆盖到该类

2.在UserService类中添加初始化方法:

package com.li.component;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; /**
* @author 李
* @version 1.0
*/
//也可以使用 @Service
@Component
public class UserService {
//定义属性
//也可以使用 @Resource
@Autowired
private UserDao userDao; public void m1() {
userDao.hi();
} //初始化方法-名称随意,需要@PostConstruct指定init为初始化方法
@PostConstruct
public void init(){
System.out.println("UserService-init()");
}
}

3.在测试类中进行测试:

package com.li;

import com.li.component.UserAction;
import com.li.component.UserDao;
import com.li.component.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; /**
* @author 李
* @version 1.0
*/
public class AppMain {
public static void main(String[] args) { ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml"); UserAction userAction = (UserAction) ioc.getBean("userAction");
UserAction userAction2 = (UserAction) ioc.getBean("userAction"); System.out.println("userAction=" + userAction);
System.out.println("userAction2=" + userAction2); UserDao userDao = (UserDao) ioc.getBean("userDao");
System.out.println("userDao=" + userDao); UserService userService = (UserService) ioc.getBean("userService");
System.out.println("userService=" + userService); System.out.print("userService对象调用m1()=");
userService.m1(); }
}

如下,后置处理器对象会作用在容器配置文件的所有bean对象中(即使bean对象没有初始化方法),根据之前的配置,容器中一共有四个对象(UserAction为多例),因此一共调用了八次。

这里userAction对象因为是多例的,强制为懒加载,因此在被获取时(getBean())才创建,因此排在最后。


问题三:原生Spring如何实现BeanPostProcessor?

1.3原生spring如何实现AOP

例子-在上述代码的基础上添加如下内容

1.SmartAnimal 接口:

package com.li.aop;

/**
* @author 李
* @version 1.0
*/
public interface SmartAnimal {
public float getSum(float i, float j); public float getSub(float i, float j);
}

2.SmartDog 实现类:

package com.li.aop;

import org.springframework.stereotype.Component;

/**
* @author 李
* @version 1.0
*/
@Component
public class SmartDog implements SmartAnimal {
@Override
public float getSum(float i, float j) {
float res = i + j;
System.out.println("SmartDog-getSum()-res=" + res);
return res;
} @Override
public float getSub(float i, float j) {
float res = i - j;
System.out.println("SmartDog-getSub()-res=" + res);
return res;
}
}

3.SmartAnimalAspect 切面类:

package com.li.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component; import java.util.Arrays; /**
* @author 李
* @version 1.0
* 切面类
*/
@Component
@Aspect
public class SmartAnimalAspect {
@Pointcut(value = "execution(public float com.li.aop.SmartAnimal.*(float,float))")
public void myPointCut() {
} //前置通知
@Before(value = "myPointCut()")
public void before(JoinPoint joinpoint) {
Signature signature = joinpoint.getSignature();
System.out.println("SmartAnimalAspect切面类-before()-" + signature.getName()
+ "-参数-" + Arrays.toString(joinpoint.getArgs()));
} //返回通知
@AfterReturning(value = "myPointCut()", returning = "res")
public void afterReturning(JoinPoint joinpoint, Object res) {
Signature signature = joinpoint.getSignature();
System.out.println("SmartAnimalAspect切面类-afterReturning()-" + signature.getName() + "-res-" + res);
} //异常通知
@AfterThrowing(value = "myPointCut()", throwing = "res")
public void afterThrowing(JoinPoint joinpoint, Throwable res) {
Signature signature = joinpoint.getSignature();
System.out.println("SmartAnimalAspect切面类-afterThrowing()-" + signature.getName() + "-res-" + res);
} //最终通知
@After(value = "myPointCut()")
public void after(JoinPoint joinpoint) {
Signature signature = joinpoint.getSignature();
System.out.println("SmartAnimalAspect切面类-after()-" + signature.getName());
}
}

4.在容器文件中开启基于注解的aop功能:

<!--配置自动扫描的包,同时引入对应的名称空间-->
<context:component-scan base-package="com.li.aop"/> <!--开启基于注解的 aop 功能-->
<aop:aspectj-autoproxy/>

5.进行测试:

package com.li;

import com.li.aop.SmartAnimal;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext; /**
* @author 李
* @version 1.0
*/
public class AppMain {
public static void main(String[] args) {
ApplicationContext ioc = new ClassPathXmlApplicationContext("beans.xml");
SmartAnimal smartDogProxy = ioc.getBean(SmartAnimal.class);
smartDogProxy.getSum(101, 99);
}
}

测试结果:

前面的输出是因为之前配置了后置处理器,它在创建ioc容器时会被调用,对所有bean对象生效。

红框处,后置处理器的 postProcessBeforeInitialization() 方法调用时,bean对象的还是原生的类型,但是到了 postProcessAfterInitialization() 方法调用时,已经变成了代理对象 $Proxy。这说明后置处理器和aop切面编程有着密切的关系。


简单分析AOP和BeanPostProcessor的关系:

1.AOP实现Spring可以通过一个类加入注解@EnableAspectJAutoProxy来执行

2.我们来追一下@EnableAspectJAutoProxy






3.看一下 AnnotationAwareAspectJAutoProxyCreator 的类图

4.解读:

(1)AOP底层是基于BeanPostProcessor机制的

(2)即在Bean对象创建好后,根据是否需要AOP处理,决定返回代理对象还是原生的Bean对象

(3)在返回代理对象时,就可以根据要代理的类和方法来返回

(4)这个机制并不难,本质就是BeanPostProcessor 机制+动态代理技术

2.Spring整体架构分析

day11-实现Spring底层机制-01的更多相关文章

  1. day12-实现Spring底层机制-02

    实现Spring底层机制-02 3.实现任务阶段1 3.1知识拓展-类加载器 Java的类加载器有三种: Bootstrap类加载器 ----- 对应路径 jre/lib Ext类加载器 ----- ...

  2. day13-实现Spring底层机制-03

    实现Spring底层机制-03 7.实现任务阶段5 7.1分析 阶段5目标:bean后置处理器的实现 7.2代码实现 新增: 1.创建 InitializingBean 接口,实现该接口的 Bean ...

  3. day05-SpringMVC底层机制简单实现-01

    SpringMVC底层机制简单实现-01 主要完成:核心分发控制器+Controller和Service注入容器+对象自动装配+控制器方法获取参数+视图解析+返回JSON格式数据 1.搭建开发环境 创 ...

  4. Hibernate工作原理及为什么要用?. Struts工作机制?为什么要使用Struts? spring工作机制及为什么要用?

    三大框架是用来开发web应用程序中使用的.Struts:基于MVC的充当了其中的试图层和控制器Hibernate:做持久化的,对JDBC轻量级的封装,使得我们能过面向对象的操作数据库Spring: 采 ...

  5. Spring事件机制详解

    一.前言 说来惭愧,对应Spring事件机制之前只知道实现 ApplicationListener 接口,就可以基于Spring自带的事件做一些事情(如ContextRefreshedEvent),但 ...

  6. spring工作机制及为什么要用?

    spring工作机制及为什么要用?1.spring mvc请所有的请求都提交给DispatcherServlet,它会委托应用系统的其他模块负责对请求进行真正的处理工作.2.DispatcherSer ...

  7. 如何妙用Spring 数据绑定机制?

    前言 在剖析完 「Spring Boot 统一数据格式是怎么实现的? 」文章之后,一直觉得有必要说明一下 Spring's Data Binding Mechanism 「Spring 数据绑定机制」 ...

  8. day08-SpringMVC底层机制简单实现-04

    SpringMVC底层机制简单实现-04 https://github.com/liyuelian/springmvc-demo.git 8.任务7-完成简单视图解析 功能说明:通过目标方法返回的 S ...

  9. Spring 事务机制详解

    原文出处: 陶邦仁 Spring事务机制主要包括声明式事务和编程式事务,此处侧重讲解声明式事务,编程式事务在实际开发中得不到广泛使用,仅供学习参考. Spring声明式事务让我们从复杂的事务处理中得到 ...

  10. java 反射机制01

    // */ // ]]>   java反射机制01 Table of Contents 1 反射机制 2 反射成员 2.1 java.lang.Class 2.2 Constructor 2.3 ...

随机推荐

  1. 小知识:PDML的注意事项补充

    关于PDML,之前在 并行,想说爱你不容易中的第一节就介绍过,今天在客户现场协助测试时又遇到几个有关PDML的问题,都蛮典型的,记录一下: 问题1:某存储过程报错ORA-12839. 查看该错误号说明 ...

  2. OGG-01496 Failed to open target trail file ./dirdat/ra000002, at RBA 2179

    1.问题描述 在启动OGG源端的投递进程时,报错:OGG-01496 OGG-01496 Failed to open target trail file ./dirdat/ra000002, at ...

  3. react 快速接入 sentry,性能监控与错误上报踩坑日记

    壹 ❀ 引 本文是我入职第一个月所写,在主导基建组的这段时间也难免会与错误监控和性能监控打交道,因为公司主要考虑接入sentry,所以对于接入sentry的基建任务也提了一些需求,主要分为: 支持查看 ...

  4. ARM 中常用的汇编指令解释汇总

    前言 嵌入式项目中经常涉及到需要通过分析编译后的汇编文件,来确定异常代码,对一些常用的指令进行了汇总. 一.处理器内部数据传输指令 在ARM架构中,包括Cortex-A7处理器内部,有一些专门用于数据 ...

  5. 【Unity3D】灯光组件Light

    1 灯光简介 ​ 在 Hierarchy 窗口右键,选择 Light,再选择具体的灯光类型,在 Inspector 窗口查看灯光组件如下: Type:灯光类型,主要有:Directional(平行光) ...

  6. Swoole从入门到入土(9)——TCP服务器[协程风格]

    上一篇,我们一起初步接触了协程.我相信只有一节的讨论,很多小伙伴对于"协程"与"线程"的区分可能还有点模糊.我们这里以两者的比较作为本篇开头,进行一番比较. 首 ...

  7. Java设计模式-迭代器模式Iterator

    介绍 根据GoF的定义,迭代器模式提供了一种顺序访问聚合对象的元素而不暴露其底层表示的方法.这是一种行为设计模式. 顾名思义,迭代器有助于以定义的方式遍历对象集合,这对客户端应用程序很有用.在迭代期间 ...

  8. Redis服务端事件处理流程分析

    一.事件处理 1.1 什么是事件 Redis 为什么运行得比较快? 原因之一就是它的服务端处理程序用了事件驱动的处理方式. 那什么叫事件处理?就是把处理程序当成一个一个的事件处理.比如我前面文章:服务 ...

  9. vscode添加gitbash终端方法

    1.打开vscode 2.点击文件,ctrl+, 3.搜索shell windows { ... // 添加如下代码 "terminal.integrated.profiles.window ...

  10. 【webserver 前置知识 02】Linux网络编程入门其一

    网络结构模式 C/S结构 服务器 - 客户机,即 Client - Server(C/S)结构.C/S 结构通常采取两层结构.服务器负责数据的管理,客户机负责完成与用户的交互任务.客户机是因特网上访问 ...