Java复习笔记(6)——SSM(1)

版权声明:本文为博主原创,未经博主允许不得转载。 https://blog.csdn.net/weixin_36904568/article/details/90765945

一:Spring

1. 特点

  • 轻量级:可以在大小只有1M多的JAR文件发布,所需的开销也小
  • 控制反转 IOC:Spring通过 IOC 技术实现低耦合,一个对象A依赖的其他对象会通过被动的方式传递进来,而不是对象A自己创建或查找依赖的对象
  • 面向切面 AOP :Spring支持面向切面编程,分离应用的业务逻辑和系统服务
  • 容器:Spring包含并管理应用对象的配置和生命周期
    • 可以配置bean的创建过程以及bean之间的关联
    • 基于一个可配置原型,bean可以创建一个单独的实例或每次都生成新实例
  • 框架集合:
    • Spring可以将简单的组件配置、组合成复杂的应用 ,应用对象被声明式地组合
    • Spring也提供了事务管理、持久化框架集成等功能,将应用逻辑的开发留给开发者
  • 事务管理:Spring 提供一个持续的事务管理接口,可以扩展到上至本地事务下至全局事务(JTA)。
  • 异常处理:Spring 提供方便的 API 把具体技术相关的异常(比如由 JDBC,Hibernate 或者 JDO 抛出的)转化为一致的 unchecked 异常。

2. 组件

在这里插入图片描述

  • Test:Spring-test模块支持在JUnit和TestNG下的spring组件的单元测试和集成测试
  • 核心容器:
    • beans和core:提供Spring框架的基本功能,主要组件是BeanFactory(工厂模式的实现)。BeanFactory通过 IOC 控制反转模式将应用程序的配置和依赖性规范与实际的应用程序代码分开
    • context:Spring-context 是一个配置文件,提供上下文信息。包括各种企业服务如JDNI、EJB、电子邮件、国际化、校验和调度功能
    • expression:提供一个强大的表达式语言,在运行时来查询和操作对象图。支持对属性的获取与赋值,方法的调用,获取数组集合索引的内容,逻辑和算数操作,命名变量,以及通过IOC容器获取对象
  • AOP:通过配置管理特性,Spring-AOP模块直接将面向切面的编程功能集成到了Spring框架中。可以将一些通用任务如安全、日志、事务等几种管理,提高了复用性和管理的便捷性
  • Aspects:单独的spring-aspects模块被集成到了AspectJ里了
  • Instrumentation:服务器的代理接口,提供了class instrumentation支持和类加载器的实现,在特定的应用服务器上使用。spring-instrument-tomcat模块包含了为tomcat提供的spring仪器(instrumentation)代理
  • Messaging:从spring集成项目(如Meaasge,MessageChannel,MessageHandler和其他基于消息应用的基础服务)而来的关键抽象。这个模块也包括一系列的用来映射消息到方法的注解
  • 数据的获取/整合:
    • Spring jdbc:为JDBC DAO抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误信息。异常层次结构简化了错误处理,降低了需要编写的异常代码数量(打开和关闭连接)。Spring DAO的面向JDBC的异常遵从通用的DAO异常层次结构
    • Spring ORM:Spring 框架插入多个ORM框架,提供了 ORM 的对象关系工具,包括JDO、Hibernate、IBatis
    • Spring OXM:提供一个支持Object/XML mapping实现(比如JAXB,XMLBeans,JiBX和XStream)的抽象层
    • Spring JMS:提供了包括生产和消费消息的特性,从spring框架4.1,提供了和spring-messaging模块的集成
    • Spring-tx:实现了特殊接口的类,为所有POJOs(Plain Old Java Objects)提供了方案和声明式事务管理.
  • Spring Web模块:
    • web:提供面向web整合的特性,如多部件的文件上传功能和使用Servlet监听器初始化的IOC容器,为应用程序提供上下文。它也包括一个Http客户端和spring远程操作
    • mvc:是一个全功能的构建Web应用程序的MVC实现。通过策略接口,能够高度可配置MVC框架;MVC包含大量视图技术,如JSP、Velocity、Tiles等
    • portlet:提供一个被用在Portlet环境和镜像的spring-webmvc模块功能的MVC实现

3. 主要JAR包

在这里插入图片描述

4. 常用注解

bean 注入与装配的的方式有很多种,可以通过 xml,get set 方式,构造函数或者注解等。简单易用的方式就是使用 Spring 的注解了,Spring 提供了大量的注解方式。

(1)类

  • @Component:泛指组件
  • @Repository:注解DAO层,在DAO Impl类上使用
  • @Service:注解业务层组件
  • @Context:注解控制层组件,使用它标注的类是一个SpringMVC Controller 对象。分发处理器会扫描该类的方法,并检查是否使用了@RequestMapping注解。
  • @RequestController:@Controller+@RequestBody

(2)方法

  • @RequestBody:用于异步请求,将Controller的方法返回的对象,通过适当的HttpMessageConverter转换为指定格式后,写入到Response对象的body区
  • @RequestMapping:处理请求地址映射的注解,作用在类上表示类的所有方法以其作为父路径
  • @Autowired:可以标注成员变量、方法、构造函数,完成自动装配,消除get(),set()方法
  • @PathVaiable:将请求URL的模板变量映射到功能处理方法的参数上,取出URL模板的变量作为参数
  • @RequestParam:获取请求参数
  • @RequestHeader:可以把Request请求header部分的值绑定到方法的参数上

(3)其他

  • @ModelAttribute:被@ModelAttribute注释的方法会在此controller每个方法执行前被执行
  • @SessionAttributes:注解在Controller上面,可以将Model中的属性同步到session作用域当中
  • @Vaild:结合Hibernate Validator作为校验
  • @CookieValue:获取Cookie值

5. 第三方集成

在这里插入图片描述

6. Spring 容器

Spring 通过一个配置文件描述 Bean 及 Bean 之间的依赖关系,利用 Java 语言的反射功能实例化Bean 并建立 Bean 之间的依赖关系。 Spring 的 IOC 容器在完成这些底层工作的基础上,还提供了 Bean 实例缓存、生命周期管理、 Bean 实例代理、事件发布、资源装载等高级服务。

Spring 启动时读取应用程序提供的 Bean 配置信息,并在 Spring 容器中生成一份相应的 Bean 配置注册表,然后根据这张注册表实例化 Bean,装配好 Bean 之间的依赖关系,为上层应用提供准备就绪的运行环境。其中 Bean 缓存池为 HashMap 实现
在这里插入图片描述

(1)BeanFactory 框架基础设施

在这里插入图片描述

BeanDefinitionRegistry 注册表

Spring 配置文件中每一个节点元素在 Spring 容器里都通过一个 BeanDefinition 对象表示,它描述了 Bean 的配置信息。而 BeanDefinitionRegistry 接口提供了向容器手工注册
BeanDefinition 对象的方法。

BeanFactory 顶层接口

位于类结构树的顶端 ,它最主要的方法就是 getBean(String beanName),该方法从容器中返回特定名称的 Bean,BeanFactory 的功能通过其他的接口得到不断扩展

ListableBeanFactory

该接口定义了访问容器中 Bean 基本信息的若干方法,如查看 Bean 的个数、获取某一类型Bean 的配置名、查看容器中是否包括某一 Bean 等方法

HierarchicalBeanFactory 父子级联

父子级联 IOC 容器的接口, Spring 的 IOC 容器可以建立父子层级关联的容器体系,子容器可以访问父容器中的 Bean,但父容器不能访问子容器的 Bean。

Spring 使用父子容器实现了很多功能,比如在 Spring MVC 中,展现层 Bean 位于一个子容器中,而业务层和持久层的 Bean 位于父容器中。这样,展现层 Bean 就可以引用业务层和持久层的 Bean,而业务层和持久层的 Bean 则看不到展现层的 Bean。

ConfigurableBeanFactory

是一个重要的接口,增强了 IOC 容器的可定制性,它定义了设置类装载器、属性编辑器、容器初始化后置处理器等方法

AutowireCapableBeanFactory 自动装配

定义了将容器中的 Bean 按某种规则(如按名字匹配、按类型匹配等)进行自动装配的方法

SingletonBeanRegistry 运行期间注册单例 Bean

定义了允许在运行期间向容器注册单实例 Bean 的方法

对于单实例( singleton)的 Bean 来说,BeanFactory 会缓存 Bean 实例,所以第二次使用 getBean() 获取 Bean 时将直接从IOC 容器的缓存中获取 Bean 实例。

Spring 在 DefaultSingletonBeanRegistry 类中提供了一个用于缓存单实例 Bean 的缓存器,它是一个用 HashMap 实现的缓存器,单实例的 Bean 以beanName 为键保存在这个 HashMap 中。

依赖日志框架

在初始化 BeanFactory 时,必须为其提供一种日志框架,比如使用 Log4J, 即在类路径下提供 Log4J 配置文件,这样启动 Spring 容器才不会报错。

(2)ApplicationContext 面向开发应用

ApplicationContext 由 BeanFactory派生而来,提供了更多面向实际应用的功能 ,几乎所有的应用场合我们都直接使用 ApplicationContext 而非底层的 BeanFactory。

ApplicationContext 继承了 HierarchicalBeanFactory 和 ListableBeanFactory 接口,在此基础上,还通过多个其他的接口扩展了 BeanFactory 的功能:
在这里插入图片描述

  • ClassPathXmlApplicationContext:默认从类路径加载配置文件
  • FileSystemXmlApplicationContext:默认从文件系统中装载配置文件
  • ApplicationEventPublisher:让容器拥有发布应用上下文事件的功能,包括容器启动事件、关闭事件等。
  • MessageSource:为应用提供国际化消息访问的功能;
  • ResourcePatternResolver : 所有 ApplicationContext 实现类都实现了类似于
    PathMatchingResourcePatternResolver 的功能,可以通过带前缀的 Ant 风格的资源文件路径装载 Spring 的配置文件。
  • LifeCycle:该接口是 Spring 2.0 加入的,该接口提供了 start()和 stop()两个方法,主要用于控制异步处理过程。在具体使用时,该接口同时被 ApplicationContext 实现及具体Bean 实现, ApplicationContext 会将 start/stop 的信息传递给容器中所有实现了该接口的 Bean,以达到管理和控制 JMX、任务调度等目的。
  • ConfigurableApplicationContext 扩展于 ApplicationContext,它新增加了两个主要
    的方法: refresh()和 close(),让 ApplicationContext 具有启动、刷新和关闭应用上下文的能力。在应用上下文关闭的情况下调用 refresh()即可启动应用上下文,在已经启动的状态下,调用 refresh()则清除缓存并重新装载配置信息,而调用 close()则可关闭应用上下文。
ApplicationContext 和 beanfactory 的区别
  • 利用 MessageSource 进行国际化:BeanFactory 是不支持国际化功能的,而 ApplicationContext 扩展了 MessageResource接口,因而具有消息处理的能力(i18N)
  • 强大的事件机制(Event):基本上牵涉到事件(Event)方面的设计,就离不开观察者模式。ApplicationContext 的事件机制主要通过 ApplicationEvent 和 AplicationListener 这两个接口来提供的,和 java swing 中的事件机制一样。即当 ApplicationContext 中发布一个事件的时,所有扩展了 ApplicationListener 的 Bean 都将会接受到这个事件,并进行相应的处理。
  • 底层资源的访问:ApplicationContext 扩展了 ResourceLoader(资源加载器)接口,从而可以用来加载多个 Resource,而 BeanFactory 没有扩展 ResourceLoader
  • BeanFactroy 采用的是延迟加载形式来注入 Bean 的,即只有在使用到某个Bean 时,才对该 Bean 进行加载实例化,这样,我们就不能发现一些存在的 Spring 的配置问题。而 ApplicationContext 则相反,它是在容器启动时,一次性创建了所有的 Bean。这样,在容器启动时,我们就可以发现 Spring中存在的配置错误。

(3)WebApplication 体系架构

WebApplicationContext 是专门为 Web 应用准备的,它允许从相对于 Web 根目录的
路径中装载配置文件完成初始化工作。

从 WebApplicationContext 中可以获得ServletContext 的引用,整个 Web 应用上下文对象将作为属性放置到 ServletContext 中,以便 Web 应用环境可以访问 Spring 应用上下文。
在这里插入图片描述

7. Spring bean

(1)Spring bean 的作用域

singleton:单例模式(多线程下不安全)

Spring IOC 容器中只会存在一个共享的 Bean 实例,无论有多少个Bean 引用它,始终指向同一对象。该模式在多线程下是不安全的。是Spring的默认作用域

<bean id="xx" class="com.ioc.xx" scope="singleton"/>
prototype:原型模式(每次使用时创建)

每次通过 Spring 容器获取 prototype 定义的 bean 时,容器都将创建一个新的 Bean 实例,每个 Bean 实例都有自己的属性和状态,而 singleton 全局只有一个对象。

  • 对有状态的bean使用prototype作用域
  • 对无状态的bean使用singleton作用域
request 一次请求一个实例

在一次 Http 请求中,容器会返回该 Bean 的同一实例。而对不同的 Http 请求则会
产生新的 Bean,而且该 bean 仅在当前 Http Request 内有效,当前 Http 请求结束,该 bean实例也将会被销毁。

<bean id="loginAction" class="com.cnblogs.Login" scope="request"/>
session 一次会话一个实例

在一次 Http Session 中,容器会返回该 Bean 的同一实例。而对不同的 Session 请
求则会创建新的实例,该 bean 实例仅在当前 Session 内有效。同 Http 请求相同,每一次session 请求创建新的实例,而不同的实例之间不共享属性,且实例仅在自己的 session 请求内有效,请求结束,则实例将被销毁。

<bean id="shoppingcar" class="com.ioc.shoppingcar" scope="session"/>
global Session 全局会话

在一个全局的 Http Session 中,容器会返回该 Bean 的同一个实例,仅在使用 portlet context 时有效。

(2)Spring Bean 的生命周期

在这里插入图片描述

实例化

实例化一个 Bean,也就是我们常说的 new。

IOC 依赖注入

按照 Spring 上下文对实例化的 Bean 进行配置,也就是 IOC 注入。

setBeanName 实现

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

BeanFactoryAware 实现

如果这个 Bean 已经实现了 BeanFactoryAware 接口,会调用它实现的 setBeanFactory,setBeanFactory(BeanFactory)传递的是 Spring 工厂自身

可以用这个方式来获取其它 Bean,只需在 Spring 配置文件中配置一个普通的 Bean 就可以

ApplicationContextAware 实现

如果这个 Bean 已经实现了 ApplicationContextAware 接口,会调用setApplication-Context(ApplicationContext)方法,传入 Spring 上下文

同样这个方式也可以实现步骤 4 的内容,但比 4 更好,因为 ApplicationContext 是 BeanFactory 的子接口,有更多的实现方法

postProcessBeforeInitialization 接口实现-初始化预处理

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

init-method

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

postProcessAfterInitialization

如果这个 Bean 关联了 BeanPostProcessor 接口,将会调用postProcessAfter-Initialization(Object obj, String s)方法。

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

Destroy 过期自动清理阶段

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

destroy-method 自配置清理

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

bean 标签有两个重要的属性(init-method 和 destroy-method)。用它们可以自己定制初始化和注销方法。它们也有相应的注解(@PostConstruct 和@PreDestroy)。
< bean id="" class="" init-method=“初始化方法” destroy-method=“销毁方法”>

8. IOC原理(java 反射机制)

所谓控制反转是指,本来被调用者的实例是由调用者来创建的,这样的缺点是耦合性太强,IOC 则是统一交给 spring 来管理创建,将对象交给容器管理,你只需要在 spring 配置文件总配置相应的 bean,以及设置相关的属性,让 spring 容器来生成类的实例对象以及管理对象。

在 spring 容器启动的时候,spring 会把你在配置文件中配置的 bean 都初始化好,然后在你需要调用的时候,就把它已经初始化好的那些 bean 分配给你需要调用这些 bean 的类。

IoC 的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过 DI(Depndency Injection,依赖注入)来实现的。

比如对象 A 需要操作数据库,以前我们总是要在 A 中自己编写代码来获得一个Conection 对象,有了 spring 我们就只需要告诉 spring,A 中需要一个 Conection,至于这个 Conection 怎么构造,何时构造,A 不需要知道。在系统运行时,spring 会在适当的时候制造一个 Conection,然后像打针一样,注射到 A 当中,这样就完成了对各个对象之间关系的控制。A 需要依赖 Conection 才能正常运行,而这个 Conection 是由 spring 注入到 A 中的,依赖注入的名字就这么来的。

那么 DI 是如何实现的呢? Java 1.3 之后一个重要特征是反射(reflction),它允许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性,spring 就是通过反射来实现注入的。

(1)Spring 依赖注入的方式

构造器注入
/*带参数,方便利用构造器进行注入*/ 
public CatDaoImpl(String message){ 
	this. message = message;  
} 
<bean id="CatDaoImpl" class="com.CatDaoImpl"> 
	<constructor-arg value=" message "></constructor-arg> 
</bean>
setter 方法注入
public class Id { 
 
private int id; 
 
public int getId() { return id; } 
 
public void setId(int id) { this.id = id; } 
} 
<bean id="id" class="com.id "> 
	<property name="id" value="123">
	</property>
</bean> 
静态工厂注入

不能直接通过"工程类.静态方法()"来获取对象,而是依然通过 spring 注入的形式获取

public class DaoFactory { //静态工厂

	public static final FactoryDao getStaticFactoryDaoImpl(){ 
		return new StaticFacotryDaoImpl(); 
	} 
} 

public class SpringAction {  

	private FactoryDao staticFactoryDao; //注入对象
 
	//注入对象的 set 方法 
	public void setStaticFactoryDao(FactoryDao staticFactoryDao) { 
		this.staticFactoryDao = staticFactoryDao; 
	} 
}
<!--此处获取对象的方式是从工厂类中获取静态方法--> 
<bean name="staticFactoryDao" class="DaoFactory" factory-method="getStaticFactoryDaoImpl"></bean>

<!--使用静态工厂的方法注入对象,对应上面的配置文件--> 
<bean name="springAction" class=" SpringAction" > 	
	<property name="staticFactoryDao" ref="staticFactoryDao"></property> 
</bean> 
实例工厂

需要首先 new 工厂类,再调用普通的实例方法

public class DaoFactory { //实例工厂
 
	public FactoryDao getFactoryDaoImpl(){ 
		return new FactoryDaoImpl(); 
	} 
} 

public class SpringAction {  
	private FactoryDao factoryDao; 
	
	//注入对象 
	public void setFactoryDao(FactoryDao factoryDao) { 
		this.factoryDao = factoryDao; 
	} 
} 
 <!--此处获取对象的方式是从工厂类中获取实例方法--> 
<bean name="daoFactory" class="com.DaoFactory"></bean> 

<bean name="factoryDao" factory-bean="daoFactory" factory-method="getFactoryDaoImpl"></bean> 

<!--使用实例工厂的方法注入对象,对应上面的配置文件--> 
<bean name="springAction" class="SpringAction"> 	
	<property name="factoryDao" ref="factoryDao"></property> 	
</bean> 

(2)Spring 自动装配的方式

  • 手动装配
    • 基于 xml 装配
    • 构造方法
    • setter 方法
  • 静态装配
    • no:默认的方式是不进行自动装配,通过显式设置 ref 属性来进行装配
    • byName:通过参数名 自动装配,Spring 容器在配置文件中发现 bean 的 autowire 属性被设置成 byname,之后容器试图匹配、装配和该 bean 的属性具有相同名字的 bean。
    • byType:通过参数类型自动装配,Spring 容器在配置文件中发现 bean 的 autowire 属性被设置成 byType,之后容器试图匹配、装配和该 bean 的属性具有相同类型的 bean。如果有多个 bean 符合条件,则抛出错误。
    • constructor:这个方式类似于 byType, 但是要提供给构造器参数,如果没有确定的带参数的构造器参数类型,将会抛出异常。
    • autodetect:首先尝试使用 constructor 来自动装配,如果无法工作,则使用 byType 方式。

9. AOP原理(代理模式)

“横切"指剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect”,即切面。

所谓"切面",简单说就是把那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。

使用"横切"技术,AOP 把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处基本相似,比如权限认证、日志、事物。AOP 的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。

  • 横切关注点:对哪些方法进行拦截,拦截后怎么处理等关注点
  • 切面(aspect):类是对物体特征的抽象,切面就是对“横切关注点”的抽象
  • 连接点(joinpoint):程序执行中某个特定的被拦截到的点。 Spring 只支持方法类型的连接点,在 Spring中连接点指的就是被拦截到的方法,实际上连接点还可以是字段或者构造器
  • 切入点(pointcut):匹配“连接点”的断言,切入点表达式和“通知”关联,“通知”在满足该切入点的“连接点”运行。Spring默认使用AspectJ切入点语法
  • 通知(advice):所谓通知指的就是指拦截到连接点之后要执行的代码,通知分为前置、后置、异常、最终、环绕通知五类。在Spring中以拦截器作为通知模型,并维护一个以连接点为中心的拦截器链
  • 代理对象(Proxy Object):由AOP框架创建的对象,用来实现切面契约。在Spring中可以是JDK动态代理或CGLIB动态代理
  • 目标对象(Target Object):也叫“被通知对象”,指被一个或多个切面所通知的对象,是Spring中的被代理对象
  • 织入(weave):将切面应用到目标对象并导致代理对象创建的过程,可以在编译时、类加载时、运行时完成。Spring是在运行时完成织入
  • 引入(introduction):在不修改代码的前提下,在运行期为类动态地添加一些额外的方法或属性。Spring允许引入新的接口及其对应实现到任何被代理的对象

(1)应用场景

  • Authentication 权限
  • Caching 缓存
  • Context passing 内容传递
  • Error handling 错误处理
  • Lazy loading 懒加载
  • Debugging 调试
  • logging, tracing, profiling and monitoring 记录,跟踪,优化,校准
  • Performance optimization 性能优化
  • Persistence 持久化
  • Resource pooling 资源池
  • Synchronization 同步
  • Transactions 事务

(2)代理过程

在这里插入图片描述

  1. 调用者 Bean 尝试调用目标方法,但是被生成的代理拦截
  2. 代理根据 Advice 的种类,对 Advice 首先进行调用
  3. 代理调用目标方法返回调用结果给调用者 Bean(由代理返回,没有体现在图中)

(3)代理方式

代理接口和类

在 java 的动态代理机制中,有两个重要的类或接口,一个是 InvocationHandler(Interface)、另一个则是 Proxy(Class),这两个类和接口是实现我们动态代理所必须用到的。

InvocationHandler:

每一个动态代理类都必须要实现 InvocationHandler 这个接口,并且每个代理类的实例都关联到了一个 handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由 InvocationHandler 这个接口的 invoke 方法来进行调用。

通过实现该接口可以定义横切逻辑,并通过反射机制调用目标类的代码,动态将横切逻辑和业务逻辑编制在一起:

Object invoke(Object proxy, Method method, Object[] args) throws Throwable
//proxy: 指代我们所代理的那个真实对象
//method: 指代的是我们所要调用真实对象的某个方法的 Method 对象
//args: 指代的是调用真实对象某个方法时接受的参数
Proxy:

Proxy 利用 InvocationHandler 动态创建一个符合某一接口的实例,生成目标类的代理对象,它提供了许多的方法,但是我们用的最多的就是 newProxyInstance 这个方法:

public staic Object newProxyInstance(ClasLoader loader, Clas<?>[] interfaces, InvocationHandler h) throws IlegalArgumentException
//loader: 一个 ClassLoader 对象,定义了由哪个 ClassLoader 对象来对生成的代理对象进行加载
//interfaces: 一个 Interface 对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口
//如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
//h:表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler 对象上
  1. 通过 Proxy 的 ProxyInstance 类创建出一个代理类,这个代理类执行的关于它代理的对象(真正的对象)的方法(代理类可以自己定义自己的方法,要区别)
  2. 通过一个InvocationHandler,InvocationHandler 是一个接口,接口中有 invoke 方法,invoke 方法关联到一个真正的对象,然后去执行真正对象的方法,来实现代理
实现子类的动态代理

Spring 提供了两种方式来生成代理对象: JDKProxy 和 Cglib,具体使用哪种方式生成由
AopProxyFactory 根据 AdvisedSupport 对象的配置来决定。

默认的策略是如果目标类是接口,则使用 JDK 动态代理技术,否则使用 Cglib 来生成代理。

JDK 动态接口代理

JDK 动态代理主要涉及到 java.lang.reflect 包中的两个类:Proxy 和InvocationHandler。

CGLib 动态代理(Code Generation Library)

一个强大的高性能,高质量的代码生成类库,可以在运行期扩展 Java 类与实现 Java 接口,CGLib 封装了 asm,可以再运行期动态生成新的 class。

CGlib 动态代理是通过继承业务类,生成的动态代理类是业务类的子类,通过重写业务方法进行代理,因此无法代理final方法

  • JDK :只能为接口创建代理实例,创建代理对象的速度快
  • CGLib :可以为业务类创建代理实例,性能高;适合不需要频繁创建代理对象

(4)通知类型

  • 前置通知(Before Advice):在连接点之前执行,通知不能阻止连接点之前的执行流程
  • 后置通知(After Returning Advice):在连接点正常完成之后执行
  • 异常通知(After Throwing Advice):在连接点抛出异常之后执行
  • 最终通知(After Advice):在连接点退出后执行,不论是否正常退出
  • 环绕通知(Around Advice):包围一个连接点的通知,在方法调用前后执行,可以选择是否继续执行连接点或者结束执行
//切面
@Aspect
public class TransactionDemo {
  
  	//切入点(连接点的断言)
	@Pointcut(value="execution(* com.test.service.*.*.*(..))")
	public void point(){ 	}
	 
	 //前置通知
	@Before(value="point()")
	public void before(){	 
		System.out.println("transaction begin"); 
	}
	 
	 //后置通知
	@AfterReturning(value = "point()")
	public void after(){	 
		System.out.println("transaction commit"); 
	}
	
	//环绕通知 
	@Around("point()")
	public void around(ProceedingJoinPoint joinPoint) throws Throwable{	 
		System.out.println("transaction begin"); 
		joinPoint.proceed(); 
		System.out.println("transaction commit"); 
	}
}

10. 事务

(1)事务的实现方式

  • 编程式事务管理对基于 POJ 的应用来说是唯一选择。我们需要在代码中调用 beginTransaction()、comit()、rolback()等事务管理相关的方法
  • 基于 TransactionProxyFactoryBean 的声明式事务管理
  • 基于 @Transactional 的声明式事务管理
  • 基于 Aspectj AOP 配置事务

根据代理机制的不同,Spring 事务的配置又有几种不同的方式:

  • 每个 Bean 都有一个代理
  • 所有 Bean 共享一个代理基类
  • 使用拦截器
  • 使用 tx 标签配置的拦截器
  • 全注解

(2)事务的传播级别

  • PROPAGATION_REQUIRED ,默认的 spring 事务传播级别,使用该级别的特点是,如果上下文中已经存在事务,那么就加入到事务中执行,如果当前上下文中不存在事务,则新建事务执行。所以这个级别通常能满足处理大多数的业务场景。
  • PROPAGATION_NESTED ,nested,嵌套级别事务。该传播级别特征是,如果上下文中存在事务,则嵌套事务执行,如果不存在事务,则新建事务
  • PROPAGATION_SUPORTS ,从字面意思就知道,suports,支持,该传播级别的特点是,如果上下文存在事务,则支持事务加入事务,如果没有事务,则使用非事务的方式执行。所以说,并非所有的包在 transactionTemplate.execute 中的代码都会有事务支持。这个通常是用来处理那些并非原子性的非核心业务逻辑操作。应用场景较少。
  • PROPAGATION_MANDATORY , 该级别的事务要求上下文中必须要存在事务,否则就会抛出异常!配置该方式的传播级别是有效的控制上下文调用代码遗漏添加事务控制的保证手段。比如一段代码不能单独被调用执行,但是一旦被调用,就必须有事务包含的情况,就可以使用这个传播级别。
  • PROPAGATION_REQUIRES_NEW ,从字面即可知道,new,每次都要一个新事务,该传播级别的特点是,每次都会新建一个事务,并且同时将上下文中的事务挂起,执行当前新建事务完成以后,上下文事务恢复再执行。这是一个很有用的传播级别,举一个应用场景:现在有一个发送 10 个红包的操作,在发送之前,要做一些系统的初始化、验证、数据记录操作,然后发送 10 封红包,然后再记录发送日志,发送日志要求 100%的准确,如果日志不准确,那么整个父事务逻辑需要回滚。怎么处理整个业务需求呢?就是通过这个 PROPAGATION_REQUIRES_NEW 级别的事务传播控制就可以完成。发送红包的子事务不会直接影响到父事务的提交和回滚
  • PROPAGATION_NOT_SUPORTED ,这个也可以从字面得知,not suported ,不支持,当前级别的特点就是上下文中存在事务,则挂起事务,执行当前逻辑,结束后恢复上下文的事务。这个级别有什么好处?可以帮助你将事务极可能的缩小。我们知道一个事务越大,它存在的风险也就越多。所以在处理事务的过程中,要保证尽可能的缩小范围。比如一段代码,是每次逻辑操作都必须调用的,比如循环 10 次的某个非核心业务逻辑操作。这样的代码如果包在事务中,势必造成事务太大,导致出现一些难以考虑周全的异常情况。所以这个事务这个级别的传播级别就派上用场了。用当前级别的事务模板包起来就可以了。
  • PROPAGATION_NEVR ,该事务更严格,上面一个事务传播级别只是不支持而已,有事务就挂起,而 PROPAGATION_EVR 传播级别要求上下文中不能存在事务,一旦有事务,就抛出 run time 异常,强制停止执行!

(3)事务的嵌套

嵌套是子事务套在父事务中执行,子事务是父事务的一部分,在进入子事务之前,父事务建立一个回滚点,叫 save point,然后执行子事务,这个子事务的执行也算是父事务的一部分,然后子事务执行结束,父事务继续执行。

如果子事务回滚,父事务会回滚到进入子事务前建立的 save point,然后尝试其他的事务或者其他的业务逻辑,父事务之前的操作不会受到影响,更不会自动回滚。

如果父事务回滚,子事务也会跟着回滚!因为父事务结束之前,子事务是不会提交的,我们说子事务是父事务的一部分,正是这个道理。

那么:事务的提交时,子事务先提交,然后父事务再提交。子事务是父事务的一部分,由父事务统一提交。

11. 设计模式

参考博客:深入解析spring中用到的九种设计模式

简单工厂

又叫做静态工厂方法(StaicFactory Method)模式,简单工厂模式的实质是由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类。

spring 中的 BeanFactory 就是简单工厂模式的体现,根据传入一个唯一的标识来获得 bean 对象,但是在传入参数后创建还是传入参数前创建这个要根据具体情况来定。如下配置,就是在 HelloA 类中创建一个 ABean。

<beans>
    <bean id="singletonBean" class="com.HelloA ">
        <constructor-arg>
            <value>Hello! 这是singletonBean!<value>
        </constructor-arg>
   </ bean>
   <bean id="ABean" class="com.HelloA " singleton="false">
        <constructor-arg>
            <value>Hello! 这是ABean! <value>
        </constructor-arg>
    </bean>
 
</beans>

工厂方法(Factory Method)

通常由应用程序直接使用new创建新的对象,为了将对象的创建和使用相分离,采用工厂模式,即应用程序将对象的创建及初始化职责交给工厂对象。

一般情况下,应用程序有自己的工厂对象来创建bean.如果将应用程序自己的工厂对象交给Spring管理,那么Spring管理的就不是普通的bean,而是工厂Bean。

就以工厂方法中的静态方法为例讲解一下:

import java.util.Random;
public class StaticFactoryBean {
      public static Integer createRandom() {
           return new Integer(new Random().nextInt());
       }
}

建一个config.xm配置文件,将其纳入Spring容器来管理,需要通过factory-method指定静态方法名称

//createRandom方法必须是static的,才能找到 scope="prototype"
<bean id="random" class="example.chapter3.StaticFactoryBean" factory-method="createRandom" />

测试:

public static void main(String[] args) {
      //调用getBean()时,返回随机数.如果没有指定factory-method,会返回StaticFactoryBean的实例,即返回工厂Bean的实例       
      XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("config.xml"));
      System.out.println("我是IT学习者创建的实例:"+factory.getBean("random").toString());
}

单例模式(Singleton)

保证一个类仅有一个实例,并提供一个访问它的全局访问点。

spring 中的单例模式完成了后半句话,即提供了全局的访问点 BeanFactory。但没有从构造器级别去控制单例,这是因为 spring 管理的是任意的 java 对象。

Spring 下默认的 bean 均为 singleton,可以通过 singleton=“true|false”或scope=“?”来指定

适配器(Adapter)

在 Spring 的 AOP 中,使用 Advice(通知)来增强被代理类的功能。

Spring 实现 AOP 功能,对类进行方法级别的切面增强,即生成被代理类的代理类, 并在代理类的方法前,设置拦截器,通过执行拦截器重的内容增强了代理方法的功能,实现面向切面编程。

//Adapter类接口:Target
public interface AdvisorAdapter {
 
	boolean supportsAdvice(Advice advice);
 
    MethodInterceptor getInterceptor(Advisor advisor);
 
} 
//MethodBeforeAdviceAdapter类,Adapter
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
 
	public boolean supportsAdvice(Advice advice) {
		return (advice instanceof MethodBeforeAdvice);
      }
 
   	public MethodInterceptor getInterceptor(Advisor advisor) {
   		MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
      	return new MethodBeforeAdviceInterceptor(advice);
      }
 
}

包装器(Decorator)

在我们的项目中遇到这样一个问题:我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。

我们以往在spring和hibernate框架中总是配置一个数据源,因而sessionFactorydataSource属性总是指向这个数据源并且恒定不变,所有DAO在使用sessionFactory的时候都是通过这个数据源访问数据库。但是现在,由于项目的需要,我们的DAO在访问sessionFactory的时候都不得不在多个数据源中不断切换,问题就出现了:如何让sessionFactory在执行数据持久化的时候,根据客户的需求能够动态切换不同的数据源?我们能不能在spring的框架下通过少量修改得到解决?是否有什么设计模式可以利用呢?

首先想到在spring的applicationContext中配置所有的dataSource。这些dataSource可能是各种不同类型的,比如不同的数据库:Oracle、SQL Server、MySQL等,也可能是不同的数据源:比如apache 提供的org.apache.commons.dbcp.BasicDataSource、spring提供的org.springframework.jndi.JndiObjectFactoryBean等。

然后sessionFactory根据客户的每次请求,将dataSource属性设置成不同的数据源,以到达切换数据源的目的。

spring中用到的包装器模式在类名上有两种表现:一种是类名中含有Wrapper,另一种是类名中含有Decorator。基本上都是动态地给一个对象添加一些额外的职责。

代理(Proxy)

为其他对象提供一种代理以控制对这个对象的访问。 从结构上来看和 Decorator 模式类似,但 Proxy 是控制,更像是一种对功能的限制,而 Decorator 是增加职责。

spring 的 Proxy 模式在 aop 中有体现,比如 JdkDynamicAopProxy 和Cglib2AopProxy。

观察者(Observer)

定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

spring 中 Observer 模式常用的地方是 listenr 的实现。如 AplicationListenr。

策略(Strategy)

定义一系列的算法,把它们一个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。

spring 中在实例化对象的时候用到 Strategy 模式,在 SimpleInstantiaonStrategy 中有如下代码说明了策略模式的使用情况:
在这里插入图片描述

模板方法(Template Method)

定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。Template Method 使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。Template Method 模式一般是需要继承的。

spring 中的 JdbcTemplate,在用这个类时并不想去继承这个类,因为这个类的方法太多,但是我们还是想用到 JdbcTemplate 已有的稳定的、公用的数据库连接,那么我们怎么办呢?

我们可以把变化的东西抽出来作为一个参数传入 JdbcTemplate 的方法中。但是变化的东西是一段代码,而且这段代码会用到 JdbcTemplate 中的变量。怎么办?

那我们就用回调对象吧。在这个回调对象中定义一个操纵 JdbcTemplate 中变量的方法,我们去实现这个方法,就把变化的东西集中到这里了。然后我们再传入这个回调对象到 JdbcTemplate,从而完成了调用。这可能是 Template Method 不需要继承的另一种实现方式吧。

在这里插入图片描述

二:Spring MVC

Spring MVC 是一个基于 MVC 架构的用来简化 web 应用程序开发的应用开发框架,它是 Spring 的一个模块,无需中间整合层来整合 ,它和 Struts2 一样都属于表现层的框架。

在 web 模型中,MVC 是一种很流行的框架,通过把 Model,View,Controler 分离,把较为复杂的 web 应用分成逻辑清晰的几部分,简化开发,减少出错,方便组内开发人员之间的配合。

1. 工作流程

在这里插入图片描述

  • Http 请求到 前端控制器:客户端请求提交到 DispatcherServlet。
  • HandlerMapping 寻找处理器映射器:由 DispatcherServlet 控制器查询一个或多个HandlerMapping,根据请求 url 找到具体的处理器,生成处理器对象及处理器拦截器(如果有)一并返回给 DispatcherSvlet
  • .DispatcherServlet 通过 HandlerAdapter 处理器适配器调用处理器:DispatcherServlet 将请求提交到 Controller后端控制器
  • 调用业务处理和返回结果:Controller 调用业务逻辑处理后,返回 ModelAndView
  • HandlerAdapter 将 controler 执行结果 ModelAndView 返回给 DispatcherServlet
  • 处理视图映射: DispatcherServlet 查询一个或多个 ViewResoler 视图解析器
  • 返回模型:ViewReslover 解析后返回具体 View
  • DispatcherServlet 对 View 进行渲染视图(即将模型数据填充至视图中)
  • DispatcherServlet反馈浏览器,Http 响应,视图负责将结果显示到客户端。

2. 加载流程

  1. Servlet 加载(监听器之后即执行)Servlet 的 init()
  2. 加载配置文件
  3. 从 ServletContext 拿到 spring 初始化 springmvc 相关对象
  4. 放入 ServletContext

3. MVC 常用注解

在这里插入图片描述

4. Spring MVC 和 Struts2 的区别

  • springmvc 的入口是一个 servlet 即前端控制器(DispatchServlet),而 struts2 入口是一个filter 过虑器(StrutsPrepareAndExecuteFilter)。
  • springmvc 是基于方法开发(一个 url 对应一个方法),请求参数传递到方法的形参,可以设计为单例或多例(建议单例),struts2 是基于类开发,传递参数是通过类的属性,只能设计为多例。
  • Struts 采用值栈存储请求和响应的数据,通过 OGNL 存取数据,springmvc 通过参数解析器是将request 请求内容解析,并给方法形参赋值,将数据和视图封装成 ModelAndView 对象,最后又将 ModelAndView 中的模型数据通过 reques 域传输到页面。Jsp 视图解析器默认使用 jstl。

三:Spring Boot

Spring Boot 是由 Pivotal 团队提供的全新框架,其设计目的是用来简化新 Spring 应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置。

通过这种方式,Spring Boot 致力于在蓬勃发展的快速应用开发领域(rapid application development)成为领导者。其特点如下:

  • 创建独立的 Spring 应用程序
  • 嵌入 Tomcat,无需部署 WAR 文件
  • 简化 Maven 配置
  • 自动配置 Spring
  • 提供生产就绪型功能,如指标,健康检查和外部配置
  • 绝对没有代码生成和对 XML 没有要求配置

猜你喜欢

转载自blog.csdn.net/weixin_36904568/article/details/90765945
今日推荐