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

Java
208
0
0
2024-01-06
标签   Spring

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

问题来源

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

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

public class PlaceOrderCommand {

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

        //开始事务
        beginTransaction();

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

        //结束事务
        commitTransaction();

        PerFormanceUtil.endTime();

        logger.debug("");

    }  
}

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

模式模式:模板方法

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

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() {

    }
}

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

BaseCommand baseCommand = new PlaceOrderCommand();
baseCommand.execute();

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

设计模式:装饰者

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

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("执行订单业务");
    }
}

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

Command command =new LoggerDecorator(new PerformanceDecorator(new PlaceOrderCommand()));
command.execute();

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

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

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

实现AOP

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

//定义切面
@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方法.可以使用下面方法调用

ApplicationContext context = new AnnotationConfigApplicationContext(AopConfig.class);
PlaceOrderCommand order = (PlaceOrderCommand) context.getBean("order");
order.execute();

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