成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Spring AOP 深度剖析與實(shí)踐

開(kāi)發(fā)
無(wú)論是實(shí)現(xiàn)系統(tǒng)級(jí)的橫切關(guān)注點(diǎn),如日志記錄、事務(wù)管理、安全控制等,還是對(duì)業(yè)務(wù)邏輯進(jìn)行精細(xì)化的分離與整合,Spring AOP 都展現(xiàn)出了無(wú)與倫比的適應(yīng)性和靈活性。

在當(dāng)今復(fù)雜多變的軟件開(kāi)發(fā)領(lǐng)域,Spring 框架無(wú)疑是一顆璀璨的明星。而其中的 Spring AOP(面向切面編程)更是以其獨(dú)特的魅力和強(qiáng)大的功能,為開(kāi)發(fā)者們打開(kāi)了一扇通往全新編程境界的大門。

當(dāng)我們深入探索 Spring AOP 的世界,就仿佛置身于一個(gè)充滿無(wú)限可能的編程宇宙之中。它猶如一把神奇的鑰匙,能夠巧妙地解開(kāi)代碼結(jié)構(gòu)中的復(fù)雜癥結(jié),讓我們可以從全新的切面視角去審視和構(gòu)建軟件。無(wú)論是實(shí)現(xiàn)系統(tǒng)級(jí)的橫切關(guān)注點(diǎn),如日志記錄、事務(wù)管理、安全控制等,還是對(duì)業(yè)務(wù)邏輯進(jìn)行精細(xì)化的分離與整合,Spring AOP 都展現(xiàn)出了無(wú)與倫比的適應(yīng)性和靈活性。它并非僅僅是一種技術(shù)手段,更是一種編程理念的革新,為我們帶來(lái)了高效、簡(jiǎn)潔且極具擴(kuò)展性的開(kāi)發(fā)方式。讓我們一同踏上這趟精彩的 Spring AOP 之旅,去領(lǐng)略它所蘊(yùn)含的奧秘與力量。

一、詳解Spring對(duì)AOP的設(shè)計(jì)與實(shí)現(xiàn)

1.對(duì)AOP的理解

AOP(Aspect-Oriented Programming:面向切面編程),它實(shí)際做的就是將業(yè)務(wù)和一些非業(yè)務(wù)進(jìn)行拆解,降低彼此業(yè)務(wù)模塊與非業(yè)務(wù)模塊的耦合度,便于后續(xù)的擴(kuò)展維護(hù)。例如權(quán)限校驗(yàn)、日志管理、事務(wù)處理等都可以使用AOP實(shí)現(xiàn)。而Spring就是基于動(dòng)態(tài)代理實(shí)現(xiàn)AOP的。如果被代理的類有實(shí)現(xiàn)接口的話,就會(huì)基于JDK Proxy完成代理的創(chuàng)建,反之就是通過(guò)Cglib完成代理創(chuàng)建,當(dāng)然你也可以強(qiáng)制使用Cglib。

2.什么是AOP中切點(diǎn)、切面、通知

AOP中有很多核心術(shù)語(yǔ),分別是:

  • 目標(biāo)(Target): 這就被代理的對(duì)象,例如我們希望對(duì)UserService每個(gè)方法進(jìn)行增強(qiáng)(在不動(dòng)它的代碼情況下增加一些非業(yè)務(wù)的動(dòng)作),那么這個(gè)UserService就是目標(biāo)。
  • 代理(Proxy): 就是給你被代理后的對(duì)象的廠商,例如我們上面說(shuō)過(guò)希望對(duì)UserService每個(gè)方法進(jìn)行增強(qiáng),那么給用戶返回增強(qiáng)后的對(duì)象的類就是代理類。
  • 連接點(diǎn)(JoinPoint):目標(biāo)對(duì)象,每一個(gè)可能可以被增強(qiáng)的方法都可以稱為連接點(diǎn),盡管它最后可能不會(huì)被增強(qiáng)。
  • 切入點(diǎn)(Pointcut): 連接點(diǎn)中即能夠應(yīng)用通知的位置。
  • 通知(Advice): 不要被表面的語(yǔ)義誤導(dǎo),通知并不是告知某人的意思,通知的意思是攔截對(duì)象后,做的增強(qiáng)操作,也就是攔截后要執(zhí)行什么代碼。
  • 切面(Aspect): 切入點(diǎn)(Pointcut)+通知(Advice)。
  • 織入(Weaving):把通知的動(dòng)作融入到對(duì)象中,生成代理對(duì)象的過(guò)程就叫做織入。

3.Spring AOP和AspectJ AOP的區(qū)別

Spring AOP屬于運(yùn)行時(shí)增強(qiáng),基于代理(Proxying)實(shí)現(xiàn)的。而AspectJ AOP屬于編譯時(shí)增強(qiáng),基于字節(jié)碼操作(Bytecode Manipulation)實(shí)現(xiàn)的。

在《精通spring4.x》一書(shū)中,我們可以知道,jdk生成的代理對(duì)象性能遠(yuǎn)遠(yuǎn)差于cglib生成代理對(duì)象,但cglib創(chuàng)建代理對(duì)象花費(fèi)的時(shí)間卻遠(yuǎn)遠(yuǎn)高于jdk代理創(chuàng)建的對(duì)象。所以在spring框架的使用中,如果是單例的bean需要實(shí)現(xiàn)aop等操作,我們建議是使用cglib動(dòng)態(tài)代理技術(shù):

4.AspectJ 通知類型

  • Before(前置通知): 目標(biāo)對(duì)象方法調(diào)用前觸發(fā)增強(qiáng)。
  • After (后置通知):目標(biāo)對(duì)象方法調(diào)用后進(jìn)行增強(qiáng)。
  • AfterReturning(返回通知):目標(biāo)對(duì)象方法執(zhí)行結(jié)束,返回值時(shí)進(jìn)行增強(qiáng)。
  • AfterThrowing(異常通知):目標(biāo)對(duì)象方法執(zhí)行報(bào)錯(cuò)并拋出時(shí)做的增強(qiáng)。
  • Around(環(huán)繞通知):這個(gè)比較常用了,目標(biāo)對(duì)象方法調(diào)用前后我們可以做各種增強(qiáng)操作,甚至不調(diào)用對(duì)象的方法都能做到。

5.多個(gè)切面執(zhí)行順序我們?nèi)绾未_定

答: 有兩種方式:

  • 注解法:使用@Order注解來(lái)決定切面bean的執(zhí)行順序。
// 值越小優(yōu)先級(jí)越高
@Order(1)
@Component
@Aspect
public class LoggingAspect implements Ordered {
  • 繼承接口法:implements Ordered接口
@Component
@Aspect
public class LoggingAspect implements Ordered {

    // ....

    @Override
    public int getOrder() {
        // 返回值越小優(yōu)先級(jí)越高
        return 1;
    }
}

6.AOP操作在bean生命周期的那個(gè)階段實(shí)現(xiàn)

在bean初始化前后也就我們常說(shuō)的BPP階段完成AOP類的緩存以及通知器創(chuàng)建。在bean初始化后,根據(jù)需要結(jié)合通知器完成代理類的改造。

7.動(dòng)態(tài)代理是什么

是在運(yùn)行期間,創(chuàng)建目標(biāo)對(duì)象的代理對(duì)象,目標(biāo)對(duì)象不變,我們通過(guò)對(duì)方法動(dòng)態(tài)攔截,進(jìn)行前置或者后置等各種增強(qiáng)操作。AOP中就有CGLIB動(dòng)態(tài)代理和JDK動(dòng)態(tài)代理技術(shù)。

8.動(dòng)態(tài)代理的創(chuàng)建過(guò)程

AOP提供了一個(gè)默認(rèn)工廠根據(jù)類是否有繼承接口或者是否就是目標(biāo)類決定創(chuàng)建的策略。然后根據(jù)不同的策略決定代理類的創(chuàng)建。

@Override
 public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
  if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
   Class<?> targetClass = config.getTargetClass();
   //如果是接口則走JdkDynamicAopProxy反之走ObjenesisCglibAopProxy
   if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
    return new JdkDynamicAopProxy(config);
   }
   return new ObjenesisCglibAopProxy(config);
  }
  else {
   return new JdkDynamicAopProxy(config);
  }
 }

以下便是jdk代理的創(chuàng)建策略:

@Override
 public Object getProxy(@Nullable ClassLoader classLoader) {
  .........
 //獲取被代理的類的接口
  Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
  findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);

  //生成代理對(duì)象并返回
  return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
 }

以下便是cglib的創(chuàng)建策略:

@Override
 public Object getProxy(@Nullable ClassLoader classLoader) {
 .......
  try {
  .......
  //將當(dāng)前類信息通過(guò)enhancer 生成代理對(duì)象
   Enhancer enhancer = createEnhancer();
   if (classLoader != null) {
    enhancer.setClassLoader(classLoader);
    if (classLoader instanceof SmartClassLoader &&
      ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
     enhancer.setUseCache(false);
    }
   }
   enhancer.setSuperclass(proxySuperClass);
   enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
   enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
   enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader));

   Callback[] callbacks = getCallbacks(rootClass);
   Class<?>[] types = new Class<?>[callbacks.length];
   for (int x = 0; x < types.length; x++) {
    types[x] = callbacks[x].getClass();
   }
  //返回最終生成的代理對(duì)象
   return createProxyClassAndInstance(enhancer, callbacks);
  }
  ........
  }
  catch (Throwable ex) {
  ......
  }
 }

二、詳解CGLIB代理

1.Spring AOP和Cglib的關(guān)系

CGLIB是一個(gè)強(qiáng)大、高性能的代碼生成包。使用ASM操作字節(jié)碼,動(dòng)態(tài)生成代理,對(duì)方法進(jìn)行增強(qiáng)。,它廣泛的被許多AOP框架使用,為他們提供方法的攔截。

例如我們希望對(duì)某個(gè)service進(jìn)行日志打印,基于CGLIB我們可以這樣實(shí)現(xiàn):

前置步驟引入依賴:

  <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.7</version>
        </dependency>

首先創(chuàng)建用戶類

public class User {
    private String name;

    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }


    public User() {
    }

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }


    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

service類

public class UserServiceImpl {

    public List<User> findUserList() {
        return Collections.singletonList(new User("xiaoming", 18));
    }
}

代理類

public class CglibProxy<T> implements MethodInterceptor {


    private static Logger logger = LoggerFactory.getLogger(CglibProxy.class);

    private Object target;


    public  T getTargetClass(Object target) {
        //設(shè)置被代理的目標(biāo)類
        this.target = target;
        // 創(chuàng)建加強(qiáng)器設(shè)置代理類以及回調(diào),當(dāng)代理類被調(diào)用時(shí),callback就會(huì)去調(diào)用intercept
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.target.getClass());
        enhancer.setCallback(this);
        //返回代理類
        return (T) enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        logger.info("調(diào)用被代理對(duì)象的方法,代理對(duì)象:[{}],代理方法:[{}]", o.getClass().getName(), method.getName());
        Object result = methodProxy.invokeSuper(o, args);
        logger.info("代理調(diào)用結(jié)束,返回結(jié)果:[{}]", String.valueOf(result));

        return null;
    }
}

測(cè)試代碼

public class Main {
    public static void main(String[] args) {
        CglibProxy<UserServiceImpl> cglibProxy = new CglibProxy();

        UserServiceImpl targetClass =cglibProxy.getTargetClass(new UserServiceImpl());
        targetClass.findUserList();

    }
}

2.Cglib代理流程是是什么樣的

如下圖所示,整體來(lái)說(shuō)就是基于enhancer去配置被代理類的各種參數(shù),然后生成代理類:

注意:final方法無(wú)法被代理,因?yàn)樗豢杀蛔宇惛采w。

3.Spring中的Cglib代理流程

源碼如下,我們可以看出和我們寫的實(shí)例代碼是差不多的。

@Override
 public Object getProxy(@Nullable ClassLoader classLoader) {
.....

  try {
   //獲取當(dāng)前類信息獲取生成代理對(duì)象
   Enhancer enhancer = createEnhancer();
   if (classLoader != null) {
    enhancer.setClassLoader(classLoader);
    if (classLoader instanceof SmartClassLoader &&
      ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
     enhancer.setUseCache(false);
    }
   }
   enhancer.setSuperclass(proxySuperClass);
   enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
   enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
   enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader));

  // 獲取當(dāng)前類中的方法
   Callback[] callbacks = getCallbacks(rootClass);
   Class<?>[] types = new Class<?>[callbacks.length];
   for (int x = 0; x < types.length; x++) {
    types[x] = callbacks[x].getClass();
   }
   
   enhancer.setCallbackFilter(new ProxyCallbackFilter(
     this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
   enhancer.setCallbackTypes(types);

   // 生成代理對(duì)象
   return createProxyClassAndInstance(enhancer, callbacks);
  }
  catch (CodeGenerationException | IllegalArgumentException ex) {
   .....
  }
  catch (Throwable ex) {
   .....
  }
 }

三、詳解JDK代理

1. JDK代理示例

答:這個(gè)是jdk自帶的一種代理,我們只需繼承InvocationHandler即可實(shí)現(xiàn)。但是前提是這個(gè)類必須繼承某些接口才能使用jdk代理。

首先我們定義接口,User類沿用上述的:

public interface UserService {

    List<User> findUserList();
}

修改UserServiceImpl:

public class UserServiceImpl implements UserService{

    @Override
    public List<User> findUserList() {
        return Collections.singletonList(new User("xiaoming", 18));
    }
}

jdk代理類:

public class JDKProxy<T> {

    private static Logger logger = LoggerFactory.getLogger(JDKProxy.class);

    private Object target;

    public JDKProxy(Object target) {
        this.target = target;
    }


    public T getTargetObj() {
        UserService proxy;
        ClassLoader loader = target.getClass().getClassLoader();
        Class[] interfaces = new Class[]{UserService.class};
        InvocationHandler handler = (p, method, args) -> {
            logger.info("代理方法被調(diào)用,類名稱[{}],方法名稱[{}]", target.getClass().getName(), method.getName());
            Object result = method.invoke(target, args);
            logger.info("代理方法調(diào)用結(jié)束,返回結(jié)果:[{}]", String.valueOf(result));
            return result;
        };

        proxy = (UserService) Proxy.newProxyInstance(loader, interfaces, handler);
        return (T) proxy;
    }


}

測(cè)試代碼:

public class Main {
    public static void main(String[] args) {
       
        JDKProxy<UserService> jdkProxy=new JDKProxy<>(new UserServiceImpl());
        UserService userService = jdkProxy.getTargetObj();
        System.out.println(userService.findUserList());
    }
}

2. JDK代理的工作流程

我們不妨在jvm在下面這樣一段參數(shù),或者在上述main方法加這個(gè)代碼:

 System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

然后我們通過(guò)debug可以發(fā)現(xiàn)它回步入這段代碼,其中他會(huì)生成一個(gè)ClassFile,方法名為generateClassFile:

public static byte[] generateProxyClass(final String name,
                                        Class<?>[] interfaces,
                                        int accessFlags)
{
    ProxyGenerator gen = new ProxyGenerator(name, interfaces, accessFlags);
    final byte[] classFile = gen.generateClassFile();
    ...
}

而代理方法做的事情,整體如下所示,可以看到它整體做的就是拿著被代理類的 各種方法封裝成ProxyMethod方法,然后寫入class文件中:

/**
    * Generate a class file for the proxy class.  This method drives the
    * class file generation process.
    */
private byte[] generateClassFile() {

    /* 第一步:將所有方法包裝成ProxyMethod對(duì)象 */
    
    // 將Object類中hashCode、equals、toString方法包裝成ProxyMethod對(duì)象
    addProxyMethod(hashCodeMethod, Object.class);
    addProxyMethod(equalsMethod, Object.class);
    addProxyMethod(toStringMethod, Object.class);

    // 將代理類接口方法包裝成ProxyMethod對(duì)象
    for (Class<?> intf : interfaces) {
        for (Method m : intf.getMethods()) {
            addProxyMethod(m, intf);
        }
    }

    //......

    /* 第二步:為代理類組裝字段,構(gòu)造函數(shù),方法,static初始化塊等 */
    try {
        // 添加構(gòu)造函數(shù),參數(shù)是InvocationHandler
        methods.add(generateConstructor());

        // 代理方法
        for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
            for (ProxyMethod pm : sigmethods) {

                // 字段
                fields.add(new FieldInfo(pm.methodFieldName,
                    "Ljava/lang/reflect/Method;",
                        ACC_PRIVATE | ACC_STATIC));

                // 上述ProxyMethod中的方法
                methods.add(pm.generateMethod());
            }
        }

        // static初始化塊
        methods.add(generateStaticInitializer());

    } catch (IOException e) {
        throw new InternalError("unexpected I/O Exception", e);
    }
 //......

    /* 第三步:寫入class文件 */

   //......

    try {
     //......
        dout.writeShort(0); // (no ClassFile attributes for proxy classes)

    } catch (IOException e) {
        throw new InternalError("unexpected I/O Exception", e);
    }

    return bout.toByteArray();
}

看看上文命令下創(chuàng)建class,可以看到它 implements UserService 以及通過(guò)我們的的代理類的InvocationHandler 調(diào)用這些方法:

public final class $Proxy0 extends Proxy implements UserService {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    //......
    
 //動(dòng)態(tài)代理了findUserList方法,后續(xù)調(diào)用時(shí)本質(zhì)上是通過(guò)代理類的method對(duì)原有方法進(jìn)行調(diào)用,即我們的InvocationHandler所實(shí)現(xiàn)的邏輯
    public final List findUserList() throws  {
        try {
            return (List)super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

     //......
    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            //初始化我們的代理方法類method
            m3 = Class.forName("com.pdai.aop.jdkProxy.UserService").getMethod("findUserList");
             //......
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

3.Spring AOP中JDK代理的實(shí)現(xiàn)

JdkDynamicAopProxy源碼如下,可以看到本質(zhì)上也是通過(guò)傳入:

  • 類加載器
  • 接口類型,通過(guò)proxiedInterfaces獲取對(duì)應(yīng)接口到代理緩存中獲取要生成的代理類型
  • 對(duì)應(yīng)的InvocationHandler 進(jìn)行邏輯增強(qiáng)。
@Override
 public Object getProxy(@Nullable ClassLoader classLoader) {
 
 //......
  return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
 }
責(zé)任編輯:趙寧寧 來(lái)源: 寫代碼的SharkChili
相關(guān)推薦

2009-09-29 10:00:40

Spring AOP框

2025-03-25 10:29:52

2024-07-11 08:17:00

2024-12-17 00:00:00

Spring線程

2012-09-28 10:20:14

IBMdw

2012-02-17 10:50:10

Java

2016-11-25 20:52:14

Linux

2009-07-30 09:23:53

Java JNI

2012-09-27 09:47:43

SpringJava面向?qū)ο?/a>

2021-02-05 18:22:51

GoC剖析

2022-09-27 18:56:28

ArrayList數(shù)組源代碼

2025-06-04 08:30:00

seata分布式事務(wù)開(kāi)發(fā)

2024-02-05 19:06:04

DartVMGC流程

2023-04-06 13:15:48

MySQL復(fù)制原理應(yīng)用實(shí)踐

2024-10-23 16:06:50

2025-03-27 04:10:00

2009-11-04 16:40:47

AOP.NET Ora

2009-06-19 13:28:30

Spring AOPSpring 2.0

2022-06-07 07:58:45

SpringSpring AOP

2025-02-24 08:00:00

線程池Java開(kāi)發(fā)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 欧美黑人一级爽快片淫片高清 | 97国产精品视频人人做人人爱 | 国产www成人| 81精品国产乱码久久久久久 | 国产精品欧美一区二区三区不卡 | 亚洲欧美成人影院 | 日韩国产高清在线观看 | 午夜精品久久久久久久久久久久久 | 免费一看一级毛片 | 亚洲欧美激情视频 | 成人福利在线观看 | 国产98色在线 | 日韩 | 三级成人片 | 在线视频一区二区 | 日本在线免费视频 | 日韩在线 | 玖玖视频国产 | 亚洲区一区二区 | 亚州视频在线 | 免费午夜视频在线观看 | 亚洲午夜一区二区 | 国产成人亚洲精品自产在线 | 国产精品国产精品国产专区不卡 | 精品日韩在线 | 亚洲欧美在线一区 | 日韩av在线一区 | 欧美激情一区二区 | 国产不卡一 | 日韩一级黄色毛片 | 成年人网站免费 | 羞羞色网站 | 日韩精品一区二区三区免费观看 | 亚洲欧美视频一区二区 | 日本国产精品视频 | 毛片视频免费观看 | 日本中文字幕一区 | 久久99久久99久久 | 国内自拍偷拍 | 超碰av人人 | 亚洲一二三区精品 | 一区二区三区亚洲视频 |