SpringFrameWork学习
Spring FrameWork;先上图:(PS-本人为C#码农,Java学习中,所以细节尽量描述详细,高手请忽略)
一. Core Container(核心容器)-Beans
核心容器包括Beans、Core、Context、SpEL组成,其中Beans、Core为基础部分,提供IOC(控制反转)与依赖注入特性,本节主要说明Beans;
何为Beans,上图:
Bean配置信息定义了Bean的实现及依赖关系,Spring容器根据各种形式的Bean配置信息在容器内部建立Bean定义注册表,然后根据注册表加载、实例化Bean,并建立Bean和Bean的依赖关系,最后将这些准备就绪的Bean放到Bean缓存池中,以供外层的应用程序进行调用。更详细资料
如何使用Bean,如何将类或属性注入进Bean,下面只描述自动装配:
组件扫描(component scanning):Spring会自动发现应用上下文中所创建的bean。
自动装配(autowiring):Spring自动满足bean之间的依赖。
[email protected] 此注解表明该类会作为组件类,并告知Spring要为这个类创建bean。
此时Sgtpeppers类已经声明其应该被Beans所管理,但是组建扫描需要显示触发;
[email protected] 启用组建扫描;
@ComponentScan默认会扫描与配置类相同的包。因为CDPlayerConfig类位于soundsystem包中,因此Spring将会扫描这个包以及这个包下的所有子包,查找带有@Component注解的类。这样的话,就能发现CompactDisc,并且会在Spring中自动为其创建一个bean。
如果你更倾向于使用XML来启用组件扫描的话,那么可以使用Spring context命名空间的、<context:component-scan>元素。
PS:@Configuration 用于定义配置类,可替换xml配置文件,被注解的类内部包含有一个或多个被@Bean注解的方法,这些方法将会被AnnotationConfigApplicationContext或AnnotationConfigWebApplicationContext类进行扫描,并用于构建bean定义,初始化Spring容器。
[email protected] 当Spring创建CDPlayerbean的时候,会通过这个构造器来进行实例化并且会传入一个可设置给CompactDisc类型的bean。@Autowired注解不仅能够用在构造器上,还能用在属性的Setter方法上。
如果没有匹配的bean,那么在应用上下文创建的时候,Spring会抛出一个异常。为了避免异常的出现,你可以将@Autowired的required属性设置为false:
将required属性设置为false时,Spring会尝试执行自动装配,但是如果 没有匹配的bean的话,Spring将会让这个bean处于未装配的状态。但是,把required属性设置为false时,你需要谨慎对待。如果在你的代码中没有进行null检查的话,这个处于未装配状态的属性有可能会出现NullPointerException。
如果有多个bean都能满足依赖关系的话,Spring将会抛出一个异常,表明没有明确指定要选择哪个bean进行自动装配。
- 验证
更高级以及更详细的的则不作赘述;
二. 面向切面
1.为什么要面向切面?
上图展示了一个典型应用,每个模块有自己的横向业务,但是又需要使用纵向业务的功能,比如安全、日志等这些类被称为切面(aspect)。如果要重用通用功能的话,最常见的面向对象技术是继承(inheritance)或委托(delegation)继承往往会导致一个脆弱的对象体系;而使用委托可能需要对委托对象进行复杂的调用。与大多数技术一样,AOP已经形成了自己的术语。描述切面的常用术语有通知(advice)、切点(pointcut)和连接点(join point)。下图展示了这些概念是如何关联在一起的。
2. 通知(Advice)
可以理解为切面所需要做的任务;
Spring切面可以应用5种类型的通知:
- 前置通知(Before):在目标方法被调用之前调用通知功能;
- 后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么;
- 返回通知(After-returning):在目标方法成功执行之后调用通知;
- 异常通知(After-throwing):在目标方法抛出异常后调用通知;
- 环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。
3. 连接点(Join Point)
连接点是在应用执行过程中能够插入切面的一个点。这个点可以是调用方法时、抛出异常时、甚至修改一个字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。
4.切点(Poincut)
如果说通知定义了切面的“什么”和“何时”的话,那么切点就定义了“何处”。切点的定义会匹配通知所要织入的一个或多个连接点。我们通常使用明确的类和方法名称,或是利用正则表达式定义所匹配的类和方法名称来指定这些切点。有些AOP框架允许我们创建动态的切点,可以根据运行时的决策(比如方法的参数值)来决定是否应用通知。
5.切面(Aspect)
切面是通知和切点的结合。通知和切点共同定义了切面的全部内容——它是什么,在何时和何处完成其功能。
6.引入(Introduction)
我们可以创建一个Auditable通知类,该类记录了对象最后一次修改时的状态。这很简单,只需一个方法,setLastModified(Date),和一个实例变量来保存这个状态。然后,这个新方法和实例变量就可以被引入到现有的类中,从而可以在无需修改这些现有的类的情况下,让它们具有新的行为和状态。
7.织入(Weaving)
织入是把切面应用到目标对象并创建新的代理对象的过程。切面在指定的连接点被织入到目标对象中。在目标对象的生命周期里有多个点可以
进行织入:
- 编译期:切面在目标类编译时被织入。这种方式需要特殊的编译器。AspectJ的织入编译器就是以这种方式织入切面的。
- 类加载期:切面在目标类加载到JVM时被织入。这种方式需要特殊的类加载器(ClassLoader),它可以在目标类被引入应用之前增强该目标类的字节码。AspectJ 5的加载时织入(load-time weaving,LTW)就支持以这种方式织入切面。
- 运行期:切面在应用运行的某个时刻被织入。一般情况下,在织入切面时,AOP容器会为目标对象动态地创建一个代理对象。SpringAOP就是以这种方式织入切面的。
简单描述下运行期的代理对象为何物?
先看流程图:
通过在代理类中包裹切面,Spring在运行期把切面织入到Spring管理的bean中。如图所示,代理类封装了目标类,并拦截被通知方法的调用,再把调用转发给真正的目标bean。当代理拦截到方法调用时,在调用目标bean方法之前,会先执行切面逻辑。直到应用需要使用被代理的bean时,Spring才创建代理对象。如果使用的是ApplicationContext的话,在ApplicationContext从BeanFactory中加载所有bean的时候,Spring才会创建被代理的对象。因为Spring运行时才创建代理对象,所以我们不需要特殊的编译器来织入Spring AOP的切面。
8.那Spring中如何实现这些功能呢?
1.首先Spring只支持方法级别的连接点,下图的指示器为Spring支持的AspectJ指示器,使用其他指示器会报错;
只有execution指示器为实际执行匹配的,其他指示器均为限制指示器,execution也是最常用的指示器;我们开始编写第一个切点;先建立一个接口;
这个接口就有一个方法,我们需要对这个方法执行时触发通知的调用;
我们使用两个点号(…)表明切点要选择任意的perform()方法,无论该方法的入参是什么。
限定ID为woodstock,或者除了这个Id以外的进行监听;
9.编写切面
代码示例:
使用**@Aspect注解声明这个类为一个切面**,在方法上增加上节说的限制条件;
这里返现,在不同的方法上虽然有的是在之前调用,有的在失败时调用但是都传入了同一个切点所以如何定义切点,以便复用呢?
通过@Pointcut 定义切点,通过performance()去使用切点,不管给切面增加了多少任务,但是它还是POJO的,它可以像一个普通类那样进行单元测试;
如果你就此止步的话,Audience只会是Spring容器中的一个bean。即便使用了AspectJ注解,但它并不会被视为切面,这些注解不会解析,也不会创建将其转换为切面的代理。
如果你使用JavaConfig的话,可以在配置类的类级别上通过使用EnableAspectJ-AutoProxy注解启用自动代理功能。下图展示了如何在JavaConfig中启用自动代理。