当前位置: 首页 > 后端技术 > Java

没有Spring如何自己实现AOP?面试必问,..

时间:2023-04-01 16:39:21 Java

作者:张希硕\来源:https://segmentfault.com/a/11...介绍开启to-do,注解认证承诺说明代理模式。正好遇到这样一个问题:抛开Spring,如何自己实现SpringAOP?我喜欢这样的问题。我可以挑战那些天天写、增、删、改、查,从来没想过的人。今天,我将向大家学习。SpringAOP模式。推荐一个SpringBoot基础教程和实例:https://github.com/javastacks...Proxy和decorator场景说明Proxy,意为替换,可以替换所有功能,即实现与原类相同的规范。代理模式与装饰者模式非常相似。前面的装饰器不太好,我们再举个例子再说。安静的午后,我来到咖啡厅,想喝杯咖啡。基本实现给你一个咖啡接口:publicinterfaceCoffee{/***打印当前咖啡的原料,即咖啡里有什么*/voidprintMaterial();}一个默认的苦咖啡实现:publicclassBitterCoffee实现Coffee{@OverridepublicvoidprintMaterial(){System.out.println("coffee");}}默认排序逻辑:publicclassMain{publicstaticvoidmain(String[]args){Coffeecoffee=newBitterCoffee();咖啡.printMaterial();}}点一杯咖啡。装潢师模式优雅的服务员端来咖啡喝了一口,苦涩的。想加糖,对服务员说:“您好,请给我的咖啡加糖”。/***糖装饰器,用于给咖啡加糖*/publicclassSugarDecoratorimplementsCoffee{/***持有的咖啡对象*/privatefinalCoffeecoffee;publicSugarDecorator(Coffeecoffee){this.coffee=coffee;}@OverridepublicvoidprintMaterial(){System.out.println("Sugar");这个.coffee.printMaterial();}}然后服务员拿了我的咖啡,去用SugarDecorator给咖啡加糖,最后放了Givemesweetenedcoffee。publicclassMain{publicstaticvoidmain(String[]args){Coffeecoffee=newBitterCoffee();咖啡=新的SugarDecorator(咖啡);咖啡.printMaterial();}}看看咖啡的成分,没错,确实加了糖!注意这两行:Coffeecoffee=newBitterCoffee();//我点了一杯苦咖啡coffee=newSugarDecorator(coffee);//往咖啡里加点糖。装饰者模式适合什么样的场景?我有一个对象,但是这个对象的功能并不令我满意,所以我会用装饰器来装饰它。代理模式周末,我抱着iPad来到咖啡店,准备享受一个安静的下午。“先生,您要喝什么?”彬彬有礼的服务员上前问道。上次点的咖啡太苦了,这次要加糖的。“我想要一杯加糖的咖啡。”公共类CoffeeWithSugar实现Coffee{privatefinalCoffeecoffee;publicCoffeeWithSugar(){this.coffee=newBitterCoffee();}@OverridepublicvoidprintMaterial(){System.out.println("Sugar");这个。咖啡。打印材料();}}这是加糖的咖啡。其实里面还是咖啡。只需添加一些食谱,就会创建一个新类,一种可以出现在菜单上的新饮料。订购咖啡:publicclassMain{publicstaticvoidmain(String[]args){Coffeecoffee=newCoffeeWithSugar();咖啡.printMaterial();}}正合我意,喝着咖啡度过了一个美好的下午。区别故事讲完了,两者都实现了对原对象的包装,持有原对象的实例,区别在于外在表现。Decorator模式:点了咖啡,觉得太苦了,不是我想要的,然后用decorator点了糖。咖啡coffee=newBitterCoffee();咖啡=新的SugarDecorator(咖啡);代理模式:直接点加糖的咖啡。咖啡coffee=newCoffeeWithSugar();区别非常细微,希望您不要混淆。批判翻看代理模式的相关资料,思路五花八门,理解方式也有很多种。还有,网上很多设计模式的文章都是你抄的,我抄你的。一个是错的,所有的都是错的。我想我需要更正它。谁说代理模式必须使用接口?代理模式是一种设计模式,设计模式不区分语言。如果一种语言没有界面,那它就不能是代理模式吗?只不过Java中的接口让我们可以按照依赖倒置的原则进行开发,降低耦合度。是否可以使用抽象类?是的。是否可以使用类继承?这也是可能的。思路明白了,拿什么写,不就是玩玩吗?另外,设计模式系列的面试题和答案都整理好了。微信搜索Java技术栈,后台发:面试,可以在线阅读。AOP设计模式是一种思想,所以我上面提到的代理模式不仅适用于接口,而且与SpringAOP密切相关。AOP:AspectOrientedProgramming,面向切面编程,是对面向对象编程的补充。不懂这句话,只要研究面向对象就知道为什么了。我们将声明方面,即切割将在方法之前、之后或同时在方法之前和之后执行。SpringAOP的实现是代理模式。场景刚好最近写一个短信验证码,就拿这个来举例。publicinterfaceSMSService{voidsendMessage();}publicclassSMSServiceImplimplementsSMSService{@OverridepublicvoidsendMessage(){System.out.println([孟云志]您正在进行密码重置操作,您的验证码为:1234,有效5分钟内请勿将验证码转发给他人。");}}主要功能:publicclassMain{publicstaticvoidmain(String[]args){SMSServicesmsService=newSMSServiceImpl();短信服务.sendMessage();短信服务.sendMessage();}}成本统计老板改需求了,发验证码要花钱,老板想看看短信花了多少钱。通常,按照Spring的思路,它必须声明一个切面来切割发送短信的方法,然后在切面统计短信的开销。只是现在没有框架,就是这个问题:如何脱离Spring,自己实现SpringAOP?写框架的时候自然要多考虑。我上面说的代理是静态代理,是在编译时确定的。框架实现是动态代理,需要在运行时生成代理对象,因为需要进行类扫描,看哪些类有切面需要生成代理对象。JDK动态代理写了一个统计短信费用的类,实现了InvocationHandler接口。写到这里,终于明白为什么每次后台调试都要跳转到invoke方法了。publicclassMoneyCountInvocationHandlerimplementsInvocationHandler{/***代理目标*/privatefinalObject目标;/***内部存储的总金额*/privateDoublemoneyCount;publicMoneyCountInvocationHandler(Objecttarget){this.target=target;这个.moneyCount=0.0;}@OverridepublicObjectinvoke(Objectproxy,Methodmethod,Object[]args)throwsThrowable{Objectresult=method.invoke(target,args);金钱数+=0.07;System.out.println("短信发送成功,总费用为:"+moneyCount+"元");返回结果;}}将main函数中的smsService替换为MoneyCountInvocationHandler处理的代理对象。publicclassMain{publicstaticvoidmain(String[]args){SMSServicesmsService=newSMSServiceImpl();smsService=(SMSService)Proxy.newProxyInstance(Main.class.getClassLoader(),newClass[]{SMSService.class},newMoneyCountInvocationHandler(smsService));短信服务.sendMessage();短信服务.sendMessage();}}根据InvocationHandler中的invoke方法动态生成一个类,该类实现了SMSService接口,代理对象就是用这个类实例化的。AOP实现了以上所有?写一个AOP不难吗?main函数的代码应该放在IOC容器初始化中,扫描包看哪些类需要生成代理对象,然后将代理对象构造到容器中。那么在invoke方法中,把成本统计的逻辑改成切面的逻辑不就好了吗?缺陷分析结束了吗?当然不是,上面的方法实现只对接口有效。因为JDK的动态代理就是生成一个实现相应接口的代理类。但是Spring不仅仅是通过接口注入的。@Autowiredprivate输入xxxxx;Spring的@Autowired使用声明的类型在容器中找到合适的对象,然后注入。接口是一种类型,类不也是一种类型吗?@AutowiredprivateSMSService短信服务;这个可以注射。@AutowiredprivateSMSServiceImpl短信服务;这个怎么样?也可以注射。因此,JDK动态代理无法代理直接注入的类类型。自古以来,cglib动态代理一直是时代造就英雄,而不是英雄造就时代。出现问题,英雄自然会出来解决。拯救世界的是cglib。如果JDK动态代理解决不了,就交给cglib解决。就此而言:@AutowiredprivateSMSServiceImplsmsService;不是使用接口注入,JDK动态代理无法解决。cglib是怎么解决的?它会在当前类的基础上动态生成一个子类,并将切面逻辑编织到子类中。然后使用子类对象来代理父类对象。所以我上面说:代理模式,不要拘泥于接口。因此,编织成功是子类可以覆盖父类的方法。所以cglib并不是万能的。该方法是最终的,子类不能重写它。当然,这无关紧要。总结一下你读到的内容?就是真正理解作者的思想,理解作者想要表扬什么,批评什么。框架学什么?不仅仅是为了提高开发效率,而是在使用的时候,就像和设计者交流一样,只有真正理解了框架设计者的思维,你才能理解一个框架。如果我们能做到这一点,为什么还要担心不能设计出一个真正属于我们自己的框架呢?近期热点文章推荐:1.1,000+Java面试题及答案(2021最新版)2.别在满屏的if/else中,试试策略模式,真的很好吃!!3.操!Java中xx≠null的新语法是什么?4、SpringBoot2.6正式发布,一大波新特性。.5.《Java开发手册(嵩山版)》最新发布,赶快下载吧!感觉不错,别忘了点赞+转发!