【Java开发】 Spring 07 :Spring AOP 实践详解(通过 AOP 打印数据访问层)

news2024/9/27 23:24:08

AOP 指是面向切面编程(通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术),利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率,阐述完 JDK 动态代理和 CGLIB 动态代理后将通过 AOP 技术打印数据访问层。

目录

1 AOP 实现原理

1.1 AOP 实现

1.2 Spring AOP 核心概念及注释

① 核心概念

② 常用注解

1.3 Spring AOP 动态代理

① JDK 动态代理

② CGLIB 动态代理

③ 基于 xml 配置(以 JDK 动态代理为例)

2 通过 AOP 打印数据访问层摘要(以 mybatis-plus 的 mapper 为例)

2.1 项目初始化

① 导入 mybatis-plus 依赖

② 连接数据库配置

③ 编写实体类

2.2 AOP 具体代码

① 编写 mapper 接口

② 编写 service 实现类

③ 编写 Aspect 类

④ 编写测试类

⑤ 输出结果

参考文章


项目源码:尹煜 / AopDemo · GitCode

1 AOP 实现原理

1.1 AOP 实现

AOP代表的是一个横向的关系,将“对象”比作一个空心的圆柱体,其中封装的是对象的属性和行为;则面向方面编程的方法,就是将这个圆柱体以切面形式剖开,选择性的提供业务逻辑。而剖开的切面,也就是所谓的“方面”了。然后它又以巧夺天功的妙手将这些剖开的切面复原,不留痕迹,但完成了效果。

简单来说,AOP 可以在不改变源码的前提下通过创建类的形式给某个方法添加新的功能。比如一个只能计算加法的方法,它想增加计算乘法的功能,但是又不想改源码!这时候可以在外面创建一个类,在这个类中通过AOP的技术给这个方法进行添加功能的操作,这就是AOP。

AOP 可用于处理系统中分布于各个模块的横切关注点,比如事务管理、日志、缓存等等。

AOP实现主要分为👇

  • 静态代理AspectJ 技术,是引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码。
  • 动态代理Spring AOP 技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行。

由于本文专注于 Spring AOP ,也就是所谓的动态代理,因此不对静态代理做过多叙述,感兴趣的可以自行百度哈,然后介绍一下Spring AOP 的核心概念及注释,初次接触这些概念可能会有点陌生,不妨敲完后边的举例代码再回头来理解一下 👇

1.2 Spring AOP 核心概念及注释

① 核心概念

概念含义
Aspect切面,切面是一个动作,也叫一个过程
Join Point连接点是在应用执行过程中能够插入切面(Aspect)的一个点,简单来说是可以被增强的方法
Advice通知,在连接点执行的动作,包括前通知、后通知、返回后通知、异常通知和环绕通知
Pointcut切入点,说明如何匹配连接点
Introduction引入,为现有类型声明额外的方法和属性
Target object目标对象
AOP proxyAOP 代理对象,可以是 JDK 动态代理,也可以是 CGLIB 代理
Weaving 织入,连接切面与目标对象或类型创建代理的过程

② 常用注解

  • @EnableAspectJAutoProxy:开启aspectj的支持,对打@aspectj相关注解的类做一个Proxy
  • @Aspect:用来声明当前这个类是一个切面,需要说明的是光加aspect还不能变成一个bean,要么使用Javaconfig将类声明成一个bean,要么增加一个@Component注解来将这个bean创建出来
  • @Pointcut:用来指定Pointcut,具体在代码中体现
  • @Before:用来指定advice是在方法执行前是执行的
  • @After / @AfterReturning / @AfterThrowing:不管什么情况,在你结束了之后执行;在你成功返回之后执行;在你报错的时候执行
  • @Around:把这个方法前后都可以处理,把代码封装到里面
  • @Order;指定切面的一个执行顺序,order越小,优先级越高

1.3 Spring AOP 动态代理

Spring AOP 使用的动态代理,动态代理指的是 AOP 框架不会去修改字节码,而是在内存中临时为方法生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法。

动态代理形式主要分为两种:JDK动态代理 和 CGLIB动态代理

首先需要在 pom.xml 文件中导入 aspectjweaver 依赖 👇

        <!--aspectj-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.2</version>
        </dependency>

① JDK 动态代理

如果目标类实现接口,那么 Spring AOP 会默认选择使用 JDK 来动态代理目标类(若默认:proxy-target-class = false),不过本项目情况不大一样,请往下看~

JDK动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口,也就是说 JDK 动态代理主要用于接口的 AOP 实现,用于给接口(实现该接口的类也会被增强)增加功能。

核心类包括 👇

  • InvocationHandler 接口
  • Proxy.newProxyInstance()

Ⅰ 定义一个 Chinese 接口 :

package com.aopdemo.ajdkDynamicProxy;

public interface Chinese {
    //定义一个 sayHello 的抽象方法
    String sayHello(String name);
}

Ⅱ 实现类–Yinyu:

Yinyu 类实现 Chinese 接口,并重写 sayHello 这个抽象方法

package com.aopdemo.ajdkDynamicProxy;

@Component //实例化到spring容器中,相当于配置文件中的 <bean id="" class=""/>
public class Yinyu implements Chinese{

    @Override
    public void sayHello(String name) {
        System.out.println(name + "hello, AOP");
    }

    public void eat(String food) {
        System.out.println("我正在吃:" + food);
    }
}

Ⅲ 定义Aspect

package com.aopdemo.ajdkDynamicProxy;

@Aspect
@Component
public class DynamicProxyAdvice {

    //针对 Yinyu 类下的 sayHello 方法进行切入
    @Pointcut("execution(* com.aopdemo.ajdkDynamicProxy.Yinyu.sayHello(..))")
    public void pointcut() {
    }

    //接入点的前置操作
    @Before("pointcut()")
    public void before() {
        System.out.println("before");
    }
}

Ⅳ 使用测试类进行测试

package com.aopdemo;

@SpringBootTest
class AopdemoTests {

    @Autowired
    private Chinese chinese;

    @Test
    void ajdkDynamicProxyTest() {
        chinese.sayHello("yinyu say:");
        System.out.println(chinese.getClass());
    }
}

Ⅴ 配置 properties + 输出结果

注意:本项目若不按以下配置,那么将采用 CGLIB 动态代理,按理说 Yinyu 对象实现了接口,默认情况下会采用 JDK 的动态代理实现 AOP(同时默认情况下 spring.aop.proxy-target-class = false),但我实践的话并非如此,因此可以得出默认情况下proxy-target-class="true"(如果目标对象实现了接口,可以强制使用CGLIB实现AOP),至少我这个项目是这样。

所以我将 proxy-target-class 配置成了 false ,如果大家不需要此配置也可输出 $Proxy61 的话就不用这个配置。

路径:src/main/resources/application.properties

spring.aop.proxy-target-class = false

输出结果 👇

可以看到类型是 class com.sun.proxy.$Proxy61,也就是前面提到的 Proxy 类,因此这里 Spring AOP 使用了 JDK 的动态代理。

② CGLIB 动态代理

如果目标类没有实现接口,那么 Spring AOP 会默认选择使用 CGLIB 来动态代理目标类;若目标对象实现了接口,也可以强制使用 CGLIB 实现 AOP(设置 proxy-target-class = true)

CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成某个类的子类,CGLIB 是通过继承的方式做动态代理,因此如果某个类被标记为 final,那么它是无法使用CGLIB做动态代理的。

核心类包括 👇

  • MethodInterceptor 接口
  • Enhancer 类

Ⅰ定义类 CGLIBYinyu

定义一个不实现任何接口的类

package com.aopdemo.aCGLIBDynamicProxy;

@Component //实例化到spring容器中,相当于配置文件中的 <bean id="" class=""/>
public class CGLIBYinyu{

        public void eat(String food) {
            System.out.println("我正在吃:" + food);
        }
}

Ⅱ 定义Aspect

package com.aopdemo.aCGLIBDynamicProxy;

@Aspect
@Component
public class CGLIBAdvice {

    //针对 CGLIBYinyu 类下的 eat 方法进行切入
    @Pointcut("execution(* com.aopdemo.aCGLIBDynamicProxy.CGLIBYinyu.eat(..))")
    public void pointcut() {
    }

    //接入点的前置操作
    @Before("pointcut()")
    public void before() {
        System.out.println("before");
    }

}

Ⅲ 使用测试类进行测试

package com.aopdemo;

@SpringBootTest
class AopdemoTests {

    @Autowired
    private CGLIBYinyu cglibYinyu;

    @Test
    void ajdkDynamicProxyTest() {
        chinese.sayHello("yinyu say:");
        System.out.println(chinese.getClass());
    }
}

Ⅳ 输出结果

此时 application.properties 文件中依然配置 proxy-target-class = false~

可以看到类被 CGLIB 增强了,也就是 CGLIB 动态代理!!说明如果目标对象没有实现接口的话,那么就会默认使用 CGLIB 动态代理。

③ 基于 xml 配置(以 JDK 动态代理为例)

当然,SpringAOP 也是可以基于 xml 实现的,此时 xml 的作用相当于 Advice类文件(定义切面),同时也可将指定类注入 Spring。

注意:以 JDK 动态代理为例,同时我注释了 proxy-target-class 的配置 👇

Ⅰ 定义一个 XmlChinese 接口 :

package com.aopdemo.aaxmlJdkDP;

public interface XmlChinese {
    //定义一个 sayHello 的抽象方法
    void sayHello(String name);
}

Ⅱ 定义实现类–XmlYinyu:

XmlYinyu 类实现 XmlChinese 接口,并重写 sayHello 这个抽象方法,同时不用注释做注入操作

package com.aopdemo.aaxmlJdkDP;

public class XmlYinyu implements XmlChinese {

    @Override
    public void sayHello(String name) {
        System.out.println(name + "hello, AOP");
    }
}

Ⅲ 编写 bean.xml 文件

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--配置IOC,把对象都配置进来-->
    <bean id="xmlYinyu" class="com.aopdemo.aaxmlJdkDP.XmlYinyu"/>
    <bean id="xmlEnpower" class="com.aopdemo.aaxmlJdkDP.XmlEnpower"/>
    <!--配置AOP-->
    <aop:config>
        <!--配置切面-->
        <aop:aspect ref="xmlEnpower">
            <!--配置通知类型,并且建立通知切入点方法打关联-->
            <aop:before method="printbefore"
                        pointcut="execution(public void com.aopdemo.aaxmlJdkDP.XmlYinyu.sayHello(..))"/>
        </aop:aspect>
    </aop:config>
</beans>

Ⅳ 使用测试类进行测试

package com.aopdemo;

@SpringBootTest
class AopdemoTests {

    @Test
    void xmlJdkDPTest() {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml");
        XmlChinese xmlChinese = (XmlChinese) applicationContext.getBean("xmlYinyu");
        xmlChinese.sayHello("yinyu say to xml:");
        System.out.println(xmlChinese.getClass());
    }
}

Ⅴ 输出结果

可以看到类型是 class com.sun.proxy.$Proxy108,显而易见这就是 JDK 的动态代理,因此使用 xml 进行动态代理时,默认 proxy-target-class = false 

2 通过 AOP 打印数据访问层摘要(以 mybatis-plus 的 mapper 为例)

2.1 项目初始化

前提是项目已创建,可通过 IDEA Spring Initializr,且已导入web、mysql、lombok等依赖。

① 导入 mybatis-plus 依赖

路径:pom.xml

        <!--mybatis-plus-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.2</version>
        </dependency>

② 连接数据库配置

路径:src/main/resources/application.properties

#数据库连接配置
spring.datasource.username=root
spring.datasource.password=root
#mysql5~8 驱动不同driver-class-name     8需要增加时区的配置serverTimezone=UTC,放在url最后
#useSSL=false 安全连接
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_plus?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

③ 编写实体类

路径:src/main/java/com/aopdemo/pojo/User.java

package com.aopdemo.pojo;

@Builder
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {

    @TableId(type = IdType.AUTO)//新增记录时未命名id时id自增
    private Long id;

    private String name;
    private  Integer age;
    private  String email;
}

2.2 AOP 具体代码

① 编写 mapper 接口

路径:src/main/java/com/aopdemo/mapper/UserMapper.java

package com.aopdemo.mapper;

//在对应的接口上面继承一个基本的接口 BaseMapper
@Mapper
public interface UserMapper extends BaseMapper<User> {
    //mybatisplus 将所有CRUD操作都编写完成了,不用像以前一样配置一大堆文件

}

② 编写 service 实现类

通过 mapper 定义了一些增删改查操作~

package com.aopdemo.service;

@Service
public class UserServiceImpl {

    @Autowired
    private UserMapper userMapper;

    /*
    Mapper CRUD(增删改查)
    */

    //增加一个User
    public int addUser_ByMapper(User user){
        return userMapper.insert(user);
    }

    //根据id删除一个User
    public int deleteUserById_ByMapper(int id){
        return userMapper.deleteById(id);
    }

    //更新User
    public boolean updateUser_ByMapper(User user){
        userMapper.updateById(user);
        return true;
    }

    //根据id查询,返回一个User
    public User queryUser_ByMapper(int id){
        return userMapper.selectById(id);
    }

    //查询全部User,返回list集合
    public List<User> queryAllUser_ByMapper(){
        return userMapper.selectList(new QueryWrapper<>());//QueryWrapper没有任何条件
    }
}

③ 编写 Aspect 类

针对 mapper 接口进行切入,该接口下的所有抽象方法都将得到增强!

路径:src/main/java/com/aopdemo/aspect/PerformanceAspect.java

package com.aopdemo.aspect;

@Aspect
@Component
@Slf4j
public class PerformanceAspect {
    //针对某个接口进行切面注入,ProceedingJoinPoint只是给 @Around 用的
    @Around(value = "execution(* com.baomidou.mybatisplus.core.mapper..*(..))")
    public Object logPerformance(ProceedingJoinPoint pjp) throws Throwable {
        long startTime = System.currentTimeMillis();
        String name = "--";
        String result = "YES";
        try {
            name = pjp.getSignature().toShortString();
            return pjp.proceed();
        } catch (Throwable t) {
            result = "NO";
            throw t;
        } finally {
            long endTime = System.currentTimeMillis();
            log.info("{};{};{}ms", name, result, endTime - startTime);
        }
    }
}

④ 编写测试类

路径:src/test/java/com/aopdemo/AopdemoTests.java

package com.aopdemo;

@Slf4j
@SpringBootTest
class AopdemoTests {

    @Autowired
    private UserServiceImpl userService;

    @Test
    void contextLoads() {
        log.info("All Users: {}", userService.queryAllUser_ByMapper());
        User user =userService.queryUser_ByMapper(1);
        log.info("The User of id = 1 : {}", user);
    }
}

⑤ 输出结果

说明 mapper 接口已被增强成功,按照切面增加的功能输出~


参考文章

通过 AOP 打印数据访问层摘要_L# S@的博客-CSDN博客

5.Spring Aop xml配置代理 注解代理_多加香菜_0505的博客-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/56036.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot 3.0 新特性,内置声明式 HTTP 客户端

http interface 从 Spring 6 和 Spring Boot 3 开始&#xff0c;Spring 框架支持将远程 HTTP 服务代理成带有特定注解的 Java http interface。类似的库&#xff0c;如 OpenFeign 和 Retrofit 仍然可以使用&#xff0c;但 http interface 为 Spring 框架添加内置支持。 什么是…

RabbitMQ之集群方案原理

对于无状态应用&#xff08;如普通的微服务&#xff09;很容易实现负载均衡、高可用集群。而对于有状态的系统&#xff08;如数据库等&#xff09;就比较复杂。 1、业界实践 主备模式&#xff1a;单活&#xff0c;容量对等&#xff0c;可以实现故障转移。使用独立存储时需要借…

【Matplotlib绘制图像大全】(二十五):Matplotlib使用figure()添加画布

前言 大家好,我是阿光。 本专栏整理了《Matplotlib绘制图像大全》,内包含了各种常见的绘图方法,以及Matplotlib各种内置函数的使用方法,帮助我们快速便捷的绘制出数据图像。 正在更新中~ ✨ 🚨 我的项目环境: 平台:Windows10语言环境:python3.7编译器:PyCharmMatp…

2022re:Invent:亚马逊云科技拥有超过15年的数据创新经验

在探索适合企业的数据战略的道路上&#xff0c;企业并不孤单。亚马逊云科技有着超过15年的数据领域创新经验。并一直在尝试通过创新的方法去改进数据存储和分析的过程&#xff1a;亚马逊云科技在云中提供了第一个可扩展存储服务S3、发布了云上第一个专门构建的数据库DynamoDB和…

easyrecovery2023免费版电脑数据恢复软件下载功能介绍

误删了重要文件&#xff1f;电脑中毒数据丢失&#xff1f;本想拷贝却手抖格式化了&#xff1f;那可以试试easyrecovery&#xff0c;这是一款十分强大的电脑数据恢复软件&#xff0c;界面简洁明了&#xff0c;操作也是十分的简单&#xff0c;只需要根据文字提示来进行操作即可进…

B端产品实战课读书笔记:第七章B端产品常用功能设计

目录 一、工作台 二、列表 三、搜索/筛选 3.1输入框搜索 3.2 单选/复选筛选 3.3 独立/联动筛选 3.4 其他 四、审批 4.1概念 4.2扩展功能 4.3设计要点 五、用户--角色--权限 5.1 什么是RBAC模型 5.2单系统的用户权限设计 5.3多系统的用户权限设计 六、可视化 6.…

用HTML+CSS做一个漂亮简单的音乐网站

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

[附源码]计算机毕业设计springboot新能源汽车租赁

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

基于STM32G431嵌入式学习笔记——九、IIC总线(一)EEPROM

一、基础原理剖析 先前的实验中我们都是通过程序设计语言去调用串口、定时器、引脚等硬件设备进行从0到有的功能实现&#xff0c;所有的指令以及数据均来自程序设计语言&#xff0c;而没有想计算机一样将一些必要数据存入存储器中。 IIC总线是我们嵌入式板子上进行数据传输的主…

太简单了,一文彻底搞懂Jenkins的用法

程序员宝藏库&#xff1a;https://gitee.com/sharetech_lee/CS-Books-Store 你想要的&#xff0c;这里都有&#xff01; 我在前公司的某个部门做过一年CV算法工程师&#xff0c;部门推崇全栈式开发。换句话说&#xff0c;做算法&#xff0c;不能只聚焦于算法&#xff0c;要全流…

阿里云NAS存储部署简介

近期在阿里云上部署OCP需要用到NAS&#xff0c;其中遇到NAS无法扩容的问题&#xff0c;在此简单笔记。 1、部署NAS 参照github.com\kubernetes-sigs\alibaba-cloud-csi-driver\docs\nas.md部署说明&#xff0c;需要执行以下步骤&#xff1a; Step 1: Create RBAC resource …

四、伊森商城 前端基础-Vue MVVM思想Vue安装单向绑定 p21

目录 一、MVVM 思想 二、Vue 简介 2.1、安装 2.1.1、初始化vue项目 -y默认确定 2.1.2、安装上vue的2.6.11版本 三、Vue的单向绑定 一、MVVM 思想 M&#xff1a;即 Model&#xff0c;模型&#xff0c;包括数据和一些基本操作 V&#xff1a;即 View&#xff0c;视图&#xf…

【Docker】常用命令总结

Docker是基于Go语言实现的云开源项目。Docker的主要目标是**“Build&#xff0c;Ship and Run Any App,Anywhere”**。也就是通过对应用组件的封装、分发、部署、运行等生命周期的管理&#xff0c;使用户的APP&#xff08;可以是一个WEB应用或数据库应用等等&#xff09;及其运…

容器开发运维人员的 Linux 操作机配置优化建议

"工欲善其事必先利其器", 作为一个PAAS平台架构师, 容器相关技术(docker, k8s等)是必不可少的. 本文简单介绍下我自己的Linux操作机配置. 提升工作效率, 提高使用体验. :heart::heart::heart: :exclamation: 注意: 本文以CentOS 7.6 为例, RHEL7.6 操作类似. Ubuntu系…

为什么简历上不要轻易写“精通分布式”,一线大厂为什么面试必问分布式?

为什么要学习分布式&#xff1f; 作为一名后端 Java 程序员&#xff0c;我们在找工作写简历的时候除了高并发经验。一般情况下都还会写上自己熟悉/了解/掌握/精通分布式系统&#xff0c;所以高并发和分布式大多是成对出现的。 在某直聘网站上搜到的 Java 岗位 这么多金的一个知…

Shiro-SpringBoot (二)

在上一节中实现了在SpringBoot中使用Shiro做权限控制&#xff0c;但是针对上一节留下的不足点&#xff0c;在这里进行一下优化和改造&#xff0c;主要有一下几点: 支持AJAX请求支持FreeMarker模板URL拦截提取到yml配置文件 (一) 支持AJAX请求 如果是AJAX请求URL接口&#xff0…

软件外包公司真的去不得吗?

各位小伙伴们&#xff01;好&#xff01;啊&#xff01;最近全国大部分地区都降温了&#xff0c;大家记得做好保暖&#xff0c;不要生病。 无论是应届毕业生&#xff0c;还是准备跳槽的测试人&#xff0c;都有面对“软件外包”公司的可能。有些人说进入外包公司就相当于给履历…

电子招标采购商城系统:优化传统采购业务,提速企业数字化升级

后疫情时代&#xff0c;电子元器件供应链发生了巨大的变化&#xff0c;缺货已经影响了大多数企业&#xff0c;电子元器件采购人员每天被“缺货”“涨价”的字眼包围着&#xff0c;对电子元器件企业的发展带来了极大的限制。当前&#xff0c;借助数字化技术对电子元器件采购管理…

[附源码]计算机毕业设计在线票务系统Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

云服务器和本地服务器的优缺点分析

服务器是企业IT基础设施的命脉&#xff0c;可用于存放文件、应用程序、网站、员工远程访问等等。当然&#xff0c;选择时有许多不同类型的服务器和许多需要考虑的因素。目前比较流行的两种服务器类型是本地服务器和基于云的服务器。 本地服务器 本地服务器放置在公司的办公室中…