wix做的网站 网址是什么,wordpress单图模式,知名企业名称有哪些,猎头公司面试一般会问什么问题proxy aspectj静态地或在运行时将代码片段注入已编译的类和方法中的功能可能会很有帮助。 这尤其适用于在没有源代码的第三方库中或在无法使用调试器或探查器的环境中对问题进行故障排除。 代码注入对于处理涉及整个应用程序的问题#xff08;例如性能监视#xff09;也很有用… proxy aspectj  静态地或在运行时将代码片段注入已编译的类和方法中的功能可能会很有帮助。 这尤其适用于在没有源代码的第三方库中或在无法使用调试器或探查器的环境中对问题进行故障排除。 代码注入对于处理涉及整个应用程序的问题例如性能监视也很有用。 以这种方式使用代码注入在面向方面编程 AOP的名字下变得很流行。 相反代码注入并不是很少使用就像相反。 每个程序员都会遇到这种能力可以避免很多痛苦和沮丧的情况。  这篇文章旨在为您提供您可能或者我宁愿说“将要”需要的知识并说服您学习代码注入的基础确实值得您花很少的时间。 我将介绍三种不同的现实情况其中代码注入使我大吃一惊用不同的工具解决了每个问题最适合手头的约束。   为什么您需要它   关于AOP的优势因此有代码注入已经有很多论述 因此从故障排除的角度来看我将只专注于几个要点。   最酷的事情是它使您能够修改第三方封闭源类 甚至实际上是JVM类。 我们中的大多数人都使用遗留代码和我们没有源代码的代码因此不可避免地我们偶尔会遇到这些第三方二进制文件的局限性或错误并且非常需要更改其中的一些小东西或获得更多了解代码的行为。 如果没有代码注入则无法修改代码或添加对代码增加可观察性的支持。 同样您通常需要在生产环境中处理问题或收集信息在生产环境中您不能使用调试器和类似工具而您通常至少可以以某种方式管理应用程序的二进制文件和依赖项。 请考虑以下情况   您正在将数据集合传递到闭源库中进行处理并且库中的一个方法对其中一个元素失败但是异常未提供有关它是哪个元素的信息。 您需要修改它以记录有问题的参数或将其包括在异常中。 并且您不能使用调试器因为它仅在生产应用程序服务器上发生。  您需要收集应用程序中重要方法的性能统计信息包括在典型生产负载下的某些封闭源组件。 在生产环境中您当然不能使用探查器并且您希望产生最小的开销。  您使用JDBC批量发送大量数据到数据库而其中一个批量更新失败。 您将需要一些不错的方法来找出批次和包含的数据。   实际上我已经遇到了这三种情况在其他情况下稍后您将看到可能的实现。   阅读本文时您应该牢记代码注入的以下优点   代码注入使您能够修改您没有源代码的二进制类  在您无法使用传统开发工具例如探查器和调试器的环境中注入的代码可用于收集各种运行时信息。  不要重复自己当您需要在多个地方使用相同的逻辑时可以定义一次然后将其注入所有这些地方。  使用代码注入时您无需修改原始源文件因此非常适合仅在有限时间内进行的可能是大规模的更改尤其是借助可以轻松打开和关闭代码注入的工具例如具有加载时间编织功能的AspectJ。 典型的情况是性能指标收集和故障排除期间增加的日志记录  您可以在构建时静态注入代码也可以在JVM加载目标类时动态注入代码。   迷你词汇   您可能会遇到以下与代码注入和AOP有关的术语   忠告   要注入的代码。 通常我们谈论建议之前之后和周围这些建议是在目标方法之前之后或代替目标方法执行的。 除了将代码注入方法之外还可以进行其他更改例如向类添加字段或接口。   AOP面向方面的编程   一个编程范例声称“跨领域关注点”在许多地方都需要的逻辑没有一个类可以在哪里实现应该实施一次然后注入这些地方。 检查维基百科以获得更好的描述。   方面   AOP中的模块化单位大致对应于一个类–它可以包含不同的建议和切入点。   联合点   程序中可能成为代码注入目标的特定点例如方法调用或方法条目。   切入点   粗略地说切入点是一个表达式它告诉代码注入工具在哪里注入特定的代码段即在哪个联合点上应用特定的建议。 它只能选择一个这样的点例如执行单个方法或选择许多类似的点例如对带有自定义注释的所有方法例如MyBusinessMethod执行。   织造   将代码建议注入目标位置联合点的过程。    工具   有许多非常不同的工具可以完成这项工作因此我们将首先了解它们之间的差异然后我们将熟悉代码注入工具的不同演化分支的三个杰出代表。   代码注入工具的基本分类   一抽象水平   表达要注入的逻辑和表达应在其中插入逻辑的切入点有多困难   关于“建议”代码   直接字节码操作例如ASM–要使用这些工具您需要了解类的字节码格式因为它们从类中提取的很少您可以直接使用操作码操作数堆栈和单个指令。 一个ASM示例  methodVisitor.visitFieldInsnOpcodes.GETSTATIC“ java / lang / System”“ out”“ Ljava / io / PrintStream;”;   由于级别较低因此很难使用但功能最强大。 通常它们用于实现更高级别的工具实际上很少需要使用它们。   中级–字符串代码类文件结构的某种抽象Javassist  Java建议例如AspectJ–要注入的代码表示为语法检查和静态编译的Java   关于将代码注入到哪里的规范   手动注入–您必须以某种方式掌握要注入代码的位置ASMJavassist  原始切入点–表达特定位置特定类类的所有公共方法或组中所有类的所有公共方法Java EE拦截器的地方代码表达的可能性非常有限。  模式匹配切入点表达式–功能强大的表达式可基于多个条件使用通配符匹配联合点对上下文的了解例如“从XY包中的类调用”等AspectJ   二。 当魔术发生时   可以在不同的时间点注入代码   在运行时手动进行–您的代码必须明确要求增强代码例如通过手动实例化包装目标对象的自定义代理可以说这不是真正的代码注入  在加载时–在JVM加载目标类时执行修改  在构建时–在打包和部署应用程序之前您需要在构建过程中添加额外的步骤来修改已编译的类。   这些注入方式中的每一种都可能更适合于不同情况。   三 它能做什么   代码注入工具可以做什么或不能做什么都存在很大差异其中一些可能性是   在方法之前/之后/而不是方法中添加代码–仅成员级方法还是静态方法  将字段添加到班级  新增方法  制作一个类以实现接口  修改方法体内的指令例如方法调用  修改泛型注释访问修饰符更改常量值…  删除方法字段等     选定的代码注入工具   最著名的代码注入工具是   动态Java代理  字节码操作库ASM  JBoss Javassist  纵横比  Spring AOP /代理  Java EE拦截器     Java ProxyJavassist和AspectJ实用介绍   我选择了三种非常不同的成熟和流行的代码注入工具并将它们呈现在我亲身经历的真实示例中。   无所不在的动态Java代理   Java.lang.reflect.Proxy使动态创建接口代理成为可能将所有调用转发到目标对象。 它不是代码注入工具因为您不能在任何地方注入它您必须手动实例化并使用代理而不是原始对象并且只能对接口执行此操作但是如我们所见它仍然非常有用。   优点   它是JVM的一部分因此随处可见  您可以为不兼容的对象使用相同的代理更确切地说是InvocationHandler 从而比平时更多地重用代码  您可以节省精力因为您可以轻松地将所有调用转发到目标对象而仅修改您感兴趣的那些调用。 如果要手动实现代理则需要实现相关接口的所有方法   缺点   您只能为接口创建动态代理如果代码需要具体的类则不能使用它  您必须实例化并手动应用它没有神奇的自动注入  有点太冗长  它的功能非常有限它只能在方法前后/周围执行一些代码   没有代码注入步骤-您必须手动应用代理。   例   我正在使用JDBC PreparedStatement的批处理更新来修改数据库中的许多数据并且由于违反完整性约束而导致其中一个批处理的处理失败。 该异常没有足够的信息来找出导致失败的数据因此我为PreparedStatement创建了一个动态代理该代理记住了每个批处理更新中传递的值并且在失败的情况下会自动打印该批处理数字和数据。 有了这些信息我就可以修复数据并保持解决方案就位这样如果再次发生类似的问题我将能够找到原因并Swift解决。   代码的关键部分   LoggingStatementDecorator.java –片段1  class LoggingStatementDecorator implements InvocationHandler {private PreparedStatement target;...private LoggingStatementDecorator(PreparedStatement target) { this.target  target; }Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable {try {Object result  method.invoke(target, args);updateLog(method, args); // remember data, reset upon successful executionreturn result;} catch (InvocationTargetException e) {Throwable cause  e.getTargetException();tryLogFailure(cause);throw cause;}}private void tryLogFailure(Throwable cause) {if (cause instanceof BatchUpdateException) {int failedBatchNr  successfulBatchCounter  1;Logger.getLogger(JavaProxy).warning(THE INJECTED CODE SAYS:  Batch update failed for batch#   failedBatchNr  (counting from 1) with values: [ getValuesAsCsv()  ]. Cause:   cause.getMessage());}}
... 笔记   要创建代理您首先需要实现一个InvocationHandler及其调用方法只要在代理上调用任何接口的方法就会调用该方法  您可以通过java.lang.reflect。*对象访问有关调用的信息例如可以通过method.invoke将调用委托给代理对象  我们还有一个实用方法用于为Prepared语句创建代理实例   LoggingStatementDecorator.java –代码段2  public static PreparedStatement createProxy(PreparedStatement target) {return (PreparedStatement) Proxy.newProxyInstance(PreparedStatement.class.getClassLoader(),new Class[] { PreparedStatement.class },new LoggingStatementDecorator(target));
}; 笔记   您可以看到newProxyInstance调用使用一个类加载器代理应实现的接口数组以及应将调用委派给其的调用处理程序如果需要处理程序本身必须管理对代理对象的引用   然后按以下方式使用   Main.java  ...
PreparedStatement rawPrepStmt  connection.prepareStatement(...);
PreparedStatement loggingPrepStmt  LoggingStatementDecorator.createProxy(rawPrepStmt);
...
loggingPrepStmt.executeBatch();
... 笔记   您会看到我们必须使用代理手动包装原始对象并在以后继续使用代理     替代解决方案   可以通过不同的方式解决此问题例如通过创建一个实现PreparedStatement的非动态代理并在记住批处理数据的同时将所有调用转发到真实语句但是对于具有许多方法的接口这将是很多无聊的键入。 调用方还可以手动跟踪已发送到准备好的语句的数据但这将使它的逻辑变得无关紧要。   使用动态Java代理我们可以得到非常干净且易于实现的解决方案。   独立的Javassist   JBoss Javassist是一个中间代码注入工具它提供了比字节码操作库更高级别的抽象并且提供了有限的但仍然非常有用的操作功能。 要注入的代码以字符串表示您必须手动进入将其注入的类方法。 它的主要优点是修改后的代码对Javassist或其他任何东西都没有新的运行时依赖项。 如果您在一家大公司工作这可能是决定性因素而在大公司中出于法律和其他原因很难部署其他开放源代码库或几乎任何其他库例如AspectJ。   优点   由Javassist修改的代码不需要任何新的运行时依赖项注入会在构建时发生并且注入的建议代码本身不依赖于任何Javassist API  比字节码操作库更高级注入的代码以Java语法编写尽管包含在字符串中  可以完成您可能需要的大多数事情例如“建议”方法调用和方法执行  您可以同时实现构建时注入通过Java代码或定制的Ant任务来执行执行/调用建议 和加载时注入通过实现自己的Java 5代理 [thx to Anton]   缺点   仍然有些太底层因此更难使用–您必须处理一些方法结构并且注入的代码未经语法检查  Javassist没有执行注入的工具因此您必须实现自己的注入代码-包括不支持根据模式自动注入代码   有关没有Javassist的大多数缺点的解决方案请参见下面的GluonJ。   使用Javassist您可以创建一个类该类使用Javassist API注入int目标代码并在编译后将其作为构建过程的一部分运行例如就像我曾经通过自定义Ant任务所做的那样。   例   我们需要在Java EE应用程序中添加一些简单的性能监控并且不允许我们部署任何未经批准的开源库至少在没有经过耗时的审批过程的情况下。 因此我们使用Javassist将性能监视代码注入到我们的重要方法中以及将重要的外部方法调用到的地方。   代码注入器   JavassistInstrumenter.java  public class JavassistInstrumenter {public void insertTimingIntoMethod(String targetClass, String targetMethod) throws NotFoundException, CannotCompileException, IOException {Logger logger  Logger.getLogger(Javassist);final String targetFolder  ./target/javassist;try {final ClassPool pool  ClassPool.getDefault();// Tell Javassist where to look for classes - into our ClassLoaderpool.appendClassPath(new LoaderClassPath(getClass().getClassLoader()));final CtClass compiledClass  pool.get(targetClass);final CtMethod method  compiledClass.getDeclaredMethod(targetMethod);// Add something to the beginning of the method:method.addLocalVariable(startMs, CtClass.longType);method.insertBefore(startMs  System.currentTimeMillis(););// And also to its very end:method.insertAfter({final long endMs  System.currentTimeMillis(); iterate.jz2011.codeinjection.javassist.PerformanceMonitor.logPerformance(\ targetMethod  \,(endMs-startMs));});compiledClass.writeFile(targetFolder);// Enjoy the new $targetFolder/iterate/jz2011/codeinjection/javassist/TargetClass.classlogger.info(targetClass  .  targetMethod  has been modified and saved under   targetFolder);} catch (NotFoundException e) {logger.warning(Failed to find the target class to modify,  targetClass  , verify that it ClassPool has been configured to look  into the right location);}}public static void main(String[] args) throws Exception {final String defaultTargetClass  iterate.jz2011.codeinjection.javassist.TargetClass;final String defaultTargetMethod  myMethod;final boolean targetProvided  args.length  2;new JavassistInstrumenter().insertTimingIntoMethod(targetProvided? args[0] : defaultTargetClass, targetProvided? args[1] : defaultTargetMethod);}
} 笔记   您会看到“底层” –您必须显式处理CtClassCtMethod之类的对象显式添加局部变量等。  Javassist在寻找要修改的类方面非常灵活-它可以搜索类路径特定文件夹JAR文件或包含JAR文件的文件夹  您将在编译过程中编译此类并运行其主要内容   类固醇的JavassistGluonJ   GluonJ是一个基于Javassist的AOP工具。 它可以使用自定义语法或Java 5批注并且围绕“修订器”的概念构建。 Reviser是一个类一个方面它可以修改即修改特定的目标类并覆盖其一个或多个方法与继承相反修订者的代码实际上被强加于目标类内部的原始代码。   优点   如果使用构建时编织则没有运行时依赖性加载时编织需要GluonJ代理库或gluonj.jar  使用GlutonJ的注释的简单Java语法-尽管自定义语法也易于理解和易于使用  使用GlutonJ的JAR工具Ant任务或在加载时动态轻松地自动织入目标类  支持构建时和加载时编织   缺点   一个方面只能修改一个类而不能将同一段代码注入多个类/方法  有限的能力–仅在执行任何代码时或仅在特定上下文中执行时即从特定的类/方法中调用时才提供字段/方法的添加和代码的执行而不是在目标方法周围/   如果您不需要将同一段代码注入多个方法中那么GluonJ比Javassist更加容易和更好地选择如果它的简单性对您来说不成问题那么也可能比AspectJ更好的选择。简单。   全能方面   AspectJ是功能完善的AOP工具几乎可以完成您可能想要的任何事情包括修改静态方法添加新字段在类的已实现接口列表中添加接口等。   AspectJ建议的语法有两种一种是Java语法的超集具有诸如Aspect和Pointcut的其他关键字另一种称为AspectJ –是标准Java 5具有 Aspect Pointcut Around等注释。 后者也许更易于学习和使用但功能却不那么强大因为它不像自定义AspectJ语法那样具有表现力。   使用AspectJ您可以定义要用非常有力的表达建议的联合点但是学习它们并使其正确起来可能并不困难。 有一个用于AspectJ开发的有用的Eclipse插件– AspectJ开发工具 AJDT–但是我上次尝试它时并没有我想要的那样有用。   优点   功能强大几乎可以完成您可能需要的所有操作  强大的切入点表达式用于定义在何处注入建议以及何时激活建议包括一些运行时检查–完全启用DRY即写入一次并多次注入  编译时和加载时代码注入编织   缺点   修改后的代码取决于AspectJ运行时库  切入点表达式非常强大但是可能很难正确使用它们尽管AJDT插件可以部分可视化它们的效果但对“调试”它们的支持不多  尽管基本用法非常简单可能会花一些时间才能开始使用使用 Aspect Around和简单的切入点表达式如我们在示例中所见     例   曾几何时我为具有相关性的闭源LMS J2EE应用程序编写了一个插件以致于无法在本地运行它。 在API调用期间应用程序内部的某个方法失败了但该异常未包含足够的信息来跟踪问题的原因。 因此我需要更改方法以在失败时记录其参数的值。   AspectJ代码非常简单   LoggingAspect.java  Aspect
public class LoggingAspect {Around(execution(private void TooQuiet3rdPartyClass.failingMethod(..)))public Object interceptAndLog(ProceedingJoinPoint invocation) throws Throwable {try {return invocation.proceed();} catch (Exception e) {Logger.getLogger(AspectJ).warning(THE INJECTED CODE SAYS: the method  invocation.getSignature().getName()   failed for the input  invocation.getArgs()[0]  . Original exception:   e);throw e;}}
} 笔记   方面是带有Aspect批注的普通Java类它只是AspectJ的标记  Around注释指示AspectJ执行该方法而不是与表达式匹配的方法即代替TooQuiet3rdPartyClass的failingMethod。  周围建议方法需要是公共的返回一个对象并采用一个特殊的AspectJ对象作为参数该对象携带有关调用的信息– ProceedingJoinPoint –并且可以具有任意名称实际上这是签名的最小形式它可以更复杂。  我们使用ProceedingJoinPoint将调用委派给原始目标TooQuiet3rdPartyClass的实例并在发生异常的情况下获取参数的值  我使用了Around建议尽管AfterThrowing会更简单更合适但这可以更好地显示AspectJ的功能并且可以与上述动态Java代理示例进行很好地比较   由于我无法控制应用程序的环境因此无法启用加载时编织因此不得不在构建时使用AspectJ的Ant任务来编织代码重新包装受影响的JAR并将其重新部署到服务器。   替代解决方案   好吧如果您不能使用调试器那么您的选择就非常有限。 我唯一想到的替代解决方案是反编译该类非法将日志记录添加到该方法中前提是反编译成功重新编译它然后用修改后的.class替换原始.class。   黑暗的一面   代码注入和面向方面的编程非常强大有时对于故障排除和作为应用程序体系结构的常规部分来说都是必不可少的例如在Java EE的Enterprise Java Beans中诸如事务管理和安全检查之类的业务问题是注入到POJO中尽管实现实际上更可能使用代理或在Spring中。   但是由于可能会降低可理解性因此需要付出一定的代价因为运行时行为和结构与您根据源代码所期望的不同除非您知道还要检查方面的源代码或者除非进行了注入通过对目标类例如Java EE的Interceptors 的注释进行显式显示。 因此您必须仔细权衡代码注入/ AOP的优缺点-尽管合理使用它们不会比接口工厂等掩盖程序流更多。 关于掩盖代码的争论可能经常被高估了 。   如果您想看一下AOP的例子请查看Glassbox的源代码 它是JavaEE性能监视工具为此您可能需要一张地图 以免丢失太多。   花式使用代码注入和AOP   在故障排除过程中代码注入的主要应用领域是日志记录它通过提取并以某种方式传达有关它的有趣运行时信息从而更准确地了解应用程序正在做什么。 但是AOP除了简单或复杂的日志记录以外还有许多有趣的用途例如   典型示例Caching等人例如 在JBoss Cache中的AOP上 事务管理日志记录安全性实施持久性线程安全错误恢复方法的自动实现例如toStringequalshashCode远程处理  基于角色的编程 例如OT / J 使用BCEL或数据上下文和交互体系结构的实现  测试中  测试覆盖率–注入代码以记录测试运行期间是否已执行某行  突变测试  µJava  Jumble –向应用程序注入“随机”突变并验证测试是否失败  模式测试 –通过AOP自动验证代码中正确实施了架构/设计/最佳实践建议  通过注入异常来模拟硬件/外部故障   帮助实现Java应用程序的零周转– JRebel对框架和服务器集成插件使用类似于AOP的方法 –即其插件 使用Javassist进行“二进制修补”  解决问题并避免使用AOP模式进行猴子编码例如Worker Object Creation通过Runnable和ThreadPool / task队列将直接调用转换为异步调用和Wormhole使调用方的上下文信息对被调用方可用而不必传递它们遍历所有层作为参数并且没有ThreadLocal–如《 AspectJ in Action》一书中所述  处理遗留代码–覆盖对构造函数的调用实例化的类此类和类似类可用于打破与可行工作量的紧密耦合 确保向后兼容 o 教导组件对环境变化做出正确React  保留API的向后兼容性同时不阻止其演化能力例如在缩小/扩展返回类型 Bridge Method Injector –使用ASM时通过添加向后兼容方法或通过重新添加旧方法并按照以下方式实现它们来实现新的API  将POJO转换为JMX bean     摘要   我们已经了解到代码注入对于故障排除是必不可少的尤其是在处理封闭源代码库和复杂的部署环境时。 我们已经看到了三种完全不同的代码注入工具动态Java代理JavassistAspectJ应用于实际问题并讨论了它们的优缺点因为不同的工具可能适用于不同的情况。 我们还提到了不应过度使用代码注入/ AOP并查看了一些代码注入/ AOP的高级应用示例。   我希望您现在了解代码注入如何为您提供帮助并知道如何使用这三个工具。   源代码   您可以从GitHub 获得示例的完整文档源代码不仅包括要注入的代码还包括目标代码和易于构建的支持。 最简单的可能是  git clone git://github.com/jakubholynet/JavaZone-Code-Injection.git
cd JavaZone-Code-Injection/
cat README
mvn -P javaproxy test
mvn -P javassist test
mvn -P aspectj   test Maven可能需要花费几分钟来下载其依赖项插件和实际项目的依赖项。   其他资源   Spring对AOP的介绍  dW AOP  WorkAOP的神话与现实  AspectJ in Action第2 章第1章 。 ed。   致谢   我要感谢所有为我提供这篇文章和演示文稿的人包括我的大学JRebel同学和GluonJ的合著者。 千叶繁   参考 Holy Java博客上我们JCG合作伙伴 JakubHolý撰写的有关 使用AspectJJavassist和Java Proxy进行代码注入的实用介绍 。   相关文章   Spring和AspectJ的领域驱动设计  使用Spring AspectJ和Maven进行面向方面的编程  使用Spring AOP进行面向方面的编程  正确记录应用程序的10个技巧   翻译自: https://www.javacodegeeks.com/2011/09/practical-introduction-into-code.htmlproxy aspectj