springaop实现相关功能(事务、异常处理、记录日志)

devtools/2024/10/18 18:13:38/

springaop_0">springaop的底层实现(代理模式)

  • 原理设计

    • 代理模式:
      Spring AOP使用了代理模式,为目标对象创建一个代理对象。当调用目标对象的方法时,实际上是调用代理对象的方法。代理对象在调用目标方法前后,可以插入额外的代码(即切面的通知),从而实现AOP的功能。
    • 字节码操作:
      Spring AOP使用字节码操作技术来创建代理对象。具体地,它会为目标对象生成一个子类(使用CGLIB代理)或接口的实现类(使用JDK动态代理),并在该类中重写目标方法,以插入额外的代码。
  • JDK动态代理

    • 如果目标对象实现了至少一个接口,Spring AOP会使用JDK动态代理。JDK动态代理通过反射机制为目标接口创建一个代理类,并调用InvocationHandler接口的invoke方法来执行目标方法。
    • JDK动态代理主要基于Java的反射机制,它要求被代理的对象必须实现至少一个接口。代理类在运行时动态生成,并实现了与被代理对象相同的接口。当调用代理对象的方法时,实际上是通过反射机制调用被代理对象的相应方法。
      • JDK动态代理优点:

        • 简单易用:JDK动态代理使用Java标准库中的Proxy类和InvocationHandler接口,无需额外引入第三方库,使用相对简单。
        • 灵活性:能够动态地创建代理类,并可以灵活地添加额外的功能,如日志记录、性能监控等。
        • 通用性:由于基于接口代理,因此可以很方便地对不同的接口进行代理,实现通用的代理逻辑。
      • JDK动态代理缺点:

        • 只能代理接口:JDK动态代理要求目标对象必须实现至少一个接口,如果目标类没有实现任何接口,则无法使用JDK动态代理进行代理。

        • 性能开销:由于JDK动态代理基于反射机制实现,因此在调用代理方法时可能存在一定的性能开销。

        • 举例,假设我们有一个接口MyInterface和一个实现了该接口的类MyClass:

          public interface MyInterface {  void doSomething();  
          }  public class MyClass implements MyInterface {  @Override  public void doSomething() {  System.out.println("Doing something in MyClass");  }  
          }
          
        • 我们可以使用JDK动态代理为MyClass创建一个代理对象:

          import java.lang.reflect.InvocationHandler;  
          import java.lang.reflect.Method;  
          import java.lang.reflect.Proxy;  public class MyInvocationHandler implements InvocationHandler {  private Object target;  public MyInvocationHandler(Object target) {  this.target = target;  }  @Override  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  System.out.println("Before method invocation");  Object result = method.invoke(target, args);  System.out.println("After method invocation");  return result;  }  public static void main(String[] args) {  MyInterface myClass = new MyClass();  MyInterface proxy = (MyInterface) Proxy.newProxyInstance(  MyClass.class.getClassLoader(),  myClass.getClass().getInterfaces(),  new MyInvocationHandler(myClass)  );  proxy.doSomething();  // 输出:Before method invocation, Doing something in MyClass, After method invocation  }  
          }
          
        • 在这个例子中,我们创建了一个实现了InvocationHandler接口的类MyInvocationHandler,并在其中添加了额外的逻辑(在方法调用前后打印消息)。然后,我们使用Proxy.newProxyInstance方法创建了一个代理对象,该对象实现了与MyClass相同的接口。当我们调用代理对象的doSomething方法时,实际上会先调用MyInvocationHandlerinvoke方法,然后再调用MyClassdoSomething方法。

  • CGLIB代理

    • 如果目标对象没有实现任何接口,Spring AOP会使用CGLIB代理。CGLIB是一个强大的高性能的代码生成库,它可以在运行时扩展Java类与实现Java接口。CGLIB通过继承目标类来创建代理类,并重写目标方法。

    • 与JDK动态代理不同,CGLIB代理是通过继承被代理类来创建代理对象的。因此,它不需要被代理对象实现任何接口。CGLIB代理在运行时动态生成被代理类的子类,并重写其中的方法。当调用代理对象的方法时,实际上是调用CGLIB生成的子类中的相应方法。

      • CGLIB代理优点:
        • 可以代理任意类:CGLIB代理通过继承目标类来创建代理对象,因此无需目标类实现接口,可以代理任意类。
        • 性能较好:CGLIB代理通过直接操作字节码生成新的类,避免了使用反射的开销,因此在性能方面通常优于JDK动态代理。
        • 提供更多控制:CGLIB代理提供了更多的控制选项,如方法拦截、方法回调等,可以实现更复杂的代理逻辑。
      • CGLIB代理缺点:
        • 无法代理final类和方法:由于CGLIB代理是通过继承目标类实现的,因此无法代理被标记为final的类和方法。

        • 使用相对复杂:CGLIB代理需要引入第三方库,并且其实现逻辑相对复杂,需要一定的编程经验和技能。

        • 性能开销:虽然CGLIB代理在性能方面通常优于JDK动态代理,但由于它涉及到字节码操作,因此在创建代理类的过程中可能会存在一定的性能开销。

        • 使用CGLIB代理,需要添加CGLIB库的依赖。然后,可以创建一个实现了MethodInterceptor接口的类来定义代理逻辑:

          import net.sf.cglib.proxy.MethodInterceptor;  
          import net.sf.cglib.proxy.MethodProxy;  
          import java.lang.reflect.Method;  
          import java.lang.reflect.Object;  public class MyMethodInterceptor implements MethodInterceptor {  @Override  public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {  System.out.println("Before method invocation");  Object result = proxy.invokeSuper(obj, args);  // 调用父类(即被代理类)的方法  System.out.println("After method invocation");  return result;  }  
          }
          

          接下来,我们可以使用CGLIB来创建MyClass的代理对象:

          import net.sf.cglib.proxy.Enhancer;  public class Main {  public static void main(String[] args) {  Enhancer enhancer = new Enhancer();  enhancer.setSuperclass(MyClass.class);  // 设置被代理类  enhancer.setCallback(new MyMethodInterceptor());  // 设置回调(即代理逻辑)  MyClass proxy = (MyClass) enhancer.create();  // 创建代理对象  proxy.doSomething();  // 输出:Before method invocation, Doing something in MyClass, After method invocation  }  
          }
          

          在这个例子中,创建了一个实现了MethodInterceptor接口的类MyMethodInterceptor,并在其中添加了额外的逻辑。然后,我们使用CGLIB的Enhancer类来创建MyClass的代理对象,并设置回调为MyMethodInterceptor的实例。当我们调用代理对象的doSomething方法时,实际上会先调用MyMethodInterceptorintercept方法,然后再调用MyClass的方法

    1. 代理类的生成:
      Spring AOP使用AspectJ的weaver组件来生成代理类的字节码。weaver组件负责解析切面定义、切点表达式,并生成相应的代理类。
    2. 通知的执行:
      当代理对象的方法被调用时,Spring AOP会根据切点表达式匹配相应的通知。然后,按照通知的类型(前置、后置、环绕等)执行相应的代码。
    3. 事务管理:
      Spring AOP的事务管理是一个重要的应用场景。通过配置事务管理器、事务属性(如传播行为、隔离级别等),Spring AOP可以在方法执行前后自动开启和提交/回滚事务。

异常处理

  • 定义一个切面来捕获和处理方法执行过程中抛出的异常:
    1. 准备:AOP 的依赖,如果你使用 Maven,可以在 pom.xml 文件中添加以下依赖:

      <dependency>  <groupId>org.springframework.boot</groupId>  <artifactId>spring-boot-starter-aop</artifactId>  
      </dependency>
      
    2. 创建一个异常处理切面 ExceptionHandlingAspect,使用 @AfterThrowing 注解来定义在方法抛出异常后执行的通知:

      import org.aspectj.lang.JoinPoint;  
      import org.aspectj.lang.annotation.AfterThrowing;  
      import org.aspectj.lang.annotation.Aspect;  
      import org.springframework.stereotype.Component;  @Aspect  
      @Component  
      public class ExceptionHandlingAspect {  @AfterThrowing(pointcut = "execution(* com.example.myapp.service.*.*(..))", throwing = "exception")  public void handleException(JoinPoint joinPoint, Throwable exception) {  // 处理异常逻辑  String methodName = joinPoint.getSignature().getName();  String className = joinPoint.getTarget().getClass().getName();  System.err.println("Exception occurred in method " + methodName + " of class " + className);  exception.printStackTrace();  // 你可以在这里记录异常到日志、发送邮件通知、执行回滚操作等  }  
      }
      
    3. 如上代码@Aspect 注解标记这个类为一个切面

      • @Component 注解使得 Spring 能够自动发现并注册这个 bean。
      • @AfterThrowing 注解定义了一个异常通知,它会在匹配 pointcut 表达式的方法抛出异常后执行。
      • throwing = "exception" 指定了通知方法的参数名,用于接收抛出的异常。
    4. 在 Spring 配置中启用 AOP 支持,Spring Boot,可以通过添加 @EnableAspectJAutoProxy 注解来启用 AOP:

      import org.springframework.boot.SpringApplication;  
      import org.springframework.boot.autoconfigure.SpringBootApplication;  
      import org.springframework.context.annotation.EnableAspectJAutoProxy;  @SpringBootApplication  
      @EnableAspectJAutoProxy  
      public class MyApplication {  public static void main(String[] args) {  SpringApplication.run(MyApplication.class, args);  }  
      }
      
    5. 当你的应用程序中的 com.example.myapp.service 包下的任意方法抛出异常时,ExceptionHandlingAspect 切面中的 handleException 方法将被自动调用,处理异常逻辑。

    6. 注意,还可以使用 @Around 注解来在方法执行前后进行更精细的控制,包括在异常发生时执行特定的逻辑。

      import org.aspectj.lang.ProceedingJoinPoint;  
      import org.aspectj.lang.annotation.Around;  
      import org.aspectj.lang.annotation.Aspect;  
      import org.springframework.stereotype.Component;  @Aspect  
      @Component  
      public class ExceptionHandlingAspect {  @Around("execution(* com.example.myapp.service.*.*(..))")  public Object handleExceptions(ProceedingJoinPoint joinPoint) throws Throwable {  try {  // 在目标方法执行之前执行一些操作(可选)  System.out.println("Before method execution: " + joinPoint.getSignature());  // 继续执行目标方法  Object result = joinPoint.proceed();  // 在目标方法执行之后执行一些操作(可选)  System.out.println("After method execution: " + joinPoint.getSignature());  return result;  } catch (Exception ex) {  // 处理异常  System.out.println("Exception occurred: " + ex.getMessage());  // 这里可以记录日志、发送通知等  // 可以选择抛出异常或者返回一个默认值、错误响应等  throw new RuntimeException("An error occurred during method execution", ex);  }  }  
      }
      

springmvc_224">springmvc拦截器实现异常处理

  • Spring MVC中使用拦截器和@ControllerAdvice与@ExceptionHandler来处理异常。

    1. 创建一个简单的拦截器来记录请求信息:
    import javax.servlet.http.HttpServletRequest;  
    import javax.servlet.http.HttpServletResponse;  import org.springframework.web.servlet.HandlerInterceptor;  
    import org.springframework.web.servlet.ModelAndView;  public class LoggingInterceptor implements HandlerInterceptor {  @Override  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)  throws Exception {  // 在请求处理之前记录日志  System.out.println("Pre-Handle: " + request.getRequestURI());  return true; // 继续处理请求  }  @Override  public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,  ModelAndView modelAndView) throws Exception {  // 在请求处理之后但在视图渲染之前记录日志  System.out.println("Post-Handle: " + request.getRequestURI());  }  @Override  public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)  throws Exception {  // 在请求处理完成之后(包括视图渲染)记录日志  System.out.println("After-Completion: " + request.getRequestURI());  // 注意:这里虽然可以访问到异常,但不建议在这里处理异常  // 因为此时请求已经处理完成,并且可能已经将异常信息发送给了客户端  if (ex != null) {  System.out.println("Exception occurred: " + ex.getMessage());  }  }  
    }
    
    1. 配置拦截器:
    import org.springframework.beans.factory.annotation.Autowired;  
    import org.springframework.context.annotation.Configuration;  
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;  
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;  @Configuration  
    public class WebConfig implements WebMvcConfigurer {  @Autowired  private LoggingInterceptor loggingInterceptor;  @Override  public void addInterceptors(InterceptorRegistry registry) {  registry.addInterceptor(loggingInterceptor);  }  
    }
    
    1. 创建一个@ControllerAdvice类来处理异常:
    import org.springframework.http.HttpStatus;  
    import org.springframework.http.ResponseEntity;  
    import org.springframework.web.bind.annotation.ControllerAdvice;  
    import org.springframework.web.bind.annotation.ExceptionHandler;  @ControllerAdvice  
    public class GlobalExceptionHandler {  @ExceptionHandler(value = Exception.class)  public ResponseEntity<Object> handleException(Exception e) {  // 这里处理异常,比如记录日志、返回错误信息等  ErrorDto errorDto = new ErrorDto("Error", e.getMessage());  return new ResponseEntity<>(errorDto, HttpStatus.INTERNAL_SERVER_ERROR);  }  // ErrorDto 是一个简单的类,用于封装错误信息  // ...  
    }
    
    1. 测试在控制器中,抛出一个异常来测试异常处理是否生效:
    import org.springframework.web.bind.annotation.GetMapping;  
    import org.springframework.web.bind.annotation.RestController;  @RestController  
    public class MyController {  @GetMapping("/testException")  public String testException() {  throw new RuntimeException("Test Exception");  }  
    }
    
  • 访问/testException端点时,会触发异常,并且GlobalExceptionHandler中的handleException方法会被调用,返回一个包含错误信息的ResponseEntity对象。同时,拦截器的preHandlepostHandleafterCompletion方法也会被调用,你可以在afterCompletion方法中看到异常信息(如果有的话)。

  • 注意:不要在afterCompletion方法中处理异常,因为它通常用于清理资源等后续操作。

springaop_333">springaop实现日志

import org.aspectj.lang.ProceedingJoinPoint;  
import org.aspectj.lang.annotation.Around;  
import org.aspectj.lang.annotation.Aspect;  
import org.slf4j.Logger;  
import org.slf4j.LoggerFactory;  
import org.springframework.stereotype.Component;  @Aspect  
@Component  
public class LoggingAspect {  private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);  @Around("execution(* com.example.myapp.service.*.*(..))")  public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {  long startTime = System.currentTimeMillis();  // 记录方法执行前的日志  logger.info("Entering method: " + joinPoint.getSignature());  Object result = joinPoint.proceed(); // 继续执行目标方法  // 记录方法执行后的日志  long endTime = System.currentTimeMillis();  logger.info("Exiting method: " + joinPoint.getSignature() + " took " + (endTime - startTime) + "ms");  return result;  }  
}

springaop_367">springaop实现事务

  • 关于事务管理,Spring AOP通过@Transactional注解和PlatformTransactionManager接口来实现。以下是如何使用Spring AOP实现事务管理的步骤:

    1. 配置事务管理器:

      • 首先,你需要在Spring配置文件中配置一个事务管理器。事务管理器的类型取决于你使用的数据库和连接池。如果你使用的是MyBatis和JDBC,你可能会使用DataSourceTransactionManager

        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  <property name="dataSource" ref="dataSource"/>  
        </bean>
        
    2. 启用事务注解:

      • 在Spring配置中启用对@Transactional注解的支持。这可以通过<tx:annotation-driven/>标签来完成。
      <tx:annotation-driven transaction-manager="transactionManager"/>
      
      • Java配置:
      @Configuration  
      @EnableTransactionManagement  
      public class AppConfig {  // ... 其他bean配置 ...  
      }
      
    3. 使用@Transactional注解:
      使用@Transactional注解来声明这些方法需要在事务上下文中执行。Spring会自动为你管理这些事务。

      @Service  
      public class MyService {  @Autowired  private MyRepository myRepository;  @Transactional  public void doSomethingInTransaction() {  // 这里执行一些数据库操作,如果发生异常,所有操作都会被回滚  myRepository.save(new MyEntity());  }  
      }
      
      • 事务属性:
        @Transactional注解还允许你指定事务的属性,比如传播行为、隔离级别、超时和只读标志等。例如:

        @Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_COMMITTED)  
        public void doSomethingWithCustomTransactionAttributes() {  // ...  
        }
        
    4. 异常处理:
      默认情况下,Spring会在运行时异常(RuntimeException及其子类)发生时回滚事务。如果你希望在其他类型的异常(如检查型异常)发生时也回滚事务,你可以使用rollbackFor属性来指定。

springaop_433">springaop实现权限认证

  • 步骤:

    1. 定义切面:创建一个切面类,用于拦截需要进行权限认证的方法。
    2. 定义切入点:在切面类中,使用AspectJ的切入点表达式定义哪些方法需要被拦截。
    3. 实现通知方法:在切面类中,实现一个或多个通知方法(如@Before、@Around等),在这些方法中实现权限认证逻辑。
  • 示例

  1. 定义一些需要权限认证的服务接口和实现类:

    // 服务接口  
    public interface MyService {  void secureMethod(String userId);  
    }  // 服务实现类  
    @Service  
    public class MyServiceImpl implements MyService {  @Override  public void secureMethod(String userId) {  // 业务逻辑  System.out.println("Executing secure method for user: " + userId);  }  
    }
    
  2. 创建一个切面类用于权限认证:

    @Aspect  
    @Component  
    public class AuthorizationAspect {  // 假设有一个权限检查服务  @Autowired  private PermissionCheckService permissionCheckService;  // 切入点表达式,拦截MyService接口的所有方法  @Before("execution(* com.example.myapp.service.MyService.*(..))")  public void checkPermission(JoinPoint joinPoint) {  // 获取方法参数  Object[] args = joinPoint.getArgs();  if (args.length > 0 && args[0] instanceof String) {  String userId = (String) args[0];  // 检查用户是否有权限执行该方法  if (!permissionCheckService.hasPermission(userId, joinPoint.getSignature().getName())) {  throw new AccessDeniedException("Access denied for user: " + userId);  }  }  }  
    }
    
  3. 接下来,定义一个简单的PermissionCheckService接口和实现类,用于模拟权限检查逻辑:

    public interface PermissionCheckService {  boolean hasPermission(String userId, String methodName);  
    }  @Service  
    public class PermissionCheckServiceImpl implements PermissionCheckService {  @Override  public boolean hasPermission(String userId, String methodName) {  // 这里只是示例,实际应用中需要根据业务逻辑和权限配置来判断  return "admin".equals(userId);  }  
    }
    
  4. 在Spring Boot项目中启用AOP支持,可以通过在启动类上添加@EnableAspectJAutoProxy注解来实现:

    @SpringBootApplication  
    @EnableAspectJAutoProxy  
    public class MyApplication {  public static void main(String[] args) {  SpringApplication.run(MyApplication.class, args);  }  
    }
    
  • 总述:当调用MyServicesecureMethod方法时,AuthorizationAspect切面会自动拦截该方法,并执行权限认证逻辑。如果用户没有权限,则会抛出异常。

springMVC_526">springMVC使用拦截器实现权限认证&日志记录

  • 创建一个拦截器类,实现 HandlerInterceptor 接口。
  • 在 preHandle 方法中,检查当前用户是否有权限访问目标资源。
    • 如果用户没有权限,则设置响应状态码或重定向到错误页面。
    • 如果用户有权限,则继续处理请求。
public class AuthenticationInterceptor implements HandlerInterceptor {  @Autowired  private AuthenticationService authenticationService; // 假设的权限服务  @Override  public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {  // 在Controller处理之前调用  // 可以在这里执行权限检查、记录日志等  // 如果返回false,则请求处理到此为止,不会调用Controller  // 获取当前用户(这里假设可以从请求中获取)  User user = ...;  // 检查用户是否有权限访问目标资源(这里假设 handler 是 HandlerMethod)  if (handler instanceof HandlerMethod) {  HandlerMethod handlerMethod = (HandlerMethod) handler;  if (!authenticationService.isAuthenticated(user, handlerMethod.getBeanType(), handlerMethod.getMethod().getName())) {  response.sendError(HttpServletResponse.SC_FORBIDDEN); // 发送 403 Forbidden 响应  return false; // 阻止后续处理  }  }  // 如果有权限,则继续处理请求  return true;  }  @Override  public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {  // 在Controller处理请求完成之后调用,但在视图渲染之前  // 可以在这里进行模型属性的修改等  System.out.println("MyInterceptor - postHandle");  }  @Override  public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {  // 在整个请求结束之后调用,即在视图渲染之后  // 通常用于清理资源、记录执行时间等  System.out.println("MyInterceptor - afterCompletion");  }  }
  • 拦截器注册
java
import org.springframework.context.annotation.Configuration;  
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;  
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;  @Configuration  
public class WebConfig implements WebMvcConfigurer {  @Override  public void addInterceptors(InterceptorRegistry registry) {  registry.addInterceptor(new MyInterceptor());  // 还可以添加多个拦截器,并为它们指定路径模式  /** registry.addInterceptor(anotherInterceptor()).addPathPatterns("/somePath/**").excludePathPatterns("/static/**");;  }  
}

http://www.ppmy.cn/devtools/27824.html

相关文章

使用FPGA实现串-并型乘法器

介绍 其实我们知道&#xff0c;用FPGA实现乘法器并不是一件很简单的事&#xff0c;而且在FPGA中也有乘法器的IP核可以直接调用&#xff0c;我这里完全就是为了熟悉一些FPGA的语法然后写了这样一个电路。 串-并型乘法器模块 从字面上看&#xff0c;串-并乘法器就是其中一个乘数…

用户中心(终)

文章目录 Ant Design Pro&#xff08;Umi 框架&#xff09;ProComponents 高级表单待优化点 todo注册逻辑增加注册页面页面重定向问题注册页面 **获取用户的登录态****前端用户管理功能** Ant Design Pro&#xff08;Umi 框架&#xff09; app.tsx 项目全局入口文件&#xff0c…

第49期|GPTSecurity周报

GPTSecurity是一个涵盖了前沿学术研究和实践经验分享的社区&#xff0c;集成了生成预训练Transformer&#xff08;GPT&#xff09;、人工智能生成内容&#xff08;AIGC&#xff09;以及大语言模型&#xff08;LLM&#xff09;等安全领域应用的知识。在这里&#xff0c;您可以找…

PySpark学习---销售情况数据统计分析案例

需求分析&#xff1a; 某公司是做零售相关业务&#xff0c;旗下出品各类收银机. 目前公司的收银机已经在全国铺开,在各个省份均有店铺使用.机器是联网的,每一次使用都会将售卖商品数据上传到公司后台.老板现在想对省份维度的销售情况进行统计分析 逻辑需求&#xff1a; 1.各省销…

模型智能体开发之metagpt-多智能体实践

参考&#xff1a; metagpt环境配置参考模型智能体开发之metagpt-单智能体实践 需求分析 之前有过单智能体的测试case&#xff0c;但是现实生活场景是很复杂的&#xff0c;所以单智能体远远不能满足我们的诉求&#xff0c;所以仍然还需要了解多智能体的实现。通过多个role对动…

笨蛋学C++之 C++连接数据库

笨蛋学C 之 VS2019使用C连接数据库 创建数据库SQL语句VS2019选择空项目&#xff0c;点击下一步创建输入项目名称&#xff0c;点击创建创建成功点击新建项创建源文件因为mysql是64位&#xff0c;此时的c项目是86位&#xff0c;所以这里需要将项目修改为x64位点击项目 -> 0501…

Web开发基础概念

Python的Web开发是指使用Python语言来开发Web应用程序&#xff0c;如网站、网络应用程序等。在Python的Web开发中&#xff0c;有一些核心概念和技术栈需要了解。本文将介绍Python的Web开发框架和技术栈&#xff0c;并提供一些相关的资源供参考。一、Python的Web开发框架Python的…

MATLAB 函数

MATLAB 函数 函数是一起执行任务的一组语句。在MATLAB中&#xff0c;函数是在单独的文件中定义的。文件名和函数名应该相同。 函数在其自己的工作空间&#xff08;也称为本地工作空间&#xff09;中对变量进行操作&#xff0c;与在MATLAB命令提示符下访问的工作空间&#xff0…