Spring in Action学习笔记

第一部分:Spring的核心

为了降低Java开发的复杂性,Spring采取了以下4种关键策略:

1.基于POJO的轻量级和最小侵入性编程;

2.通过依赖注入和面向接口实现松耦合;

3.基于切面和惯例进行声明式编程;

4.通过切面和模板减少样板式代码。

Spring自带了多种类型的应用上下文。下面罗列的几个是你最有可能遇到的。

1.AnnotationConfigApplicationContext:从一个或多个基于Java的配置类中加载Spring应用上下文。

2.AnnotationConfigWebApplicationContext:从一个或多个基于Java的配置类中加载Spring Web应用上下文。

3.ClassPathXmlApplicationContext:从类路径下的一个或多个XML配置文件中加载上下文定义,把应用上下文的定义文件作为类 资源。

4.FileSystemXmlapplicationcontext:从文件系统下的一个或多个XML配置文件中加载上下文定义。

5.XmlWebApplicationContext:从Web应用下的一个或多个XML配置文件中加载上下文定义。

使用FileSystemXmlApplicationContext和使用ClassPathXmlApp-licationContext的区别在 于:

FileSystemXmlApplicationContext在指定的文件系统路径下查找.xml文件;

ClassPathXmlApplicationContext是在所有的类路径(包含JAR文件)下查找 .xml文件。

bean的生命周期

在传统的Java应用中,bean的生命周期很简单。使用Java关键字new进行bean实例化,然后该bean就可以使用了。一旦该bean不再被使用, 则由Java自动进行垃圾回收。

Spring中Bean的生命周期

1、实例化一个Bean--也就是我们常说的new;

2、按照Spring上下文对实例化的Bean进行配置--也就是IOC注入;

3、如果这个Bean已经实现了BeanNameAware接口,会调用它实现的setBeanName(String)方法,此处传递的就是Spring配置文件中Bean的id值

4、如果这个Bean已经实现了BeanFactoryAware接口,会调用它实现的setBeanFactory(setBeanFactory(BeanFactory)传递的是Spring工厂自身(可以用这个方式来获取其它Bean,只需在Spring配置文件中配置一个普通的Bean就可以);

5、如果这个Bean已经实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文(同样这个方式也可以实现步骤4的内容,但比4更好,因为ApplicationContext是BeanFactory的子接口,有更多的实现方法);

6、如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessBeforeInitialization(Object obj, String s)方法,BeanPostProcessor经常被用作是Bean内容的更改,并且由于这个是在Bean初始化结束时调用那个的方法,也可以被应用于内存或缓存技术;

7、如果Bean在Spring配置文件中配置了init-method属性会自动调用其配置的初始化方法。

8、如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法、;

注:以上工作完成以后就可以应用这个Bean了,那这个Bean是一个Singleton的,所以一般情况下我们调用同一个id的Bean会是在内容地址相同的实例,当然在Spring配置文件中也可以配置非Singleton,这里我们不做赘述。

9、当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用那个其实现的destroy()方法;

10、最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。

组成Spring框架的六个模块

1spring核心容器

管理bean的创建配置和管理。包括了spring工厂,为spring提供了DI功能。

2spring的aop模块

提供开发切面的功能,帮助对象解耦。

3数据集成与访问

4web远程调用

5instrumentation

提供了JVM添加代理的功能

6测试

关于耦合

耦合具有两面性(two-headed beast)。一方面,紧密耦合的代码难以测试、难以复用、难以理解,并且典型地表现出“打地鼠”式的bug特性 (修复一个bug,将会出现一个或者更多新的bug)。另一方面,一定程度的耦合又是必须的——完全没有耦合的代码什么也做不了。为了完成 有实际意义的功能,不同的类必须以适当的方式进行交互。总而言之,耦合是必须的,但应当被小心谨慎地管理。

Spring从两个角度来实现自动化装配:

组件扫描(component scanning):Spring会自动发现应用上下文中所创建的bean。

自动装配(autowiring):Spring自动满足bean之间的依赖。

@Component:表明该类会作为组件类

@ComponentScan:启用组件扫描

在java中提供了三种主要的装配机制

1在XML中进行显式配置

2在java中进行显式配置

3隐式的bean发现机制和自动装配

@profile

会使用@Profile来分开开发环境和生产环境

@Primary

Primary可以理解为默认优先选择,不可以同时设置多个,内部实质是设置BeanDefinition的primary属性

@Qualifier

此注解所设置的参数就是想要注入bean的ID;

java8允许出现重复的注解,只要这个注解本身在定义的时候带有@repeatable注解就可以。

@Conditional

设置给@Conditional的类可以是任意实现了Condition接口的类型。可以看出来,这个接口实现起来很简单直接,只需提 供matches()方法的实现即可。如果matches()方法返回true,那么就会创建带有@Conditional注解的bean。如果matches()方法返 回false,将不会创建这些bean。

通过ConditionContext,我们可以做到如下几点:

1.借助getRegistry()返回的BeanDefinitionRegistry检查bean定义; 借助getBeanFactory()返回的

2.ConfigurableListableBeanFactory检查bean是否存在,甚至探查bean的属性; 借助getEnvironment()返回的

3.Environment检查环境变量是否存在以及它的值是什么; 读取并探查getResourceLoader()返回的

4.ResourceLoader所加载的资源; 借助getClassLoader()返回的ClassLoader加载并检查类是否存在。

Bean的作用域

一般情况下,Spring应用上下文中所有bean都是以单例的形式创建的。也就是说不管给定的一个bean被注入到其它bean中多少次,每次所注入的都是同一个实例。

@Scope:声明bean的作用域。

Spring切面可以应用5种类型的通知:

前置通知(Before):在目标方法被调用之前调用通知功能;

后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么;

返回通知(After-returning):在目标方法成功执行之后调用通知;

异常通知(After-throwing):在目标方法抛出异常后调用通知;

环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。

连接点(Join point)

电力公司为多个住户提供服务,甚至可能是整个城市。每家都有一个电表,这些电表上的数字都需要读取,因此每家都是抄表员的潜在目标。 抄表员也许能够读取各种类型的设备,但是为了完成他的工作,他的目标应该房屋内所安装的电表。 同样,我们的应用可能也有数以千计的时机应用通知。这些时机被称为连接点。连接点是在应用执行过程中能够插入切面的一个点。这个点可 以是调用方法时、抛出异常时、甚至修改一个字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。

切点(Poincut)

如果让一位抄表员访问电力公司所服务的所有住户,那肯定是不现实的。实际上,电力公司为每一个抄表员都分别指定某一块区域的住户。类 似地,一个切面并不需要通知应用的所有连接点。切点有助于缩小切面所通知的连接点的范围。 如果说通知定义了切面的“什么”和“何时”的话,那么切点就定义了“何处”。切点的定义会匹配通知所要织入的一个或多个连接点。我们通常使用 明确的类和方法名称,或是利用正则表达式定义所匹配的类和方法名称来指定这些切点。有些AOP框架允许我们创建动态的切点,可以根据运 行时的决策(比如方法的参数值)来决定是否应用通知。

切面(Aspect)

当抄表员开始一天的工作时,他知道自己要做的事情(报告用电量)和从哪些房屋收集信息。因此,他知道要完成工作所需要的一切东西。 切面是通知和切点的结合。通知和切点共同定义了切面的全部内容——它是什么,在何时和何处完成其功能。

引入(Introduction)

引入允许我们向现有的类添加新方法或属性。例如,我们可以创建一个Auditable通知类,该类记录了对象最后一次修改时的状态。这很简 单,只需一个方法,setLastModified(Date),和一个实例变量来保存这个状态。然后,这个新方法和实例变量就可以被引入到现有的类 中,从而可以在无需修改这些现有的类的情况下,让它们具有新的行为和状态。

织入(Weaving)

织入是把切面应用到目标对象并创建新的代理对象的过程。切面在指定的连接点被织入到目标对象中。在目标对象的生命周期里有多个点可以 进行织入: 编译期:切面在目标类编译时被织入。这种方式需要特殊的编译器。AspectJ的织入编译器就是以这种方式织入切面的。 类加载期:切面在目标类加载到JVM时被织入。这种方式需要特殊的类加载器(ClassLoader),它可以在目标类被引入应用之前增 强该目标类的字节码。AspectJ 5的加载时织入(load-time weaving,LTW)就支持以这种方式织入切面。 运行期:切面在应用运行的某个时刻被织入。一般情况下,在织入切面时,AOP容器会为目标对象动态地创建一个代理对象。Spring AOP就是以这种方式织入切面的。

第二部分::Web中的Spring

跟踪Spring MVC的请求


请求旅程的第一站是Spring的DispatcherServlet。与大多数基于Java的Web框架一样,Spring MVC所有的请求都会通过一个前端控制器 (front controller)Servlet。前端控制器是常用的Web应用程序模式,在这里一个单实例的Servlet将请求委托给应用程序的其他组件来执行实际 的处理。在Spring MVC中,DispatcherServlet就是前端控制器。

DispatcherServlet的任务是将请求发送给Spring MVC控制器(controller)。控制器是一个用于处理请求的Spring组件。在典型的应用程 序中可能会有多个控制器,DispatcherServlet需要知道应该将请求发送给哪个控制器。所以DispatcherServlet以会查询一个或多个 处理器映射(handler mapping) 来确定请求的下一站在哪里。

处理器映射会根据请求所携带的URL信息来进行决策。 一旦选择了合适的控制器,DispatcherServlet会将请求发送给选中的控制器 。到了控制器,请求会卸下其负载(用户提交的信息)并 耐心等待控制器处理这些信息。(实际上,设计良好的控制器本身只处理很少甚至不处理工作,而是将业务逻辑委托给一个或多个服务对象进 行处理。)

控制器在完成逻辑处理后,通常会产生一些信息,这些信息需要返回给用户并在浏览器上显示。这些信息被称为模型(model)。不过仅仅给 用户返回原始的信息是不够的——这些信息需要以用户友好的方式进行格式化,一般会是HTML。所以,信息需要发送给一个视图(view), 通常会是JSP。 控制器所做的最后一件事就是将模型数据打包,并且标示出用于渲染输出的视图名。

它接下来会将请求连同模型和视图名发送 回DispatcherServlet 。 这样,控制器就不会与特定的视图相耦合,传递给DispatcherServlet的视图名并不直接表示某个特定的JSP。实际上,它甚至并不能确 这样,控制器就不会与特定的视图相耦合,传递给DispatcherServlet的视图名并不直接表示某个特定的JSP。实际上,它甚至并不能确 定视图就是JSP。相反,它仅仅传递了一个逻辑名称,这个名字将会用来查找产生结果的真正视图。

DispatcherServlet将会使用视图解 析器(view resolver) 来将逻辑视图名匹配为一个特定的视图实现,它可能是也可能不是JSP。 既然DispatcherServlet已经知道由哪个视图渲染结果,那请求的任务基本上也就完成了。

a它的最后一站是视图的实现(可能是JSP) , 在这里它交付模型数据。请求的任务就完成了。视图将使用模型数据渲染输出,这个输出会通过响应对象传递给客户端(不会像听上去那样硬 编码)

POJO(Plain Ordinary Java Object)简单的Java对象,实际就是普通JavaBeans

EJB: 企业级JavaBean(Enterprise JavaBean, EJB)是一个用来构筑企业级应用的服务器端可被管理组件。

JavaBean :是一种JAVA语言写成的可重用组件

原文地址:https://www.cnblogs.com/lzxulxy/p/11693172.html