今天我们就搞一下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对象了,最后执行的对象其实是一个代理类