上篇文章举了个例子,就是公司车辆出门登记这件事情,本篇我们先在不使用AOP的情况下实现这个功能,然后看看有什么毛病,然后再使用AOP实现它,看看有什么好处。
本章建立一个简单的Java工程就可以了,除了导入之前一直说的jar包,还需要一个cglib-3.2.10.jar
,这个是Spring AOP所需要的。
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("轿车进门"); } }
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; } }
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("出门登记");
,这就是重复代码啊。
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("轿车进门"); } }
注意要添加注解@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; } }
此处的切面,就是指的车辆出门这件事,对所有车辆出门都应该执行登记,定义切面类如下:
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("出门登记"); } }
同样进行测试:
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(); } }
结果:
出门登记 卡车出门 出门登记 轿车出门
可见没问题。
要先理解例子,再了解概念,否则容易懵。
execution(public void out())")
即为接入点,一般切入点描述了一组接入点。其实AOP应用很简单,首先发现了有重复代码,然后这些重复代码有共同的使用场合,然后就可以定义一个切面,通过切入点把这些场合找出来,然后执行通知方法。
这样所有切入点描述的方法(即接入点)执行的时候,都会被通知所影响了。
当然,切入点定义很灵活,通知类型也很灵活,下篇文章我们来具体讲解。