轻松理解Spring

前言

这篇文章在前一段时间看到一位大佬写的,由于写的逻辑清晰、通俗易懂,当时我就整理了一下笔记,但愿往后本身也行写出这样的文章。若是想对spring了解和入门的同窗,相信认真阅读了本篇文章定会收获颇丰。java

特此说明:此篇文章主要目的是当作范文,以此鼓励本身在不断学习总结中创做出更多更好的优质原创文章;找了半天没有找到原文章,若有打扰即刻删除。程序员

主要内容:

默认题主说的Spring是Spring framework,而不是Spring家族spring

  • 盲点
  • Spring说,万物皆可定义
  • 默默付出的后置处理器
  • 利用后置处理器返回代理对象

盲点

若是你从未独立看过源码(和我同样),那么你极可能至今都未曾注意某两个概念。编程

你觉得我会说IOC和AOP?No。app

看到这里,一部分读者内心一惊:卧槽,说的啥玩意,Spring不就IOC和AOP吗?!这两个都不说,你这篇文章为啥能写这么长?框架

不错,我就是这么长。其实我要讲的是:模块化

  • BeanDefinition
  • BeanPostProcessor

大部分人一听到“请你谈谈对Spring的理解”,就会下意识搬出IOC和AOP两座大山,赶忙糊弄过去。大概是这样的:post

IOC学习

所谓的控制反转。通俗地讲,就是把本来须要程序员本身建立和维护的一大堆bean通通交由Spring管理。测试

也就是说,Spring将咱们从盘根错节的依赖关系中解放了。当前对象若是须要依赖另外一个对象,只要打一个@Autowired注解,Spring就会自动帮你安装上。

AOP

所谓的面向切面编程。通俗地讲,它通常被用来解决一些系统交叉业务的织入,好比日志啦、事务啥的。打个比方,UserService的method1可能要打印日志,BrandService的method2可能也须要。亦即:一个交叉业务就是要切入系统的一个方面。具体用代码展现就是:

交叉业务的编程问题即为面向切面编程。AOP的目标就是使交叉业务模块化。作法是将切面代码移动到原始方法的周围:

原先不用AOP时(图一),交叉业务的代码直接硬编码在 方法内部的先后,而AOP则是把交叉业务写在 方法调用先后。那么,为何AOP不把代码也写在方法内部的先后呢?两点缘由:

  • 首先,这与AOP的底层实现方式有关:动态代理其实就是代理对象调用目标对象的同名方法,并在调用先后加加强代码。

  • 其次,这两种最终运行效果是同样的,因此没什么好纠结的。

而所谓的模块化,我我的的理解是将切面代码作成一个可管理的状态。好比日志打印,再也不是直接硬编码在方法中的零散语句,而是作成一个切面类,经过通知方法去执行切面代码。

我相信大部分培训班出来的朋友也就言尽于此,讲完上面内容就准备收拾打卡下班了。

怎么说呢,IOC按上面的解释,虽然很浅,但也马马虎虎吧。然而AOP,不少人对它的认识是很是片面的...

这样吧,我问你一个问题,如今我本身写了一个UserController,以及UserServiceImpl implements UserService,而且在UserController中注入Service层对象:

@Autowired
private UserService userService;
复制代码

若是你听不懂我要问什么,说明你对Spring的AOP理解仍是太少了。

实际上,Spring依赖注入的对象并不必定是咱们本身写的类的实例,也多是userServiceImpl的代理对象。下面分别演示这两种状况:

  • 注入userServiceImpl对象

  • 注入userServiceImpl的代理对象(CGLib动态代理)

为何两次注入的对象不一样?

由于第二次我给UserServiceImpl加了@Transactional 注解。

此时Spring读取到这个注解,便知道咱们要使用事务。而咱们编写的UserService类中并无包含任何事务相关的代码。若是给你,你会怎么作?动态代理嘛!

看到这里,我仿佛听到有一部分兄弟默默说了句:卧槽...

可是,上面对IOC和AOP的理解,也仅仅是应用级别,是一个面。仅仅了解到这个程度,对Spring的了解仍是很是扁平的,不够立体。

Spring说,万物皆可定义

上帝说,要有光。因而特斯拉搞出了交流电。

Java说,万物皆对象。可是Spring另外搞了BeanDefinition...

什么BeanDefinition呢?其实它是bean定义的一个顶级接口:

正如BeanDefinition的类注释所言:一个BeanDefinition是用来描述一个bean实例的

哎呀卧槽,啥玩意啊。描述一个bean实例?我咋想起了Class类呢。

其实,二者并无矛盾。

Class只是描述了一个类有哪些字段、方法,可是没法描述如何实例化这个bean!若是说,Class类描述了一块猪肉,那么BeanDefinition就是描述如何作红烧肉:

  • 单例吗?
  • 是否须要延迟加载?
  • 须要调用哪一个初始化方法/销毁方法?

大部分初学者觉得Spring解析或者@Bean后,就直接搞了一个bean存到一个大Map中,其实并非。

  • Spring首先会扫描解析指定位置的全部的类获得Resources(能够理解为.Class文件)
  • 而后依照TypeFilter和@Conditional注解决定是否将这个类解析为BeanDefinition
  • 稍后再把一个个BeanDefinition取出实例化成Bean

就比如什么呢?你从海里吊了一条鱼,可是你还没想好清蒸仍是红烧,那就干脆先晒成鱼干吧。一条咸鱼,其实蕴藏着无线可能,由于它可能会翻身!

默默付出的后置处理器

接下来,咱们讨论一下咸鱼如何翻身。

最典型的例子就是AOP。上面AOP的例子中我说过了,若是不加@Transactional,那么Controller层注入的就是普通的userServiceImpl,而加了之后返回的实际是代理对象。

为何要返回代理对象?由于咱们压根就没在UserServiceImpl中写任何commit或者rollback等事务相关的代码,可是此时此刻代理对象却能完成事务操做。毫无疑问,这个代理对象已经被Spring加了佐料。

那么Spring是什么时候何地加佐料的呢?说来话长。

大部分人把Spring比做容器,其实潜意识里是将Spring彻底等同于一个Map了。其实,真正存单例对象的map,只是Spring中很小很小的一部分,仅仅是BeanFactory的一个字段,我更习惯称它为“单例池”。

/** Cache of singleton objects: bean name --> bean instance */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);
复制代码

这里的ApplicationContext和BeanFactory是接口,实际上都有各自的子类。好比注解驱动开发时,Spring中最关键的就是AnnotationConfigApplicationContext和DefaultListableBeanFactory。

因此,不少人把Spring理解成一个大Map,仍是太浅了。就拿ApplicationContext来说,它也实现了BeanFactory接口,可是做为容器,其实它是用来包含各类各样的组件的,而不是存bean:

那么,Spring是如何给咸鱼加佐料(事务代码的织入)的呢?关键就在于后置处理器。

后置处理器其实能够分好多种,属于Spring的扩展点之一。

上面BeanFactory、BeanDefinitionRegistryPostProcessor、BeanPostProcessor都算是后置处理器,这里篇幅有限,只介绍一下BeanPostProcessor。

BeanFactoryPostProcessor是用来干预BeanFactory建立的,而BeanPostProcessor是用来干预Bean的实例化。不知道你们有没有试过在普通Bean中注入ApplicationContext实例?你第一时间想到的是:

@Autowired
ApplicationContext annotationConfigApplicationContext;
复制代码

除了利用Spring自己的IOC容器自动注入之外,你还有别的办法吗?

咱们可让Bean实现ApplicationContextAware接口:

后期,Spring会调用setApplicationContext()方法传入ApplicationContext实例。

Spring官方文档:通常来讲,您应该避免使用它,由于它将代码耦合到Spring中,而且不遵循控制反转样式

这是我认为Spring最牛逼的地方:代码具备高度的可扩展性,甚至你本身都懵逼,为何实现了一个接口,这个方法就被莫名其妙调用,还传进了一个对象...

这其实就是后置处理器的工做!

什么意思呢?

就是说啊,明面上咱们看得见的地方只要实现一个接口,可是背地里Spring在本身框架的某一处搞了个for循环,遍历全部的BeanPostProcessor,其中就包括处理实现了ApplicationContextAware接口的bean的后置处理器:ApplicationContextAwareProcessor。

上面这句话有点绕,你们停下来多想几遍。

也就是说,要扩展的类是不肯定的,可是处理扩展类的流程是写死的。总有一个要定下来吧。也就是说,在这个Bean实例化的某一紧要处,必然要通过不少BeanPostProcessor。可是,BeanPostProcessor也不是谁都处理,有时也会作判断。好比:

if (bean instanceof Aware) {
	if (bean instanceof ApplicationContextAware) {
       ((ApplicationContextAware)bean).setApplicationContext(this.applicationContext);
    }
}
复制代码

因此,此时此刻一个类实现ApplicationContextAware接口,有两层含义:

  • 做为后置处理器的判断依据,只有你实现了该接口我才处理你
  • 提供被后置处理器调用的方法

利用后置处理器返回代理对象

大体了解Spring Bean的建立流程后,接下来咱们尝试着用BeanPostProcessor返回当前Bean的代理对象。

pom.xml

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

AppConfig

@Configuration//JavaConfig方式,即当前配置类至关于一个applicationConotext.xml文件
@ComponentScan//默认扫描当前配置类(AppConfig)所在包及其子包
public class AppConfig {

}
复制代码

Calculator

public interface Calculator {
	public void add(int a, int b);
}
复制代码

CalCulatorImpl

@Component
public class CalculatorImpl implements Calculator {
    public void add(int a, int b) {
        System.out.println(a+b);
    }
}
复制代码

后置处理器MyAspectJAutoProxyCreator

使用步骤:

  1. 实现BeanPostProcessor
  2. @Component加入Spring容器
@Component
public class MyAspectJAutoProxyCreator implements BeanPostProcessor {
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
         final Object obj = bean;
         //若是当前通过BeanPostProcessors的Bean是Calculator类型,咱们就返回它的代理对象
        if (bean instanceof Calculator) {
            Object proxyObj = Proxy.newProxyInstance(
                 this.getClass().getClassLoader(),
                 bean.getClass().getInterfaces(),
                 new InvocationHandler() {
                      public Object invoke(Object proxy,Method method, Object[] args) throws Throwable {
                          System.out.println("开始计算....");
                          Object result = method.invoke(obj, args);
                          System.out.println("结束计算...");
                          return result;
                           }
                    }
            );
            return proxyObj;
             }
        //不然返回自己
        return obj;
    }
}
复制代码

测试类

public class TestPostProcessor {
    public static void main(String[] args) {
        System.out.println("容器启动成功!");
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
         String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        //打印当前容器全部BeanDefinition
        for (String beanDefinitionName : beanDefinitionNames) {
            System.out.println(beanDefinitionName);
            }
        System.out.println("============");
        //取出Calculator类型的实例,调用add方法
        Calculator calculator = (Calculator)applicationContext.getBean(Calculator.class);
        calculator.add(1, 2);
    }
复制代码

先把MyAspectJAutoProxyCreator的@Component注释掉,此时Spring中没有咱们自定义的后置处理器,那么返回的就是CalculatorImpl:

把@Component加上,此时MyAspectJAutoProxyCreator加入到Spring的BeanPostProcessors中,会拦截到CalculatorImpl,并返回代理对象:

相关文章
相关标签/搜索