《Spring揭秘》

第一章Spring框架的由来##

1.2 Spring 框架概述

在这里插入图片描述
Spring为我们提供了一个IoC容器( IoC Container)实现,用于帮助我们以依赖注入的方式管理对象之间的依赖关系;
在IoC之上是AOP模块。该模块提供了一个轻便但功能强大的AOP框架,让我们可以以AOP的形式增强各POJO的能力,进而补足OOP/OOSD之缺憾。Spring的AOP框架符合AOP Alliance规范,采用Proxy模式构建,与IoC容器相结合,可以充分显示出Spring AOP的强大威力。
Spring框架在Core核心模块和AOP模块的基础上,为我们提供了完备的数据访问和事务管理的抽象和集成服务。Spring框架中的事务管理抽象层是Spring AOP的最佳实践,它直接构建在Spring AOP的基础之上,为我们提供了编程式事务管理和声明式事务管理的完备支持。
为了简化各种Java EE服务(像JNDI、JMS以及JavaMail等)的使用,Spring框架为我们提供了针对这些Java EE服务的集成服务。
最后要提到的就是Web模块。在该模块中,Spring框架提供了一套自己的Web MVC框架,职责分明的角色划分让这套框架看起来十分地“醒目”。

1.3 Spring 大观园

在这里插入图片描述
Spring Core:框架的最基础部分,提供 IoC 容器,对 bean 进行管理。

Spring Context:继承BeanFactory,提供上下文信息,扩展出JNDI、EJB、电子邮件、国际化等功能。

Spring DAO:提供了JDBC的抽象层,还提供了声明性事务管理方法。

Spring ORM:提供了JPA、JDO、Hibernate、MyBatis 等ORM映射层.

Spring AOP:集成了所有AOP功能

Spring Web:提供了基础的 Web 开发的上下文信息,现有的Web框架,如JSF、Tapestry、Structs等,提供了集成

Spring Web MVC:提供了 Web 应用的 Model-View-Controller 全功能实现。

第二章 IOC的基本概念##

2.1 我们的理念是:让别人为你服务

如果我们依赖于某个类或服务,最简单而有效的方式就是直接在类的构造函数中新建相应的依赖类。我们都是自己主动地去获取依赖的对象!

其实IoC就这么简单!原来是需要什么东西自己去拿,现在是需要什么东西就让别人送过来。也就是让IoC Service Provider来为你服务!

2.2 注入方式

IoC模式中,被注入对象是通过什么方式通知IoC Service provider的呢?有三种方式:
构造方法注入,setter方法注入,接口注入。

  1. 构造方法注入
    构造方法注入就是被注入对象在其构造方法中声明依赖对象的参数列表,让外部知道它需要哪些对象。
    构造方法注入方式比较直观,对象被构造完成后,即进入就绪状态,可以马上使用。
  2. setter方法注入
    即通过setxxx(依赖对象)形式注入,在对象构造完成后即可使用。
  3. 接口注入
    被注入对象如果想要IoC Service provider为其注入依赖对象,就必须实现某个接口。这个接口提供一个方法,用来为其注入对象。

三种注入方式的比较

  • 接口注入。从注入方式的使用上来说,接口注入是现在不甚提倡的一种方式,基本处于“退役状态”。因为它强制被注入对象实现不必要的接口,带有侵入性。而构造方法注入和setter方法注入则不需要如此。
  • 构造方法注入。这种注入方式的优点就是,对象在构造完成之后,即已进入就绪状态,可以马上使用。缺点就是,当依赖对象比较多的时候,构造方法的参数列表会比较长。而通过反射构造对象的时候,对相同类型的参数的处理会比较困难,维护和使用上也比较麻烦。而且在Java中,构造方法无法被继承,无法设置默认值。对于非必须的依赖处理,可能需要引入多个构造方法,而参数数量的变动可能造成维护上的不便。
  • setter方法注入。因为方法可以命名,所以setter方法注入在描述性上要比构造方法注入好一些。 另外,setter方法可以被继承,允许设置默认值,而且有良好的IDE支持。缺点当然就是对象无法在构造完成后马上进入就绪状态。

2.3 IoC 的附加值

不会对业务对象构成很强的侵入性;
使用IoC后,对象具有更好的可测试性、可重用性和可扩展性

如果要用一句话来概括IoC可以带给我们什么,那么我希望是, IoC是一种可以帮助我们解耦各业务对象间依赖关系的对象绑定方式!

掌管大局的IoC Service Provider

3.1 IoC Service Provider 的职责

IoC Service Provider的职责相对来说比较简单,主要有两个:

  • 业务对象的构建管理
    在IoC场景中,业务对象无需关心所依赖的对象如何构建如何取得,但这部分工作始终需要有人来做。所以,IoC Service Provider需要将对象的构建逻辑从客户端对象那里剥离出来,以免这部分逻辑污染业务对象的实现。
  • 业务对象间的依赖绑定
    IoC Service Provider通过结合之前构建和管理的所有业务对象,以及各个业务对象间可以识别的依赖关系,将这些对象所依赖的对象注入绑定,从而保证每个业务对象在使用的时候,可以处于就绪状态。

3.2 IoC Service Provider 如何管理对象间的依赖关系

直接编码方式
在容器启动之前,我们就可以通过程序编码的方式将被注入对象和依赖对象注册到容器中,并明确它们相互之间的依赖注入关系。

IoContainer container = ...; 
container.register(FXNewsProvider.class,new FXNewsProvider()); 
container.register(IFXNewsListener.class,new DowJonesNewsListener()); 
... 
container.bind(IFXNewsListenerCallable.class, container.get(IFXNewsListener.class)); 
... 
FXNewsProvider newsProvider = (FXNewsProvider)container.get(FXNewsProvider.class); 
newProvider.getAndPersistNews();

配置文件方式
这是一种较为普遍的依赖注入关系管理方式。像普通文本文件、properties文件、XML文件等,都可以成为管理依赖注入关系的载体。

<bean id="newsProvider" class="..FXNewsProvider"> 
 <property name="newsListener"> 
 <ref bean="djNewsListener"/> 
 </property> 
 <property name="newPersistener"> 
 <ref bean="djNewsPersister"/> 
 </property> 
</bean> 
<bean id="djNewsListener" 
 class="..impl.DowJonesNewsListener"> 
</bean> 
<bean id="djNewsPersister" 
 class="..impl.DowJonesNewsPersister"> 
</bean>

元数据方式
我们可以直接在类中使用元数据信息来标注各个对象之间的依赖关系。

public class FXNewsProvider 
{ 
 private IFXNewsListener newsListener; 
 private IFXNewsPersister newPersistener; 
 @Inject 
public FXNewsProvider(IFXNewsListener listener,IFXNewsPersister persister) 
 { 
 this.newsListener = listener; 
 this.newPersistener = persister; 
 }  ... 
}

Spring的IoC容器之BeanFactory

Spring IoC 容器由两个部分组成:IoC和容器;所谓IoC,即IoC Service Provider,也就是说它能够完成掌握大局的IoC Service Provider这篇文章里提到的相关工作;除了基本的IoC支持,作为轻量级容器,它还提供了相应AOP框架、企业级服务集成等支持。如下图:
在这里插入图片描述

Spring提供了两种容器类型:BeanFactory和ApplicationContext。

  • BeanFactory。基础类型IoC容器,提供完整的IoC服务支持。如果没有特殊指定,默认采用延迟初始化策略(lazy-load)。只有当客户端对象需要访问容器中的某个受管对象的时候,才对该受管对象进行初始化以及依赖注入操作。所以,相对来说,容器启动初期速度较快,所需要的资源有限。对于资源有限,并且功能要求不是很严格的场景,BeanFactory是比较合适的IoC容器选择。
  • ApplicationContext。ApplicationContext在BeanFactory的基础上构建,是相对比较高级的容器实现,除了拥有BeanFactory的所有支持,ApplicationContext还提供了其他高级特性,比如事件发布、国际化信息支持等,这些会在后面详述。ApplicationContext所管理的对象,在该类型容器启动之后,默认全部初始化并绑定完成。所以,相对于BeanFactory来说,ApplicationContext要求更多的系统资源,同时,因为在启动时就完成所有初始化,容器启动时间较之BeanFactory也会长一些。在那些系统资源充足,并且要求更多功能的场景中,ApplicationContext类型的容器是比较合适的选择。

在这里插入图片描述

作为Spring提供的基本的IoC容器,BeanFactory可以完成作为IoC Service Provider的所有职责,包括业务对象的注册和对象间依赖关系的绑定。

beanFactory会在bean的生命周期的各个阶段中对bean进行各种管理,并且spring将这些阶段通过各种接口暴露给我们,让我们可以对bean进行各种处理,我们只要让bean实现对应的接口,那么spring就会在bean的生命周期调用我们实现的接口来处理该bean。
参考:https://blog.csdn.net/qq_36688143/article/details/84968036

4.1拥有BeanFactory之后的生活

与以前唯一的不同,就是对象之间依赖关系的解决方式改变了。
在BeanFactory出现之前,我们通常会自己实例化相应的对象并调用之。

FXNewsProvider newsProvider = new FXNewsProvider(); 
newsProvider.getAndPersistNews();

有了BeanFactory后:

BeanFactory container = new XmlBeanFactory(new ClassPathResource("配置文件路径")); 
FXNewsProvider newsProvider = (FXNewsProvider)container.getBean("djNewsProvider"); 
newsProvider.getAndPersistNews();
或者
ApplicationContext container = new ClassPathXmlApplicationContext("配置文件路径"); 
FXNewsProvider newsProvider = (FXNewsProvider)container.getBean("djNewsProvider"); 
newsProvider.getAndPersistNews();
或者
ApplicationContext container = new FileSystemXmlApplicationContext("配置文件路径"); 
FXNewsProvider newsProvider = (FXNewsProvider)container.getBean("djNewsProvider"); 
newsProvider.getAndPersistNews();

4.2 BeanFactory的对象注册与依赖绑定方式

DI依赖注入流程? (实例化,处理Bean之间的依赖关系)
过程在Ioc初始化后,依赖注入的过程是用户第一次向IoC容器索要Bean时触发

如果设置lazy-init=true,会在第一次getBean的时候才初始化bean, lazy-init=false,会容器启动的时候直接初始化(singleton bean);

调用BeanFactory.getBean()生成bean的;

生成bean过程运用装饰器模式产生的bean都是beanWrapper(bean的增强);

依赖注入怎么处理bean之间的依赖关系?
其实就是通过在beanDefinition载入时,如果bean有依赖关系,通过占位符来代替,在调用getbean时候,如果遇到占位符,从ioc里获取bean注入到本实例来。

对象注册

  • 直接编码
  • 外部配置文件方式
    Spring的IoC容器支持两种配置文件格式: Properties文件格式和XML文件格式。如果你愿意也可以引入自己的文件格式。
    Spring根据不同的外部配置文件格式,会给出相应的BeanDefinitionReader实现类,由BeanDefinitionReader的相应实现类负责将相应的配置文件内容读取并映射到BeanDefinition,然后将映射后的BeanDefinition注册到一个BeanDefinitionRegistry,之后, BeanDefinitionRegistry即完成Bean的注册和加载。
    可以看出,基本上在Spring对通过外部配置文件来描述的依赖关系的处理流程为:BeanDefinitionReader->BeanDefinition->BeanDefinitionRegistry。我们需要做的就是按照配置文件所要求的语法来描述对象之间的依赖关系;
  • 注解方式
    注解是一种类级别的描述依赖关系的方式;它使用类似@Component、@Autowired等注解告诉Spring,其所在的类需要被Spring IoC容器管理

Bean的作用域

BeanFactory除了拥有作为IoC Service Provider的职责,作为一个轻量级容器,它还有着其他一些职责,其中就包括对象的生命周期管理。

scope用来声明容器中的对象所应该处的限定场景或者说该对象的存活时间。

Spring容器最初提供了songleton和prototype,后来又增加了request,session,globe session,后三种只能用在Web应用中。

  • singleton:Spring的IoC容器中,只有一个对象实例;所有对该对象的引用共享该实例;它几乎和IoC容器有着同样的寿命;需要注意的一点是,不要因为名字的原因而与GoF所提出的Singleton模式相混淆,二者的语意是不同的:标记为singleton的bean是由容器来保证这种类型的bean在同一个容器中只存在一个共享实例;而Singleton模式则是保证在同一个Classloader中只存在一个这种类型的实例;
  • prototype:针对声明为拥有prototype scope的bean定义,容器在接到该类型对象的请求的时候,会每次都重新生成一个新的对象实例给请求方。虽然这种类型的对象的实例化以及属性设置等工作都是由容器负责的,但是只要准备完毕,并且对象实例返回给请求方之后,容器就不再拥有当前返回对象的引用,请求方需要自己负责当前返回对象的后继生命周期的管理工作,包括该对象的销毁也就是说,容器每次返回给请求方一个新的对象实例之后,就任由这个对象实例“自生自灭”了。
  • request:Spring容器为每个HTTP请求创建一个全新的对象供当前请求使用,当请求结束后,对象生命周期宣布结束;类似prototype,只是使用场景略有不同;
  • session:Spring容器为每个独立的session创建属于它们自己的标记对象;
  • global session:只有在基于portlet的web应用程序中才有意义。

工厂方法与FactoryBean

工厂方法( Factory Method)模式
提供一个工厂类来实例化具体的接口实现类,这样,主体对象只需要依赖工厂类,具体使用的实现类有变更的话,只是变更工厂类,而主实现类对象不需要做任何变动。

FactoryBean

FactoryBean 是Spring容器提供的一种可以扩展容器对象实例化逻辑的接口。
它本身与其他注册到容器的对象一样,只是一个Bean而已,只不过,这种类型的Bean本身就是生产对象的工厂(Factory)。

 public class NextDayDateFactoryBean implements FactoryBean {
        public Object getObject() throws Exception {
            return new DateTime().plusDays(1);
        }

        public Class getObjectType() {
            return DateTime.class;
        }

        public boolean isSingleton() {
            return false;
        }
    }

拥有prototype类型scope的bean,在请求方每次向容器请求该类型对象的时候,容器都
会返回一个全新的该对象实例。

容器背后的秘密

容器功能实现分为两部分:容器启动阶段和Bean实例化阶段;Spring根据这两个阶段的不同特点,为我们提供了一系列的扩展点,以便我们根据具体的场景加入自定义扩展逻辑。这体现了Spring在设计上的可扩展性;

容器启动阶段
容器启动阶段的工作流程:加载Configuration Metadata,进行解析和分析,然后生成对应的BeanDefinition,之后将其保存在BeanDefinitionRegistry。这样容器启动阶段就结束了,整体来看,该阶段进行的工作是准备性质的,重点在于对象管理信息的收集,当然,会进行一些验证性或者辅助性的工作。

Bean实例化阶段
首先,经过容器启动阶段,所有的配置信息都已经转换为BeanDefinition并且存储在BeanDefinitionRegistry中。所以,当客户端显式调用getBean或者因为依赖关系容器隐式调用getBean方法时,对象就会被创建,也就是完成实例化;

在该阶段,容器会首先检查之前是否已经完成了初始化,如果没有,那么就会根据BeanDefinition去创建并为其注入依赖;同时如果该Bean实现了一些回调接口,那么容器也会调用这些接口;

插手容器的启动

Spring提供了一种BeanFactoryPostProcessor的容器扩展机制,该机制将在容器启动之后,Bean实例化之前起作用,目的是对BeanDefinition进行相应的修改,如修改、完善相应的BeanDefinition。

我们只需要实现BeanFactoryPostProcessor接口即可实现自定义处理逻辑。因为容器中可能有多个BeanFactoryPostProcessor实例,所以一般来说还要实现Ordered接口以便指定容器调用的顺序;

了解Bean的一生

我们知道,容器(BeanFactory)启动之后,并不会马上就实例化相应的bean定义。只有当请求方通过BeanFactory的getBean()方法来请求某个对象实例的时候,才有可能触发Bean实例化阶段的活动。
对于BeanFactory来说,对象实例化默认采用延迟初始化;而ApplicationContext启动之后会实例化所有的bean定义;

Bean的实例化过程如下图:
在这里插入图片描述
Spring容器对其所管理的对象给予统一的生命周期管理,这些对象摆脱了“new 之后使用,然后被回收”的命运。

Bean的实例化
容器内部采用策略模式来决定采用何种方式实例化Bean,通常是反射和动态字节码生成来初始化Bean及其子类;

默认情况下,容器内部采用的是CglibSubclassingInstantiationStrategy。

容器只要根据相应bean定义的BeanDefintion取得实例化信息,结合CglibSubclassingInstantiationStrategy以及不同的bean定义类型,就可以返回实例化完成的对象实例但是,返回方式上有些“点缀”。不是直接返回构造完成的对象实例,而是以BeanWrapper对构造完成的对象实例进行包裹,返回相应的BeanWrapper实例;

至此,流程图中的第一步——实例化Bean对象,就完成啦;
BeanWrapper定义继承了org.springframework.beans.PropertyAccessor接口,可以以统一的方式对对象属性进行访问;

各色的Aware接口

对于BeanFactory来说,有如下几个常见的Aware接口:

  • BeanNameAware:注入当前bean的配置文件中的beanName;
  • BeanClassLoaderAware:注入加载当前bean的ClassLoader;
  • BeanFactoryAware:注入BeanFactory实例;

对于ApplicationContext来说,也有一些Aware接口,只是检测和设置方法的机理上略有不同,它是使用BeanPostProcessor:

  • ResourceLoaderAware。ApplicationContext接口实现了ResourceLoader接口,所以它会将自己注入实现了该接口的bean中;
  • ApplicationEventPublisherAware:ApplicationContext同样实现了该接口,所以它仍然会将自己注入实现了该接口的bean中;
  • MessageResourceAware:同上;
  • ApplicationContextAware:同上;

BeanPostProcessor
其实不应该提一个看上去很相似,实际上没什么关系的接口:BeanFactoryPostProcessor。实际上,后者是在容器启动阶段结束、Bean实例化阶段尚未开始之前,对BeanDefinition进行修改以及补充容器需要的一些自定义内容(比如自定义PropertyEditor)时起作用。而前者是在Bean实例化,也就是第二阶段工作的;

public interface BeanPostProcessor {
	default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}
    default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

}

通常比较常见的使用BeanPostProcessor的场景,是处理标记接口实现类,或者为当前对象提供代理实现。
ApplicationContext对应的那些Aware接口实际上就是通过BeanPostProcessor的方式进行处理的。

InitializingBean和Init-Method
InitializingBean是容器内部广泛使用的一个对象生命周期标识接口。在BeanPostProcessor前置处理结束之后,会检查该对象是否实现了InitializingBean接口,如果是,则会调用其afterPropertiesSet方法对对象的状态进行进一步调整;

虽然该接口在Spring容器内部广泛使用,但如果真的让我们的业务对象实现这个接口,则显得Spring容器比较具有侵入性。所以, Spring还提供了另一种方式来指定自定义的对象初始化操作,那就是在XML配置的时候,使用的init-method属性。 它们的目标一致,就是在对象状态已经确定后,确保对象处于可用状态!

至此,流程图中的第五、六步——属性再调整,也就完成了。接下来因为第七步和第四步涉及的内容一致,只是调用时机不同,所以就略过啦,接下来,我们看看第八步~

DisposableBean与Destroy-Method
实现了org.springframework.beans.factory.DisposableBean接口。如果是,那么容器就会为其注册一个用于对象销毁的回调,以便对象销毁前,该调用的会被调用;

面试题:
spring ioc初始化流程?
resource定位 即寻找用户定义的bean资源,由 ResourceLoader通过统一的接口Resource接口来完成 beanDefinition载入 BeanDefinitionReader读取、解析Resource定位的资源 成BeanDefinition 载入到ioc中(通过HashMap进行维护BD) BeanDefinition注册 即向IOC容器注册这些BeanDefinition, 通过BeanDefinitionRegistery实现

BeanDefinition加载流程?
定义BeanDefinitionReader解析xml的document BeanDefinitionDocumentReader解析document成beanDefinition。

初始化阶段

1> 首先是读取bean的xml配置文件,然后解析xml文件中的各种bean的定义,将xml文件中的每一个元素分别转换成一个BeanDefinition对象,其中保存了从配置文件中读取到的该bean的各种信息。
2> 然后通过BeanDefinitionRegistry将这些bean注册到beanFactory中。

实例化阶段

如果该对象实现了某些回调接口,也会根据回调接口的要求来装配它
1> 各种的Aware接口,比如 BeanFactoryAware,MessageSourceAware,ApplicationContextAware
2> BeanPostProcessor接口
3> InitializingBean接口
4> DisposableBean接口

bean 生命周期

Bean的实例化过程:

  • 实例化Bean:Ioc容器通过获取BeanDefinition对象中的信息进行实例化,实例化对象被包装在BeanWrapper对象中。
  • 设置对象属性(DI):通过BeanWrapper提供的设置属性的接口完成属性依赖注入;
  • 注入Aware接口(BeanFactoryAware, 可以用这个方式来获取其它Bean,ApplicationContextAware):Spring会检测该对象是否实现了xxxAware接口,并将相关的xxxAware实例注入给bean。
  • BeanPostProcessor:自定义的处理(分前置处理和后置处理)
    InitializingBean和init-method:执行我们自己定义的初始化方法 使用。
  • destroy:bean的销毁

第五章 ApplicationContext

作为Spring提供的较之BeanFactory更为先进的IoC容器实现, ApplicationContext除了拥有BeanFactory支持的所有功能之外,还进一步扩展了基本容器的功能,包括BeanFactoryPostProcessor、 BeanPostProcessor以及其他特殊类型bean的自动识别、容器启动后bean实例的自动初始化、国际化的信息支持、容器内事件发布等。

ApplicationContext相对于BeanFactory特有的性质包括:国际化信息支持、统一资源加载、容器内事件发布等;

public interface ApplicationContext extends EnvironmentCapable, ListableBeanFactory, HierarchicalBeanFactory,MessageSource, ApplicationEventPublisher, ResourcePatternResolver {
}

统一资源加载策略

Spring提供了基于Resource和ResourceLoader接口的资源抽象和加载策略;

Resource接口作为所有资源的抽象和访问接口;Resource接口可以根据资源的不同类型,或者资源所在的不同场合给出相应的具体实现;常见的Resource有ClassPathResource、ByteArrayResource、FileSystemResource、UrlResource等;

ResourceLoader,更广义的URL
资源是有了,但如何去查找和定位这些资源,则应该是ResourceLoader的职责所在了。ResourceLoader接口是资源查找定位策略的统一抽象,具体的资源查找定位策略则由相应的ResourceLoader实现类给出;

ResourcePatternResolver
扩展自ResourceLoader,根据传入的资源路径,匹配并返回多个Resource实例;

ApplicationContext继承了ResourcePatternResolver,也就是间接继承了ResourceLoader,这就是ApplicationContext支持Spring统一资源加载的真相;
ApplicationContext 继承了ResourcePatternResolver,也就具备了统一资源加载策略。

特定情况下ApplicationContext的加载行为
我们知道,ResourceLoader定义了一种资源路径协议——classpath:;ResourcePatternResolver则定义了classspath*:,于是我们就可以通过资源路径协议前缀,明确告诉容器从classpath中加载资源;

classpath*:与classpath:的唯一区别就在于,如果能够在classpath中找到多个指定的资源,则返回多个;

国际化信息支持

Java对于I18n的支持主要涉及两个类:一个用来标记地区,一个用来表示资源,即Local和ResourceBundle;
Spring在Java SE的国际化支持的基础上,进一步抽象了国际化信息的访问接口,也就是MessageSource,传入相应的Locale、资源的键以及相应参数,就可以取得相应的信息。
在默认情况下, ApplicationContext将委派容器中一个名称为messageSource的MessageSource接口实现来完成MessageSource应该完成的职责。

容器内部事件发布

自定义事件发布
Java SE 提供了实现自定义事件发布( Custom Event publication)功能的基础类,即EventObject类和EventListener接口;

所有的自定义事件类型可以通过扩展EventObject来实现,而事件的监听器则扩展自EventListener。

要实现自定义事件发布,需要三个角色:事件、事件监听器和事件发布者;

事件标志着时机:有些操作要执行的时机;事件监听器:操作的执行者;事件发布者:将事件监听器和事件连接起来;

Spring容器内的事件发布类结构分析

ApplicationContext内允许以ApplicationEvent的形式发布事件,ApplicationListener监听事件。
一旦容器内发布ApplicationContext及其子类,注册到容器的ApplicationListener就会监听事件。

多配置模块加载的简化

使用ApplicationContext加载多个配置文件。

Spring IoC容器之扩展篇

@Autowired
@Autowired可以标注于类定义的多个位置,包括如下 几个。
域(Filed)或者说属性(Property)。
构造方法定义(Constructor)。
方法定义(Method)。
@Autowired注解是按类型装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它required属性为false。与@Qualifier(“reutersNewsListner”) 配合使用按名称限定需要注入的实例名称。
@Resource默认按byName自动注入,也提供按照byType 注入;

AOP

Java平台上的AOP实现机制

动态代理
JDK1.3之后,引入了动态代理机制,可以在运行期间,为相应的接口动态生成对应的代理对象。
这种方式缺点是:所有需要织入横切关注点逻辑的模板类都得实现相应的接口,因为动态代理机制只针对接口有效。

动态字节码增强
Java虚拟机加载的class文件有其规范,所有遵循该规范的class文件均可由Java虚拟机处理执行;所谓动态字节码增强就是说通过CGLIB等Java库,在程序运行期间,动态构建字节码文件,为需要横切逻辑的类生成子类,并将横切逻辑加入其中;这时,应用程序执行期间使用的不再是原来的业务类,而是动态生成的子类,从而达到将横切逻辑织入系统的目的;

Spring AOP在无法使用动态代理进行AOP功能扩展时,就会采用CGLIB库的动态字节码增强来支持AOP的功能扩展;

AOP的核心概念

1、切面(aspect):类是对物体特征的抽象,切面就是对横切关注点的抽象

2、横切关注点:对哪些方法进行拦截,拦截后怎么处理,这些关注点称之为横切关注点。

3、连接点(joinpoint):被拦截到的点,因为 Spring 只支持方法类型的连接点,所以在Spring 中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器。

4、切入点(pointcut):对连接点进行拦截的定义

5、通知(advice):所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类。

6、目标对象:代理的目标对象

7、织入(weave):将切面应用到目标对象并导致代理对象创建的过程

8、引入(introduction):在不修改代码的前提下,引入可以在运行期为类动态地添加方法或字段。

在这里插入图片描述

SpringAOP概述及其实现机制

AOP实现机制

代理模式
代理处于访问者和被访问这之间,可以隔离这两种之间的直接交互,访问者与代理打交道就行与被访问者一样。代理可以减少被访问者的负担,即使最终要真正的被访问者,也可以在这之前加入特定的逻辑。

静态代理模式,目标对象不一致,就必须为每个对象创建一个代理类,如果目标对象非常多,就需要创建分非常多的代理类。

/**
 * 学生代理类,也实现了Person接口,保存一个学生实体,这样既可以代理学生产生行为
 * @author Gonjan
 *
 */
public class StudentsProxy implements Person{
    //被代理的学生
    Student stu;
    
    public StudentsProxy(Person stu) {
        // 只代理学生对象
        if(stu.getClass() == Student.class) {
            this.stu = (Student)stu;
        }
    }
    
    //代理上交班费,调用被代理学生的上交班费行为
    public void giveMoney() {
        stu.giveMoney();
    }
}

动态代理
JDK 1.3之后引入了动态代理机制。使用该机制,可以为指定的接口在系统运行期间动态地生成代理对象。
动态代理机制实现主要由一个类和一个接口组成,即Proxy类和InvocationHandler接口。

public class StuInvocationHandler<T> implements InvocationHandler {
   //invocationHandler持有的被代理对象
    T target;
    
    public StuInvocationHandler(T target) {
       this.target = target;
    }
    
    /**
     * proxy:代表动态代理对象
     * method:代表正在执行的方法
     * args:代表调用目标方法时传入的实参
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理执行" +method.getName() + "方法");
     */   
        //代理过程中插入监测方法,计算该方法耗时
        MonitorUtil.start();
        Object result = method.invoke(target, args);
        MonitorUtil.finish(method.getName());
        return result;
    }
}


public class ProxyTest {
    public static void main(String[] args) {
        
        //创建一个实例对象,这个对象是被代理的对象
        Person zhangsan = new Student("张三");
        
        //创建一个与代理对象相关联的InvocationHandler
        InvocationHandler stuHandler = new StuInvocationHandler<Person>(zhangsan);
        
        //创建一个代理对象stuProxy来代理zhangsan,代理对象的每个执行方法都会替换执行Invocation中的invoke方法
        Person stuProxy = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, stuHandler)//代理执行上交班费的方法
        stuProxy.giveMoney();
    }
}

动态字节码生成
通过动态字节码生成技术进行织入的原理是继承;通过继承,为目标对象实现一个加入了横切逻辑的子类作为目标对象的代理;
需要横切逻辑实现MethodInterceptor 接口,然后使用 Enhancer 对象生成一个子类,并将横切逻辑附加到子类中。

/**
 * 这个是没有实现接口的实现类
 * 
 * @author student
 * 
 */
public class BookFacade {
	public void addBook() {
		System.out.println("增加图书的普通方法...");
	}
}
/**
 * 使用cglib动态代理
 * 
 * @author student
 * 
 */
public class BookFacadeCglib implements MethodInterceptor {
	private Object target;
 
	/**
	 * 创建代理对象
	 * 
	 * @param target
	 * @return
	 */
	public Object getInstance(Object target) {
		this.target = target;
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(this.target.getClass());
		// 回调方法
		enhancer.setCallback(this);
		// 创建代理对象
		return enhancer.create();
	}
 
	@Override
	// 回调方法
	public Object intercept(Object obj, Method method, Object[] args,
			MethodProxy proxy) throws Throwable {
		System.out.println("事物开始");
		proxy.invokeSuper(obj, args);
		System.out.println("事物结束");
		return null;
 
 
	}
 
}

CGLIB对类扩展的唯一限制就是无法对final方法进行覆写。

AOP使用哪种动态代理?

使用CGLIB条件:
如果目标类没有实现任何接口,不管proxyTargetClass属性值是什么。
proxyTargetClass属性为true。
optimize属性为true。

使用jdk动态代理条件: 当bean的是实现中存在接口或者是Proxy的子类。

JDK 动态代理主要涉及到 java.lang.reflect 包中的两个类:Proxy 和 InvocationHandler。
Proxy 利用 InvocationHandler(定义横切逻辑) 接口动态创建 目标类的代理对象。

AOP应用案例

记录日志
监控性能
权限控制
事务管理

AOP扩展篇

同一个对象内的嵌套方法调用拦截失效问题。

同一对象内部方法嵌套调用后,代理对象最终会调用目标对象的方法。

在这里插入图片描述
解决方案

  • 在目标对象声明一个代理对象的引用实例,然后通过构造器或者setter方法注入。
  • 在目标对象声明一个getter方法,返回AopContext.currentProxy().
  • 通过BeanPostProcessor

下表总结了 Spring AOP 和 AspectJ 之间的关键区别:
在这里插入图片描述

统一数据访问异常体系

JdbcTemplate

 public <T> T execute(StatementCallback<T> action) throws DataAccessException 
 {
 }

JDBC API 最佳实践

JDBC流程详解
JDBC操作数据库共包括6个步骤:

注冊驱动 (仅仅做一次)
建立连接(Connection)
创建传输器Statement
运行SQL语句
处理运行结果(ResultSet)
释放资源

public class JDBCTest {

	/**
	 * 加载的mysql的驱动
	 */
	public static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";

	/**
	 * 连接数据库的url
	 */
	public static final String JDBC_URL = "jdbc:mysql://localhost:3306/test";

	/**
	 * 连接数据库用户名
	 */
	public static final String JDBC_USERNAME = "root";

	/**
	 * 数据库密码
	 */
	public static final String JDBC_PASSWORD = "123456";

	/**
	 * 测试数据库删除,修改和插入
	 */
	@Test
	public void testUpdate() {
		Connection connection = null;
		PreparedStatement preparedStatement = null;

		try {
			// 加载Driver类,注册数据库驱动
			Class.forName(JDBC_DRIVER);
			// 通过DriverManager,使用url,用户名和密码建立连接(Connection)
			connection = DriverManager.getConnection(JDBC_URL, JDBC_USERNAME, JDBC_PASSWORD);
			// 通过Connection,使用sql语句打开Statement对象;
			preparedStatement = connection.prepareStatement("UPDATE student SET age=20 WHERE name=?");
			// 传入参数,防止sql注入
			preparedStatement.setString(1, "xiaoming");
			// 执行语句,将结果返回resultSet
			int count = preparedStatement.executeUpdate();
			// 对结果进行处理
			System.out.println(count);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			// 倒序释放资源
			try {
				if (preparedStatement != null && !preparedStatement.isClosed()) {
					preparedStatement.close();
				}
			} catch (SQLException e) {
				e.printStackTrace();
			}
			try {
				if (connection != null && connection.isClosed()) {
					connection.close();
				}
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * 测试批量操作
	 */
	@Test
	public void testBatch() {
		Connection connection = null;
		PreparedStatement preparedStatement = null;
		String insertSql = "insert into student values(?,?)";
		try {
			// 加载Driver类,注册数据库驱动
			Class.forName(JDBC_DRIVER);
			// 通过DriverManager,使用url,用户名和密码建立连接(Connection)
			connection = DriverManager.getConnection(JDBC_URL, JDBC_USERNAME, JDBC_PASSWORD);
			// 通过Connection,使用sql语句打开Statement对象;
			preparedStatement = connection.prepareStatement(insertSql);
			// 传入参数,防止sql注入
			for (int i = 0; i < 10; i++) {
				preparedStatement.setString(1, 100 + i + "user");
				preparedStatement.setInt(2, 100 + i);
				preparedStatement.addBatch();
			}
			// 执行语句,将结果返回resultSet
			int[] count = preparedStatement.executeBatch();
			// 对结果进行处理
			System.out.println(count);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			// 倒序释放资源
			try {
				if (preparedStatement != null && !preparedStatement.isClosed()) {
					preparedStatement.close();
				}
			} catch (SQLException e) {
				e.printStackTrace();
			}

			try {
				if (connection != null && connection.isClosed()) {
					connection.close();
				}
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}
}

Spring中的DataSource
1.简单的DataSource
使用DriverManager连接数据库
2.用于连接缓冲池的DataSource实现
DBCP和C3P0属于此类。
3.支持分布式事物的DataSource实现

事务管理

认识事物

4个限定属性:ACID属性

  • 原子性-Automicity 一个事务所包含的操作是一个不可分割的整体
  • 一致性-Consistency 数据资源在事务执行前后都要处于数据一致性状态。其中数据一致性的问题有:
  • 隔离性-Isolation
    规定各个事务之间相互影响的程度。当多个事务对一个数据资源并发访问时,不同隔离级别决定了对该数据资源访问的不同行为。
  • 持久性-Durability 一旦事务操作成功提交,那么对数据的变更将被记载且不可逆转,因此需要通过冗余存储、多数据网络备份等方式来保证

4个隔离级别:

1.编程式事务
2.声明式事务
3.注解式事务

TransactionDefinition
TransactionDefinition 事务定义信息: (配置信息来自xml配置文件和注解)

包括事务的隔离级别,事务的传播特性,事务超时时间,事务只读特性.
这个接口中有很多常量:
ISOLATION_xxx 事务隔离级别
PROPAGATION_xxx 事务传播行为
int getTimeout() 获得超时信息
boolean isReadOnly() 判断事务是否只读

Strategy 模式在开发过程中的应用

策略模式:是对算法的包装,是把使用算法的责任和算法本身分割开来,委派给不同的对象管理。策略模式通常把一个系列的算法包装到一系列的策略类里面,作为一个抽象策略类的子类。

Spring MVC

Servlet 独行天下的时代
Servlet 时代, 数据访问逻辑与业务处理对应的视图渲染逻辑相互混杂。
繁盛一时的JSP时代
模板化方法
在这里插入图片描述
springMVC流程:
1):用户请求发送给DispatcherServlet,DispatcherServlet调用HandlerMapping处理器映射器;

(2):HandlerMapping根据xml或注解找到对应的处理器,生成处理器对象返回给DispatcherServlet;

(3):DispatcherServlet会调用相应的HandlerAdapter;

(4):HandlerAdapter经过适配调用具体的处理器去处理请求,生成ModelAndView返回给DispatcherServlet

(5):DispatcherServlet将ModelAndView传给ViewReslover解析生成View返回给DispatcherServlet;

(6):DispatcherServlet根据View进行渲染视图;

->DispatcherServlet->HandlerMapping->Handler ->DispatcherServlet->HandlerAdapter处理handler->ModelAndView ->DispatcherServlet->ModelAndView->ViewReslover->View ->DispatcherServlet->返回给客户

  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值