最初の2つの記事の基礎の後、ついにSpring AOP知識の学習に来ました
AOPについて説明する前に、AOPで使用されている設計パターン(プロキシ設計パターン)について説明しましょう
エージェントとは何ですか?なぜエージェントが必要なのですか?
人生の小さな例:
现在很多公寓式租房就是采用代理模式
角色:
房东、租房代理、房客
角色需求:
房东:需要房客入租客房,只想签合同收钱,但房源信息的宣传,打广告等额外的工作较为繁杂
房客:需要了解房源信息,以确保租房体验得到保证
矛盾:房东不想要宣传和打广告这些繁杂的工作,他只希望房客直接来跟他交钱签合同,然后入住就ok了,但是,如果没有宣传和广告这部分工作就会导致房客不能看到相应的房源信息,得不到保障,从而不敢或者不愿意去租房
解决问题者:租房代理
租房代理责任:代替房东,对房源信息进行宣传和做广告,包括一切帮助房客了解房源的工作
最后再来梳理一下每个角色的需求和责任:
房东:负责入住合同的签署和收取房费
代理:负责对房源信息进行宣传和帮助房客了解房源,大型代理还可以帮助房东管理房客入住后的所有事情(合同收钱除外)
房客:通过房源信息广告和实地了解后跟房东签署入住合同
这样上面的矛盾就得到了解决,并且房东和房客都不会有太多的顾虑和多余的繁杂事儿
分析:
上記の実例によると、賃貸業者は家主の契約に署名する方法を呼び出す必要があります。この要件に基づいて、プロキシオブジェクトとプロキシオブジェクト(ターゲットオブジェクト)が同じインターフェイスを実装して、プロキシオブジェクトがプロキシオブジェクト(ターゲット)を持つことができるようにします。オブジェクト)メソッド、プロキシオブジェクトのコントラクト署名メソッドでプロキシオブジェクト(ターゲットオブジェクト)のコントラクト署名メソッドを呼び出す
上記の小さな生活の例から、住宅の重要性とその存在の意味を見ることができます——————— 住宅情報による家主とテナントの矛盾を解決します
上記の単語を理解するのは少し混乱するかもしれませんが、Javaコードに実装して見てみましょう。
JEEの開発は主に3つの層に分かれています:コントローラー->サービス-> Dao
サービスレイヤーは最も重要なレイヤーです。私たちのビジネスコードはこのレイヤーで記述されているため、サービスレイヤーに含まれるコードを見てみましょう。
現時点で問題がありますが、サービス層に直接追加機能のコードを書けますか?なぜそれを別の作品として取り上げる必要があるのですか?
分析:
サービスの呼び出し元:追加の機能がサービスレイヤーに記述されていることを願っています————————ゲスト
ソフトウェアデザイナー:サービスレイヤーがビジネスコードのみを記述し、ビジネスを混乱させる追加の機能コードを必要としないことを願っています————————家主
だから違いがあります!————これは、上場情報による家主とテナント間の競合に似ています
解決策:プロキシモードを使用してください!
代理店モデル
代理(Proxy)是一种设计模式,提供了对目标对象的另一种(非直接)访问方式
访问者通过代理对象来间接的访问目标对象
优点:实现目标对象业务功能的同时,能增加额外的功能和操作,即扩展目标对象
日常开发思想:不要随意去修改别人已经写好的代码或者方法,如需修改,则可以通过代理的方式来扩展该方法
代理模式的关键点:代理对象和目标对象,代理对象是对目标对象的扩展,并会调用目标对象
プロキシモードの概念:プロキシクラスを通じて、元のクラス(ターゲット)の追加機能を増やす
ターゲットクラス、元のクラス:ビジネスクラスを参照(コア関数-> ビジネスオペレーションDAO 呼び出し)
ターゲットメソッド、元のメソッド:ターゲットクラスまたは元のクラスのメソッド
追加機能、追加機能:ログ、トランザクション、パフォーマンス
プロキシクラス=ターゲットクラス(元のクラス)+追加機能+元のクラス(ターゲットクラス)と同じインターフェイスを実装
#接口
public interface UserService{
A(); //A方法
B(); //B方法
}
#原始类(目标类)————房东
public class UserServiceImpl implements UserService{
A(){} //A方法
B(){} //B方法
}
#代理类(额外功能)————————租房代理
public class UserServiceProxy implements UserService{
A(){} //A方法
B(){} //B方法
}
さて、この時点で、エージェントは実生活からコードまで明確に説明されているはずなので、エージェントの設計パターンを深く理解する必要があります!
エージェントモデルの分類
静的プロキシ:プリミティブクラスごとに、手動でプロキシクラス(.java .class)を記述します。
動的プロキシ:プロキシクラスごとに手動でプロキシクラスを記述する必要はありません。サードパーティの動的バイトコード技術により、プロキシクラスに.classバイトコードテンプレートが提供されます。
扩展:
正常情况下我们创建一个类————在这里指的是代理类
1.从本地或网络加载.calss文件
2.根据.class文件在方法区中创建一个class字节码模板用于提供该类的模板信息(元信息)
3.根据class字节码模板在堆空间中创建一个java.lang.class对象
4.当我们需要实例对象的时候再通过class对象在堆空间中创建相应的实例对象
在这些步骤中,最重要的是我们必须要拿到.class字节码模板,
但是在动态代理中这个字节码模板是由第三方动态字节码技术来提供的——————具体是哪些第三方技术,请往下看!!!!!
理論を理解し、説明し、コード化してください!!!!
静的プロキシ:
步骤:
1.提供业务接口
2.编写业务类(目标类)及方法
3.编写代理类,要求和目标类实现相同的接口,并调用目标类的方法
4.调用代理类的方法
1.ビジネスインターフェースを提供する
public interface UserService {
public void register();
}
2.ビジネスクラス(ターゲットクラス)とメソッドを記述する
public class UserServiceImpl implements UserService {
@Override
public void register() {
System.out.println("我是业务代码!!!");
}
}
3.プロキシクラスを記述し、ターゲットクラスと同じインターフェイスを必要とし、ターゲットクラスのメソッドを呼び出す
public class UserServiceProxy implements UserService {
private final UserServiceImpl userService = new UserServiceImpl();
@Override
public void register() {
System.out.println("我是额外功能!!!");
userService.register();
}
}
4.プロキシクラスのメソッドを呼び出す
5.結果
さて、理論とコーディングは実現しました、次のステップは分析することです
分析:
静的プロキシ
利点:ビジネスクラス(ターゲットクラス)コードを変更せずにビジネスクラスを拡張
短所:プロキシクラスはターゲットクラスと同じインターフェイスを実装する必要があるため、ビジネスクラスがさらにある場合は、多くのプロキシクラスが存在します。同時に、インターフェイスがメソッドを追加すると、ターゲットオブジェクトとプロキシオブジェクトを維持する必要があります
それでは、静的プロキシによって引き起こされる問題を解決するにはどうすればよいでしょうか?!
現時点では、Springの動的エージェントがマセラティベンツを運転しました。
まず、Springの動的プロキシ開発の使用方法を見てみましょう。
動的プロキシ:
Spring动态代理编程步骤:
1.引入依赖
2.创建接口和目标类(业务类)
3.编写额外功能类
4.定义切入点
5.组装切面(组装步骤3和步骤4)
6.调用测试
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.1.14.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.8</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.3</version>
</dependency>
#准备接口和目标类(业务类)
public interface OrderService {
void productOder();
Boolean consumOrder();
}
public class OrderServiceImpl implements OrderService {
@Override
public void productOder() {
System.out.println("下单");
}
@Override
public Boolean consumOrder() {
System.out.println("消费订单");
return true;
}
}
#准备额外功能类
public class OrderExtra1 implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("我是订单需要的额外功能!!!");
}
}
#在xml中将目标类和额外功能类交给Spring去管理
<bean id="orderSevice" class="com.xiaozhao.spring.service.impl.OrderServiceImpl"/>
<bean id="orderExtra" class="com.xiaozhao.spring.service.extra.orderExtra1"/>
#设置切入点和组装切面
<aop:config>
<aop:pointcut id="extra" expression="execution(* *(..))"/> //切入点
<aop:advisor advice-ref="orderExtra" pointcut-ref="extra"/> //组装
</aop:config>
测试:
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
OrderService orderSevice = (OrderService) ctx.getBean("orderSevice");
orderSevice.productOder();
orderSevice.consumOrder();
分析:
public class orderExtra1 implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println("我是订单需要的额外功能!!!");
}
}
結果から、MethodBeforeAdviceインターフェースの役割を確認できます。
パラメータ分析:
メソッドメソッド:ターゲットメソッド(ターゲットクラスのメソッド)
Object []オブジェクト:ターゲットメソッドのパラメーター
オブジェクトo:ターゲットクラス(ターゲットオブジェクト)
MethodBeforeAdviceインターフェイス実装クラスのbeforeメソッドに追加の関数を記述し、元のメソッドが実行される前に操作してからターゲットメソッドを拡張しますが、このメソッドには、元のメソッドの前に対応する追加の関数しか追加できないという欠点があります。元のメソッドの前後に対応する追加機能を追加しますか?たとえば、トランザクションが元のメソッドの前にトランザクションを開き、元のメソッドの後にトランザクションを閉じて、操作がアトミックであることを確認する必要があります。このような要件の下では、インターフェイスそれは私たちのニーズを満たすことができません!
私たちはこの欠点を検討しました。明らかに、Springの設計者もそれを考慮します。そのため、Springは、この問題を解決するのに役立つ別のインターフェースを用意しています。
MethodInterceptor(メソッドインターセプター):
@FunctionalInterface
public interface MethodInterceptor extends Interceptor {
Object invoke(MethodInvocation var1) throws Throwable;
}
その機能はMethodBeforeAdviceインターフェイスと同じですが、元のメソッドに追加の関数も追加しますが、MethodInterceptorは元のメソッドの前後に追加の関数を追加できるという点が異なります。具体的には、実装方法について、コーディングを続けます!
#在这里重复的步骤我就不在贴出来了,就是简单的将上面的MethodBeforeAdvice实现添加额外功能中额外功能的代码替换成一下代码:
/**
* @author : Carson-Zhao
* @date : 2020/9/16 23:02
*/
public class OrderExtra2 implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("原始方法之前!");//原始方法前
Object ret = methodInvocation.proceed();//原始方法回调
System.out.println("原始方法之后!");//原始方法后
return ret;
}
}
#在xml中修改组装的额外功能
<bean id="orderExtra" class="com.xiaozhao.spring.service.extra.OrderExtra2"/>
#最后调用一下这两个方法
ClassPathXmlApplicationContext ctx =
new ClassPathXmlApplicationContext("/applicationContext.xml");
OrderService orderSevice = (OrderService) ctx.getBean("orderSevice");
orderSevice.productOder();
System.out.println("我是分割线================");
orderSevice.consumOrder();
演算結果:
分析:
public class OrderExtra2 implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("原始方法之前!");//原始方法前
Object ret = methodInvocation.proceed();//原始方法回调
System.out.println("原始方法之后!");//原始方法后
return ret;
}
}
MethodInterceptorインターフェースにはメソッド呼び出しがあり、メソッドパラメーターはmethodInvocationです。このパラメーターを使用する続行メソッドは、元のメソッド(コールバックは元のメソッドを実行することを意味します)をコールバックし、元のメソッドの戻り値を返すことができます(注:元のメソッドの場合戻り値がない場合、ここでのretはnullです)、このコード行の前後および前後に追加の関数コード(トランザクション、ログ、パフォーマンス監視など)を追加できます。
拡張:
MethodInterceptorインターフェースを使用して追加の関数を追加する場合、元のメソッドが例外をスローすると、catchブロック(シーン:例外ログ)に追加の関数を追加できます。具体的な実装は、単純なtry / catchブロックです。デモしないでください!
補足:
MethodInterceptorインターフェースは使いやすく、より一般的ですが、完全ではありません。欠点の1つは、それが欠点ではないことです。つまり、メソッドコールバックプロセスの後で、元のメソッドの戻り値が変更され、元のメソッドの戻り値が変更されます。
/**
* @author : Carson-Zhao
* @date : 2020/9/16 23:02
*/
public class OrderExtra2 implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("原始方法之前!");
Object ret = methodInvocation.proceed();
System.out.println("原始方法之后!");
return true;//不将原始方法的返回值进行返回,就可以改变原始方法的返回值
}
}
総括する:
これに対し、実際の開発では、MethodBeforeAdviceとMethodInterceptorの2つのインターフェースが一般的に使用されます。ただし、MethodInterceptorインターフェースのinvokeメソッドは、元のメソッドの戻り値を変更する可能性があります。
さて、これまでは、動的プロキシの基本原則についてより一般的な方法でのみ説明してきました。SpringDynamic Proxy(AOP)をより深く理解するために、少しそれを崇高にして、基礎となる実装原則を見てみましょう。
上記の例から、Springダイナミックプロキシはプロキシクラスを手動で作成する必要がなくなり、ビジネスクラスのビジネス処理に集中するだけでよいことがわかります。静的プロキシと比較すると、明らかに大きな利点です。次に、このプロセスにプロキシがあるかどうかを考えます。クラス?もしそうなら、AOPはどのようにプロキシクラスを作成しますか?Springはプロキシオブジェクトをどのように処理しますか?
プロキシクラスの作成方法:
JDK動的プロキシ:
Spring中动态代理类的相关细节分析:
动态代理类特点:
1.访问修饰符为public,final和非抽象类型的
2.继承了java.lang.reflect.Proxy类,实现了getProxyClass()和newProxyInstance方法
3.有一个public类型的构造方法,该方法有一个InvocationHandler类型的参数
4.当程序调用动态代理实例的方法时,会调用与之关联的InvocationHandler对象的invoke方法
扩展:
1、Proxy类的isProxyClass静态方法可以用来判断指定的类是否为动态代理类
上記の特性から、動的プロキシクラスはjava.lang.reflect.Proxyと密接に関連していることがわかります。そこで、分析されるふりをして、Proxyのソースコードを見てみましょう。
JDK实现的动态代理:
Object ProxyClass = Proxy.newProxyInstance(classLoader,interfaces,invocationHandler);
分析:
ProxyClass:动态代理类
classLoader:类加载器
interfaces: 原始类实现的接口
invocationHandler:额外功能
为了减省太多干扰信息,我就截取相关重要的部分源码进行强行分析
public class Proxy implements java.io.Serializable {
//生成代理类实例
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
//验证传入的InvocationHandler是否为空
Objects.requireNonNull(h);
//克隆代理类实现的接口
final Class<?>[] intfs = interfaces.clone();
//获得安全管理器
final SecurityManager sm = System.getSecurityManager();
//检查创建代理类所需的权限
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
* 查找或者生成特定的代理类(如果缓存中存在,则直接获取)
*/
// 查找缓存或者生成一个特定的代理类对象(缓存中没有对应的代理对象时,就会去生成新的代理对象)—————————关键!!!!
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
//权限校验
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
//获取参数类型是InvocationHandler.class的代理类构造器
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
//如果代理类是不可访问的, 就使用特权将它的构造器设置为可访问
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
//传入InvocationHandler实例去构造一个代理类的实例,所有代理类都继承自Proxy,而Proxy构造方法需要InvocationHandler实例作为参数
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
/**
* Generate a proxy class. Must call the checkProxyAccess method
* to perform permission checks before calling this.
*/
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// If the proxy class defined by the given loader implementing
// the given interfaces exists, this will simply return the cached copy;
// otherwise, it will create the proxy class via the ProxyClassFactory
//如果缓存中有实现给定接口的代理类存在,就返回缓存中的副本;否则,将通过ProxyClassFactory创建代理类
return proxyClassCache.get(loader, interfaces);
}
//当缓存中不存在副本时,则由ProxyClassFactory生成
/**
* A factory function that generates, defines and returns the proxy class given
* the ClassLoader and array of interfaces.
*/
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
// prefix for all proxy class names 做一个标识所有代理类名称前缀
private static final String proxyClassNamePrefix = "$Proxy";
// next number to use for generation of unique proxy class names 用原子类来生成代理类的序号, 保证序号唯一
private static final AtomicLong nextUniqueNumber = new AtomicLong();
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
for (Class<?> intf : interfaces) {
/*
* Verify that the class loader resolves the name of this
* interface to the same Class object.
*/
Class<?> interfaceClass = null;
try {
interfaceClass = Class.forName(intf.getName(), false, loader);
} catch (ClassNotFoundException e) {
}
//intf是否可以由指定的类加载进行加载
if (interfaceClass != intf) {
throw new IllegalArgumentException(
intf + " is not visible from class loader");
}
/*
* Verify that the Class object actually represents an
* interface.//判断intf是不是接口
*/
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
/*
* Verify that this interface is not a duplicate//验证intf在数组中是不是重复的
*/
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
}
//生成代理类的包名
String proxyPkg = null; // package to define proxy class in
//代理类的访问标志, 默认是public和final
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
/*
* Record the package of a non-public proxy interface so that the
* proxy class will be defined in the same package. Verify that
* all non-public proxy interfaces are in the same package.
* //验证所有非公共代理接口都在同一个包中
*/
for (Class<?> intf : interfaces) {
int flags = intf.getModifiers();
//如果接口的访问标志不是public, 那么生成代理类的包名和接口包名相同
if (!Modifier.isPublic(flags)) {
//生成代理类的访问标志设置改为final
accessFlags = Modifier.FINAL;
String name = intf.getName();
int n = name.lastIndexOf('.');
String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
if (proxyPkg == null) {
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException(
//代理类如果实现不同包接口, 并且接口都不是public的, 那么就会在这里报错
"non-public interfaces from different packages");
}
}
}
if (proxyPkg == null) {
// if no non-public proxy interfaces, use com.sun.proxy package 如果不存在非公正代理包则使用com.sun.proxy package
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
/*
* Choose a name for the proxy class to generate.
* 生成代理类的全限定名, 包名+前缀+序号, 例如:com.sun.proxy.$Proxy0
*/
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg(包名) + proxyClassNamePrefix(类名前缀) + num(序号);
/*
* Generate the specified proxy class.
* 生成代理代理的字节码!!!!——————————————————————动态代理的关键
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);//获取代理字节码文件
try {
//根据二进制文件生成相应的Class实例
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
/*
* A ClassFormatError here means that (barring bugs in the
* proxy class generation code) there was some other
* invalid aspect of the arguments supplied to the proxy
* class creation (such as virtual machine limitations
* exceeded).
*/
throw new IllegalArgumentException(e.toString());
}
}
}
//根据指引我们来到generateProxyClass这个方法
public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);//构造ProxyGenerator对象
final byte[] var4 = var3.generateClassFile();——————————————获取字节码!!!!!——————关键方法
//如果saveGeneratedFiles为true,则表示需要保存生成的字节码文件,将字节码文件写入磁盘
if (saveGeneratedFiles) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
try {
int var1 = var0.lastIndexOf(46);
Path var2;
if (var1 > 0) {
Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));//路径
Files.createDirectories(var3);
var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
} else {
var2 = Paths.get(var0 + ".class");
}
Files.write(var2, var4, new OpenOption[0]);//写入磁盘
return null;
} catch (IOException var4x) {
throw new InternalError("I/O exception saving generated file: " + var4x);
}
}
});
}
//将字节码数组返回
return var4;
}
//继续跟随以上方法,追溯ProxyGenerator.generateClassFile方法
private byte[] generateClassFile() {
//第一步、将所有方法组装成ProxyMethod对象,并为代理类生成toString, hashCode, equals等代理方法
this.addProxyMethod(hashCodeMethod, Object.class);
this.addProxyMethod(equalsMethod, Object.class);
this.addProxyMethod(toStringMethod, Object.class);
Class[] var1 = this.interfaces;
int var2 = var1.length;
int var3;
Class var4;
for(var3 = 0; var3 < var2; ++var3) {
//遍历每一个接口的每一个方法, 并生成ProxyMethod对象
var4 = var1[var3];
Method[] var5 = var4.getMethods();
int var6 = var5.length;
for(int var7 = 0; var7 < var6; ++var7) {
Method var8 = var5[var7];
this.addProxyMethod(var8, var4);
}
}
Iterator var11 = this.proxyMethods.values().iterator();
List var12;
while(var11.hasNext()) {
var12 = (List)var11.next();
checkReturnTypes(var12);
}
//第二步、组装要生成的class文件的所有字段信息和方法信息
Iterator var15;
try {
添加构造器
this.methods.add(this.generateConstructor());
var11 = this.proxyMethods.values().iterator();
//遍历缓存中的代理方法
while(var11.hasNext()) {
var12 = (List)var11.next();
var15 = var12.iterator();
while(var15.hasNext()) {
ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next();
//添加代理类的静态字段
this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10));
//添加代理类的代理方法
this.methods.add(var16.generateMethod());
}
}
//添加代理类的静态字段初始化方法
this.methods.add(this.generateStaticInitializer());
} catch (IOException var10) {
throw new InternalError("unexpected I/O Exception", var10);
}
if (this.methods.size() > 65535) {
throw new IllegalArgumentException("method limit exceeded");
} else if (this.fields.size() > 65535) {
throw new IllegalArgumentException("field limit exceeded");
} else {
//第三步、写入最终class文件
//验证常量池中存在代理类的全限定名
this.cp.getClass(dotToSlash(this.className));
//验证常量池中存在代理类父类的全限定名
this.cp.getClass("java/lang/reflect/Proxy");
var1 = this.interfaces;
var2 = var1.length;
//验证常量池存在代理类接口的全限定名
for(var3 = 0; var3 < var2; ++var3) {
var4 = var1[var3];
this.cp.getClass(dotToSlash(var4.getName()));
}
//开始写入文件了,设置常量池只读
this.cp.setReadOnly();
ByteArrayOutputStream var13 = new ByteArrayOutputStream();
DataOutputStream var14 = new DataOutputStream(var13);
try {
//1.写入魔数
var14.writeInt(-889275714);
//2.写入次版本号
var14.writeShort(0);
//3.写入主版本号
var14.writeShort(49);
//4.写入常量池
this.cp.write(var14);
//5.写入访问修饰符
var14.writeShort(this.accessFlags);
//6.写入类索引
var14.writeShort(this.cp.getClass(dotToSlash(this.className)));
//7.写入父类索引, 生成的代理类都继承自Proxy
var14.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));
//8.写入接口计数值
var14.writeShort(this.interfaces.length);
Class[] var17 = this.interfaces;
int var18 = var17.length;
//9.写入接口集合
for(int var19 = 0; var19 < var18; ++var19) {
Class var22 = var17[var19];
var14.writeShort(this.cp.getClass(dotToSlash(var22.getName())));
}
//10.写入字段计数值
var14.writeShort(this.fields.size());
var15 = this.fields.iterator();
//11.写入字段集合
while(var15.hasNext()) {
ProxyGenerator.FieldInfo var20 = (ProxyGenerator.FieldInfo)var15.next();
var20.write(var14);
}
//12.写入方法计数值
var14.writeShort(this.methods.size());
var15 = this.methods.iterator();
//13.写入方法集合
while(var15.hasNext()) {
ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo)var15.next();
var21.write(var14);
}
//14.写入属性计数值, 代理类class文件没有属性所以为0
var14.writeShort(0);
//转换成二进制数组输出
return var13.toByteArray();
} catch (IOException var9) {
throw new InternalError("unexpected I/O Exception", var9);
}
}
}
}
上記のコードは、より混乱しやすく、
簡単に要約できます。
思想:通过类加载器获取类字节码,通过类实现的接口反射获得该类的属性和方法,并生成新的字节码文件
参数分析:
参数一:
类加载器————————创建实例对象所需的类加载器,可借用(前文有提及创建实例对象需要类加载器)
参数二:
原始类实现的接口———————JDK动态代理最重要的就是保证代理类和原始类实现相同的结果,代理类的代理方法中调用原始类的原始方法,所以我们这里需要传一个原始类实现的接口作为参数
参数三:
额外功能——————需要实现InvocationHandler接口的invoke方法
CGLIB動的エージェント:
CGLIB动态代理原理和JDK动态代理原理有些类似
区别是:
在Object ProxyClass =Proxy.newProxyInstance(classLoader,SuperClass,invocationHandler)
这里的时候第二个参数不再是是原始类实现的接口,而是直接传原始类
原因:代理类不再需要和原始类实现同一个接口去获取方法,而是直接继承原始类来获取原始方法
在这里Spring给我们提供了一个新的对象Enhancer,通过这个对象来set所需的三个参数(类加载器,原始类,额外功能)
例子:
/**
* @author : Carson-Zhao
* @date : 2020/9/11 23:44
*/
public class demo {
public static void main(String[] args) {
OrderServiceImpl orderService = new OrderServiceImpl();
Enhancer enhancer = new Enhancer();
enhancer.setClassLoader(demo.class.getClassLoader());
enhancer.setSuperclass(orderService.getClass());
MethodInterceptor interceptor = new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("额外功能1");
Object ret = method.invoke(orderService, args);
System.out.println("额外功能2");
return ret;
}
};
enhancer.setCallback(interceptor);
OrderService orderServiceProxy = (OrderService) enhancer.create();
orderServiceProxy.productOder();
orderServiceProxy.consumOrder();
}
}
上記のコードの結果:
総括する:
AOPが動的エージェントを作成する方法は2つあります。
1. JDK動的プロキシ
2. CGLIB動的エージェント
2つの違い:
JDKは、プロキシクラスと元のクラスの間に同じインターフェイスを実装することにより、元のクラスを呼び出す元のメソッドを実装します。CGLIBは、プロキシクラスを使用して元のクラスを実装し、元のクラスの元のメソッドを呼び出します。
JDK ————元のクラスによって実装されたものと同じインターフェースを実装します
CGLIB ————元のクラスを継承
しつこい:
この時点で、AOPはほぼ明確に説明されています。基本と少し深みがあります。知識のポイントを整理した後、AOPは統合されると考えられます。記事を読んだ太っている友達が一緒に励ましてくれることを願っています
予定:
次の計画は、主に日常の開発に使用されるRedisTemplateの二次パッケージを学ぶことです
やっと:
素晴らしい人はだれでも苦労しますが、SBのように固執する限り、あなたは素晴らしい人になります!