15. AOP
第一次听说AOP是在spring框架中听到的。
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
看文字晦涩难懂,直接看个例子。
下面的这个是模板方法的实现。
TemplatePatternDemo,主类
public class TemplatePatternDemo {
public static void main(String[] args) {
Game game = new Cricket();
game.play();
System.out.println();
game = new Football();
game.play();
}
}
Game抽象类
其中,play方法的final很重要,防止了子类覆写此方法,打乱了执行顺序。
public abstract class Game {
abstract void initialize();
abstract void startPlay();
abstract void endPlay();
//模板
public final void play(){
//初始化游戏
initialize();
//开始游戏
startPlay();
//结束游戏
endPlay();
}
}
Cricket子类
public class Cricket extends Game {
@Override
void endPlay() {
System.out.println("Cricket Game Finished!");
}
@Override
void initialize() {
System.out.println("Cricket Game Initialized! Start playing.");
}
@Override
void startPlay() {
System.out.println("Cricket Game Started. Enjoy the game!");
}
}
Football子类
public class Football extends Game {
@Override
void endPlay() {
System.out.println("Football Game Finished!");
}
@Override
void initialize() {
System.out.println("Football Game Initialized! Start playing.");
}
@Override
void startPlay() {
System.out.println("Football Game Started. Enjoy the game!");
}
}
现在我要在每个球类的play()方法都要加一个计算玩了多久的功能。如果要实现可以每个paly()方法中再加一个计算时间的功能。这里只有两种球类运动,如果有更多的运动呢?那这个要每个都加,代码冗余。
我们如果把这个功能提取出来,用动态代理模式,完成这个功能的添加。这样就 减少了代码重复问题。这儿就是用动态代理模式实现了AOP。
康康怎么实现。
因为动态代理需要用接口实现。所以我这里在Game抽象类的上层加入了一个接口
public interface GameInterface {
void initialize();
void startPlay();
void endPlay();
//模板
void play();
}
public abstract class Game implements GameInterface{
//模板
@Override
public final void play(){
//初始化游戏
initialize();
//开始游戏
startPlay();
//结束游戏
endPlay();
}
}
public class Cricket extends Game {
@Override
public void endPlay() {
System.out.println("Cricket Game Finished!");
}
@Override
public void initialize() {
System.out.println("Cricket Game Initialized! Start playing.");
}
@Override
public void startPlay() {
System.out.println("Cricket Game Started. Enjoy the game!");
}
}
public class Football extends Game {
@Override
public void endPlay() {
System.out.println("Football Game Finished!");
}
@Override
public void initialize() {
System.out.println("Football Game Initialized! Start playing.");
}
@Override
public void startPlay() {
System.out.println("Football Game Started. Enjoy the game!");
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;
public class GameInovationHandler implements InvocationHandler {
private GameInterface gameInterface ;
public GameInovationHandler(GameInterface gameInterface){
this.gameInterface = gameInterface;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(System.currentTimeMillis()+"开始玩的");
method.invoke(gameInterface,args);
TimeUnit.SECONDS.sleep(2);
System.out.println(System.currentTimeMillis()+"结束的");
return null;
}
}
public class TemplatePatternDemo {
public static void main(String[] args) {
GameInterface game = new Cricket();
InvocationHandler gameInovationHandler = new GameInovationHandler(game);
GameInterface gameProxy = (GameInterface) Proxy.newProxyInstance(game.getClass().getClassLoader(),
game.getClass().getSuperclass().getInterfaces(), gameInovationHandler);
gameProxy.play();
System.out.println();
game = new Football();
gameInovationHandler = new GameInovationHandler(game);
gameProxy = (GameInterface) Proxy.newProxyInstance(game.getClass().getClassLoader(),
game.getClass().getSuperclass().getInterfaces(), gameInovationHandler);
gameProxy.play();
}
}
输出结果
1620121457799开始玩的
Cricket Game Initialized! Start playing.
Cricket Game Started. Enjoy the game!
Cricket Game Finished!
1620121459805结束的
1620121459805开始玩的
Football Game Initialized! Start playing.
Football Game Started. Enjoy the game!
Football Game Finished!
1620121461807结束的
以上就是用动态代理实现AOP的例子。
下面来康康名词。
切面:横切关注点,模块化后称为切面。个人理解是抽取出来的相同代码。比如上述invoke方法抽取出来的功能(记录时间)的代码。
连接点(Join point):程序执行过程中可被识别的某个点叫做连接点。就是有意义的代码。比如赋值操做,方法调用。
增强/通知(Advice):连接点处执行的逻辑叫增强。
上边的例子里是环绕增强。
//增强
System.out.println(System.currentTimeMillis()+"开始玩的");
method.invoke(gameInterface,args);
TimeUnit.SECONDS.sleep(2);
//增强
System.out.println(System.currentTimeMillis()+"结束的");
切入点(Pointcut):某些特定连接点的逻辑叫做切入点。根据切入点我们可以判断哪些连接点需要增强。
织入(Weaving):把这些水平的切面连接到执行过程以组合成完整流程的行为称为织入。织入可以发生在编辑期,编译期,类加载期,运行期。