Spring 框架的核心技术(五)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring 框架的核心技术(五)相关的知识,希望对你有一定的参考价值。

Spring

5.6. 选择要使用的 AOP 声明样式

一旦你确定一个方面是实现给定的最佳方法 要求,您如何决定在使用Spring AOP或AspectJ之间以及在 方面语言(代码)样式,@AspectJ注释样式,还是Spring XML样式?这些 决策受许多因素的影响,包括应用程序要求, 开发工具,以及团队对 AOP 的熟悉程度。

5.6.1. 弹簧 AOP 还是全方面 J?

使用最简单的方法。Spring AOP比使用完整的AspectJ更简单,因为 无需将 AspectJ 编译器/编织者引入您的开发中 和构建流程。如果您只需要建议在 Spring 上执行操作 豆,春季AOP是正确的选择。如果您需要建议未由 Spring 容器(例如域对象,通常),您需要使用 方面J.如果您希望建议除 简单的方法执行(例如,字段获取或设置连接点等)。

当您使用 AspectJ 时,您可以选择 AspectJ 语言语法(也称为 “代码样式”)或@AspectJ注释样式。显然,如果你不使用Java。 5+,已经为您做出了选择:使用代码样式。如果方面发挥很大 在您的设计中的角色,并且您可以使用AspectJ Eclipse的开发工具(AJDT)插件,AspectJ语言语法是 首选选项。它更干净,更简单,因为语言是有目的地的 专为写作方面而设计。如果您不使用 Eclipse 或只有几个方面 在您的应用程序中不发挥重要作用,您可能需要考虑使用 @AspectJ风格,坚持在 IDE 中进行常规 Java 编译,并添加 构建脚本的方面编织阶段。

5.6.2. Spring AOP 的@AspectJ或 XML?

如果您选择使用 Spring AOP,则可以选择@AspectJ或 XML 样式。 需要考虑各种权衡。

XML风格可能是现有Spring用户最熟悉的,它由真正的支持。 波约。当使用 AOP 作为配置企业服务的工具时,XML 可能是一个很好的 选择(一个好的测试是你是否认为切入点表达式是你的一部分 您可能希望单独更改的配置)。使用 XML 样式,它是 可以说,从您的配置中可以更清楚地了解系统中存在哪些方面。

XML 样式有两个缺点。首先,它没有完全封装 在一个地方实现它所解决的需求。干燥原则说 任何部分都应该有一个单一的、明确的、权威的表示 系统内的知识。使用 XML 样式时,了解如何要求 实现在支持Bean类和XML的声明中被拆分 配置文件。使用@AspectJ样式时,将封装此信息 在单个模块中:方面。其次,XML 样式在以下方面受到更多限制 它可以表达比@AspectJ样式:只有“单例”方面实例化模型 受支持,并且无法组合在 XML 中声明的命名切入点。 例如,在@AspectJ样式中,您可以编写如下内容:

@Pointcut("execution(* get*())")
public void propertyAccess()

@Pointcut("execution(org.xyz.Account+ *(..))")
public void operationReturningAnAccount()

@Pointcut("propertyAccess() && operationReturningAnAccount()")
public void accountPropertyAccess()

在 XML 样式中,可以声明前两个切入点:

<aop:pointcut id="propertyAccess"
expression="execution(* get*())"/>

<aop:pointcut id="operationReturningAnAccount"
expression="execution(org.xyz.Account+ *(..))"/>

XML 方法的缺点是无法通过组合这些定义来定义点切。​​accountPropertyAccess​

@AspectJ样式支持其他实例化模型和更丰富的切入点 组成。它的优点是将方面保留为模块化单元。它还具有 @AspectJ方面的优点是可以理解(从而消耗)两者 春季AOP和AspectJ。因此,如果您后来决定需要AspectJ的功能 要实现其他要求,您可以轻松迁移到经典的 AspectJ 设置。 总的来说,Spring 团队更喜欢自定义方面的@AspectJ风格,而不仅仅是简单 企业服务的配置。

5.7. 混合宽高比类型

完全可以通过使用自动代理支持混合@AspectJ样式方面, 模式定义的方面,声明的顾问,甚至代理 以及相同配置中其他样式的拦截器。所有这些都已实现 通过使用相同的底层支持机制,可以毫无困难地共存。​​<aop:aspect>​​​​<aop:advisor>​

5.8. 代理机制

Spring AOP 使用 JDK 动态代理或 CGLIB 为给定的代理创建代理 目标对象。JDK 动态代理内置于 JDK 中,而 CGLIB 是常见的 开源类定义库(重新打包到)。​​spring-core​

如果要代理的目标对象实现至少一个接口,则 JDK 动态 使用代理。目标类型实现的所有接口都是代理的。 如果目标对象未实现任何接口,则会创建 CGLIB 代理。

如果要强制使用 CGLIB 代理(例如,代理每个方法 为目标对象定义,而不仅仅是由其接口实现的对象), 你可以这样做。但是,您应该考虑以下问题:

  • 使用 CGLIB,不能建议使用方法,因为它们不能在 运行时生成的子类。final
  • 从 Spring 4.0 开始,代理对象的构造函数不再被调用两次, 因为 CGLIB 代理实例是通过 Objenesis 创建的。仅当您的 JVM 这样做时 不允许绕过构造函数,您可能会看到双重调用和 来自 Spring AOP 支持的相应调试日志条目。

要强制使用 CGLIB 代理,请设置属性的值 元素为 true,如下所示:​​proxy-target-class​​​​<aop:config>​

<aop:config proxy-target-class="true">
<!-- other beans defined here... -->
</aop:config>

要在使用 @AspectJ 自动代理支持时强制 CGLIB 代理,请将元素的属性设置为 如下:​​proxy-target-class​​​​<aop:aspectj-autoproxy>​​​​true​

<aop:aspectj-autoproxy proxy-target-class="true"/>

5.8.1. 了解 AOP 代理

Spring AOP 是基于代理的。掌握 在你写自己的方面或使用任何方面之前,最后一句话实际上意味着什么 Spring 框架提供的基于 Spring AOP 的方面。

首先考虑以下情况:您有一个普通的、未代理的、 没有什么特别的,直接的对象引用,如下所示 代码片段显示:

public class SimplePojo implements Pojo 

public void foo()
// this next method invocation is a direct call on the this reference
this.bar();


public void bar()
// some logic...

如果在对象引用上调用方法,则直接在 该对象引用,如下图所示和列表所示:

Spring

public class Main 

public static void main(String[] args)
Pojo pojo = new SimplePojo();
// this is a direct method call on the pojo reference
pojo.foo();

当客户端代码具有的引用是代理时,情况会略有变化。考虑 下图和代码片段:

Spring

public class Main 

public static void main(String[] args)
ProxyFactory factory = new ProxyFactory(new SimplePojo());
factory.addInterface(Pojo.class);
factory.addAdvice(new RetryAdvice());

Pojo pojo = (Pojo) factory.getProxy();
// this is a method call on the proxy!
pojo.foo();

这里要了解的关键是方法中的客户端代码 的类具有对代理的引用。这意味着该方法调用 对象引用是对代理的调用。因此,代理可以委派给所有 与该特定方法调用相关的拦截器(建议)。然而 一旦调用最终到达目标对象(引用 在这种情况下),它可能对自身进行的任何方法调用(例如 asor)都将针对引用而不是代理调用。 这具有重要意义。这意味着不会产生自调用 在与方法调用相关的建议中,获得运行的机会。​​main(..)​​​​Main​​​​SimplePojo​​​​this.bar()​​​​this.foo()​​​​this​

好的,那么该怎么办呢?最佳方法(使用术语“最佳” 松散地在这里)是重构你的代码,这样就不会发生自调用。 这确实需要您做一些工作,但这是最好的、侵入性最小的方法。 下一个方法绝对是可怕的,我们犹豫是否要指出它,确切地说 因为它太可怕了。你可以(对我们来说很痛苦)完全将逻辑联系起来 在您的类中到 Spring AOP,如以下示例所示:

public class SimplePojo implements Pojo 

public void foo()
// this works, but... gah!
((Pojo) AopContext.currentProxy()).bar();


public void bar()
// some logic...

这完全将你的代码与Spring AOP耦合,它使类本身知道 它被用于 AOP 上下文的事实,这与 AOP 背道而驰。它 创建代理时还需要一些额外的配置,因为 以下示例显示:

public class Main 

public static void main(String[] args)
ProxyFactory factory = new ProxyFactory(new SimplePojo());
factory.addInterface(Pojo.class);
factory.addAdvice(new RetryAdvice());
factory.setExposeProxy(true);

Pojo pojo = (Pojo) factory.getProxy();
// this is a method call on the proxy!
pojo.foo();

最后,必须注意的是,AspectJ 没有这个自调用问题,因为 它不是基于代理的AOP框架。

5.9. @AspectJ代理的程序化创建

除了使用 anyor 声明配置中的方面外,还可以以编程方式创建代理 建议目标对象。有关 Spring 的 AOP API 的完整详细信息,请参阅下一章。在这里,我们要关注的是自动的能力 使用@AspectJ方面创建代理。​​<aop:config>​​​​<aop:aspectj-autoproxy>​

您可以使用类 为目标对象创建由一个或多个@AspectJ方面建议的代理。 此类的基本用法非常简单,如以下示例所示:​​org.springframework.aop.aspectj.annotation.AspectJProxyFactory​

// create a factory that can generate a proxy for the given target object
AspectJProxyFactory factory = new AspectJProxyFactory(targetObject);

// add an aspect, the class must be an @AspectJ aspect
// you can call this as many times as you need with different aspects
factory.addAspect(SecurityManager.class);

// you can also add existing aspect instances, the type of the object supplied must be an @AspectJ aspect
factory.addAspect(usageTracker);

// now get the proxy object...
MyInterfaceType proxy = factory.getProxy();

有关更多信息,请参阅javadoc。

5.10. 将 AspectJ 与弹簧应用程序一起使用

到目前为止,我们在本章中介绍的所有内容都是纯粹的春季AOP。在本节中, 我们将介绍如何使用 AspectJ 编译器或 weaver 而不是 or in 除了Spring AOP,如果你的需求超出了Spring AOP提供的设施 独自。

弹簧附带一个小型的AspectJ方面库,可在您的 分布为。您需要按顺序将其添加到类路径中 以使用其中的各个方面。使用 AspectJ 使用 Spring 依赖注入域对象,以及 AspectJ的其他 Spring 方面讨论 此库的内容以及如何使用它。使用 Spring IoC 配置 AspectJ 方面讨论了如何 依赖关系注入使用 AspectJ 编译器编织的 AspectJ 方面。最后,在弹簧框架中使用AspectJ进行加载时编织介绍了弹簧应用的加载时间编织 使用AspectJ。​​spring-aspects.jar​

5.10.1. 使用 AspectJ 通过 Spring 依赖注入域对象

Spring 容器实例化和配置应用程序中定义的 bean 上下文。也可以要求 Bean 工厂配置预先存在的 对象,给定包含要应用的配置的 Bean 定义的名称。包含利用此参数的注释驱动方面 允许任何对象的依赖关系注入的能力。该支持旨在 用于在任何容器控制之外创建的对象。域对象 通常属于此类别,因为它们通常是使用运算符或由ORM工具以编程方式创建的,作为数据库查询的结果。​​spring-aspects.jar​​​​new​

注释将类标记为符合弹簧驱动的条件 配置。在最简单的情况下,您可以纯粹将其用作标记注释,如 以下示例显示:​​@Configurable​

package com.xyz.myapp.domain;

import org.springframework.beans.factory.annotation.Configurable;

@Configurable
public class Account
// ...

当以这种方式用作标记接口时,Spring 会配置新的实例 注释类型(在本例中)通过使用 Bean 定义(通常 原型范围),与完全限定的类型名称同名 ().由于 Bean 的默认名称是 其类型的完全限定名称,这是声明原型定义的便捷方法 是省略属性,如以下示例所示:​​Account​​​​com.xyz.myapp.domain.Account​​​​id​

<bean class="com.xyz.myapp.domain.Account" scope="prototype">
<property name="fundsTransferService" ref="fundsTransferService"/>
</bean>

如果要显式指定要使用的原型 Bean 定义的名称,请 可以直接在注释中执行此操作,如以下示例所示:

package com.xyz.myapp.domain;

import org.springframework.beans.factory.annotation.Configurable;

@Configurable("account")
public class Account
// ...

Spring 现在寻找一个名为 bean 定义并将其用作 定义以配置新实例。​​account​​​​Account​

您还可以使用自动连线来避免在 都。要让 Spring 应用自动接线,请使用注释的属性。您可以按类型或名称指定 bothor for 自动布线, 分别。作为替代方法,最好指定显式的、注释驱动的 通过字段或方法级别对 bearbean 进行依赖注入(有关更多详细信息,请参阅基于注释的容器配置)。​​autowire​​​​@Configurable​​​​@Configurable(autowire=Autowire.BY_TYPE)​​​​@Configurable(autowire=Autowire.BY_NAME)​​​​@Configurable​​​​@Autowired​​​​@Inject​

最后,您可以在新的 使用属性创建和配置对象(例如,)。如果此属性为 设置为,Spring 在配置后验证所有属性( 不是基元或集合)已设置。​​dependencyCheck​​​​@Configurable(autowire=Autowire.BY_NAME,dependencyCheck=true)​​​​true​

请注意,单独使用注释不会执行任何操作。正是thein作用于存在 批注。从本质上讲,该方面说,“从初始化返回后 批注类型的新对象,配置新创建的对象 根据注释的属性使用弹簧”。在这种情况下, “初始化”是指新实例化的对象(例如,实例化的对象 与操作员)以及正在经历的对象 反序列化(例如,通过readResolve())。​​AnnotationBeanConfigurerAspect​​​​spring-aspects.jar​​​​@Configurable​​​​new​​​​Serializable​

为此,必须用AspectJ编织机编织带注释的类型。您可以 使用构建时 Ant 或 Maven 任务来执行此操作(例如,请参阅AspectJ 开发 环境指南​)或加载时编织(参见Spring 框架中的 Load-time Weaving with AspectJ​)。本身需要由 Spring 配置(为了获得 对用于配置新对象的 Bean 工厂的引用)。如果你 使用基于 Java 的配置,可以添加到任意类,如下所示:​​AnnotationBeanConfigurerAspect​​​​@EnableSpringConfigured​​​​@Configuration​

@Configuration
@EnableSpringConfigured
public class AppConfig

如果你更喜欢基于 XML 的配置,Spring上下文命名空间定义了一个方便的元素,你可以按如下方式使用它:​​context:spring-configured​

<context:spring-configured/>

在配置方面之前创建的对象的实例 导致向调试日志发出消息,并且没有配置 对象正在发生。一个例子可能是 Spring 配置中的 bean,它创建 域对象,当它由 Spring 初始化时。在这种情况下,您可以使用 bean 属性手动指定 bean 依赖于 配置方面。下面的示例演示如何使用属性:​​@Configurable​​​​depends-on​​​​depends-on​

<bean id="myService"
class="com.xzy.myapp.service.MyService"
depends-on="org.springframework.beans.factory.aspectj.AnnotationBeanConfigurerAspect">

<!-- ... -->

</bean>
单元测试对象​​@Configurable​

支持的目标之一是实现独立的单元测试 的域对象,没有与硬编码查找相关的困难。 iftype没有被AspectJ编织,注释没有影响 在单元测试期间。您可以在对象中设置模拟或存根属性引用 测试并照常进行。如果类型是由AspectJ编织的, 您仍然可以像往常一样在容器外部进行单元测试,但会看到警告 每次构造对象时的消息,指示它具有 不是由 Spring 配置的。​​@Configurable​​​​@Configurable​​​​@Configurable​​​​@Configurable​

使用多个应用程序上下文

用于实现支持 是一个方面J单例方面。单例方面的范围与范围相同 ofmembers:每个类加载器都有一个方面实例来定义类型。 这意味着,如果在同一类装入器中定义多个应用程序上下文 层次结构,您需要考虑在哪里定义 thebean 和 类路径的放置位置。​​AnnotationBeanConfigurerAspect​​​​@Configurable​​​​static​​​​@EnableSpringConfigured​​​​spring-aspects.jar​

考虑具有共享父应用程序的典型 Spring Web 应用程序配置 定义常见业务服务的上下文,以及支持这些服务所需的一切, 以及每个 servlet 的一个子应用程序上下文(其中包含特定的定义 到那个奴仆)。所有这些上下文都共存于同一个类装入器层次结构中, 因此,可以只引用其中之一。 在这种情况下,我们建议在共享中定义 thebean (父)应用程序上下文。这定义了您可能想要的服务 注入到域对象中。结果是您无法配置域对象 引用在子(特定于 servlet)上下文中定义的 bean,方法是使用 @Configurable机制(无论如何,这可能不是您想要做的事情)。​​AnnotationBeanConfigurerAspect​​​​@EnableSpringConfigured​

在同一容器中部署多个 Web 应用程序时,请确保每个 Web 应用程序使用自己的类加载器加载类型 (例如,通过放置)。仅添加到容器范围的类路径的 ifis (因此由共享父级加载) 类加载器),所有 Web 应用程序共享相同的方面实例(这可能是 不是你想要的)。​​spring-aspects.jar​​​​spring-aspects.jar​​​​WEB-INF/lib​​​​spring-aspects.jar​

5.10.2. AspectJ 的其他弹簧方面

除了方面,还包含一个方面J 可用于驱动 Spring 类型和方法事务管理的方面 用注释注释。这主要适用于以下用户: 想要在 Spring 容器之外使用 Spring 框架的事务支持。​​@Configurable​​​​spring-aspects.jar​​​​@Transactional​

解释注释的方面是。使用此方面时,必须注释 实现类(或该类中的方法或两者),而不是接口(如果 任何)类实现的。AspectJ遵循Java的规则,即注释 接口不是继承的。​​@Transactional​​​​AnnotationTransactionAspect​

类上的注释指定 执行类中的任何公共操作。​​@Transactional​

对类中方法的注释将覆盖默认值 由类注释(如果存在)给出的事务语义。任何方法 可见性可以注释,包括私有方法。注释非公共方法 直接是获取此类方法执行的交易划分的唯一方法。​​@Transactional​

对于想要使用 Spring 配置和事务的 AspectJ 程序员 管理支持但不想(或不能)使用注释,还包含可以扩展以提供自己的切入点的方面 定义。有关更多信息,请参阅来源以获取和方面。例如,以下 摘录显示了如何编写一个方面来配置对象的所有实例 使用与 完全限定的类名:​​spring-aspects.jar​​​​abstract​​​​AbstractBeanConfigurerAspect​​​​AbstractTransactionAspect​

public aspect DomainObjectConfiguration extends AbstractBeanConfigurerAspect 

public DomainObjectConfiguration()
setBeanWiringInfoResolver(new ClassNameBeanWiringInfoResolver());


// the creation of a new bean (any object in the domain model)
protected pointcut beanCreation(Object beanInstance) :
initialization(new(..)) &&
CommonPointcuts.inDomainModel() &&
this(beanInstance);

5.10.3. 使用 Spring IoC 配置 AspectJ 方面

当您在 Spring 应用程序中使用 AspectJ 方面时,很自然地既想要又想要和 希望能够使用Spring配置这些方面。AspectJ 运行时本身是 负责方面创建,以及配置AspectJ创建的方法 通过 Spring 的方面依赖于 AspectJ 实例化模型(子句) 由方面使用。​​per-xxx​

AspectJ的大多数方面都是单例方面。这些配置的配置 方面很容易。您可以创建一个将方面类型引用为 的 Bean 定义 正常并包含 thebean 属性。这可确保 Spring 通过向 AspectJ 请求它而不是尝试创建 aspect 实例来获取它。 实例本身。下面的示例演示如何使用属性:​​factory-method="aspectOf"​​​​factory-method="aspectOf"​

<bean id="profiler" class="com.xyz.profiler.Profiler"
factory-method="aspectOf">

<property name="profilingStrategy" ref="jamonProfilingStrategy"/>
</bean>

非单一实例方面更难配置。但是,可以通过 创建原型 Bean 定义并使用 Support From 在创建 Bean 后配置方面实例 AspectJ 运行时。​​@Configurable​​​​spring-aspects.jar​

如果您有一些@AspectJ方面想要与AspectJ编织(例如, 对域模型类型使用加载时编织)和所需的其他@AspectJ方面 与Spring AOP一起使用,并且这些方面都在Spring中配置,您可以 需要告诉 Spring AOP @AspectJ自动代理支持哪个确切的子集 配置中定义的@AspectJ方面应用于自动代理。您可以 通过在声明中使用一个或多个元素来执行此操作。每个元素指定一个名称模式,并且只有 与至少一种模式匹配的名称用于 Spring AOP 自动代理 配置。以下示例演示如何使用元素:​​<include/>​​​​<aop:aspectj-autoproxy/>​​​​<include/>​​​​<include/>​

<aop:aspectj-autoproxy>
<aop:include name="thisBean"/>
<aop:include name="thatBean"/>
</aop:aspectj-autoproxy>

5.10.4. 在 Spring 框架中使用 AspectJ 进行加载时编织

加载时间编织(LTW)是指将AspectJ方面编织成 应用程序在加载到 Java 虚拟机 (JVM) 时的类文件。 本节的重点是在 弹簧框架。本节不是对 LTW 的一般介绍。有关以下详细信息的完整详细信息 LTW 的细节以及仅使用 AspectJ 配置 LTW(Spring 不是 完全涉及),请参阅AspectJ的LTW部分 开发环境指南。

Spring 框架为 AspectJ LTW 带来的价值在于实现了很多 对织造过程进行更精细的控制。“香草”AspectJ LTW通过使用 Java (5+) 代理程序,通过在启动 JVM.因此,它是一个 JVM 范围的设置,在某些情况下可能很好,但通常是一个 有点太粗糙了。启用弹簧的 LTW 可让您在 每个基础,哪个更细粒度,哪个可以制作更多 在“单 JVM 多应用程序”环境中(例如在典型的环境中发现 应用程序服务器环境)。​​ClassLoader​

此外,在某些环境中,此支持使 加载时编织,而不对应用程序服务器的启动进行任何修改 添加器所需的脚本(正如我们所描述的 在本节的后面)。开发人员配置 启用加载时编织而不是依赖管理员的应用程序上下文 通常负责部署配置的人员,例如启动脚本。​​-javaagent:path/to/aspectjweaver.jar​​​​-javaagent:path/to/spring-instrument.jar​

现在销售宣传已经结束,让我们首先通过AspectJ的快速示例 使用 Spring 的 LTW,然后是有关 例。有关完整示例,请参阅Petclinic 示例应用程序。

第一个例子

假设您是负责诊断的应用程序开发人员 系统中某些性能问题的原因。而不是爆发 分析工具,我们将打开一个简单的分析方面,让我们 快速获取一些性能指标。然后,我们可以应用更细粒度的分析 工具紧接着到该特定区域。

以下示例显示了分析方面,这并不花哨。 它是一个基于时间的分析器,使用@AspectJ样式的方面声明:

package foo;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.util.StopWatch;
import org.springframework.core.annotation.Order;

@Aspect
public class ProfilingAspect

@Around("methodsToBeProfiled()")
public Object profile(ProceedingJoinPoint pjp) throws Throwable
StopWatch sw = new StopWatch(getClass().getSimpleName());
try
sw.start(pjp.getSignature().getName());
return pjp.proceed();
finally
sw.stop();
System.out.println(sw.prettyPrint());



@Pointcut("execution(public * foo..*.*(..))")
public void methodsToBeProfiled()

我们还需要创建一个文件,以通知 AspectJ 编织者 我们想把我们的东西编织到我们的课堂上。此文件约定,即 在调用的 Java 类路径上存在一个或多个文件是 标准方面J。以下示例显示该文件:​​META-INF/aop.xml​​​​ProfilingAspect​​​​META-INF/aop.xml​​​​aop.xml​

<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "https://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>

<weaver>
<!-- only weave classes in our application-specific packages -->
<include within="foo.*"/>
</weaver>

<aspects>
<!-- weave in just this aspect -->
<aspect name="foo.ProfilingAspect"/>
</aspects>

</aspectj>

现在我们可以转到配置的 Spring 特定部分。我们需要 配置(稍后解释)。这个加载时间编织者是 负责将方面配置编织在一个或 更多文件到应用程序中的类中。好的 问题是它不需要很多配置(还有一些 您可以指定的选项,但稍后会详细介绍这些选项),如 以下示例:​​LoadTimeWeaver​​​​META-INF/aop.xml​

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">

<!-- a service object; we will be profiling its methods -->
<bean id="entitlementCalculationService"
class="foo.StubEntitlementCalculationService"/>

<!-- this switches on the load-time weaving -->
<context:load-time-weaver/>
</beans>

现在所有必需的工件(方面、文件和 Spring 配置)都已就绪,我们可以创建以下内容 驱动程序类,带有演示 LTW 运行的方法:​​META-INF/aop.xml​​​​main(..)​

package foo;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public final class Main

public static void main(String[] args)
ApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml", Main.class);

EntitlementCalculationService entitlementCalculationService =
(EntitlementCalculationService) ctx.getBean("entitlementCalculationService");

// the profiling aspect is woven around this method execution
entitlementCalculationService.calculateEntitlement();

我们还有最后一件事要做。本节的介绍确实说可以 使用 Spring 有选择地打开 LTW,这是真的。 但是,对于此示例,我们使用Java代理(随Spring一起提供)来打开LTW。 我们使用以下命令运行前面显示的类:​​ClassLoader​​​​Main​

java -javaagent:C:/projects/foo/lib/global/spring-instrument.jar foo.Main

这是用于指定和启用代理的标志 来检测在 JVM 上运行的程序。Spring 框架附带了这样的 代理, 的, 它被打包在 那 作为参数的值提供 前面的示例。​​-javaagent​​​​InstrumentationSavingAgent​​​​spring-instrument.jar​​​​-javaagent​

程序执行的输出类似于下一个示例。 (我在实现中引入了一个语句,以便探查器实际上捕获 0 以外的内容 毫秒(毫秒不是 AOP 引入的开销)。 以下清单显示了我们在运行探查器时获得的输出:​​Main​​​​Thread.sleep(..)​​​​calculateEntitlement()​​​​01234​

Calculating entitlement

StopWatch ProfilingAspect: running time (millis) = 1234
------ ----- ----------------------------
ms % Task name
------ ----- ----------------------------
01234 100% calculateEntitlement

由于此LTW是通过使用成熟的AspectJ来实现的,因此我们不仅限于建议 春豆。程序的以下细微变化产生相同的结果 结果:​​Main​

package foo;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public final class Main

public static void main(String[] args)
new ClassPathXmlApplicationContext("beans.xml", Main.class);

EntitlementCalculationService entitlementCalculationService =
new StubEntitlementCalculationService();

// the profiling aspect will be woven around this method execution
entitlementCalculationService.calculateEntitlement();

请注意,在前面的程序中,我们如何引导 Spring 容器和 然后创建一个新的实例 完全在外面 春天的背景。分析建议仍然被编织在一起。​​StubEntitlementCalculationService​

诚然,这个例子太简单了。但是,春季LTW支持的基础知识 在前面的示例中都进行了介绍,本节的其余部分将解释 详细介绍了配置和使用背后的“原因”。

方面

您在 LTW 中使用的方面必须是 AspectJ 方面。你可以把它们写在 要么是AspectJ语言本身,要么你可以用@AspectJ风格来写你的方面。 然后,您的方面既是有效的AspectJ和Spring AOP方面。 此外,编译的方面类需要在类路径上可用。

“元信息/AOP.xml”

AspectJ LTW 基础结构是通过使用 Java 类路径上的一个或多个文件(直接使用或更典型的在 jar 文件中)来配置的。​​META-INF/aop.xml​

该文件的结构和内容在AspectJ参考的LTW部分中有详细说明 文档。因为该文件是 100% AspectJ,所以我们在这里不再进一步描述它。​​aop.xml​

所需库 (罐)

至少,您需要以下库才能使用 Spring 框架的支持。 对于AspectJ LTW:

  • ​spring-aop.jar​
  • ​aspectjweaver.jar​

如果您使用Spring 提供的代理启用 检测,您还需要:

  • ​spring-instrument.jar​
弹簧配置

Spring 的 LTW 支持的关键组件是接口(在包中)和众多实现 它与弹簧发行版一起发货。艾斯负责 添加一个或多个到 AAT 运行时,它为各种有趣的应用程序打开了大门,其中之一是 恰好是各方面的LTW。​​LoadTimeWeaver​​​​org.springframework.instrument.classloading​​​​LoadTimeWeaver​​​​java.lang.instrument.ClassFileTransformers​​​​ClassLoader​

为特定配置 a可以像 添加一行。(请注意,您几乎肯定需要使用 anas 您的 Spring 容器 — 通常,ais 不是 足够了,因为 LTW 支持使用。​​LoadTimeWeaver​​​​ApplicationContext​​​​ApplicationContext​​​​BeanFactory​​​​BeanFactoryPostProcessors​

要启用 Spring 框架的 LTW 支持,您需要配置一个, 这通常是通过使用注释来完成的,如下所示:​​LoadTimeWeaver​​​​@EnableLoadTimeWeaving​

@Configuration
@EnableLoadTimeWeaving
public class AppConfig

或者,如果您更喜欢基于 XML 的配置,请使用元素。请注意,该元素是在命名空间中定义的。以下示例演示如何使用:​​<context:load-time-weaver/>​​​​context​​​​<context:load-time-weaver/>​

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">

<context:load-time-weaver/>

</beans>

上述配置自动定义并注册了许多特定于 LTW 的 基础设施豆,如 Aand An,为你。 默认值是类,它尝试 来装饰自动检测。“自动检测”的确切类型取决于您的运行时环境。 下表总结了各种实现:​​LoadTimeWeaver​​​​AspectJWeavingEnabler​​​​LoadTimeWeaver​​​​DefaultContextLoadTimeWeaver​​​​LoadTimeWeaver​​​​LoadTimeWeaver​​​​LoadTimeWeaver​

Table 13. DefaultContextLoadTimeWeaver LoadTimeWeavers

运行时环境

​LoadTimeWeaver​​实现

在阿帕奇雄猫中运行

​TomcatLoadTimeWeaver​

在GlassFish 中运行(仅限于 EAR 部署)

​GlassFishLoadTimeWeaver​

在Red Hat的JBoss AS​或WildFly中运行

​JBossLoadTimeWeaver​

在IBM的WebSphere中运行

​WebSphereLoadTimeWeaver​

在Oracle的WebLogic中运行

​WebLogicLoadTimeWeaver​

Spring框架参考手册翻译——第三部分 核心技术 6.1 Spring IoC容器和bean的介绍

Spring 核心技术

Spring Security OAuth:源码解析之还是内味儿

快速切入:Spring框架核心概念总览

Spring框架的新手收到此错误:无法自动装配字段:

spring技术核心概念纪要

(c)2006-2024 SYSTEM All Rights Reserved IT常识