AOPの静的エージェントと動的エージェント
転載先:http://listenzhangbin.com/post/2016/09/spring-aop-cglib/
AOP(Aspect Orient Prograamming)は、一般的には、指向プログラミングと呼ばれ、対象に向けた補足として、処理システムにおいて各モジュールに分布する横断的な注目点、例えば、事務管理、ログ、キャッシュなどに用いられます.AOP実現の鍵はAOPフレームが自動的に作成されるAOPエージェントであり、AOPエージェントは主に静的エージェントと動的エージェントに分けられ、静的エージェントの代表はAsppectJである.動的エージェントはSpring AOPに代表される.本論文はAspectJとSpring AOPの実現をそれぞれ分析して紹介します.
AsppectJのコンパイルでAOPを強化します.
前に述べたように、AsppectJは静的エージェントの強化であり、静的エージェントとはAOPフレームワークがコンパイル段階でAOPエージェントクラスを生成するので、コンパイル時に強化されるとも言われています.
実例をあげて話す.まず普通のハロークラスがあります.
コンパイルしたHello.classを確認してください.
Spring AOPでAOPを実現します.
Aspring AOPで使用されている動的エージェントとは、AOPフレームはバイトコードを修正することなくメモリに一時的にAOPオブジェクトを生成し、このAOPオブジェクトは目標オブジェクトのすべての方法を含み、特定の接点で強化処理を行い、元のオブジェクトを元に戻す方法である.
Spring AOPにおける動的エージェントは主に2つの方法があり、JDKダイナミックエージェントとCGLOIBダイナミックエージェントがあります.JDK動的エージェントは、反射によってプロキシされたクラスを受信し、プロキシが要求されるクラスは、インターフェースを実装しなければならない.JDKダイナミックエージェントの中核は、InvocationHandlerインターフェースとProxyクラスです.
ターゲットクラスがインターフェースを実現していない場合、Spring AOPはCGLOIBを使用してターゲットクラスを動的に代理することを選択します.CGLOIB(Code Generation Library)は、コード生成のクラスであり、実行時にある種類のサブクラスを動的に生成することができます.CGLOIBは継承方式による動的エージェントですので、ある種類がfinalと表記されていると、CGLOIBを動的エージェントとして使用することはできません.
以上の言い方を検証するために、簡単なテストができます.まずインターフェースを実現する状況をテストします.
インターフェースを定義する
Asppectを定義する
また、インターフェースを実現しない状況を見て、Chinese類を修正します.
結び目
AspectJはコンパイル時にターゲットオブジェクトを強化し、Spring AOPの動的エージェントは各動作時に動的に強化され、AOPプロキシオブジェクトを生成し、AOPプロキシオブジェクトを生成するタイミングが違っています.相対的にAsppectJの静的プロキシ方式はより良い性能を持っていますが、AspectJは特定のコンパイラを必要として処理します.Spring AOPは特定のコンパイラ処理が不要です.
参考文献 Spring AOP実現原理とCGLOIBアプリケーション Spring容器AOPの実現原理——ダイナミックエージェント AOPの下層実装-CGLOIB動的エージェントとJDK動的エージェント
AOP(Aspect Orient Prograamming)は、一般的には、指向プログラミングと呼ばれ、対象に向けた補足として、処理システムにおいて各モジュールに分布する横断的な注目点、例えば、事務管理、ログ、キャッシュなどに用いられます.AOP実現の鍵はAOPフレームが自動的に作成されるAOPエージェントであり、AOPエージェントは主に静的エージェントと動的エージェントに分けられ、静的エージェントの代表はAsppectJである.動的エージェントはSpring AOPに代表される.本論文はAspectJとSpring AOPの実現をそれぞれ分析して紹介します.
AsppectJのコンパイルでAOPを強化します.
前に述べたように、AsppectJは静的エージェントの強化であり、静的エージェントとはAOPフレームワークがコンパイル段階でAOPエージェントクラスを生成するので、コンパイル時に強化されるとも言われています.
実例をあげて話す.まず普通のハロークラスがあります.
public class Hello {
public void sayHello() {
System.out.println("hello");
}
public static void main(String[] args) {
Hello h = new Hello();
h.sayHello();
}
}
AsppectJを使ってAsppectを作成します.public aspect TxAspect {
void around():call(void Hello.sayHello()){
System.out.println(" ...");
proceed();
System.out.println(" ...");
}
}
ここではSpringのような事務の場面をシミュレーションした.AsppectJのコンパイラを使ってコンパイルします.ajc -d . Hello.java TxAspect.aj
コンパイルが完了したら、このハロークラスを実行します.以下の出力が見られます. ...
hello
...
明らかに、AOPはすでに発効しました.一体、AsppectJはHello類を修正しないで、ハロー類のために新しい機能を追加したのですか?コンパイルしたHello.classを確認してください.
public class Hello {
public Hello() {
}
public void sayHello() {
System.out.println("hello");
}
public static void main(String[] args) {
Hello h = new Hello();
sayHello_aroundBody1$advice(h, TxAspect.aspectOf(), (AroundClosure)null);
}
}
これはAsppectJのスタティックエージェントであり、コンパイル段階でAsppectをJavaバイトコードに編入し、実行する時は強化された後のAOPオブジェクトである.public void ajc$around$com_listenzhangbin_aop_TxAspect$1$f54fe983(AroundClosure ajc$aroundClosure) {
System.out.println(" ...");
ajc$around$com_listenzhangbin_aop_TxAspect$1$f54fe983proceed(ajc$aroundClosure);
System.out.println(" ...");
}
Asppectがコンパイルした後のclassファイルから、実行の論理がより明確に見られます.プロシージャ方法は、被プロキシクラスをリピートして実行する方法です.Spring AOPでAOPを実現します.
Aspring AOPで使用されている動的エージェントとは、AOPフレームはバイトコードを修正することなくメモリに一時的にAOPオブジェクトを生成し、このAOPオブジェクトは目標オブジェクトのすべての方法を含み、特定の接点で強化処理を行い、元のオブジェクトを元に戻す方法である.
Spring AOPにおける動的エージェントは主に2つの方法があり、JDKダイナミックエージェントとCGLOIBダイナミックエージェントがあります.JDK動的エージェントは、反射によってプロキシされたクラスを受信し、プロキシが要求されるクラスは、インターフェースを実装しなければならない.JDKダイナミックエージェントの中核は、InvocationHandlerインターフェースとProxyクラスです.
ターゲットクラスがインターフェースを実現していない場合、Spring AOPはCGLOIBを使用してターゲットクラスを動的に代理することを選択します.CGLOIB(Code Generation Library)は、コード生成のクラスであり、実行時にある種類のサブクラスを動的に生成することができます.CGLOIBは継承方式による動的エージェントですので、ある種類がfinalと表記されていると、CGLOIBを動的エージェントとして使用することはできません.
以上の言い方を検証するために、簡単なテストができます.まずインターフェースを実現する状況をテストします.
インターフェースを定義する
public interface Person {
String sayHello(String name);
}
実装クラス@Component
public class Chinese implements Person {
@Timer
@Override
public String sayHello(String name) {
System.out.println("-- sayHello() --");
return name + " hello, AOP";
}
public void eat(String food) {
System.out.println(" :" + food);
}
}
ここの@Timer注解は自分で定義した普通の注釈で、Pointcutをマークします.Asppectを定義する
@Aspect
@Component
public class AdviceTest {
@Pointcut("@annotation(com.listenzhangbin.aop.Timer)")
public void pointcut() {
}
@Before("pointcut()")
public void before() {
System.out.println("before");
}
}
実行@SpringBootApplication
@RestController
public class SpringBootDemoApplication {
// Person
@Autowired
private Person chinese;
@RequestMapping("/test")
public void test() {
chinese.sayHello("listen");
System.out.println(chinese.getClass());
}
public static void main(String[] args) {
SpringApplication.run(SpringBootDemoApplication.class, args);
}
}
出力before
-- sayHello() --
class com.sun.proxy.$Proxy53
このタイプはcomp.sun.proxy.53で、前述のProxy類ですので、ここのSpring AOPはJDKのダイナミックエージェントを使用しています.また、インターフェースを実現しない状況を見て、Chinese類を修正します.
@Component
public class Chinese {
@Timer
// @Override
public String sayHello(String name) {
System.out.println("-- sayHello() --");
return name + " hello, AOP";
}
public void eat(String food) {
System.out.println(" :" + food);
}
}
実行@SpringBootApplication
@RestController
public class SpringBootDemoApplication {
// Chinese
@Autowired
private Chinese chinese;
@RequestMapping("/test")
public void test() {
chinese.sayHello("listen");
System.out.println(chinese.getClass());
}
public static void main(String[] args) {
SpringApplication.run(SpringBootDemoApplication.class, args);
}
}
出力before
-- sayHello() --
class com.listenzhangbin.aop.Chinese$$EnhancerBySpringCGLIB$$56b89168
CGLOIBによってクラスが強化された、いわゆる動的エージェントが見られます.ここのCGLOIBエージェントはSpring AOPの代理であり、このクラスはいわゆるAOPエージェントであり、AOPエージェント類は接点で動的に補強処理に織り込まれている.結び目
AspectJはコンパイル時にターゲットオブジェクトを強化し、Spring AOPの動的エージェントは各動作時に動的に強化され、AOPプロキシオブジェクトを生成し、AOPプロキシオブジェクトを生成するタイミングが違っています.相対的にAsppectJの静的プロキシ方式はより良い性能を持っていますが、AspectJは特定のコンパイラを必要として処理します.Spring AOPは特定のコンパイラ処理が不要です.
参考文献