JAVA面试部分——后端-框架前篇

7.1 JSP+Servlet

JSP(JavaServer Pages)和Servlet是Java Web开发中常用的两种技术,它们通常一起使用来构建动态的Web应用程序。下面简要介绍一下JSP和Servlet以及它们是如何协同工作的。

1. Servlet:

Servlet是一种Java编写的服务器端程序,它在Web服务器上运行,接收和处理客户端请求,然后生成响应发送回客户端。Servlet通常用于处理业务逻辑、数据库交互等服务器端的任务。Servlet可以接收HTTP请求并返回HTTP响应,因此它是构建Web应用程序的基本组件之一。

示例Servlet代码:

import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
​
public class HelloWorldServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        out.println("");
        out.println("

Hello World from Servlet!

");        out.println("");   } }
2. JSP:

JSP是一种在HTML中嵌入Java代码的技术,它允许开发者将Java代码嵌套在HTML页面中,从而简化了页面的动态生成过程。JSP最终会被转换成Servlet并在服务器上执行。JSP页面通常包含HTML标签和嵌套的Java代码块。

示例JSP代码:

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>



    
    Hello World JSP


    

Hello World from JSP!

3. 结合使用:

通常,JSP和Servlet可以一起使用,以实现更灵活和结构化的Web应用程序。Servlet用于处理业务逻辑和控制流程,而JSP用于呈现HTML页面。Servlet接收请求,执行必要的处理,然后将控制转发给JSP页面,最终由JSP生成HTML响应返回给客户端。

使用Servlet中的转发(forward)或重定向(redirect)机制,可以将请求从Servlet发送到JSP。例如,Servlet可以在其doGetdoPost方法中执行以下操作:

RequestDispatcher dispatcher = request.getRequestDispatcher("/hello.jsp");
dispatcher.forward(request, response);

上述代码表示将请求转发到名为hello.jsp的JSP页面。在这个JSP页面中,你可以嵌套Java代码块,执行业务逻辑,并生成动态的HTML响应。

总体而言,JSP和Servlet是Java Web开发中的经典技术,提供了一种强大而灵活的方式来构建动态的Web应用程序。然而,随着技术的发展,现代的Java Web开发中更常使用基于MVC模式的框架(如Spring MVC),以提供更高级、模块化和可维护的Web应用程序。

7.2 Cookie的分类和优缺点,应用场景

Cookie可以根据其不同的属性和用途进行分类。以下是常见的几种Cookie分类:

  1. 会话Cookie(Session Cookie):这种Cookie保存在用户的计算机上,直到关闭浏览器时才会被删除。会话Cookie用于存储用户在一次会话期间所需的临时信息,如登录状态、购物车内容等。

  2. 持久Cookie(Persistent Cookie):持久Cookie保存在用户的计算机上一段时间,即使关闭浏览器也不会被删除。它们有一个到期日期,在到期之前会一直存在于用户的计算机上。持久Cookie通常用于记录用户的偏好设置、个性化信息等。

  3. 第一方Cookie(First-party Cookie):第一方Cookie由正在访问的网站设置,并且与该网站的域名相对应。它们用于跟踪用户在同一网站上的活动和行为,并为用户提供个性化的体验。

  4. 第三方Cookie(Third-party Cookie):第三方Cookie由不同于当前访问的网站的域名设置。它们通常用于广告追踪和跨网站行为分析。第三方Cookie可以跟踪用户在多个网站上的活动,并提供针对用户兴趣的广告。

7.3 cookie与session区别

Cookie和Session都是在Web开发中用于存储和管理用户状态信息的机制,但它们有一些关键的区别。

Cookie:
  1. 存储位置: Cookie是存储在客户端(用户浏览器)上的小型文本文件,通常由服务器在HTTP响应中发送给客户端,并由浏览器保存。

  2. 大小限制: 每个域名对于一个浏览器的所有Cookie的总大小通常有限制,通常在几KB到几十KB之间。

  3. 生命周期: Cookie有一个过期时间,可以设置为会话级(浏览器关闭时失效)或持久性(在过期时间之前一直有效)。

  4. 安全性: Cookie存储在客户端,因此对于敏感信息的存储需要考虑安全性。可以通过设置Secure属性和HttpOnly属性来增加安全性。

Session:
  1. 存储位置: Session是存储在服务器上的数据结构,通常以一种键值对的形式存储用户信息。每个用户会话都有一个唯一的标识符(通常是一个会话ID),该标识符存储在Cookie中(或通过URL重写等方式传递)。

  2. 大小限制: 通常,服务器上存储Session数据的大小可以比较大,不像Cookie受到较小的限制。

  3. 生命周期: Session通常有一个相对较短的生命周期,可能是会话级(在用户关闭浏览器时失效)或者通过超时设置来控制。

  4. 安全性: 由于Session数据存储在服务器端,相对于Cookie更安全,因为客户端无法直接访问或修改Session数据。

主要区别:
  1. 存储位置: Cookie存储在客户端,Session存储在服务器端。

  2. 大小限制: Cookie有较小的存储限制,而Session的存储限制通常较大。

  3. 生命周期: Cookie的生命周期由设置的过期时间决定,而Session的生命周期由服务器设置的超时时间决定。

  4. 安全性: 由于Session存储在服务器端,相对于Cookie更安全。 Cookie存储在客户端,可能受到一些安全威胁,需要采取额外的安全措施。

通常,Cookie和Session会一起使用。服务器使用Cookie来在客户端存储一个唯一的标识符(Session ID),而实际的用户数据则存储在服务器端的Session中。这样,可以在保持一定的安全性的同时,实现用户状态的跟踪和管理。

7.4 token校验权限?(在哪儿检验,怎么校验)

通常,token 是在用户登录成功后生成,并且会被保存在客户端(例如浏览器)的 cookie 或本地存储中。下面是一个常见的校验流程:

  1. 用户登录成功后,服务器生成一个 token,并将其返回给客户端。

  2. 客户端将 token 保存在 cookie 或本地存储中。

  3. 在后续的请求中,客户端会将 token 添加到请求的头部、参数或者 cookie 中。

  4. 服务器在接收到请求时,会先从请求中获取 token。

  5. 服务器使用预设的密钥或者公钥对 token 进行解密和验证。这通常涉及到对 token 的签名验证和过期时间验证。

  6. 如果 token 验证通过,服务器会认为用户有权限访问该资源,否则拒绝访问,并返回相应的错误信息。

值得注意的是,具体的实现方式会因开发框架和需求而有所不同。一些常见的实现方式有使用 JWT(JSON Web Token)或者基于 Session 的验证机制。一般来说,校验权限的逻辑在服务器端处理,可以在 API 接口或者中间件中进行校验。

7.5 token的组成

简单token的组成;uid(用户唯一的身份标识)、time(当前时间的时间戳)、sign(签名,token的前几位以哈希算法压缩成的一定长度的十六进制字符串。为防止token泄露)。

7.6 如果用户选择禁用Cookie, 如何实现保持登录的效果?
  1. 使用Token:在用户登录成功后,生成一个唯一的Token,然后将Token返回给客户端。客户端可以在后续的请求中将Token放在请求头中发送给服务器。服务器可以通过验证Token来验证用户的身份。Token可以存储在客户端的LocalStorage或者Cookie中,但要注意安全性,避免Token被盗用或篡改。

  2. 使用Session:当用户登录成功后,服务器会创建一个Session,并将Session ID发送给客户端。客户端可以在后续的请求中将Session ID放在HTTP请求头中发送给服务器。服务器通过查找对应的Session来验证用户的身份。如果用户禁用了Cookie,可以使用URL重写的方式将Session ID放在URL中传递给服务器。

  3. 使用JWT(JSON Web Tokens):JWT是一种基于Token的认证和授权机制,可以用于实现无状态的会话管理。当用户登录成功后,服务器会生成一个JWT,并将其返回给客户端。客户端可以在后续的请求中将JWT放在HTTP请求头中发送给服务器。服务器通过解析JWT中的信息来验证用户的身份。JWT也可以存储在LocalStorage或者Cookie中,但同样需要注意安全性。

需要注意的是,以上方法都需要服务器端实现相应的验证机制,确保用户的身份得到正确的验证。同时,如果用户禁用了Cookie,可能会增加跨站请求伪造(CSRF)等安全问题的风险,需要注意防范。

7.7 CSRF

CSRF(跨站请求伪造)是一种攻击方法,也被称为One-Click Attack或者Session Riding。这种攻击利用的是网站对用户网页浏览器的信任,通过欺骗用户的浏览器发送恶意请求,以执行非授权的操作。

CSRF攻击通常需要攻击者通过一些技术手段,如利用XSS漏洞,来在用户的浏览器中植入恶意代码。恶意代码会向网站发送伪造的请求,以执行攻击者想要的操作,例如发送邮件、发消息、盗取用户账号等。

为了防止CSRF攻击,网站可以采用各种防范措施,例如在服务器端验证请求的来源、添加随机数验证等。同时,也可以通过在网页上添加CSRF X-Token等措施来提高安全性。

如果用户禁用了Cookie,可能会增加跨站请求伪造(CSRF)等安全问题的风险。为了防止CSRF攻击,可以采用以下措施:

  1. 验证请求来源:在服务器端验证请求的来源,确保请求来自可信的源。可以检查请求的IP地址、User-Agent等,或者使用Token验证等机制来验证请求的合法性。

  2. 添加随机数验证:在服务器端生成一个随机的Token,并将Token返回给客户端。客户端在后续的请求中将Token放在请求头中发送给服务器,服务器验证Token的合法性。

  3. 使用CSRF X-Token:在网页上添加CSRF X-Token,每次请求时都需要从服务器获取新的CSRF X-Token,并验证其有效性。

  4. 使用验证码:在关键操作时添加验证码,例如输入正确的验证码才能提交表单等。

需要注意的是,以上措施并非必须全部采用,可以根据实际情况选择合适的措施来提高安全性。同时,也需要及时更新安全补丁,以应对新的安全威胁。

7.8 ajax了解吗?

是的,我了解Ajax(Asynchronous JavaScript and XML)。Ajax是一种用于在Web应用程序中实现异步数据交互的技术。它允许在不刷新整个页面的情况下,通过JavaScript向服务器发送请求并更新部分页面内容。这种技术使得Web应用程序可以更加动态和交互性。

以下是Ajax的一些主要特点和组成部分:

  1. 异步通信: Ajax使用异步通信方式,即在发送请求的同时,用户界面仍然可以继续响应用户的其他操作,而不会阻塞用户界面。

  2. XMLHttpRequest对象: 在JavaScript中,Ajax使用XMLHttpRequest对象来与服务器进行通信。这个对象提供了在后台发送HTTP请求和接收响应的能力。

  3. 数据格式: 尽管名字中包含XML,但实际上Ajax不限于使用XML作为数据格式。可以使用多种数据格式,如JSON(JavaScript Object Notation)等。

  4. DOM操作: Ajax通常与DOM(Document Object Model)结合使用,以便在客户端更新页面内容。通过JavaScript,可以动态修改页面的一部分,而不需要整个页面的刷新。

  5. 事件驱动: Ajax通常基于事件驱动的编程模型。当异步操作完成时,触发相应的事件,以便处理响应数据。

  6. 跨域请求: Ajax的同源策略限制了在不同域的页面之间进行Ajax请求。为了解决这个问题,可以使用JSONP(JSON with Padding)或CORS(Cross-Origin Resource Sharing)等技术。

  7. 框架和库: 有许多流行的JavaScript框架和库,如jQuery、axios、Fetch API等,简化了使用Ajax的过程,提供了更高级的功能和更友好的API。

示例Ajax请求的基本模式:

// 创建XMLHttpRequest对象
var xhr = new XMLHttpRequest();
​
// 配置请求
xhr.open("GET", "example.com/api/data", true);
​
// 设置回调函数,处理响应
xhr.onreadystatechange = function() {
    if (xhr.readyState == 4 && xhr.status == 200) {
        // 处理响应数据
        var responseData = JSON.parse(xhr.responseText);
        console.log(responseData);
    }
};
​
// 发送请求
xhr.send();

上述代码演示了一个简单的Ajax GET请求。实际的应用中,可以根据需要使用POST请求,处理更复杂的响应数据,以及处理错误等情况。

7.9 怎么进行异步处理?

在Spring框架中工作,那么可以使用@Async注解来简化异步编程。首先,需要在Spring配置类上加上@EnableAsync,然后就可以在需要异步执行的方法上加上@Async注解了。

@Configuration  
@EnableAsync  
public class SpringAsyncConfig { ... }  
​
@Service  
public class SomeService {  
 @Async  
 public void asyncMethod() {  
     // long running task here...  
 }  
}

7.10 @Async 注解使用默认线程池还是自定义线程池?

在Spring框架中,@Async注解可以用来将一个方法标记为异步方法,以实现异步处理。@Async注解可以指定一个自定义的线程池,也可以使用默认的线程池。

默认情况下,@Async注解使用的是Spring的默认线程池,即SimpleAsyncTaskExecutor。SimpleAsyncTaskExecutor是一个基于内存的线程池,它不具有任何线程生命周期管理的复杂性,不提供线程池的参数配置。

如果你想使用自定义的线程池,可以通过在@Async注解上设置value属性来指定线程池的名称。例如:

@Async("myThreadPool")  
public void asyncMethod() {  
// 异步方法的代码  
}

在这个例子中,@Async注解使用了名为"myThreadPool"的自定义线程池。你需要在Spring配置中定义该线程池。

7.11 @Async注解失效怎么处理

@Async注解失效可能产生的原因及解决方案如下:

  1. 异步配置未开启。在SpringBoot启动类上添加@EnableAsync注解,或者在异步线程池配置类中添加@EnableAsync。

  2. 异步方法调用未设置为异步执行。确保被@Async注解的方法从另一个类调用时,是从类的外部调用,而不是从类的内部调用。

  3. 异步方法调用未设置为异步执行。如果需要从类的内部调用,需要先获取其代理类,通过代理类调用异步方法。

  4. @Async注解方法不是public公开方法。注解的方法必须是public方法。

  5. 未使用Spring的代理类。使用Spring的代理类,因为只有Spring的代理类才会对@Async注解进行解析。

  6. 异步方法中有调用同步方法的情况。如果有同步方法,需要处理,否则会影响整体异步执行的效果。

  7. 线程池参数设置不当,如核心线程数不足等,影响线程池执行效率。需要根据实际情况调整线程池参数。

7.12 循环依赖情况怎么解决?

循环依赖是指在程序中的两个或多个类之间存在相互依赖的情况,即每个类都需要在其他类中实例化,形成一种闭环关系。在Spring中,循环依赖通常发生在通过Spring的依赖注入机制进行Bean注入时,尤其是在使用构造方法注入或setter方法注入时。

解决循环依赖问题有以下几种方法:

  1. 构造器注入:这种方式是将依赖对象作为构造函数的参数传入,这要求在构造对象的时候必须提供所有的依赖。但是,当存在循环依赖的时候,这种方法会失败,因为每个对象在构造的时候都要求其依赖已经构造完成。Spring框架通过一种名为"三级缓存"的技术来解决这个问题。在创建对象的过程中,Spring会在缓存中查找是否已经创建了该对象的实例。如果找到了,就直接使用,否则就创建一个新的实例。

  2. setter注入:这种方式是通过setter方法或者注解将依赖注入到对象中。这种方式允许对象在构造后,再完成依赖的注入,这也使得循环依赖成为可能。Spring框架通过"三级缓存"来解决这个问题。在创建对象的过程中,Spring会在缓存中查找是否已经创建了该对象的实例。如果找到了,就直接使用,否则就创建一个新的实例。

  3. 使用@Lazy注解:在setter方法中使用@Lazy注解可以让Spring延迟Bean的初始化,从而避免循环依赖问题。当使用@Lazy注解时,Spring会在需要使用Bean时才初始化Bean,而不是在构造其他Bean时提前初始化。

  4. 使用三级缓存:在Spring中,可以通过三级缓存解决循环依赖问题。当出现循环依赖问题时,Spring会将Bean放入缓存中,然后通过ObjectFactory提前暴露Bean实例,从而解决循环依赖问题。

需要注意的是,在解决循环依赖问题时,需要根据具体情况选择合适的方法,并且要避免使用可能导致循环依赖的代码结构。

7.13 spring如何通过三级缓存解决循环依赖问题
  • SingletonObjects:缓存已经创建的单例对象。

  • EarlySingletonObjects:缓存早期创建的单例对象(提前暴露的对象)。

  • SingletonFactories:缓存单例对象的工厂(用于解决循环依赖)。

  1. 前提条件:被依赖的Bean A和依赖的Bean B都已经实例化,但是Bean B还未完全初始化。

  2. 第一级缓存(singletonObjects):Spring首先检查第一级缓存中是否存在Bean A的实例。如果存在,直接返回该实例。

  3. 第二级缓存(earlySingletonObjects):如果第一级缓存中不存在Bean A的实例,Spring会检查第二级缓存中是否存在Bean A的ObjectFactory。如果存在,说明Bean A正在创建过程中,通过ObjectFactory获取Bean A的代理对象,并返回。

  4. 创建Bean A的实例:如果第一级和第二级缓存中都不存在Bean A的实例,Spring会继续创建Bean A的实例。创建过程中会将ObjectFactory添加到第二级缓存中。

  5. 创建过程中的循环依赖:当创建Bean A的实例过程中需要依赖Bean B时,Spring会从第二级缓存中获取Bean B的代理对象。这样,即使Bean B尚未完成初始化,也能避免循环依赖问题。

  6. 创建完成并加入第一级缓存:当Bean A创建完成后,将其加入第一级缓存中,并从第二级缓存中移除。

7.14 对Spring 中 IOC 和 AOP 的理解

在Spring框架中,IOC(Inversion of Control)和AOP(Aspect-Oriented Programming)是两个关键的概念,它们分别用于解耦和增强应用程序的模块性。以下是对它们的简要理解:

1. IOC(Inversion of Control):

IOC是一种设计思想,它反转了传统应用程序中对象的创建和管理方式。在传统的编程模型中,应用程序自己负责创建和管理对象,而在IOC中,这种控制权被反转到了框架(例如Spring容器)。

主要特点和原理:

  • 依赖注入(DI): Spring使用依赖注入来实现IOC,即通过容器将一个对象的依赖注入到该对象中,而不是对象自己创建或查找依赖。

  • 配置文件或注解: IOC容器可以通过XML配置文件或注解来描述对象之间的依赖关系和配置信息,这样可以更灵活地管理对象的生命周期和依赖关系。

  • 解耦: IOC减少了类之间的直接耦合,使得代码更易于维护和扩展。

示例:

public class MyApp {
    public static void main(String[] args) {
        // 创建Spring容器
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
​
        // 从容器中获取Bean
        MyService myService = context.getBean(MyService.class);
​
        // 使用Bean
        myService.doSomething();
    }
}
2. AOP(Aspect-Oriented Programming):

AOP是一种编程范式,它的目标是将应用程序的关注点(concern)分离出来,使得关注点的模块化更容易。关注点可以是例如日志、事务、安全等与业务逻辑无关的横切关注点。

主要特点和原理:

  • 横切关注点: AOP通过将横切关注点(cross-cutting concerns)从主业务逻辑中分离出来,实现了更好的模块化。

  • 切面(Aspect): AOP通过切面来实现横切关注点的模块化,切面是一个包含了通知(advice)和切点(pointcut)的模块。

  • 通知: 通知定义了在什么时机、以何种方式执行横切关注点。例如,可以在方法执行前、后或异常抛出时执行通知。

  • 切点: 切点定义了何处应该执行横切关注点。可以通过表达式或注解来指定切点。

示例:

@Aspect
public class LoggingAspect {
​
    @Before("execution(* com.example.MyService.*(..))")
    public void beforeMethod(JoinPoint joinPoint) {
        // 在方法执行前执行日志记录
        System.out.println("Before method: " + joinPoint.getSignature().getName());
    }
}

在上述示例中,LoggingAspect定义了一个通知(beforeMethod),并指定了一个切点,表示在MyService类的所有方法执行前执行这个通知。这样,日志记录的关注点被横切出来,不再直接嵌入到MyService的业务逻辑中。

综合来说,IOC和AOP是Spring框架的两个核心概念,IOC通过反转控制权实现了对象的解耦和管理,而AOP通过横切关注点的模块化提高了代码的可维护性和可扩展性。在实际应用中,它们通常一起使用以提供更强大的功能。

7.15 怎么实现控制反转?(Spring中AOP与DI)

在Spring框架中,控制反转(Inversion of Control,IOC)主要通过依赖注入(Dependency Injection,DI)实现。同时,Spring的AOP(Aspect-Oriented Programming)也是通过IOC的概念来实现的。下面简要介绍如何在Spring中实现控制反转。

1. 依赖注入(DI):

在Spring中,DI是一种通过容器负责创建和管理对象之间的依赖关系的方式。通过DI,对象不再负责自己的依赖关系的创建,而是通过外部的容器(ApplicationContext)来注入所需的依赖。

示例:

// 服务接口
public interface MyService {
    void doSomething();
}
​
// 服务实现类
public class MyServiceImpl implements MyService {
    public void doSomething() {
        System.out.println("Doing something...");
    }
}
​
// 应用类
public class MyApp {
    private MyService myService;
​
    // 通过构造方法注入依赖
    public MyApp(MyService myService) {
        this.myService = myService;
    }
​
    public void runApp() {
        myService.doSomething();
    }
}

在上述示例中,MyApp通过构造方法注入了MyService的实例,实现了依赖注入。在Spring中,这个过程会由Spring容器负责。

2. Spring的IOC容器:

Spring的IOC容器是实现依赖注入的核心。它负责管理应用程序中的所有对象,以及它们之间的依赖关系。Spring容器提供了两种主要的IOC容器:

  • BeanFactory: 基本的IOC容器,提供了基本的IOC功能,适用于简单的应用场景。

  • ApplicationContext: 是BeanFactory的扩展,提供了更多的功能,如AOP、事件传播、国际化等。在实际开发中,通常使用ApplicationContext。

3. Spring AOP与IOC的结合:

AOP是通过在Spring容器中使用AspectJ的AOP实现来实现的。在Spring AOP中,切面(Aspect)是一个带有通知(Advice)和切点(Pointcut)的类。通知定义了在何时以何种方式执行横切关注点,而切点定义了何处应该执行横切关注点。

示例:

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
​
@Aspect
@Component
public class LoggingAspect {
​
    @Before("execution(* com.example.MyService.*(..))")
    public void beforeMethod() {
        System.out.println("Before method execution: Logging...");
    }
}

在上述示例中,LoggingAspect是一个切面,通过@Before注解定义了一个在MyService类的所有方法执行前执行的通知。这个切面是一个由Spring容器负责创建和管理的Bean。

综合来说,Spring的控制反转通过依赖注入实现,而AOP则是通过在IOC容器中使用AspectJ的AOP实现。这两者结合在一起,使得Spring框架提供了一种强大的方式来构建松散耦合和可维护性强的应用程序。

7.16 aop 的好处?

提高代码的复用性,将一些公共逻辑提取出来进行复用,比如:记录日志、用户缓存、事务管理、权限控制。

AOP(面向切面编程)是一种编程范式,它的好处包括:

  1. 代码模块化:AOP通过将横切关注点(cross-cutting concerns)从核心业务逻辑中分离出来,实现了更好的代码模块化。关注点,如日志记录、性能监测、事务管理等,可以在不影响主要业务逻辑的情况下被独立开发和维护。

  2. 可维护性:AOP的分离关注点的特性使得系统更容易维护。当一个横切关注点需要修改或添加新功能时,只需修改或添加一个切面,而不需要修改整个系统的核心业务逻辑。

  3. 可重用性:AOP允许将关注点抽象成切面,并在不同的模块中重用这些切面。这样,类似的关注点可以在不同的部分得到重复使用。

  4. 解耦:AOP通过将关注点与主要业务逻辑解耦,降低了系统各部分之间的耦合度。这使得系统更加灵活,更容易适应变化。

  5. 横向切入:AOP允许在不同模块中横向切入横切关注点,即在不同层次、不同模块或不同对象中对相同的关注点进行处理。

  6. 提高可读性:AOP可以提高代码的可读性,因为关注点的实现被封装在切面中,使得主要业务逻辑更加清晰。

  7. 统一性:AOP提供了一种统一处理横切关注点的方式,例如日志记录、事务管理等。这样可以确保这些关注点在整个系统中得到一致性的应用。

总体而言,AOP通过分离关注点、提高可维护性、可重用性、解耦等特性,帮助提升了软件的设计和开发质量。

你可能感兴趣的:(#,后端,java,面试,开发语言)