一、Spring AOP

1、Spring AOP执行原理

AOP执行原理

1、连接点(JoinPoint)范围比较小

  • 程序执行过程中的一个点,例如方法的调用,或抛出异常
  • 就是 AOP 切面的插入点

2、切入点(Pointcut)范围比较大

  • 选择一个或多个连接点的表达式,告诉AOP选择哪些切入位置,切入点包括连接点
  • 0bean(userServiceImpl), 选择userServiceImpl Bean 全部方法

3、通知(Advice)

  • 在选择的每个连接点执行的代码
  • 通过注解配置在连接点执行代码的位置,例如@Around表示在方法前后都可以插入代码

4、切面(Aspect)

  • 一个囊括了切入点和Advice的模块
  • 一个类,包含全部的切入点、通知等

5、织入(Weaving)(采用什么方式切进去)了解即可

  • 将切面与主要代码进行结合的技术,Spring底层的代码,采用动态代理技术将Aspect嵌入目标的代码

6、目标对象(Target)

  • 织入(通知) Advice 的目标对象

2、通知的类型

1、前置通知(@Before):在目标方法执行前触发的通知,在该通知中可以进行一些预处理逻辑。

2、后置通知(@After):在目标方法执行后触发的通知,不论是否有异常都执行。

3、返回通知(@AfterReturning):在目标方法执行并返回结果后触发的通知,该通知中可以访问目标方法的返回值。

4、异常通知(@AfterThrowing):在目标方法执行时出现异常触发的通知,该通知中可以访问目标方法抛出的异常对象。

5、环绕通知(@Around):在目标方法调用之前和之后,均可以执行自定义的代码,环绕通知需要使用ProceedingJoinPoint参数,来控制连接点执行。

/**
 * 定义一个切面类
 * 一个横切关注点
 * 目的:统一处理日志功能
 */
@Component //交给Spring IOC容器管理
@Aspect //声明这是一个切面类
public class TimerAspect {
    private static final Logger logger = LoggerFactory.getLogger(TimerAspect.class);

    //前置通知
    @Before("bean(userServiceImpl)")
    public void before(JoinPoint point) {
        //JoinPoint point获取目标方法的签名(方法名+参数列表)
        logger.info("日志代码,执行{}操作前执行了日志:{}", point.getSignature().getName(), LocalDateTime.now());

    }

    //后置通知
    @After("bean(userServiceImpl)")
    public void after(JoinPoint point) {
        //JoinPoint point获取目标方法的签名(方法名+参数列表)
        logger.info("日志代码,执行{}操作后执行了日志:{}", point.getSignature().getName(), LocalDateTime.now());

    }

    /**
     * 返回通知
     *
     * @param joinPoint 连接点信息
     * @param object    执行目标方法的返回值
     */
    @AfterReturning(value = "bean(userServiceImpl)", returning = "object")
    public void afterReturning(JoinPoint joinPoint, Object object) {
        logger.info("返回通知,执行了{}操作后执行了日志:{},返回值{}",
                joinPoint.getSignature().getName(), LocalDateTime.now(), object);
    }

    /**
     * 异常通知
     * @param joinPoint
     * @param e 执行目标方法发生的异常
     */
    @AfterThrowing(value = "bean(userServiceImpl)",throwing = "e")
    public void afterThrowing(JoinPoint joinPoint,Exception e){
        logger.info("异常通知,执行了{}操作后执行了日志:{},异常:{}",
                joinPoint.getSignature().getName(), LocalDateTime.now(), e.getMessage());
    }

    //定义一个环绕通知 说明当前的方法在什么时候通知
    //bean(userServiceImpl)表示一个切入点(在什么时候执行通知中的内容)
    //执行userServiceImpl这个bean的任何方法,都会执行当前通知
    @Around("bean(userServiceImpl)")
    public Object log(ProceedingJoinPoint point) throws Throwable {
        logger.info("执行" + point.getSignature().getName() + "操作");
        //调用目标方法
        Object result = point.proceed();
        return result;
    }
}

3、切点表达式

切点表达式

execution(方法修饰符 返回类型 类名.方法名(参数)):表示通过方法签名匹配目标方法,其中execution是固定的前缀,后面的括号里面是关于方法的限定信息。其中:

1、方法修饰符:可选项,表示访问修饰符;如public、private、protected、*(任意修饰符)等。

2、返回类型:可选项,表示方法的返回值类型;如void、int、*(匹配任意返回值类型)等。

3、类名:表示要拦截的类名,可以使用正则表达式来匹配;如com.example.service.UserService。

4、方法名:表示要拦截的方法名,也可以使用正则表达式来匹配;例:add、get*。

5、参数:可选项,用于匹配方法的参数。可以指定参数的类型,也可以使用“..”表示任意数量和类型的参数。

6、&&、||、!:用于组合切点表达式,与Java中的逻辑运算符相似。

@Around("execution(* cn.highedu.coolsharkhub.service.impl.*.find*(..))")
public Object exe(ProceedingJoinPoint joinPoint) throws Throwable {
    logger.info("环绕通知开始,执行{}方法",joinPoint.getSignature().getName());
    Object result = joinPoint.proceed(); //调用目标方法
    logger.info("环绕通知结束,执行{}方法",joinPoint.getSignature().getName());
    return result;
}

4、@Pointcut 统一管理切入点

@Pointcut是Spring框架中的一个注解,用于定义切点表达式,让通知方法能够准确地匹配到某个切入点。

@Pointcut 统一管理切入点

二、Spring Boot

1、Spring 与 Spring Boot

Spring中不同的模块可能会出现版本不兼容的问题,整合框架时核心类要交给Spring去管理,Spring Boot简化了应用程序开发的框架。

Spring Boot框架的主要特点有依赖管理、自动化配置、微服务支持、生态系统完善

2、依赖管理

在Spring Boot中有依赖管理,如Maven

依赖管理

dependencyManagement标签

用在父项目中,Maven的dependencyManagement标签是用来管理依赖项的一个特殊标签。它可以让开发者在一个pom.xml文件中集中管理多个子项目所共有的依赖项,并对其进行统一的版本和范围管理,避免不同子项目之间相同依赖包版本的冲突问题。

3、Spring Boot 中的组合注解

@SpringBootApplication注解

表示这个类是启动类

@SpringBootApplication注解是一个组合注解,主要包含了以下三个元注解。

1、@Configuration:用于定义配置类,Spring Boot项目的启动类现在也具有Spring框架配置类的功能。

在单独使用Spring框架时,一般会创建一个ContextConfig类,并添加@Configuration注解。ContextConfig类一般用于通过@Bean注解声明容器中使用的Bean。在Spring Boot项目中,不需要单独开发ContextConfig类,可以直接在启动类中可以使用@Bean注解定义Bean。

2、@ComponentScan:默认扫描启动类所在的包及其所有子包中的类前添加的注解,自动注册Bean。

3、@EnableAutoConfiguration:开启Spring Boot的自动配置功能,可以根据依赖自动配置Spring应用。

@SpringBootApplication
public class SctDay03Application {

    public static void main(String[] args) {
        SpringApplication.run(SctDay03Application.class, args);
    }
}
@Conditional注解

可以用在类和方法上,当给定条件满足时,就会创建一个Bean或者执行一个配置类。这个注解可以和@Configuration,@Bean注解结合使用。

1、@ConditionalOnClass:在classpath下存在指定类名时生效,可以用来控制某些依赖包是否存在。

2、@ConditionalOnBean:在BeanFactory中存在指定的Bean时生效,用于确保使用的Spring Bean已经实例化。

3、@ConditionalOnMissingBean:在BeanFactory中不存在指定的Bean时生效,用于确保必要的Spring Bean不存在。

4、@ConditionalOnProperty:在指定的配置属性存在为true、false或不存在时生效的注解。

5、@ConditionalOnWebApplication:只在当前应用是Web应用时才生效。

6、@ConditionalOnResource:在classpath中有指定文件存在时生效。

注解示例:

创建工具接口:

public interface Tool {
}

创建电锯类:

@Component
@ConditionalOnBean(Worker.class) //如果worker这个对象存在,创建电锯对象
public class Saw implements Tool {
    Logger logger = LoggerFactory.getLogger(Saw.class);

    public Saw() {
        logger.info("创建电锯");
    }

    @Override
    public String toString() {
        return "电锯";
    }
}

创建斧头类:

@Component
@ConditionalOnMissingBean(name = "saw") //当电锯不存在 创建斧子对象
@ConditionalOnBean(Worker.class) //如果worker这个对象存在,创建斧子对象
public class Axe implements Tool {
    Logger logger = LoggerFactory.getLogger(Axe.class);

    public Axe() {
        logger.info("创建斧子");
    }

    @Override
    public String toString() {
        return "斧子";
    }
}

创建工人类:

@Component
public class Worker {
    Logger logger = LoggerFactory.getLogger(Worker.class);
    private String name = "光头强";
    @Autowired
    private Tool tool;

    public Worker() {
        logger.info("创建工人");
    }


    //工作方法
    public void work() {
        logger.info("{}使用{}砍树", name, tool);
    }

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

创建测试类:

@SpringBootTest
class WorkerTest {

    Logger logger = LoggerFactory.getLogger(WorkerTest.class);

    @Autowired
    private Worker worker;

    @Test
    void work() {
        worker.work();
    }
}

测试结果:

: 创建电锯
: 创建工人
: Adding welcome page: class path resource [static/index.html]
: Started WorkerTest in 1.061 seconds (process running for 1.819)
: 光头强使用Saw{}砍树

4、Spring Boot自动配置

一些常见的Spring Boot自动配置如下:

1、Web开发中,Spring Boot可以自动配置Tomcat、Jetty、Undertow等嵌入式Web服务器,无需手动配置web.xml文件、Servlet API等内容。

2、在数据库操作中,Spring Boot可以自动配置JPA、Hibernate、MyBatis等ORM框架,无需手动配置数据源、连接池等内容。

3、安全认证中,Spring Boot可以自动配置Spring Security框架,提供多种身份认证、权限控制和资源保护的方案。

spring-boot-starter-jdbc自动配置

当开发者在Spring Boot项目中添加spring-boot-starter-jdbc 依赖时,Spring Boot的自动配置机制将会执行如下操作:

1、首先检查使用有自定义的DataSource对象,如果有DataSource类型的对象,就直接使用,如果没有会自动创建一个。

2、然后检查application.properties配置文件中是否有DataSource配置信息。如果有配置信息,就根据配置信息创建DataSource对象,连接到数据库,如果没有会自动创建一个。

3、接下来检查是否有Java内嵌的数据库(Derby、...),如果有内嵌数据库,就自动创建DataSource对象,自动连接到数据库。

4、如果上述检查都没有找到创建DataSource对象的信息,启动项目时会抛出异常:Error creating bean with name 'dataSource'。

如果检测到项目中有DataSource对象,还会自动创建JdbcTemplate对象,JdbcTemplate 是JDBC工具,速度比mybatis慢,不常用,可以简化JDBC开发。

三、Spring MVC

1、Spring MVC概述

可以在一个类里面处理多个业务,能处理响应、请求和类型转换等等内容,只需专注写业务代码。

基于JavaEE原生组件的web应用架构

引入Spring MVC后的web应用架构

2、Spring MVC核心概念**

项目核心组件

1、DispatcherServlet:前端控制器,Spring MVC 是围绕前端控制器设计的,本质上是一个Servlet,相当于一个中转站,所有的访问都会走到这个Servlet中,再根据配置进行中转到相应的Handler中进行处理。

2、HandlerMapping:映射处理器,本质上是一段映射关系,将访问路径和对应的Handler存储为映射关系,在需要时供前端控制器查阅。主要的实现是RequestMappingHandlerMapping(支持@RequestMapping注解的方法)。

3、HandlerAdapter :处理器适配器,本质上是一个适配器,可以根据要求找到对应的Handler来运行。前端控制器通过处理器映射器找到对应的Handler信息之后,将请求响应和对应的Handler信息交由处理器适配器处理,处理器适配器找到真正Handler执行后,将结果即model和view返回给前端控制器。

4、Controller 控制器(需要编写):用于处理请求功能的程序,控制器中的方法是Handler实际执行的方法,控制器的处理结果是ModelAndView,或者最终被封装为ModelAndView。

  • 如果返回 @ResponseBody 则使用MessageConverter处理响应结果

5、ModelAndView(不用):模型和视图,代表一个由控制器返回的模型和视图。视图是字符串视图名称。模型是一个Map,包含需要在视图上显示的数据。

6、ViewResolver:视图解析器,将控制器返回的视图名称解析为实际的视图,并将其渲染到响应。

7、View(基本现在也不用):视图,本质上就是将Handler处理器中返回的Model数据(现在都是返回json数据,通过ajax处理json数据)嵌入到视图解析器解析后得到的JSP/Thymeleaf页面中,向客户端做出响应。

MappingJackson2HttpMessageConverter

appingJackson2HttpMessageConverter是Spring MVC提供的HttpMessageConverter的一个实现。它将Java对象和JSON数据之间进行转换,并通过HTTP Response和Request进行传递。它底层使用Jackson库作为JSON处理库。

MappingJackson2HttpMessageConverter

导入依赖

<!--操作以.xlsx为后缀的文件 EXCEL API-->
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>4.1.2</version>
</dependency>

新建转换器类:

/**
 * Excel转换器
 */

@Component
public class ExcelMessageConverter implements HttpMessageConverter {
    //加载POI Excel主类
    private Class excelObjectClass = XSSFWorkbook.class;
    //请求媒体类型
    private MediaType mediaType = new MediaType("application", "xlsx");
    //设置响应类型
    private String contentType = "application/vnd.ms-excel";

    @Override
    public boolean canRead(Class clazz, MediaType mediaType) {
        return false;
    }

    @Override
    public boolean canWrite(Class clazz, MediaType mediaType) {
        if (clazz.equals(excelObjectClass)) {
            return true;
        }
        return false;
    }

    @Override
    public List<MediaType> getSupportedMediaTypes() {
        List<MediaType> list = new ArrayList<>();
        list.add(mediaType);
        return list;
    }

    @Override
    public Object read(Class clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
        return null;
    }

    @Override
    public void write(Object o, MediaType mediaType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
        //判断是不是excel请求
        if (!(o instanceof XSSFWorkbook)) {
            return;
        }
        //如果是excel请求,转换
        XSSFWorkbook workbook = (XSSFWorkbook) o;
        //写入文件
        ByteArrayOutputStream buf = new ByteArrayOutputStream();
        workbook.write(buf);
        workbook.close();
        buf.close();
        byte[] bytes = buf.toByteArray();

        //对文件进行设置
        String fileName = "hello.xlsx"; //文件名
        fileName = URLEncoder.encode(fileName, "UTF-8");
        //定义一个响应
        ServletServerHttpResponse response = (ServletServerHttpResponse) outputMessage;
        //设置响应
        response.getServletResponse().setCharacterEncoding("UTF-8");
        //设置响应头
        response.getServletResponse().setHeader("Content-Length",
                String.valueOf(bytes.length));
        response.getServletResponse().setHeader("Content-Type",
                String.valueOf(contentType));
        response.getServletResponse().setHeader("Content-Disposition",
                "attachment; filename=" + fileName);
        //写入文件
        response.getBody().write(bytes);
    }

    @GetMapping("/excel")
    @ResponseBody
    public XSSFWorkbook excel() {
        XSSFWorkbook workbook = new XSSFWorkbook();
        XSSFSheet sheet = workbook.createSheet("Demo");
        sheet.createRow(0).createCell(0).setCellValue("Hello World");
        return workbook;
    }
}
最后修改:2023 年 09 月 18 日
如果觉得我的文章对你有用,请随意赞赏