关于SpringMvc中的IOC和AOP的理解笔记

理解Spring的AOP和IOC实现原理

1、 IOC

IOC(控制反转)就是依赖倒置原则的一种代码设计思路。就是把原先在代码里面需要实现的对象创建、对象之间的依赖,反转给容器来帮忙实现。
Spring IOC容器通过xml,注解等其它方式配置类及类之间的依赖关系,完成了对象的创建和依赖的管理注入。实现IOC的主要设计模式是工厂模式。

这句话的意思是:

1、IOC控制反转的含义,它是把java面向对象的创建对象的过程,交由容器来进行管理一种机制。

2、它实现的方式,是通过XML,获取注解,配置类的方式实现类和类之间的依赖关系。

3、来完成的对象的创建和依赖管理的注入。

02、SpringIoc的设计初衷是什么?

  • 集中管理,实现类的可配置和易管理。
  • 降低了类与类之间的耦合度。实现java高内聚低耦合的设计思想
  • 同时可以提升java应用程序的性能,减轻JVM的内存压力。

03、SpringIOC的核心概念

其实本质是通过:java语言衍生出来的框架和项目产品,只不过开源出来。你使用spring最大感受是什么?

是不是再也没有去new对象呢或者说你很少在去是实例化对象了

核心:创建对象,管理对象,注入对象,控制对象等,一句话:SpringIoc管理对象的整个生命周期。

非常经典的面试题:Spring的bean的生命周期

04、springioc容器的bean是单列的吗?

这个所谓单列不是指:用所谓的单列设计模式开发和你们所认识的单列不一样。

在你们眼中的单列模式:恶汉模式,懒汉模式,双重检查机制等等。

它们的共同点是:在内存空间中只有一份内存。思想是一样的。

那么sprignioc的bean的单列指是什么?

答案:就是一个容器Map,因为这个Map是一个全局的变量。

public class Factory{
   private final Map<String, BeanDefinition> beanDefinitionMap;

   beanDefinitionMap.put("userServiceImpl",new BeanDefinition());
   beanDefinitionMap.put("userServiceImpl2",new BeanDefinition());
}

05、所谓的依赖注入

DI—Dependency Injection,即“依赖注入”:是组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。

理解DI的关键是:“谁依赖谁,为什么需要依赖,谁注入谁,注入了什么”,那我们来深入分析一下:

●谁依赖于谁:当然是应用程序依赖于IoC容器;
●为什么需要依赖:应用程序需要IoC容器来提供对象需要的外部资源;
●谁注入谁:很明显是IoC容器注入应用程序某个对象,应用程序依赖的对象;
●注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)。

IoC和DI由什么关系呢?其实它们是同一个概念的不同角度描述,由于控制反转概念比较含糊(可能只是理解为容器控制对象这一个层面,很难让人想到谁来维护对象关系),所以2004年大师级人物Martin Fowler又给出了一个新的名字:“依赖注入”,相对IoC 而言,“依赖注入”明确描述了“被注入对象依赖IoC容器配置依赖对象”。

所谓的依赖注入:其实就告诉你,在实际开发和生产中,类和类直接很多时候会存在所谓的依赖关系。为了这种一个对象依赖另外一个对象的时候,把存在依赖的对象实例化的过程称之为:依赖注入。

DI:它其实就把容器中创建存放好的对象,通过DI机制去完成类执行对象的实例化的问题。说白了就是:具体ioc动作。

1、IoC(控制反转)

==首先想说说IoC(Inversion of Control,控制反转)。这是spring的核心,贯穿始终。所谓IoC,对于spring框架来说,就是由spring来负责控制对象的生命周期和对象间的关系。==这是什么意思呢,举个简单的例子,我们是如何找女朋友的?常见的情况是,我们到处去看看找到自己喜欢的,然后打听她们的兴趣爱好、qq号、电话号………,想办法认识她们,投其所好送其所要……这个过程是复杂深奥的,我们必须自己设计和面对每个环节。传统的程序开发也是如此,在一个对象中,如果要使用另外的对象,就必须得到它(自己new一个),使用完之后还要将对象销毁,对象始终会和其他的接口或类耦合起来。

那么IoC是如何做的呢?有点像通过婚介找女朋友,在我和女朋友之间引入了一个第三者:婚姻介绍所。婚介管理了很多男男女女的资料,我可以向婚介提出一个要求的列表,告诉它我想找个什么样的女朋友,然后婚介就会按照我们的要求,提供一个女孩,我们只需要去和她谈恋爱、结婚就行了。简单明了,如果婚介给我们的人选不符合要求,我们就会抛出异常。整个过程不再由我自己控制,而是有婚介这样一个类似容器的机构来控制。Spring所倡导的开发方式就是如此,所有的类都会在spring容器中登记,告诉spring你是个什么东西,你需要什么东西,然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转。

2、DI(依赖注入)
==IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的。比如对象A需要操作数据库,以前我们总是要在A中自己编写代码来获得一个Connection对象,有了 spring我们就只需要告诉spring,==A中需要一个Connection,至于这个Connection怎么构造,何时构造,A不需要知道。在系统运行时,spring会在适当的时候制造一个Connection,然后像打针一样,注射到A当中,这样就完成了对各个对象之间关系的控制。A需要依赖 Connection才能正常运行,而这个Connection是由spring注入到A中的,依赖注入的名字就这么来的。那么DI是如何实现的呢? Java 1.3之后一个重要特征是反射(reflection),它允许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性,spring就是通过反射来实现注入的。

DI:它其实就把容器中创建存放好的对象,通过DI机制去完成类中存在依赖关系的对象实例化的问题。

如果你完成依赖注入的话:

就必须遵从它的注入规范:

1:set注入

2:构建函数注入

3:注解注入 @Resource/@Autowired

4:工厂方法注入

5:其他。。。。

06:总结

面试题:谈谈你对SpringIoc的理解和认识是什么?

我的理解是,首先spring框架是通过java语言开发出来的一个框架或者一个项目产品。我们java最大的特点是:面向对象,在没有使用spring框架的时候,都是用通过new去实例化对象,使用spring框架以后,它内部提供ioc机制,也就控制反转的含义,它核心基础是:把对象创建的过程,交由容器(Map)来进行管理一种机制,我们会把通过容器来管理对象的方式,将传统的创建方式更换成交由容器管理对象的过程称之为ioc控制反转的过程。总之如果要实现对象交由spring的ioc的来管理话,

就必须遵循spring提供的规范,而这些规范在传统spring开发中,是通过xml的方式来进行注入和管理的bean到容器中,现在主流的方式都是采用注解的方式进行管理和控制bean到容器中等,这样做得好处是:

  • 集中管理,实现类的可配置和易管理。
  • 降低了类与类之间的耦合度。实现java高内聚低耦合的设计思想 DI机制
  • 同时可以提升java应用程序的性能,减轻JVM的内存压力。

我对DI的理解是:它其实就把容器中存在的类,容器中类和另外一个类存在依赖关系的话,DI机制会把这个类中存在的类进行实例化的过程称之为:依赖注入,但是前提是:必须要遵循依赖注入的规则:

1:set注入

2:构建函数注入

3:注解注入 @Resource/@Autowired

4:工厂方法注入

Springbean的三级缓存(循环依赖的问题)?

三个Map来处理,和交换它们的依赖关系

参考网址:https://www.cnblogs.com/ITtangtang/p/3978349.html

请添加图片描述

使用IOC的好处

  • 集中管理,实现类的可配置和易管理。
  • 降低了类与类之间的耦合度。

简单模拟IOC

public interface BeanFactory {
    
    
    Object getBean(String id);  
}

public class ClassPathXmlApplicationContext implements BeanFactory {
    
    
    //容器,用来存放注入的Bean  
    private Map<String, Object> container = new HashMap<String, Object>();  

    //解析xml文件,通过反射将配置的bean放到container中  
    public ClassPathXmlApplicationContext(String fileName) throws Exception{
    
      
        SAXBuilder sb = new SAXBuilder(); 

        Document doc 
        =sb.build(ClassPathXmlApplicationContext.class.getResource("/"+fileName));
       
        Element root = doc.getRootElement();  
        List<Element> list = XPath.selectNodes(root, "/beans/bean");  

        for (int i = 0; i < list.size(); i++) {
    
                  
           Element bean = list.get(i);  
           String id = bean.getAttributeValue("id");  
           String clazz = bean.getAttributeValue("class");  
           Object o = Class.forName(clazz).newInstance();  
           container.put(id, o);  
          }  
    }
    @Override  
    public Object getBean(String id) {
    
            
        return container.get(id);  
    }  

}

需要导入 jdom.jar包。

<?xml version="1.0" encoding="UTF-8"?>  
<beans>  
  <bean id="people" class="com.ioc.People" />  
  <bean id="chicken" class="com.ioc.Chicken" />  
  <bean id="dog" class="com.ioc.Dog" />  
</beans>  
public interface Animal {
    
    
     void say();  
}

public class Dog implements Animal {
    
    
    @Override
    public void say() {
    
    
         System.out.println("汪汪"); 
    }
}
public class Chicken implements Animal {
    
    
    @Override
    public void say() {
    
    
        System.out.println("鸡你很美");  
    }
}
public class People {
    
    
    public void info(){
    
    
        System.out.println("小明-23岁");
    }
}
public static void main(String[] args) throws Exception {  

        //加载配置文件  
        BeanFactory f = new ClassPathXmlApplicationContext("applicationContext.xml");  

        Object os = f.getBean("dog");  
        Animal dog = (Animal)os;  
        dog.say();  

        Object op = f.getBean("chicken");  
        Animal chicken = (Animal)op;  
        chicken.say();  

        Object p = f.getBean("people");  
        People people= (Animal)p;  
        people.info();  
    } 

2、AOP

AOP(面向切面)是一种编程范式,提供从另一个角度来考虑程序结构以完善面向对象编程(OOP)。
AOP为开发者提供了一种描述横切关注点的机制,并能够自动将横切关注点织入到面向对象的软件系统中,从而实现了横切关注点的模块化。
AOP能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任,例如事务处理、日志管理、权限控制等,封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。

01、为什么要学习AOP

场景:

  • 其实在开发中,很多时候,有一些业务逻辑非常通用和公用,比如:接口的登录校验、限流处理、安全性处理等
  • 或者开发很多业务会执行处理附属业务,比如:下单、用户保存日志,下单要发送系统消息等。
public class OrderService {
    
    
    
    public void makeorder(){
    
    
        // 1: 限流
        // 2: 角色是不是超级管理员

        // 3: 下单 --------需要的东西

        // 4: 保存日志
    }

    public void reguser(){
    
    
        // 1: 注册用户
        // 2: 保存日志
    }
}

public static void main(String[] args){
    
    
    OrderService orderService = new OrderService();
    orderService.makeorder();
    orderService.reguser();
}

结论:

所谓的AOP:当对象执行方法的时候,会对你对象执行的方法进行横切面的拦截处理。并对其方法的业务进行织入逻辑增强处理.

02、如何进行的拦截的呢?

在java中只有两种中方式可以对对象执行方法进行拦截处理:

  • jdk动态代理(必须要接口)
    • 在平时的开发中,如果你有接口IUserService,进行子类的实例化的时候,其实就是使用jdk的动态代理,然后通过IOC的DI机制,完成注入。默认是:jdk动态代理
  • cglib动态代理 (不需要接口)
    • 在平时的开发中,如果没有接口IUserService,进行子类的实例化的时候,其实就是使用cglib的动态代理,然后通过IOC的DI机制,完成注入。
  • Aop:采用就是:Jdk动态代理 和 cglib动态代理
public static void main(String[] args){
     
     
    OrderService orderService = new OrderService();
    orderService.makeorder();
    orderService.reguser();
}

// orderService – 普通java对象
// 但是如果OrderService一旦被代理了,这个时候orderServiceproxy就是一个代理对象。

public static void main(String[] args){
     
     
   OrderService orderServiceproxy  =   Proxy.newIntances(OrderService接口,LogAspect.class,xxx);
   orderServiceproxy.makeorder();
   orderServiceproxy.reguser();
}

// 只有是代理对象才可以进行拦截处理

而代理产生,必须通过jdk动态代理或者cglib代理才可以生成所谓的代理对象。

03、AOP是如何增强的呢?

通过切面类

public interface IOrderService {
    
    
    public void makeorder();
    public void reguser();
}

public class OrderService implments IOrderService{
    
    
    
    public void makeorder(){
    
    
        // 1: 限流
        // 2: 角色是不是超级管理员
		// 3: 保存日志
        // 4: 下单 --------需要的东西
        
    }

    public void reguser(){
    
    
        // 1: 注册用户
        // 2: 保存日志
    }
}

切面类:

public class LogAspect{
    
    
   public void log(){
    
    
       // 保存日志
   }
}

注意上面的切面类,只是一个概念,真正的切面类必须要遵循:jdk动态代理的规范。切面类必须使用接口:InvocationHandler

public class LogAspect implements InvocationHandler{
    
    

    
     // 目标对象  
    private Object targetObject;  
    
    public TimeHandler(Object targetObject){
    
    
          this.targetObject = targetObject;
    }
    @Override  
    //关联的这个实现类的方法被调用时将被执行  
    /*InvocationHandler接口的方法,proxy表示代理,method表示原对象被调用的方法,      
        args表示方法的参数*/  
    public Object invoke(Object proxy, Method method, Object[] args)  
            throws Throwable {
    
      
        Object ret=null;  
        try{
    
      
            
            System.out.println("方法之前:"+System.currentTimeMillis());    
            //调用目标方法  
            
            // 前置通知 beforeAdvice 
            ret=method.invoke(targetObject, args);  
            // 保存日志
            // 后置通知 afterAdvice
      
            System.out.println("方法之后:"+System.currentTimeMillis());  
        }catch(Exception e){
    
      
            e.printStackTrace();  
            System.out.println("error");  
             // 异常通知 afterAdvice
            throw e;  
        }  
        return ret;  
    }  
}
public static void main(String[] args){
    
    
   IOrderService orderServiceproxy  =   Proxy.newIntances(IOrderService.class,LogAspect.class,OrderService.class);
   orderServiceproxy.makeorder();
   orderServiceproxy.reguser();
}

总结

1、Aop是面向切面编程的一种思想,它是指对象 执行方法的时候,对执行的方法进行逻辑的增强处理

2、AOP是如何进行拦截处理的?采用jdk动态代理和cglib进行切换处理。

3、执行的对象必须是:代理对象,而代理对象产生必须使用jdk的动态代理和cglib来产生。

4:有代理对象,它是如何增强逻辑的呢?它是使用:切面类来进行增强逻辑处理。如何你使用的jdk的动态来来实现AOP的话,那么你切面类就必须实现InvocationHandler,覆盖invoke方法,这个方式就是让你去执行代码增强的方法。

5、使用AOP的好处

  • 降低模块的耦合度
  • 使系统容易扩展
  • 提高代码复用性

AOP的基本概念

  • 连接点(JoinPoint):需要在程序中插入横切关注点的点,连接点可能是在类初始化、方法调用、字段调用或处理异常等等。Spring中只支持方法执行连接点。具体执行的方法

  • 切入点(Pointcut):一组相关连接点的集合。一句话:你所以需要拦截方法的集合(@Poincut())

  • 通知(Advice):在连接点上执行的行为,增强提供了在AOP中需要在切入点所选择的连接点处进行扩展现有行为的手段。包括前置增强(before advice)、后置增强 (after advice)、环绕增强 (around advice)。

    其实就是指:具体处理业务的位置和时间点。它是切面类的invoke方法。其实通知:就是你具体要做得事情。比如日志处理

  • 切面(Aspect):通知和切入点的结合。(具体增强逻辑处理的类,也就切面类)

  • 织入(Weaving):织入是一个过程,是将切面应用到目标对象从而创建出AOP代理对象的过程。)就把对象对象执行的方法和通知关联的过程称之为织入,一句话:代理对象执行方法,要去那个通知方法执行增强逻辑处理

  • 代理(Proxy):通过代理方式来对目标对象应用切面。AOP代理可以用JDK动态代理或CGLIB代理实现。

  • 目标对象(Target):需要被织入关注点的对象。即被代理的对象。具体的:orderServiceImpl,为什么要传递一个目标对象进来呢? 代理对象目的是什么?进行逻辑增强,增强以后你是不是还要去处理后续方法业务呢?

    img

JDK动态代理模拟
JDK动态代理的两个核心接口(类)分别是InvocationHandler和Proxy。注意:只能代理接口。

public class TimeHandler implements InvocationHandler {
    
      
      
    // 目标对象  
    private Object targetObject;  
    
    public TimeHandler(Object targetObject){
    
    
          this.targetObject = targetObject;
    }
    @Override  
    //关联的这个实现类的方法被调用时将被执行  
    /*InvocationHandler接口的方法,proxy表示代理,method表示原对象被调用的方法,      
        args表示方法的参数*/  
    public Object invoke(Object proxy, Method method, Object[] args)  
            throws Throwable {
    
      
        Object ret=null;  
        try{
    
      
            System.out.println("方法之前:"+System.currentTimeMillis());    
            //调用目标方法  
            ret=method.invoke(targetObject, args);  
            System.out.println("方法之后:"+System.currentTimeMillis());  
        }catch(Exception e){
    
      
            e.printStackTrace();  
            System.out.println("error");  
            throw e;  
        }  
        return ret;  
    }  
  
} 

TimeHandler 类实现了InvocationHandler接口。实现核心方法invoke,共有3个参数。第一个参数 生成的代理类实例,第二个参数 目标对象的方法,第三个参数 方法的参数值数组。

public class ProxyUtil {
    
    
    
    @SuppressWarnings("unchecked")
    public static <T> T proxyOne(ClassLoader loader,Class<?>[] clz,InvocationHandler handler){
    
    
        return (T)Proxy.newProxyInstance(loader, clz, handler);
    }
}

ProxyUtil 类简单封装了一下Proxy.newProxyInstance()方法。该方法也有3个参数。第一个参数产生代理对象的类加载器,第二个参数目标对象的接口数组,第三个参数就是实现InvocationHandler接口的类实例。

public interface UserManager {
    
    
    public void addUser(String userId, String userName);
}
public class UserManagerImpl implements UserManager {
    
    
    @Override
    public void addUser(String userId, String userName) {
    
    
        System.out.println("addUser(id:"+userId+",name:"+userName+")");
    }

}
public static void main(String[] args) {
    
    
         UserManager um=new UserManagerImpl(); 
         LogHandler log =new LogHandler(um); 
     um=ProxyUtil.proxyOne(um.getClass().getClassLoader(), 
                 um.getClass().getInterfaces(), log);
         
       TimeHandler time = new TimeHandler(um);
       um=ProxyUtil.proxyOne(um.getClass().getClassLoader(), 
                 um.getClass().getInterfaces(), time);
         
         um.addUser("1111", "张三");
    }

为了演示需要,这边又增加了一个LogHandler,跟TimeHandler代码一样。

CGLIB动态代理模拟
CGLIB动态代理的两个核心接口(类)分别是MethodInterceptor和Enhancer。是不是跟JDK动态代理很相似,用法也差不多。但CGLIB可以代理类和接口。注意:不能代理final类。

public class TimeInterceptor implements MethodInterceptor {
    private Object target;  
    public TimeInterceptor(Object target) {
        this.target = target;
    }
    @Override
    public Object intercept(Object proxy, Method method, 
            Object[] args, MethodProxy invocation) throws Throwable {
        System.out.println("方法之前:"+System.currentTimeMillis());
        Object ret = invocation.invoke(target, args); 
        System.out.println("方法之后:"+System.currentTimeMillis());
        
        return ret;
    }
}

intercept方法4个参数。1.生成的代理类实例。2.被代理对象的方法引用。3.方法参数值数组。4.代理类对方法的代理引用。

public class ProxyUtil {
    
    
    @SuppressWarnings("unchecked")
    public static <T> T proxyOne(Class<?> clz,MethodInterceptor interceptor){
    
    
        return (T)Enhancer.create(clz, interceptor);
    }

}

Enhancer类是CGLib中的字节码增强器。

public class UserManage {
    
    
    public void addUser(String userId, String userName) {
    
    
        System.out.println("addUser(id:"+userId+",name:"+userName+")");
    }
}


public static void main(String[] args) {
    
    
        UserManage um = new UserManage();
        TimeInterceptor time = new TimeInterceptor(um);
        um = ProxyUtil.proxyOne(um.getClass(), time);
        um.addUser("111", "老王");
}

04、总结

1、Aop是一种面向切面编程思想,它是指对象执行方法的时候,对执行的方法进行逻辑的增强处理

2、AOP是如何进行拦截处理的?采用jdk动态代理和cglib进行切换处理。

3、执行的对象必须是:代理对象,而代理对象产生必须使用jdk的动态代理和cglib来产生。

4:有代理对象,它是如何增强逻辑的呢?它是使用:切面类来进行增强逻辑处理。如何你使用的jdk的动态来来实现AOP的话,那么你切面类就必须实现InvocationHandler,覆盖invoke方法,这个方式就是让你去执行代码增强的方法。

5、使用AOP的好处

  • 降低模块的耦合度
  • 使系统容易扩展
  • 提高代码复用性

05、谈谈你对Sprign的AOP的认识?

其实在之前项目开发中,很多时候,在处理一些业务的时候,很多业务逻辑非常通用和公用的,比如:接口的登录校验、限流处理、安全性处理、下单、用户保存日志,下单要发送系统消息等。都可以采用AOP的思想进行处理。AOP实现机制是通过采用jdk动态代理和cglib进行切换处理。默认情况下是采用:jdk动态代理实现,可以通过proxy=true/false来切换jdk动态代理还是cglib代代理,cglib代理和jdk动态区别就是有没有接口的区别。我以jdk动态代理为例,如果要实现方法的动态代理增强的话,就需要准备一个接口,一个实现类,一个代理对象、还有切面类,而这个切面类必须要实现接口InvocationHandler接口。覆盖invoke方法。把需要增强的逻辑放入到invoke方法进行处理,而代理对象的产生是通过Proxy.newInstance()产生的。

而SpringAop把jdk动态代理的所谓的细节和控制进行了封装了。提出了很多的一个概念,其中就包括:

  • 切面(Aspect):通知和切入点的结合。==(具体增强逻辑处理的类,也就切面类)==这个类必须要增加注解:@Aspect .来标准当前类是一个切面类。

  • 代理(Proxy):通过代理方式来对目标对象应用切面。AOP代理可以用JDK动态代理或CGLIB代理实现。

  • 通知(Advice):在连接点上执行的行为,增强提供了在AOP中需要在切入点所选择的连接点处进行扩展现有行为的手段。包括前置增强(before advice)、后置增强 (after advice)、环绕增强 (around advice)。

    其实就是指:具体处理业务的位置和时间点。它是切面类的invoke方法。

    其实通知:就是你具体要做得事情。比如日志处理

  • 目标对象(Target):需要被织入关注点的对象。即被代理的对象。具体的:orderServiceImpl,为什么要传递一个目标对象进来呢? 代理对象目的是什么?进行逻辑增强,增强以后你是不是还要去处理后续方法业务呢?

  • 连接点(JoinPoint):需要在程序中插入横切关注点的点,连接点可能是在类初始化、方法调用、字段调用或处理异常等等。Spring中只支持方法执行连接点。具体执行要拦截的方法,一句话:就是代理对象执行的方法

  • 切入点(Pointcut):一组相关连接点的集合。一句话:你所以需要拦截方法的集合,对所有满足@Poincut()条件的方法进行过滤。(@Poincut())

  • 织入(Weaving):织入是一个过程,是将切面应用到目标对象从而创建出AOP代理对象的过程。)就把对象对象执行的方法和通知关联的过程称之为织入,一句话:代理对象执行方法,要去那个通知方法执行增强逻辑处理

猜你喜欢

转载自blog.csdn.net/L_994572281_LYA/article/details/121758673