Spring是怎么回事?新手入门就看这篇吧

news2024/11/19 16:44:10

前言

今天壹哥给大家介绍一套开源的轻量级框架,它就是Spring。在给大家详细讲解Spring框架之前,壹哥先给大家介绍Spring框架的主要内容:

  • Spring的基本概念

  • Spring核心思想之ioc

  • Spring核心思想之aop

  • Spring框架对事务的支持

 在本系列文章的讲解中,壹哥会给大家配备丰富的案例、图片以及对应的学习视频,目的就是让大家更好的理解和运用Spring框架,并给大家带来足够的启发和思考。接下来我们正式学习Spring框架。

一. Spring框架的基本概述

1. 什么是Spring

在Spring官网(https://spring.io/projects/spring-frameworkhttps://Spring.io/projects/Spring-framework)里面,是这样介绍Spring的:

如果大家看不懂英文,壹哥结合官网以及自己的理解,给大家介绍什么是Spring:

Spring是一个全栈的、开源的javaEE轻量级企业框架。以IOC(Inverse Of Control:控制反转)和AOP(Aspect Oriented Programming:面向切面编程)为核心思想提供了基于持久层、业务层和控制层的众多企业级技术解决方案。此外Spring还整合了众多第三方优秀的框架和类库,逐渐成为业界最流行的企业应用框架。

壹哥先给大家画一幅图,让大家更好的理解Spring的概念:

相信通过这幅图,大家对Spring的概念有了清晰的理解。

了解了Spring的基本概念之后,我们再来了解一下Spring的发展历程。

2. Spring的发展历程

在介绍Spring的发展历程之前,壹哥有必要提及一下Spring的开山鼻祖Rod Johnson(罗德·约翰逊),他是Spring框架的缔造者。

其实Spring并不是凭空存在的。早在Spring框架诞生之前,java企业级技术解决方案主要依靠javase和EJB框架。但是EJB框架在企业级开发中存在一些弊端和缺陷。

  1. 2002年,Rod Johnson 撰写了一本名为《 Expert One-to-One J2EE Design and Development 》的书。这本书介绍了当时 Java 企业应用程序开发的情况,并系统阐述了 J2EE 使用 EJB 开发设计的优点及解决方案。

  2. 同年又推出了《Expert one-on-one J2EE Development without EJB》,对EJB的各种笨重臃肿的结构进行了逐一的分析和否定,并分别以简洁实用的方式替换之,堪称经典。

  3. 2003年,推出的《Expert one on one J2EE design and development》中阐述的部分理念,在这本书中,他提出了一个基于普通 Java 类和依赖注入的思想。在书中,他展示了如何在不使用 EJB 的情况下构建高质量,可扩展的系统--这就是Spring的雏形!

  4. 2004年,Spring横空出世!Spring1.0版本发布,随后Spring框架迅速发展。

  5. 2006年10月Spring 2.0于发布。

  6. 经过多年的发展,2017 年 9 月份发布了 Spring具有里程碑意义的版本 Spring 5.0 通用版(GA),并提出了最令人兴奋的响应式编程模型。

  7. 现在Spring的最新版本已经更迭到了Spring 6。

到这里,壹哥就给大家详细介绍了Spring的发展历史,希望大家心中有数,以后你在跟别人聊Spring的时候,这也是一种不可或缺的谈资。接下来,壹哥再给大家介绍一下Spring的优势。

3. Spring的优势

对于javaee开发人员来说,基于Spring庞大的生态系统,Spring几乎是每个人离不开的框架,所以Spring的优势不言而喻!在这里壹哥给大家简单罗列Spring的优势都有哪些:

  • 降低程序耦合度,简化开发:通过 Spring 提供的 IoC 核心容器,可以将对象之间的依赖关系交给 Spring帮我我们进行控制,避免因为硬编码所造成的程序高程序耦合。

  • AOP 面向切面思想的支持:通过 Spring 的 AOP 思想,对传统 OOP 思想进行了延续和扩展。通过面向切面编程,在不侵入源代码的前提下,轻松实现对应用进行更新和扩展。

  • 基于对数据库事务的支持:将我们从单调烦闷的数据库事务管理代码中解脱出来,通过声明式方式和注解式的方式进行灵活的事务管理,将事务代码和业务代码剥离开来。降低业务代码的耦合度,提高开发效率和质量。

  • 方便集成各种第三方优秀框架:Spring降低了各种框架的使用难度,提供了对各种优秀框架( Struts、 Hibernate、 rabbitmq、 elasticsearch、redis...)的无缝支持。

  • 降低 JavaEE API 的使用难度:Spring 对 JavaEE API(如 JDBC、 Redis)进行了薄薄的封装层,提供了模板对象操作这些API,使这些 API 的使用难度大为降低。

  • Java 源码是经典学习范例:Spring 的源代码博大精深,它设计巧妙、别具风格,处处体现着大师对 Java 设计模式的灵活运用以及对 Java 技术的高深造诣。它的源代码无意是 Java 技术的最佳实践范例。通过对源码的阅读,我们可以更加清晰了解框架的设计逻辑,可以拓宽我们对java知识的认知广度和深度。

聊完了Spring的优势以后,Spring到底包含哪些内容呢?下面壹哥通过一幅图来描述Spring的体系结构。

4. Spring的体系结构

 下面壹哥就这幅图给大家做详细的介绍:

4.1 核心容器(Core Container)

Spring的核心容器是Spring其他功能正常运行的基础,它包含了Spring-core、Spring-beans、Spring-context和Spring-expression(Spring表达式语言)等模块。

  • Spring-core模块:Spring框架的核心部分,包括控制反转(Inversion of Control,IOC)和依赖注入(Dependency Injection,DI)的核心功能。

  • Spring-beans模块:提供了BeanFactory,我们俗称bean工厂。Spring将管理的对象称为Bean。Spring容器就是通过这个bean工厂来帮助我们管理bean。

  • Spring-context模块:我们称为Spring 上下文环境,基于Spring-core和Spring-beans模块的基础之上,提供了通过XML和注解的方式来管理bean对象。ApplicationContext接口是Spring-context模块的核心。也是我们在后续学习中需要关注的焦点。

  • Spring-expression模块:提供了强大的表达式语言去支持对bean进行查询和设置。Spring表达式语言支持设置和获取备案的属性值、以及进行逻辑和算术运算等操作。

4.2 数据访问/集成(Data Access/Integration)

数据访问/集成层由JDBC、ORM、OXM、JMS和Transactions(事务)模块组成。

  • Spring-jdbc模块:Spring对原生的jdbc技术进行了封装,提供了jdbc Template来进行数据库操作。是Spring基于对数据持久层的解决方案之一。

  • Spring-orm模块:Spring 框架提供了对若干个 ORM 框架的集成层,比如Hibernate等。

  • Spring-oxm模块:是Spring3.0版本中的一个新特性,它提供了一个支持o(对象)/XML映射的抽象层实现,例如JAXB、Castor、JiBX和XStream。

  • Spring-jms模块(Java Messaging Service):指Java消息传递服务,提供了对消息的生产和发送功能,通过对jms的封装,简化了对jms api的使用难度。

  • Spring-tx模块:事务模块支持使用编程式事务、声明式事务和注解式事务对事务进行管理。

4.3 Web层

Web层由Spring-web、Spring-webmvc、Spring-websocket和Portlet模块组成。

  • Spring-web模块:提供了基本的Web开发功能,比如通过监听的机制初始化Spring的上下文环境。

  • Spring-webmvc模块:提供了对Spring MVC的实现。

  • Spring-websocket模块:Spring4.0以后新增的模块,它提供了WebSocket和SocketJS的实现。

  • Portlet模块:提供了Portlet环境下的MVC实现。

4.4 AOP(Aspect Oriented Programming)模块

  • Spring-aop模块:提出了AOP思想的面向切面编程的实现,允许我们定义通知方法对切点方法进行增强,对代码进行解耦合。

  • Spring-aspects模块:提供了对第三方AOP框架AspectJ的集成功能,AspectJ是一个功能强大且易于上手的的AOP框架。我们在后面也会对AspectJ框架进行详细学习。

4.5 植入(Instrumentation)模块

  • Spring-instrument模块:在特定的应用服务器中,提供了类植入(instrumentation)支持和类加载器的实现,比如Spring-instrument-tomcat 模块支持对Tomcat服务器的植入代理。

4.6 消息传输(Messaging)

  • 这是Spring4.0以后新增了消息(Spring-messaging)模块,集成了消息api,对消息协议也提供了支持。

4.7 测试(Test)模块

  • Spring-test模块:支持使用JUnit对Spring组件进行单元测试。Spring提供了对junit的整合,提供了一个带有Spring环境的标准测试模板,后续我们也会学习Spring对junit单元测试框架的整合。

二. 程序中的耦合

前面壹哥给大家介绍Spring优势的时候,给大家描述了Spring框架可以降低代码的耦合度。那到底什么是程序的耦合,现在壹哥用几个例子给大家介绍清楚。

1. 耦合案例1

1.1 代码示例

我们来看一段代码,这段代码相信大家都很熟悉,就是使用jdbc技术进行数据表的查询操作。首先我们新建一个maven工程,并导入相关依赖。

<dependencies>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.6</version>
    </dependency>
  
   <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.13</version>
      <scope>test</scope>
   </dependency>
</dependencies>

注意:壹哥友情提示,这个依赖适配的mysql数据库版本是5.x哦。

我们在这里编写一段jdbc代码。

public class TestMySql {

    @Test
    public void test01() throws Exception{
        //注册驱动,此时我们不要Class.Name()的方式进行驱动加载,为什么?请看下文分解~
        DriverManager.registerDriver(new com.mysql.jdbc.Driver());
        String url = "jdbc:mysql://192.168.10.137:3306/spring";
        //获取连接对象
        Connection connection = DriverManager.getConnection(url, "root", "Admin123!");
        PreparedStatement statement = connection.prepareStatement("select * from account");
        ResultSet resultSet = statement.executeQuery();
        while (resultSet.next()){
            Integer id = resultSet.getInt("id");
            String name = resultSet.getString("name");
            Double money = resultSet.getDouble("money");
            System.out.println(id + " " + name + " " + money);
        }
        resultSet.close();
        statement.close();
        connection.close();
    }
}

我们运行程序,查看效果是没有问题的。但是如果我们把POM文件中的依赖注释掉呢?

<!--
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.6</version>
</dependency>
-->

这个时候,可能有小伙伴要问了,老师,你为什么要把这个依赖注释掉呢?其实壹哥是用来模拟在项目中,如果资源缺失,对程序带来的影响是什么。

此时我们在重新编译程序(Ctrl+F9)。我们发现程序报错,程序编译不通过!

小伙伴们想一下:程序编译不通过意味着什么呢?壹哥给大家解释一下:

一个企业级的项目,里面的业务功能肯定有很多,假设一个业务功能出现了问题,但是其他业务功能是正常的。如果一个业务功能出现了问题,导致程序编译不通过的后果是什么?是不是其他正常的业务功能也无法正常运行了,这实在是我们不想看到的情况。

大家想想,如果出现了这种情况,理想的解决方案是什么?当然最好的解决方案是修复当前程序的bug,让程序正常编译通过。但是假设一时半会解决不了这个问题呢?

即使这个业务功能出现了问题,我们也要期望其他能功能也应该正常执行,换句话说,就是一定要让程序编译通过!!!而之所以会出现这种编译不通过的情况,就是因为我们的代码出现了高耦合!

1.2 耦合的分析以及对应的解决方案

什么是耦合?

耦合指的是程序之间的依赖关系。这种依赖关系包括类与类之间存在依赖关系,类与方法之间存在依赖关系。

注意:壹哥说重点,我们所说的降低耦合,就是降低程序的依赖关系,但是依赖关系只能尽量降低,不能彻底消除!

上面的案例,其实就是类与类之间存在高耦合的体现。在TestMySql这个类里面使用了Driver这个驱动类,说明TestMySql依赖于Driver驱动类。

由于我们注释了驱动类,导致Driver这个类不存在,最后导致TestMySql这个类编译不通过。

如何解耦?我们解耦的最终目的要达到一个效果,就是即使依赖缺失了。程序也应该正常编译通过,这就叫编译时不依赖,也就是在编译期不检查程序间的依赖关系。所以我们需要对原来的代码进行改造,如何改造?基于反射的技术降低代码的耦合度。

@Test
public void test01() throws Exception{
    //注册驱动
    //DriverManager.registerDriver(new com.mysql.jdbc.Driver());
    //使用反射的机制加载数据库驱动
    Class.forName("com.mysql.jdbc.Driver");
    String url = "jdbc:mysql://192.168.10.137:3306/spring";
    //获取连接对象
    Connection connection = DriverManager.getConnection(url, "root", "Admin123!");
    PreparedStatement statement = connection.prepareStatement("select * from account");
    ResultSet resultSet = statement.executeQuery();
    while (resultSet.next()){
        Integer id = resultSet.getInt("id");
        String name = resultSet.getString("name");
        Double money = resultSet.getDouble("money");
        System.out.println(id + " " + name + " " + money);
    }
    resultSet.close();
    statement.close();
    connection.close();
}

此时重新编译代码,这个时候就不会报错。我们运行试试(此时pom.xml的坐标被注释了,在运行时会报异常)。

但此时报的是运行时异常,而不是编译时异常,换句话说就是整个应用编译通过,其他正常功能可以运行,但是有问题的功能会抛出运行时异常。虽然有异常,但至少没有拖后腿,影响其他正常功能的执行!

在这里壹哥给大家总结一下,如何降低代码的耦合性:

使用反射的机制,避免使用new关键字。

2. 耦合案例2

接下来我们再看一个案例,进一步理解耦合的概念,以及解决耦合的方案。

2.1 代码示例

现在我们定义一段代码,模拟新增用户的功能。

dao部分:

我们定义一个模拟用户新增的dao接口。

public interface UserDao { 
    //模拟新增user的方法 
    public void addUser(); 
}

接下来,我们对接口进行实现,假设我们现在对持久层的实现技术是基于jdbc技术实现的。我们现在就在接口实现类里面输出一句话,模拟我们完成了对数据库的新增操作。

public class UserDaoImpl implements UserDao{
    
    //模拟新增user的dao
    public void addUser() {
        System.out.println("基于jdbc技术实现了对新增用户的操作.....");
    }
}

service部分:

接下来我们再定义一个新增用户的业务接口。

public interface UserService {
    //模拟新增user的业务
    public void add();
}

我们定义业务实现类:

public class UserServiceImpl implements UserService {
    //实现新增用户的业务方法
    public void add() {
        UserDao dao = new UserDaoImpl();
        dao.add();
    }
}

2.2 耦合的分析以及对应的解决方案

在这里壹哥做一个假设。假设现在因为客户需求的变更,我们需要对持久层的技术进行变化,我们需要对原来jdbc技术实现接口的方法变成使用mybatis框架实现。所以我们需要重新定义一个接口实现类:

public class UserDaoImpl2 implements UserDao{
    
    //模拟新增user的dao
    public void addUser() {
        System.out.println("基于mybatis技术实现了对新增用户的操作.....");
    }
}

然后我们需要修改业务实现类的源代码,更改接口的调用方式:

public class UserServiceImpl implements UserService {
    //实现新增用户的业务方法
    public void add() {
        //UserDao dao = new UserDaoImpl();
        UserDao dao = new UserDaoImpl2();
        dao.addUser();
    }
}

这样修改会有什么问题呢?

这会造成源代码的改动

做过开发的小伙伴们都知道,如果我们的应用要进行更新迭代,或者需要扩展一些新功能,必须在不修改原来功能代码的基础上完成,因为一旦修改源代码,很有可能会导致其他正常的功能代码也出现问题。上面出现修改源代码的原因其实也是高耦合的一种体现!究其原因还是UserServiceImpl这个类依赖了UserDao的实现类。

那么又该如何解决这个问题呢?

在解决这个问题之前,壹哥先要明确一个概念,什么是javabean?

javaBean就是在java程序中,可以重用的一些组件,比如实体类、dao和service接口实现类的对象。解决方案如下:

1.需要一个配置文件来配置我们的service和dao。配置的内容:唯一标志=全限定类名(key=value)。

2.在一个工厂类里面,通过读取配置文件中配置的内容,反射创建对象。也就是通过工厂来管理我们的bean。

我们选取什么样的配置文件最合适?我们就选取大家最熟悉的properties配置文件。

定义配置文件,管理我们的service和dao:

#key=value  key bean的名称  value:类的全限定名
userDao=com.qf.dao.impl.UserDaoImpl
userDao2=com.qf.dao.impl.UserDaoImpl2
userService=com.qf.service.impl.UserServiceImpl

定义的配置文件,效果如下:

定义一个工厂类,通过工厂来获取bean

public class BeanFactory {

    static Properties properties;

    static{
        //通过类加载器读取properties配置文件
        InputStream inputStream = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
        properties = new Properties();
        try {
            properties.load(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //定义一个获取bean的方法
    public static Object getBean(String name){
        String value = properties.getProperty(name);
        try {
            //基于反射的机制实例化bean对象
            Object o = Class.forName(value).newInstance();
            return o;
        } catch (Exception e) {
            throw  new RuntimeException();
        }
    }
}

改造业务实现类的代码:

public class UserServiceImpl implements UserService {
    //实现新增用户的业务方法
    public void add() {
        //UserDao dao = new UserDaoImpl();
        //UserDao dao = new UserDaoImpl2();
        UserDao userDao = (UserDao) BeanFactory.getBean("userDao");
        userDao.addUser();
    }
}

如果我们想要切换使用mybatis的技术实现dao接口的话,我们只需要把getBean方法里面的bean的名称修改即可。

UserDao userDao = (UserDao) BeanFactory.getBean("userDao2");

现在我们通过工厂模式解决了代码出现的耦合度问题。接下来,大家再思考一下,目前我们的代码有没有出现问题。如果我们循环调用5次service,会出现什么结果?

我们进一步改造代码:

public class UserServiceImpl implements UserService {

    public void add() {
        UserDao accountDao = (UserDao) BeanFactory.getBean("accountDao2");
        //输出accountDao实例对象
        System.out.println(accountDao);
        accountDao.addUser();
    }
}

定义测试类:

@Test
public void test02(){
    for(int i = 0;i<5;i++){
        UserService accountService = new UserServiceImpl();
        accountService.add();
    }
}

查看输出结果:

结论:该业务实现类调用的dao实现类对象是多例的。

我们的”工厂类”生产的bean之所以是多例模式,是因为下面这句代码:

在单线程的场景下面,我们期望工厂生产的bean的单例的。因为单例bean带来的优势也比较明显:

  • 可以减少JVM垃圾回收的次数。

  • 提高了获取bean的速度。

  • 降低了实例化bean带来的性能消耗。

接下来,我们就来改造我们的工厂代码。

2.3 优化工厂代码,生产单例bean

改造工厂类:

public class BeanFactory {

    static Properties properties = null;
    //用来存储实例对象的 key是bean的名称 value是实例对象
    static Map<String,Object> map = new HashMap<String, Object>();

    static {
        try {
            properties = new Properties();
            //通过类加载器加载properties配置文件
            InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
            properties.load(in);
            //获取properties配置文件中的所有key值
            Enumeration<Object> keys = properties.keys();
            while(keys.hasMoreElements()){
                String name = keys.nextElement().toString();
                String value = properties.get(name).toString();
                //通过key的名称获取对应的value值
                Class<?> clazz = Class.forName(value);
                Object o = clazz.newInstance();
                map.put(name,o);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //根据key获取对应的value
    public static Object getBean(String name){
        return map.get(name);
    }
}

测试:

查看控制台输出效果:

现在我们终于使用第二种方案降低了代码的耦合性,在这里壹哥再次总结一下:

可以使用工厂+反射的机制降低代码的耦合度!

但是我们每次都自己通过工厂+配置的方式创建对象的过程过于繁琐,其实Spring已经帮助我们做了这些事情,spring如何帮助我们管理bean的?在下期文章,壹哥再给大家细细道来。

三. 结语

通过这篇文章的介绍,壹哥带领大家一步步剖析了什么是耦合,以及解耦合对应的具体方案,希望大家能够理解为什么要使用这种思想帮助我们管理bean。也希望大家理解为什么我们要手动基于反射+工厂的方式去管理bean的用意。那么Spring到底如何使用ioc管理bean的?请大家继续关注壹哥的后续文章,谢谢大家的支持和鼓励!

四. 今日作业

1. 什么是程序的耦合,在评论区给出你们的答案。

2. 如何自定义容器管理bean? 在评论区说出你们的解决方案。

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

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

相关文章

解决前端如何使用插件crypto-js进行AES加密方式数据加密

一、问题 目录 一、问题 1.1 问题概述 1.2 操作过程描述 二、解决 2.1 说明 2.2 crypto-js安装 2.3 使用crypto-js 1.1 问题概述 如何进行加密和解密以及采用什么方式进行加密解密是本文主要解决的内容~ 之前有小伙伴问了关于加密解密的事&#xff0c;确实是的&#xff…

pdfbox / XSL + FOP 转换 PDF文档

XSL-FO是XSL Formatting Objects的缩写&#xff0c;它是一种用于文档格式的XML 置标语言。XSL-FO是XSL的一部分&#xff0c;而XSL是一组定义XML数据转换与格式的W3C技术。XSL的其他部分有XSLT与XPath。 XSL-FO是用于格式化XML数据的语言&#xff0c;全称为Extensible Styleshe…

python基础讲解 02

人工智能学习基础四剑客&#xff08;库&#xff09;Numpy ( Numerical Python)使用创建随机数组查看数组的属性数组与标量之间的计算四剑客&#xff08;库&#xff09; Python被大量应用在数据挖掘和深度学习领域,其中使用极其广泛的是Numpy&#xff08;N维数组对象和向量运算&…

Nessus学习

攻击主机&#xff1a; Kali 192.168.11.106靶机&#xff1a;windows server 2008 r2 192.168.11.134 x64 32位nessus实验原理&#xff1a;利用漏洞扫描器能够自动应用漏洞扫描原理&#xff0c;对目标主机安全漏洞进行检测&#xff0c;附带识别主机漏洞的特征库的功能&#xf…

使用客户端证书登录MySQL

使用客户端证书登录MySQL登录MySQL具有安全性高、不用输入密码的优点&#xff0c;这里说明生成证书和登录的过程。 实验环境是Linux上的的MySQL 8.0.31社区版。 生成证书 使用openssl req创建X.509证书&#xff0c;下面的命令创建有效期10年的私钥&#xff0c;使用man req可…

Kali Linux中shutdown指令的用法3-3

3 TIME介绍 TIME是shutdown指令的第二个参数&#xff0c;用来表示实现关机计划的时间&#xff0c;如果不指定TIME&#xff0c;则默认是1分钟之后实现关机计划。 3.1 通过hh:mm格式指定时间 可以通过hh:mm格式指定关机的具体时间&#xff0c;其中hh表示小时&#xff0c;mm表示…

CredSSP加密数据库修正

新部署的机器&#xff0c;远程登录时&#xff0c;提示如下&#xff1a; 原因是注册表里面缺少东西&#xff0c;新建一个文本文件&#xff0c;复制如下内容保存&#xff0c;然后将保存的文本文件保存为后缀为reg的可执行文件 Windows Registry Editor Version 5.00[HKEY_LOCAL_…

【Django项目开发】权限相关的模型设计(五)

文章目录一、什么是权限二、RBAC模型三、权限模型类设计1、需要设计那些字段2、特别注意四、角色模型类设计1、需要定义的字段有2、 多对多模型类设计(重点)五、用户模型类设计(前面已经设计好了)六、菜单模型类设计(前面已经设计好了)七、总结:上面4个表的关联关系为:如下八、…

Spring注解详解(使用注解的方式完成IOC)

补充&#xff1a;xml配置 最开始(Spring 1.x)&#xff0c;Spring都是通过xml配置控制层(controller)--业务逻辑层(service)--dao层--数据源的关系&#xff0c;但是比较复杂 Spring 2.x的时候&#xff0c;随着JDK1.5支持注解的方式&#xff0c;实现了 "xml 注解" 的开…

Vue自定义指令,自定义插件,过滤器,混入,nextTick

自定义指令&#xff1a;自己定义类似指令的技术。V-开关的特殊属性&#xff0c;是一个对象&#xff0c;自己定义其作用。 指令: v-特殊属性 * vue内置指令: v-html v-text v-pre * v-bind v-on v-if v-show v-for …

我的前端学习经历

我最近在开发一个NFT相关的Saas&#xff0c;部分截图如下&#xff1a;这是我一段时间前&#xff0c;朋友圈发的图&#xff0c;现在Saas在页面上有点变化&#xff0c;但懒得再截图了。客观而言&#xff0c;布局还可以&#xff0c;这一套的技术栈是&#xff1a;React TailwindCs…

​无线数据终端DTU的电路防护元器件推荐产品型号

​无线数据终端DTU是专门用于将串行数据转换为IP数据或将IP数据转换为串行数据通过无线通信网络传输的无线终端设备&#xff0c;因此对于它来说ESD静电放电及雷击浪涌的防护显得尤其重要。 DTU已广泛应用于电力、环保、LED信息发布、物流、水文、气象等行业&#xff0c;其硬件…

【人工智能原理自学】方差代价函数:知错

&#x1f60a;你好&#xff0c;我是小航&#xff0c;一个正在变秃、变强的文艺倾年。 &#x1f514;笔记来自B站UP主Ele实验室的《小白也能听懂的人工智能原理》。 &#x1f514;本文讲解一元一次函数感知器&#xff1a;如何描述直觉&#xff0c;一起卷起来叭&#xff01; 目录…

[oeasy]python0037_字符画艺术_asciiview_自制小动物_imagick_asciiart

牛说(cowsay) 回忆上次内容 我们狂飙了一路 从用shell 直接执行 python程序到用shell 循环执行 python程序 循环体中 把 python的 输出结果 用管道 交给了 figlet 把 figlet的 输出结果 用管道 交给了 cowsay 把 cowsay的 输出结果 用管道 交给了 lolcat 最后 提权 直接运行 s…

课程设计 | 学生成绩管理系统

&#x1f388; 作者&#xff1a;Linux猿 &#x1f388; 简介&#xff1a;CSDN博客专家&#x1f3c6;&#xff0c;华为云享专家&#x1f3c6;&#xff0c;Linux、C/C、云计算、物联网、面试、刷题、算法尽管咨询我&#xff0c;关注我&#xff0c;有问题私聊&#xff01; &…

减小 Go 代码编译后的二进制体积

1 基线用例 减小编译后的二进制的体积&#xff0c;能够加快程序的发布和安装过程。接下来呢&#xff0c;我们分别从编译选项和第三方压缩工具两方面来介绍如何有效地减小 Go 语言编译后的体积。 我们采用同一个测试工程来测试不同方式的效果。 使用的测试工程如下&#xff0…

SQL全自动化检查神器

介绍 Yearning MYSQL 是一个SQL语句审核平台。提供查询审计&#xff0c;SQL审核等多种功能&#xff0c;支持Mysql&#xff0c;可以在一定程度上解决运维与开发之间的那一环&#xff0c;功能丰富&#xff0c;代码开源&#xff0c;安装部署容易&#xff01; 功能介绍 SQL查询查询…

Prometheus配合 alertmanager 使用邮箱报警

部署Prometheus 和 Alertmanager略 安装包部署prometheusGrafananode_exporter_争取不加班&#xff01;的博客-CSDN博客 prometheus监控报警部署Alertmanager_争取不加班&#xff01;的博客-CSDN博客 编辑Alertmanager配置文件 vim alertmanager/alertmanager.yml global: res…

MySQL高可用之主备同步:MySQL是如何保证主备一致的

&#x1f3c6;今日学习目标&#xff1a; &#x1f340;MySql是如何保证主备一致的 ✅创作者&#xff1a;林在闪闪发光 ⏰预计时间&#xff1a;30分钟 &#x1f389;个人主页&#xff1a;林在闪闪发光的个人主页 &#x1f341;林在闪闪发光的个人社区&#xff0c;欢迎你的加入:…

【Java编程进阶】封装继承多态详解

推荐学习专栏:Java 编程进阶之路【从入门到精通】 文章目录 1. 封装2. 继承2.1 继承的语法2.2 子类重写父类的方法2.3 子类隐藏父类的方法2.4 super 关键字2.5 final 关键字2.6 父类子类类型转换3. 多态4. 总结Java 是一门面向对象程序设计语言,其具有封装,继承和多态三大特…