首页 / 财经

图纸上fb代理什么意思,图纸中fb表示什么

发布时间:2022-11-25 19:07:41
  

  什么是AOP?首先,AOP不是Spring框架的核心技术之一。AOP代表面向方面编程,即面向方面编程。要解决的问题是在不改变源代码的情况下修改逻辑函数。常见的场景包括日志记录、异常处理、性能监控、安全控制(如拦截器)等。综上所述,任何想改变当前功能,但又不想修改源代码的人,都可以考虑能否用AOP实现。   

  

  为什么要面对切面?我直接改源代码不是很好吗?当然,如果情况允许的话。但是考虑到以下情况,我本来写了1000个方法。有一天,我想添加一些控件。在执行方法逻辑之前,我想检查一些系统参数。如果检查了参数,再执行一次逻辑,否则,我不执行。这种情况我们该怎么办?这1000个方法一定要修改吗?这是一场灾难。还有,一些在线逻辑执行起来很慢,但是我不想重新部署环境,因为那会影响在线业务。在这种情况下,也可以考虑AOP。Btrace就是这样一个在线性能故障排除的工件。   

  

  Spring AOP的用法是面向方面编程的,名字看起来很酷,但是它的用法已经被Spring封装了,只需要简单的配置就可以实现。如何使用不是本文的重点。这里只演示最简单最基本的用法,这样调用的方法就可以费时计算并打印出来。   

  

  描述:JDK 1.8,Spring mvc版本4.3.2.RELEASE   

  

  1.首先引用Spring mvc相关的maven包。包太多了,就不列举了。只列出了那些与Spring-aop相关的。   

  

  依赖关系groupIdorg.springframework/groupId artifactIdspring-AOP/artifactId版本4 . 3 . 2 . release/version/dependency dependency groupidorg . AspectJ/groupId artifactIdaspectjrt/artifactId版本1 . 8 . 9/version/dependency dependency groupidorg . AspectJ/groupId artifactIdaspectjweaver/Artiid版本1.8.9/version/dependency2。将AOP配置添加到Spring mvc配置文件中,内容如下:   

  

  ?xml版本='1.0 '编码='UTF-8 '?bean default-lazy-init=' true ' xmlns=' http://www . spring framework . org/schema/beans ' xmlns : xsi=' http://www . w3 . org/2001/XML schema-instance ' xmlns : context=' http://www . spring framework . org/schema/context ' xmlns : MVC=' 3http://www . spring framework . org/schema/MVC ' xmlns : p-自动   

扫描与装配bean --> <context:component-scan base-package="kite.lab.spring"></context:component-scan> <!-- 启动 @AspectJ 支持 --> <aop:aspectj-autoproxy proxy-target-class="true" /></bean>3、创建切面类,并在 kite.lab.spring.service 包下的方法设置切面,使用 @Around 注解监控,实现执行时间的计算并输出,内容如下:

  

import org.aspectj.lang.JoinPoint;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.*;import org.springframework.stereotype.Component;import org.springframework.util.StopWatch;@Component@Aspectpublic class PerformanceMonitor { //配置切入点,该方法无方法体,主要为方便同类中其他方法使用此处配置的切入点 @Pointcut("execution(* kite.lab.spring.service..*(..))") public void aspect(){ } @Around("aspect()") public Object methodTime(ProceedingJoinPoint pjp) throws Throwable { StopWatch stopWatch = new StopWatch(); stopWatch.start(); // 开始 Object retVal = pjp.proceed(); stopWatch.stop(); // 结束 System.out.println(String.format("方法 %s 耗时 %s ms!",pjp.getSignature().toShortString(), stopWatch.getTotalTimeMillis())); return retVal; }}4、被切面监控的类定义如下:

  

package kite.lab.spring.service;public class Worker { public String dowork(){ System.out.println("生活向来不易,我正在工作!"); return ""; }}5、加载 Spring mvc 配置文件,并调用 Worker 类的方法

  

public static void main(String<> args) { String filePath = "spring-servlet.xml"; applicationContext ac = new FileSystemXmlapplicationContext(filePath); Worker worker = (Worker) ac.getBean("worker"); worker.dowork(); }6、显示结果如下:

  

  

用法介绍完了,让我们脱下 AOP 华丽的衣衫,不,是揭下她神秘的面罩,来看一看她的芳容,了解一下她的内心。

  

Spring AOP 原理AOP 的实现原理就是动态的生成代理类,代理类的执行过程为:执行我们增加的代码(例如方法日志记录)―-> 回调原方法 ――> 增加的代码逻辑。看图比较好理解:

  

  

Spring AOP 动态代理可能采用 JDK 动态代理或 CGlib 动态生成代理类两种方式中的一种, 决定用哪一种方式的判断标准就是被切面的类是否有其实现的接口,如果有对应的接口,则采用 JDK 动态代理,否则采用 CGlib 字节码生成机制动态代理方式。

  

代理模式是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。代理类和委托类实现相同的接口,所以调用者调用代理类和调用委托类几乎感觉不到差别。

  

是不是看完了定义,感觉正好可以解决切面编程方式要解决的问题。下图是基本的静态代理模式图:

  

  

而动态代理的意思是运行时动态生成代理实现类,由于 JVM 的机制,需要直接操作字节码,生成新的字节码文件,也就是 .class 文件。

  

JDK 动态代理JDK 动态代理模式采用 sun 的 ProxyGenerator 的字节码框架。要说明的是,只有实现了接口的类才能使用 JDK 动态代理技术,实现起来也比较简单。

  

1、只要实现 InvocationHandler 接口,并覆写 invoke方法即可。具体实现代码如下:

  

package kite.lab.spring.aop.jdkaop;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;/** * JdkProxy * * @author fengzheng */public class JdkProxy implements InvocationHandler { private Object target; /** * 绑定委托对象并返回一个代理类 * * @param target * @return */ public Object bind(Object target) { this.target = target; //取得代理对象 return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); } /** * 调用方法 */ @Override public Object invoke(Object proxy, Method method, Object<> args) throws Throwable { Object result = null; System.out.println("事物开始"); //执行方法 result = method.invoke(target, args); System.out.println("事物结束"); return result; }}Proxy.newProxyInstance 方法用于动态生成实际生成的代理类,三个参数依次为被代理类的类加载器、被代理类所实现的接口和当前代理拦截器。

  

覆写的 invoke 中可以加入我们增加的业务逻辑,然后回调原方法。

  

2、被代理的类仍然用的前面 spring aop 介绍的那个worker 类,只不过我们需要让这个类实现自接口,接口定义如下:

  

package kite.lab.spring.service;/** * IWorker ***/public interface IWorker { String dowork();}3、实际调用如下:

  

public static void main(String<> args) { JdkProxy jdkProxy = new JdkProxy(); IWorker worker = (IWorker) jdkProxy.bind(new Worker()); worker.dowork();}原理说明: jdkProxy.bind 会生成一个实际的代理类,这个生成过程是利用的字节码生成技术,生成的代理类实现了IWorker 接口,我们调用这个代理类的 dowork 方法的时候,实际在代理类中是调用了 JdkProxy (也就是我们实现的这个代理拦截器)的 invoke 方法,接着执行我们实现的 invoke 方法,也就执行了我们加入的逻辑,从而实现了切面编程的需求。

  

我们把动态生成的代理类字节码文件反编译一下,也就明白了。由于代码较长,只摘出相关部分。

  

首先看到类的接口和继承关系:

  

public final class $Proxy0 extends Proxy implements IWorker代理类被命名为 $Proxy0 ,继承了 Proxy ,并且实现了IWorker ,这是关键点。

  

找到 dowork 方法,代码如下:

  

public final String dowork() throws { try { return (String)super.h.invoke(this, m3, (Object<>)null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } }super.h 就是我们实现的JdkProxy 这个类,可以看到调用了这个类的 invoke 方法,并且传入了参数 m3 ,再来看 m3 是什么,

  

m3 = Class.forName("kite.lab.spring.service.IWorker") .getMethod("dowork", new Class0);看到了吧,m3 就是 dowork 方法,是不是流程就明确了。

  

但是,并不是所有的被代理的类(要被切面的类)都实现了某个接口,没有实现接口的情况下,JDK 动态代理就不行了,这时候就要用到 CGlib 字节码框架了。

  

CGLIB 动态代理CGlib库使用了ASM这一个轻量但高性能的字节码操作框架来转化字节码,它可以在运行时基于一个类动态生成它的子类。厉害了吧,不管有没有接口,凡是类都可以被继承,拥有这样的特点,原则上来说,它可以对任何类进行代码拦截,从而达到切面编程的目的。

  

CGlib 不需要我们非常了解字节码文件(.class 文件)的格式,通过简单的 API 即可实现字节码操作。

  

基于这样的特点,CGlib 被广泛用于如 Spring AOP 等基于 代理模式的AOP框架中。

  

下面就基于 CGlib 实现一个简单的动态代理模式。

  

1、创建拦截类实现 MethodInterceptor接口,并覆intercept 方法,在此方法中加入我们增加的逻辑,代码如下:

  

public class MyAopWithCGlib implements MethodInterceptor { @Override public Object intercept(Object o, Method method, Object<> objects, MethodProxy methodProxy) throws Throwable { System.out.println("嘿,你在干嘛?"); methodProxy.invokeSuper(o, objects); System.out.println("是的,你说的没错。"); return null; }2、被代理的类依然是上面的 Worker 类,并且不需要接口。

  

3、客户端调用代理方法的代码如下:

  

public static void main(String<> args) { System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "cglib"); MyAopWithCGlib aop = new MyAopWithCGlib(); Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(Worker.class); enhancer.setCallback(aop); Worker worker = (Worker) enhancer.create(); worker.dowork(); }代码第一行是要将动态生成的字节码文件持久化到磁盘,方便反编译观察。

  

利用 CGlib 的 Enhancer 对象,设置它的继承父类,设置回调类,即上面实现的 拦截类,然后用create 方法创造一个 Worker 类,实际这个类是 Worker 类的子类,然后调用dowork方法。执行结果如下:

  

  

可以看到我们织入的代码起作用了。

  

4、上面功能比较简单,它会横向切入被代理类的所有方法中,下来我们稍微做的复杂一点。控制一下,让有些方法被织入代码,有些不被织入代码,模仿 Spring aop ,我们新增一个注解,用于注解哪些方法要被横向切入。注解如下:

  

package kite.lab.spring.aop.AopWithCGlib;import java.lang.annotation.*;/** * CGLIB * * @author fengzheng */@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)@Documentedpublic @interface CGLIB { String value() default "";}5、然后再 Worker 中增加一个方法,并应用上面的注解

  

package kite.lab.spring.service;import kite.lab.spring.aop.AopWithCGlib.CGLIB;/** * Worker * * @author fengzheng */public class Worker { public String dowork(){ System.out.println("生活向来不易,我正在工作!"); return ""; } @CGLIB(value = "cglib") public void dowork2(){ System.out.println("生活如此艰难,我在奔命!"); }}我们在 dowrok2 上应用了上面的注解

  

6、在拦截方法中加入注解判断逻辑,如果加了上面的注解,就加入织入的代码逻辑,否则不加入,代码如下:

  

@Override public Object intercept(Object o, Method method, Object<> objects, MethodProxy methodProxy) throws Throwable { Annotation<> annotations = method.getDeclaredAnnotations(); boolean isCglib = false; for(Annotation annotation: annotations){ if (annotation.annotationType().getName().equals("kite.lab.spring.aop.AopWithCGlib.CGLIB")){ isCglib = true; } } if(isCglib) { System.out.println("嘿,你在干嘛?"); methodProxy.invokeSuper(o, objects); System.out.println("是的,你说的没错。"); } return null; }7、调用方法如下:

  

public static void main(String<> args) {System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "cglib"); MyAopWithCGlib aop = new MyAopWithCGlib(); Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(Worker.class); enhancer.setCallback(aop); Worker worker = (Worker) enhancer.create(); worker.dowork(); worker.dowork2(); }执行结果应该为 dowork 不执行被织入的逻辑,dowork2 执行被织入的代码逻辑,执行结果如下:

  

  

另外说一下,CGlib 不支持 final 类, CGlib 的执行速度比较快,但是创建速度比较慢,所以如果两种动态代理都适用的场景下,有大量动态代理类创建的场景下,用 JDK 动态代理模式,否则可以用 CGlib 。

  

标准的 Spring MVC 框架,一般都是一个服务接口类对应一个实现类,所以根据Spring AOP 的判断逻辑,应该大部分情况下都是使用的 JDK 动态代理模式。当然也可以手动改成 CGlib 模式。

  

最后掌握 AOP 的实现原理,你就掌握了又一个核心科技。回过头一看,害,也挺简单。

  


作者:古时的风筝
链接:https://juejin.im/post/5ed723606fb9a047a07f2ec2

以上就是帮企客小编为大家带来的全部内容,希望可以帮助到大家。

免责声明:本文为转载,非本网原创内容,不代表本网观点。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。

如有疑问请发送邮件至:bangqikeconnect@gmail.com