1. 9IM首页
  2. 热点

Spring大神之路(41)–不使用AOP与使用AOP对比实例

背景

上篇文章举了个例子,就是公司车辆出门登记这件事情,本篇我们先在不使用AOP的情况下实现这个功能,然后看看有什么毛病,然后再使用AOP实现它,看看有什么好处。

环境

本章建立一个简单的Java工程就可以了,除了导入之前一直说的jar包,还需要一个cglib-3.2.10.jar,这个是Spring AOP所需要的。

不使用AOP实现

1、先来两种车,卡车(拉货)、轿车(拉人)。
package org.maoge.aopdemo.noaop;
/**
 * 卡车
 */
public class Truck {
	public void out() {
		System.out.println("出门登记");
		System.out.println("卡车出门");
	}
	public void in() {
		System.out.println("卡车进门");
	}
}
package org.maoge.aopdemo.noaop;
/**
 * 轿车
 */
public class Car {
	public void out() {
		System.out.println("出门登记");
		System.out.println("轿车出门");
	}
	public void in() {
		System.out.println("轿车进门");
	}
}
2、编写配置类,定义容器及车辆bean
package org.maoge.aopdemo.noaop;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
 * 配置类
 */
@Configuration // 配置类,用来配置容器
public class SpringConfig {
	@Bean // 注册卡车bean
	public Truck Truck() {
		Truck truck = new Truck();
		return truck;
	}
	@Bean // 注册轿车bean
	public Car car() {
		Car car = new Car();
		return car;
	}
}
3、测试运行
package org.maoge.aopdemo.noaop;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main {
	public static void main(String[] args) {
		// 获取容器
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
		// 取出bean
		Truck truck = (Truck) context.getBean(Truck.class);
		// 执行bean方法
		truck.out();
		Car car = (Car) context.getBean(Car.class);
		car.out();
	}
}

执行结果如下,这个没啥疑问,常规的JavaConfig的Spring案例。

出门登记
卡车出门
出门登记
轿车出门

有什么问题

问题就是,实际上登记这个事情,和出门这个事情没关系。此时如果再来一辆车要出门,还要单独写System.out.println("出门登记");,这就是重复代码啊。

使用AOP实现

1、车辆出门不需要自行登记了
package org.maoge.aopdemo.useaop;
/**
 * 货车
 */
public class Truck {
	public void out() {
		//System.out.println("出门登记");//无须自行登记
		System.out.println("卡车出门");
	}
	public void in() {
		System.out.println("卡车进门");
	}
}
package org.maoge.aopdemo.useaop;
/**
 * 轿车
 */
public class Car {
	public void out() {
		//System.out.println("出门登记");//无须自行登记
		System.out.println("轿车出门");
	}
	public void in() {
		System.out.println("轿车进门");
	}
}
2、配置类要开启AOP功能

注意要添加注解@EnableAspectJAutoProxy,以便启用AOP。

package org.maoge.aopdemo.useaop;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
/**
 * 配置类
 */
@Configuration // 配置类,用来配置容器
@EnableAspectJAutoProxy // 开启AOP
@ComponentScan(basePackages = { "org.maoge.aopdemo.useaop" }) // 扫描包以便发现注解配置的bean
public class SpringConfig {
	@Bean // 注册卡车bean
	public Truck Truck() {
		Truck truck = new Truck();
		return truck;
	}
	@Bean // 注册轿车bean
	public Car car() {
		Car car = new Car();
		return car;
	}
}
3、编写切面

此处的切面,就是指的车辆出门这件事,对所有车辆出门都应该执行登记,定义切面类如下:

package org.maoge.aopdemo.useaop;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
/**
* 车辆出门切面
*/
@Component//切面也是Spring的bean
@Aspect//使用该注解标志此类是一切面
public class OutAspect {
   /**
    * 出门登记
    */
   public void outNote() {
   	System.out.println("出门登记");
   }
}

此时我们已经定义了一个切面,且切面的方法执行出门登记。但是我们还没告诉计算机,这个切面是针对的什么事情。接下来我们就具体制定切面针对的事情:

package org.maoge.aopdemo.useaop;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
/**
 * 车辆出门切面
 */
@Component//切面也是Spring的bean
@Aspect//使用该注解标志此类是一切面
public class OutAspect {
	/*
	 * @Before表示在切面指定的事件之前执行outNote方法
	 * execution(public void out())表示切面切的是public类型的、返回值是void、名字是out的、无参数的方法
	 * 也就是说针对public void out()这种类型的方法,在执行前执行outNote方法
	 */
	@Before("execution(public void out())")
	public void outNote() {
		System.out.println("出门登记");
	}
}
4、测试

同样进行测试:

package org.maoge.aopdemo.useaop;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main {
	public static void main(String[] args) {
		// 获取容器
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
		// 取出bean
		Truck truck = (Truck) context.getBean(Truck.class);
		// 执行bean方法
		truck.out();
		Car car = (Car) context.getBean(Car.class);
		car.out();
	}
}

结果:

出门登记
卡车出门
出门登记
轿车出门

可见没问题。

几个概念

要先理解例子,再了解概念,否则容易懵。

  • 通知:就是具体发现切面相应的事务时候,应该执行的指定行为,上面outNote就是通知。
  • 接入点:实际代码中需要运行通知的点,比如上面Car类、Truck类中的out()方法,就是接入点。
  • 切入点:注意切入点是描述接入点的表达式,上面execution(public void out())")即为接入点,一般切入点描述了一组接入点。
  • 目标:执行逻辑时被更改的其方法运行的对象,例如上面的car和truck这两个bean,他们的out方法都被改了。
  • 编织:是切面切入对象的过程,此时可以简单理解为切面生效的过程,这个属于技术方面了。

总结

其实AOP应用很简单,首先发现了有重复代码,然后这些重复代码有共同的使用场合,然后就可以定义一个切面,通过切入点把这些场合找出来,然后执行通知方法。

这样所有切入点描述的方法(即接入点)执行的时候,都会被通知所影响了。

当然,切入点定义很灵活,通知类型也很灵活,下篇文章我们来具体讲解。

原创文章,作者:9IM,如若转载,请注明出处:https://www.9im.cn/1425.html

发表评论

电子邮件地址不会被公开。 必填项已用*标注