SpringAOPでのJDK動的プロキシの実現
動的プロキシメカニズム
-
InvocationHandlerインターフェイスを実装して、独自の呼び出しハンドラーを作成します
-
ClassLoaderオブジェクトとProxyクラスのインターフェイスのセットを指定して、動的プロキシクラスを作成します
-
動的プロキシクラスのコンストラクタはリフレクションメカニズムを介して取得され、パラメータタイプはコールプロセッサインターフェイスタイプのみです。
-
動的プロキシクラスインスタンスはコンストラクターを介して作成され、プロセッサーオブジェクトは構築中にパラメーターとして呼び出されます。
AOP思考
-
OOPは、カプセル化、継承、多態性などの概念を導入して、オブジェクトの階層を確立し、パブリック動作のコレクションをシミュレートします。
-
AOPテクノロジーは、「クロスカット」と呼ばれる手法を使用して、カプセル化されたオブジェクトの内部を分析し、複数のクラスに影響を与える一般的な動作を再利用可能なモジュールにカプセル化します。これにより、システムコードの重複を減らし、モジュール間の重複を減らすことができます。結合の程度は、将来の操作性と保守性を助長します。
-
AOPは、ソフトウェアシステムをコアの懸念と分野横断的な懸念の2つの部分に分けます。ビジネス処理の主なプロセスはコアの懸念であり、それとはほとんど関係のない部分は分野横断的な懸念です。
AopProxyFactoryを実装するための動的プロキシソースコード
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: " +
"Either an interface or a target is required for proxy creation.");
}
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
// 表示在有接口实现的时候采用JDK动态代理
return new JdkDynamicAopProxy(config);
}
// 在没有接口实现的时候采用Cglib动态代理
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
春のAOPの実装
プログラムは上から下に実行され、主要なビジネスロジックとはほとんど関係がありません。AOPアスペクト指向プログラミングは、主要なビジネスを分野横断的なコードから分離し、デカップリングを実現することです。
一般的な実装は次のとおりです。
- 統合ログ処理
- 統一された例外処理
- 春のトランザクション管理
コード
インターフェイスを定義する
/**
* @ClassName: BuyService
* @Description: 购买基础接口
* @Author: 尚先生
* @CreateDate: 2019/6/17 14:52
* @Version: 1.0
*/
public interface BuyService {
String buyPhone(BuyService buyService);
String buyComputer(BuyService buyService);
}
実装クラスを定義する
/**
* @ClassName: BusiServiceImpl
* @Description: 购买实现类
* @Author: 尚先生
* @CreateDate: 2019/6/17 14:53
* @Version: 1.0
*/
@Service
public class BuyServiceImpl implements BuyService {
@Intercept("buyPhone")
@Override
public String buyPhone(BuyService buyService) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("==========当前类描述 " + buyService.getClass().getName() + "=============" + " buyPhone");
this.buyComputer(this);
return "buy phone";
}
@Intercept("buyComputer")
@Override
public String buyComputer(BuyService buyService) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("==========当前类描述 " + buyService.getClass().getName() + "=============" + " buyComputer");
return "buy computer";
}
}
カスタムブロック注釈
/**
* @ClassName: Intercept
* @Description: 自定义拦截器注解
* @Author: 尚先生
* @CreateDate: 2019/6/17 16:28
* @Version: 1.0
*/
@Target({
ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Intercept {
String value() default "";
}
カスタムインターセプターの実装
/**
* @ClassName: Interceptor
* @Description: 拦截器实现类
* @Author: 尚先生
* @CreateDate: 2019/6/17 15:12
* @Version: 1.0
*/
@Component
@Aspect
public class Interceptor {
@Pointcut(value = "@annotation(com.learn.demo.java.proxy.Intercept)")
public void buySomething() {
System.out.println("===========自定义切入点===============");
}
@Around("buySomething()")
public Object around(ProceedingJoinPoint point) throws Throwable {
try {
//通过获取 Intercept 注解
Method proxyMethod = ((MethodSignature) point.getSignature()).getMethod();
Method targetMethod = point.getTarget().getClass().getMethod(proxyMethod.getName(), proxyMethod.getParameterTypes());
Intercept intercept = targetMethod.getAnnotation(Intercept.class);
String methodName = targetMethod.getName();
//处理注解逻辑
String value = intercept.value();
System.err.println("=========== " + methodName + " 获取前置拦截信息 ===========" + value);
return point.proceed();
} catch (Throwable e) {
System.out.println("执行异常"+ e.getMessage());
}finally {
System.err.println("=========== " + " 后置处理结果返回 ===========");
}
return "执行异常,请查看详细日志信息";
}
}
カスタムインターセプター構成クラス
/**
* @ClassName: AspectJConfig
* @Description: 开启Spring对AspectJ的支持
* @Author: 尚先生
* @CreateDate: 2019/6/17 18:39
* @Version: 1.0
*/
@Configuration
@ComponentScan("com.learn.demo.java.proxy")
@EnableAspectJAutoProxy
public class AspectJConfiguration {
}
ブートクラスを開始します
/**
* @ClassName: Bootstrap
* @Description: 启动测试类
* @Author: 尚先生
* @CreateDate: 2019/6/17 14:58
* @Version: 1.0
*/
public class Bootstrap {
public static void main(String[] args) {
// spring 采用的 jdk 动态代理
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.scan("com.learn.demo.java.proxy");
context.register(Interceptor.class);
context.refresh();
BuyService bean = context.getBean("buyServiceImpl", BuyService.class);
String phone = bean.buyPhone(bean);
System.err.println("=========Bootstrap.class============== " + phone);
// 输出代理对象 class 文件
createProxyClassFile();
}
/**
* 生成代理文件
*/
private static void createProxyClassFile() {
String name = "ProxyBuyService";
byte[] data = ProxyGenerator.generateProxyClass(name,
new Class[] {
BuyService.class});
FileOutputStream out = null;
try {
out = new FileOutputStream("D://" + name + ".class");
out.write(data);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (null != out)
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
の結果
=========== buyPhone 获取前置拦截信息 ===========buyPhone
==========当前类描述 com.sun.proxy.$Proxy22============= buyPhone
=========== 后置处理结果返回 ===========
=========Bootstrap.class============== buy phone
==========当前类描述 com.learn.demo.java.proxy.BuyServiceImpl============= buyComputer
プロセス分析
-
実行結果から、buyPhone()で実行されたオブジェクトcom.sun.proxy。$ Proxy22を明確に確認し、this.buyComputer(this);を実行すると、実行されたオブジェクトはcom.learn.demo.java.proxy.BuyServiceImplになります。 、したがって、アスペクト指向のプログラミングでは、buyPhone()メソッドのみがインターセプトされます。
-
主な理由は、生成されたプロキシオブジェクトがプロキシオブジェクトと同じではないため、プロキシオブジェクトは、全面的に行われることなく、直接呼び出しであるかのようにクラスメソッドを呼び出します。
-
コードはGitHubで更新されました。詳細については、dwyanewedeをフォローしてください。JDK動的プロキシの詳細については、前の記事「JDK動的プロキシの実装原理」を参照してください。
より優れた記事
JDK動的プロキシ実装の原則
https://blog.csdn.net/shang_xs/article/details/92772437javaの
小学生
https://blog.csdn.net/shang_xs