Java学习Day17

AOP 

面向切面编程(AOP)是一种编程范式,旨在通过将横切关注点(例如日志记录、事务管理、安全性等)与业务逻辑分离来提高代码的模块化。AOP是OOP(面向对象编程)的一个补充,它允许开发者以声明方式实现关注点,而不是通过在业务逻辑代码中散布大量重复代码。AOP 通过“切面”模块化跨多个类的功能,这些功能通常与业务逻辑不直接相关。

AOP主要一般应用于签名验签、参数校验、日志记录、事务控制、权限控制、性能统计、异常处理等。

AOP的优势

增强模块化:AOP 可以将那些与业务逻辑无关但对系统功能至关重要的关注点(如日志、安全、事务管理等)从业务代码中分离出来,使得业务逻辑更加清晰和集中。

减少代码重复:横切关注点往往会在多个地方出现,使用 AOP 可以避免在每个相关的地方重复编写相同的代码。通过定义一个方面(Aspect)来集中处理这些横切关注点,可以减少代码的冗余。

提高代码可维护性:由于关注点被分离,维护起来更容易。更改某个横切关注点的实现时,只需修改相应的方面,不必遍及整个系统。

支持代码重用:通过方面的定义和配置,可以很方便地在多个业务逻辑中重用相同的横切关注点处理逻辑。

降低耦合:AOP 通过将横切关注点与核心业务逻辑分离,降低了系统各部分之间的耦合,使得系统更加模块化和解耦。

AOP中的相关概念

切面(Aspect)是 AOP 中的核心概念,用来定义一个横切关注点的实现。它封装了对某一特定关注点的处理逻辑,比如日志、事务管理、安全检查等。一个方面可以在多个连接点上应用。

切点(Pointcut)是一个表达式,用于定义在何处(即哪些连接点)应用方面的逻辑。它描述了在程序执行过程中,哪些方法或对象的操作会触发方面的行为。切点通过匹配方法的执行、对象的调用等条件来确定应用方面的具体位置。

连接点(Join Point)是程序执行过程中一个明确的点,在这个点上可以插入方面的逻辑。例如,方法调用、构造函数调用、字段访问等都可以是连接点。连接点是切点所匹配的实际程序执行位置。

通知(Advice)是 AOP 中的一个动作,它定义了在连接点处需要执行的代码。根据执行时机的不同,通知可以分为几种类型:

前置通知(Before Advice):在连接点之前执行的通知。

后置通知(After Advice):在连接点之后执行的通知,无论连接点的执行结果如何。

返回通知(After Returning Advice):在连接点成功完成后执行的通知。

异常通知(After Throwing Advice):在连接点抛出异常时执行的通知。

环绕通知(Around Advice):包围连接点执行的通知,可以控制是否执行连接点及如何处理返回结果或异常。

织入(Weaving)是将方面的代码与业务逻辑代码结合的过程。织入可以在不同的阶段进行:

编译时织入:在编译时将方面织入到字节码中。

类加载时织入:在类被加载到 JVM 中时织入。

运行时织入:在运行时通过代理机制将方面织入。

引介(Introduction)是一种特殊的通知,它允许在运行时向现有的类中添加新的方法或属性。它使得类能够动态地实现接口或添加行为。

目标对象(Target Object)是被方面增强的实际业务对象。它是切点所匹配的对象,其业务逻辑被方面所横切和增强。

代理(Proxy)是 AOP 中用于实现横切关注点的一个机制。通过代理,可以在目标对象的前后插入方面的逻辑,通常代理是通过动态代理或静态代理技术实现的。

AOP的实现方式

编译时织入:编译时织入是在代码编译阶段,将方面的代码织入到目标代码中。这通常通过编译器插件或扩展来实现,但可以提供更高的性能。

类加载时织入:类加载时织入是在类被加载到 JVM 时,将方面的代码织入到类中。这需要特定的类加载器来实现织入。灵活性高,可以在类加载时进行织入,但可能需要复杂的配置。

运行时织入:运行时织入是在程序运行时通过代理机制将方面的代码织入。通常使用动态代理或 CGLIB 生成的代理对象来实现。实现简单,适合动态环境,但可能有性能开销。

代理模式:代理模式是一种实现 AOP 的设计模式,通过创建代理对象来封装实际的目标对象。代理对象可以在调用目标对象的方法之前或之后执行额外的逻辑。

静态代理:在编译时创建代理类,并在其中实现横切关注点。

动态代理:在运行时动态生成代理类,通常使用 JDK 动态代理或 CGLIB。

适用于对象的行为增强,特别是在运行时的动态代理。

代理模式是一种结构型设计模式,它通过引入代理对象来控制对目标对象的访问。代理模式提供了对目标对象的间接访问,通过代理对象可以在调用目标对象的方法之前或之后插入额外的逻辑。这种模式常用于实现控制、缓存、日志、安全等功能。

代理模式在Java中的Spring框架和Dubbo框架中都有广泛的应用:

Spring框架中的AOP(面向切面编程):Spring使用代理模式实现AOP功能,允许开发者定义切面(Aspect),并通过代理机制将切面织入到目标对象的方法调用中,实现横切关注点的管理,如日志记录、事务管理等。

Dubbo框架中的远程服务代理:Dubbo是一种高性能的分布式服务框架,其中的服务消费者与服务提供者之间的通信通过代理模式来实现。Dubbo会根据配置信息动态生成接口的代理实现类,在远程调用时通过代理对象进行通信,隐藏了远程调用的复杂性,使得调用方可以像调用本地方法一样调用远程服务。

两种实现代理模式的方法:

静态代理(Static Proxy)

静态代理在编译时创建代理类,代理类通常与目标类实现相同的接口,并在其中实现横切关注点。

特点:

代理类和目标类在编译时就已经确定。

适用于目标对象接口明确且变化不频繁的情况。

每个代理类都需要手动编写,可能会增加代码量。

动态代理(Dynamic Proxy)

动态代理是在运行时创建代理对象,通常基于接口,通过反射机制生成代理类。Java 提供了 Proxy 类和 InvocationHandler 接口来实现动态代理。

特点:

代理类在运行时生成,不需要提前定义。

适用于接口驱动的代理逻辑。

可以减少手动编写代理类的工作量。

两种常用的动态代理

JDK 动态代理

JDK 动态代理利用 Java 反射机制来创建代理对象。在运行时,代理对象会实现目标对象的接口,并通过 InvocationHandler 接口来处理对目标对象方法的调用。Proxy 类负责创建这个代理对象。

步骤:

定义一个接口(目标接口)。

实现一个 InvocationHandler 接口的类,该类中定义了代理逻辑。

使用 Proxy.newProxyInstance 方法创建代理对象,将目标对象和 InvocationHandler 传递进去。

优点

无需修改目标类:JDK 动态代理只需目标对象实现接口,不需要修改目标类。

简单易用:利用 Java 标准库中的 Proxy 和 InvocationHandler 实现,易于理解和使用。

适用场景明确:适用于接口驱动的设计。

缺点

只能代理接口:JDK 动态代理只能用于代理实现了接口的类,不能用于代理具体类。

性能开销:由于使用反射机制,可能会有一定的性能开销。

代码

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 目标接口
public interface Subject {
    void request();
}

// 目标实现类
public class RealSubject implements Subject {
    @Override
    public void request() {
        System.out.println("RealSubject request");
    }
}

// 动态代理处理器
public class DynamicProxyHandler implements InvocationHandler {
    private Object target;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 代理逻辑
        System.out.println("DynamicProxy request");
        return method.invoke(target, args);
    }
}

public class Main {
    public static void main(String[] args) {
        RealSubject realSubject = new RealSubject();
        Subject proxy = (Subject) Proxy.newProxyInstance(
            realSubject.getClass().getClassLoader(),
            realSubject.getClass().getInterfaces(),
            new DynamicProxyHandler(realSubject)
        );
        proxy.request();
    }
}

CGLIB 代理

CGLIB(Code Generation Library)使用字节码生成技术动态创建目标类的子类(代理类)。通过继承目标类,CGLIB 可以在运行时创建代理对象,并重写目标类的方法来插入代理逻辑。它不需要目标类实现接口。

步骤:

使用 Enhancer 类创建一个代理类。

设置代理类的父类(目标类)和回调(MethodInterceptor)。

通过 Enhancer.create 方法创建代理对象。

优点

支持代理具体类:CGLIB 可以代理没有实现接口的类。

强大的功能:适用于需要复杂的字节码操作的场景。

缺点

不能代理 final 类和方法:CGLIB 不能代理 final 类或 final 方法,因为它依赖于继承。

增加了类的复杂性:通过继承方式创建代理类,可能会增加内存占用。

代码

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class RealSubject {
    public void request() {
        System.out.println("RealSubject request");
    }
}

public class CglibProxy implements MethodInterceptor {
    private Object target;

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

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        // 代理逻辑
        System.out.println("CglibProxy request");
        return proxy.invoke(target, args);
    }
}

public class Main {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(RealSubject.class);
        enhancer.setCallback(new CglibProxy(new RealSubject()));

        RealSubject proxy = (RealSubject) enhancer.create();
        proxy.request();
    }
}

 二者区别

 

Spring MVC 是 Spring 框架中的一个模块,用于构建 Web 应用程序。它基于 MVC(Model-View-Controller)设计模式,提供了一系列功能和优势,通过明确的分层架构、灵活的请求映射、强大的数据绑定和验证机制、多种视图技术支持、集中异常处理、良好的测试支持以及与 Spring 生态系统的无缝集成,提供了一个全面而强大的 Web 开发框架。它能够帮助开发者快速构建可维护、可扩展和安全的 Web 应用程序。使得开发 Web 应用程序变得更加高效和灵活。

一系列用于构建控制器和处理请求的注解

@Controller

描述:@Controller 注解用于标记一个类为控制器组件,处理用户的请求并返回视图名称。它是处理 Web 请求的核心注解之一。

功能:将控制器类中的方法映射到 HTTP 请求路径,并返回视图名称或模型数据。

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class MyController {

    @RequestMapping("/hello")
    public ModelAndView hello() {
        ModelAndView modelAndView = new ModelAndView("hello");
        modelAndView.addObject("message", "Hello, World!");
        return modelAndView;
    }
}

@RestController

描述:@RestController 是 @Controller 和 @ResponseBody 的组合注解,通常用于构建 RESTful Web 服务。它表示控制器的所有方法都返回 JSON 或 XML 格式的响应,而不是视图名称。

功能:自动将方法的返回值序列化为 JSON 或 XML 格式,直接写入 HTTP 响应体。

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api")
public class ApiController {

    @GetMapping("/greet")
    public String greet() {
        return "Hello, World!";
    }
}

@RequestMapping

描述:@RequestMapping 注解用于将 HTTP 请求映射到控制器的方法。它可以指定请求的路径、方法(GET、POST 等)、请求参数等。

功能:定义 URL 路径和请求方法,从而将 HTTP 请求与处理方法关联起来。

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api")
public class MyController {

    @RequestMapping(value = "/welcome", method = RequestMethod.GET)
    public String welcome() {
        return "Welcome!";
    }
}

@PathVariable

描述:@PathVariable 注解用于从 URL 路径中提取变量值,并将其绑定到方法参数。

功能:使控制器方法能够获取 URL 模板中的变量部分。

import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api")
public class MyController {

    @RequestMapping("/user/{id}")
    public String getUserById(@PathVariable("id") String userId) {
        return "User ID: " + userId;
    }
}

@RequestParam

描述:@RequestParam 注解用于从请求参数中提取值,并将其绑定到方法参数。它通常用于处理查询参数或表单数据。

功能:获取请求中的参数值并将其传递给控制器方法。

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api")
public class MyController {

    @RequestMapping("/search")
    public String search(@RequestParam(name = "query", defaultValue = "Spring") String query) {
        return "Search query: " + query;
    }
}

@GetMapping

描述:@GetMapping 是 @RequestMapping 的快捷方式,用于处理 HTTP GET 请求。

功能:简化 GET 请求的映射定义。

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api")
public class MyController {

    @GetMapping("/info")
    public String getInfo() {
        return "This is a GET request.";
    }
}

@PostMapping

描述:@PostMapping 是 @RequestMapping 的快捷方式,用于处理 HTTP POST 请求。

功能:简化 POST 请求的映射定义。

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api")
public class MyController {

    @PostMapping("/submit")
    public String submitData(@RequestBody String data) {
        return "Data submitted: " + data;
    }
}

@PutMapping

描述:@PutMapping 是 @RequestMapping 的快捷方式,用于处理 HTTP PUT 请求。

功能:简化 PUT 请求的映射定义。

import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api")
public class MyController {

    @PutMapping("/update")
    public String updateData(@RequestBody String data) {
        return "Data updated: " + data;
    }
}

@DeleteMapping

描述:@DeleteMapping 是 @RequestMapping 的快捷方式,用于处理 HTTP DELETE 请求。

功能:简化 DELETE 请求的映射定义。

import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api")
public class MyController {

    @DeleteMapping("/delete/{id}")
    public String delete(@PathVariable("id") String id) {
        return "Deleted item with ID: " + id;
    }
}

springmvc 运行原理图

拦截器 

在 Spring MVC 中,拦截器(Interceptor)是一种强大的机制,用于在请求处理的不同阶段对请求和响应进行拦截和处理。拦截器是实现 HandlerInterceptor 接口的 Java 类。通过拦截器,可以在请求处理的生命周期中插入自定义的逻辑,比如记录日志、检查用户权限、修改请求和响应等。 

HandlerInterceptor接口包含以下三个方法:

preHandle:在请求到达处理器之前执行,可以用于权限验证、数据校验等操作。如果返回true,则继续执行后续操作;如果返回false,则中断请求处理。

postHandle:在处理器处理请求之后执行,可以用于日志记录、缓存处理等操作。

afterCompletion:在视图渲染之后执行,可以用于资源清理等操作。

生命周期:拦截器可以在请求到达控制器之前(preHandle)、控制器处理请求之后但在视图渲染之前(postHandle),以及视图渲染完成之后(afterCompletion)进行操作,从而实现对请求的预处理和后处理,增强应用的功能。

springmvc异常处理机制

1.使用 @ExceptionHandler 注解

@ExceptionHandler 注解用于在控制器类中定义方法,以处理该控制器范围内抛出的特定异常或所有异常。这个方法会在出现指定异常时被调用,从而进行定制化的异常处理。

2.使用 @ControllerAdvice 注解

@ControllerAdvice 注解用于定义全局异常处理器,可以在该类中使用 @ExceptionHandler 注解处理所有控制器中的异常。@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;
import org.springframework.web.bind.annotation.ResponseBody;

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(NullPointerException.class)
    @ResponseBody
    public ResponseEntity<String> handleNullPointerException(NullPointerException ex) {
        return new ResponseEntity<>("Null pointer exception occurred: " + ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
    }

    @ExceptionHandler(Exception.class)
    @ResponseBody
    public ResponseEntity<String> handleGenericException(Exception ex) {
        return new ResponseEntity<>("An error occurred: " + ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

@ControllerAdvice:标记类为全局异常处理器。可以处理所有控制器中的异常。

@ExceptionHandler:定义处理特定异常的方法。

   

  

  • 24
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值