前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Spring入门到精通-AOP到底解决了什么问题呢

Spring入门到精通-AOP到底解决了什么问题呢

作者头像
小土豆Yuki
发布2023-09-02 15:48:39
1100
发布2023-09-02 15:48:39
举报
文章被收录于专栏:洁癖是一只狗洁癖是一只狗

今天我们就搞一下Spring中最重要的概念AOP和IOC,这次我们重点说一下AOP

问题来源

系统设计中有一个原则就是低耦合,高内聚,分而治之等等,我们项目代码中往往会重复的代码,因此我们就会把这些代码提取出来,写一个工具类,比如我们的日志打印,安全,事务,性能统计,让业务功能可以直接调用,如下图

按照上面的逻辑,我们写代码,就会如下写代码

代码语言:javascript
复制
public class PlaceOrderCommand {

    public void execute(){
        //打印日志
        Logger logger =Logger.getLogger("");
        logger.debug();
        //性能统计
        PerFormanceUtil.startTime();

        //开始事务
        beginTransaction();

        ///这里才是真正执行业务的地方

        //结束事务
        commitTransaction();

        PerFormanceUtil.endTime();

        logger.debug("");

    }  
}

实际上功能上是没有问题的,但是感觉不太顺眼,一个方法中真正的业务代码只有那么几句,其他全部是非业务性代码,往往这些非业务性代码,不一定都记得写

模式模式:模板方法

升级问题,一般部门的大佬就站出来了,大佬就说,小问题,我写一个模板方法,你们以后就按我的壳子写代码就行,如下代码

代码语言:javascript
复制
public abstract  class BaseCommand {
    public void execute(){
        //打印日志
        Logger logger =Logger.getLogger("");
        logger.debug();
        //性能统计
        PerFormanceUtil.startTime();
        //开始事务
        beginTransaction();
        ///这里才是真正执行业务的地方
        doBusiness();
        //结束事务
        commitTransaction();
        PerFormanceUtil.endTime();
        logger.debug("");
    }
    public abstract void  doBusiness();
}

class PlaceOrderCommand extends BaseCommand{
    @Override
    public void doBusiness() {
    }
}

class PaymentCommand extends BaseCommand{
    @Override
    public void doBusiness() {

    }
}

直接把乱七八糟的代码,全部抽到了父类,只有一个抽象接口让子类实现,此时代码好看多了,大佬就是牛.以后你们只关心业务代码即可,调用也很简单,如下代码

代码语言:javascript
复制
BaseCommand baseCommand = new PlaceOrderCommand();
baseCommand.execute();

此时,领导提出了质疑,你这样搞,感觉有点过分,子类的一切都得由父类支配,父类使用的功能以及执行顺序,子类必须无条件接受,但是如果子类就是不愿意打印日志呢,是不是没有办法了.

设计模式:装饰者

大家都看向了大佬,看看大佬如何解决呢,大佬陷入了沉思,默默抽了根烟,思路一下打开了,利用装饰者模式,针对上面问题,则可以带来更大的灵活性

代码语言:javascript
复制
public interface Command {
    public void execute();
}
//日志打印
public class LoggerDecorator implements Command {

    private Command command;

    public LoggerDecorator(Command command) {
        this.command = command;
    }

    public void execute() {
        Logger logger = Logger.getLogger("");
        logger.debug();
        command.execute();
        logger.debug();
    }
}
//性能统计
public class PerformanceDecorator implements Command {

    private Command command;

    public PerformanceDecorator(Command command) {
        this.command = command;
    }

    public void execute() {
        PerFormanceUtil.startTime();
        command.execute();
        PerFormanceUtil.endTime();
    }
}
//订单业务
public class PlaceOrderCommand implements Command {

    public void execute() {
        System.out.println("执行订单业务");
    }
}

现在我们想让订单业务能够打印日志和性能统计,直接使用下面代码

代码语言:javascript
复制
Command command =new LoggerDecorator(new PerformanceDecorator(new PlaceOrderCommand()));
command.execute();

可以随意组合非业务功能进行使用,接近完美,领导也给出了肯定,但是领导说,那有的业务模块就是没有实现Command类,那要怎么办呢?

大家又看向了大佬,大佬此时有陷入了沉思,连续抽了两根烟,突然灵光一闪最好的办法是,把日志,安全,事务,性能统计这样的非功能向代码和业务代码完全隔离开,因为他们的关注点和业务的关注点完全不同,他们应该是正交

把业务功能看成一层层面包,把写日志和事务,安全,性能统计,都是一个个切面,如果我们把这些切面和业务独立来,并且能非常灵活的织入业务代码中,那么我们的代码是不是就妥妥的完美呢?

代码语言:javascript
复制
代码语言:javascript
复制

实现AOP

最后的结论,使用面向切面编程AOP,不用实现什么杂七杂八的接口了,比如以事物为例

代码语言:javascript
复制
//定义切面
@Aspect
public class Transaction {

   //定义切点
    @Pointcut("execution(* com.example.demo.PlaceOrderCommand.*(..))")
    public void pointCut(){}
   //前置通知
    @Before("pointCut()")
    public void begin(){
        System.out.println("事务开始");
    }
    //后置通知
    @After("pointCut()")
    public void end(){
        System.out.println("事务结束");
    }
}

//配置文件,开启切面生效
@EnableAspectJAutoProxy
@Configuration
public class AopConfig  {


    @Bean(name = "order")
    public PlaceOrderCommand getPlaceOrderCommand(){
        return new PlaceOrderCommand();
    }

    @Bean
    public Transaction getTransaction(){
        return new Transaction();
    }
}

//订单页面
public class PlaceOrderCommand {
    public void  execute(){
        System.out.println("执行订单业务");
    }
}

我们想要达到的目的,就是对于com.example.demo.PlaceOrderCommand类中的execute方法,在调用execute方法的时候就会执行事物的begin和end方法.可以使用下面方法调用

代码语言:javascript
复制
代码语言:javascript
复制
代码语言:javascript
复制
ApplicationContext context = new AnnotationConfigApplicationContext(AopConfig.class);
PlaceOrderCommand order = (PlaceOrderCommand) context.getBean("order");
order.execute();

这里其实已经说完了AOP,但是我们要明确一个东西,我们上面调用的order对象已经不是原来的order对象了,最后执行的对象其实是一个代理类

代码语言:javascript
复制
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2023-06-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 洁癖是一只狗 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档