Java代码审计入门基础之Spring

2023-10-27

Java代码审计入门基础之Spring

       目前大部分的 Java 互联网项目,都是用 SSM(Spring MVC + Spring + MyBatis)框架组合搭建的,当然现在SpringBoot也很流行,但是学习Java代码审计,先从SSM框架开始学起,这篇文章将会先介绍Spring的基础。

三层架构
在这里插入图片描述

       终于来到Spring框架了,前面讲了Spring MVC和MyBatis框架,现在就讲了Spring框架的作用是什么以及如何在开发中去配置。
       Spring框架是Java应用最广的框架,是一个轻量级的开源框架,它的主要理念包括 IoC (Inversion of Control,控制反转)AOP(Aspect Oriented Programming,面向切面编程)。Spring框架的结构,见下图。

在这里插入图片描述

  • Data Access/Integration(持久层):包含有JDBC、ORM、OXM、JMS和Transaction模块。
  • Web层:包含了Web、Web-Servlet、WebSocket、Web-Porlet模块。
  • AOP模块:提供了一个符合AOP联盟标准的面向切面编程的实现。
  • Core Container(IoC):包含有Beans、Core、Context和SpEL模块。
  • Test模块:支持使用JUnit和TestNG对Spring组件进行测试。

Spring框架能实现的功能:

①Spring 能帮我们根据配置文件创建及组装对象之间的依赖关系
②Spring 面向切面编程能帮助我们无耦合的实现日志记录,性能统计,安全控制
③Spring 能非常简单的帮我们管理数据库事务
④Spring 还提供了与第三方数据访问框架(如Hibernate、JPA)无缝集成,而且自己也提供了一套JDBC访问模板来方便数据库访问。
⑤Spring 还提供与第三方Web(如Struts1/2、JSF)框架无缝集成,而且自己也提供了一套Spring MVC框架,来方便web层搭建。
⑥Spring 能方便的与Java EE(如Java Mail、任务调度)整合,与更多技术整合(比如缓存框架)。

Spring框架的意义:

低侵入,方便解耦,简化开发
       通过 Spring 提供的 IoC 容器,可以将对象间的依赖关系交由 Spring 进行控制,避免硬编码所造成的过度程序耦合。用户也不必再为单例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。
AOP 编程的支持
       通过 Spring 的 AOP 功能,方便进行面向切面的编程,许多不容易用传统 OOP 实现的功能可以
通过 AOP 轻松应付。
声明式事务的支持(基于切面和惯例)
       可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务的管理,
提高开发效率和质量。
方便程序的测试
       可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可
做的事情。
方便集成各种优秀框架
       Spring 可以降低各种框架的使用难度,提供了对各种优秀框架( Struts、 Hibernate、 Hessian、Quartz
等)的直接支持。
降低 JavaEE API 的使用难度
       Spring对JavaEE API(如 JDBC、 JavaMail、远程调用等)进行了薄薄的封装层,使这些 API 的
使用难度大为降低。

IoC(控制反转)

       IoC并不是什么技术,而是一种设计思想,就是将原本在程序中手动创建对象的控制权,交由Spring框架来管理。

正控:若要使用某个对象,需要自己去负责对象的创建,即采用new的方式。

反控:若要使用某个对象,只需要从 Spring 容器中获取需要使用的对象,不关心对象的创建过程,也就是把创建对象的控制权反转给了Spring框架

IoC的作用:削减计算机程序中的耦合(即解除我们代码中的依赖关系)。

耦合:程序间的依赖关系,包括类之间的依赖和方法之间的依赖。
解耦:降低程序间的依赖关系。

在实际开发中,应该做到:编译期间不依赖,运行时才依赖。

解耦的思路:
	1、使用反射来创建对象,而避免使用new关键字来创建对象;
	2、通过读取配置文件来获取要创建的对象的全限定类名。

       接下来,我将通过自定义一个BeanFactory类(先不使用Spring框架),使用工厂模式的方式去解耦程序,从而讲解IoC的大概原理。但首先,我会先通过传统的方式(使用工厂模式前)写一个demo,然后再通过工厂模式去优化代码。

传统的创建对象方式

先新建一个项目,目录结构如下:
3
       在该项目中,我将模拟表现层,通过ui中的类Client来调用业务层service中的接口来模拟保存账户(这里只是模拟保存账户,在console中打印“保存了账户”该信息,并不是真的创建并保存一个账户)。

表现层中的com.nick.ui.Client.java,代码如下:

package com.nick.ui;

import com.service.IAccountService;
import com.nick.service.impl.AccountServiceImpl;

/**
 * 模拟一个表现层,用于调用业务层
 *
 */

public class Client {

    public static void main(String[] args) {

            IAccountService as = new AccountServiceImpl();
            System.out.println(as);
            as.saveAccount();
    }

}

4
业务层中的com.service.IAccountService.java,代码如下:

package com.nick.service;

public interface IAccountService {

    void saveAccount();
}

5
业务层中的实现类com.nick.service.impl.AccountServiceImpl,代码如下:

package com.nick.service.impl;

import com.nick.dao.IAccountDao;
import com.nick.dao.impl.AccountDaoImpl;
import com.nick.service.IAccountService;

public class AccountServiceImpl implements IAccountService {

    private IAccountDao accountDao = new AccountDaoImpl();
    public void saveAccount(){
        accountDao.saveAccount();
    }
}

6
在业务层中通过调用持久层接口的方法来模拟保存账户。

持久层中的com.nick.dao.IAccountDao.java,代码如下:

package com.nick.dao;

public interface IAccountDao {

    void saveAccount();
}

7
持久层中的实现类com.nick.dao.impl.AccountDaoImpl.java,代码如下:

package com.nick.dao.impl;

import com.nick.dao.IAccountDao;

public class AccountDaoImpl implements IAccountDao{

    public void saveAccount(){
        System.out.println("已经保存了账户");
    }
}

8
然后在Client类右键点击运行:

9
运行成功,这里成功“保存了账户”,并打印了业务层对象。

       但是,在上面的代码中,业务层调用持久层,并且此时业务层在依赖持久层的接口和实现类。如果此时没有持久层实现类,编译将不能通过。 在表现中也依赖了业务层的接口和实现类,见下图(通过new的方式来产生依赖),这种编译期依赖关系,应该在我们开发中杜绝,因此我们需要优化代码来解决。
10
11

使用工厂模式来解决程序耦合

       下面,我将对上述代码进行优化,通过工厂模式来减少程序间的耦合。
优化后的项目目录结构如下:

12
       首先创建一个BeanFactory类,一个创建bean的工厂,用于创建service和dao对象。
方法:

1、需要一个配置文件来配置service和dao

      配置内容为:key=value -> 唯一标志名=全限定类名

2、通过读取配置文件中的配置内容,反射创建对象

com.nick.factory.BeanFactory.java,代码如下:

package com.nick.factory;

import java.io.InputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

/**
 *一个创建bean的工厂
 *      用于创建service和dao对象
 *方法:
 *      1、需要一个配置文件来配置service和dao
 *      配置内容为:key=value :唯一标志=全限定类名
 *      2、通过读取配置文件中的配置内容,反射创建对象
 */

public class BeanFactory {

    //定义一个Properties对象
    private static Properties props;

    //定义一个Map,用于存放我们的对象,该Map对象称之为“容器”
    private static Map<String,Object> beans;
    //使用静态代码块为Properties对象赋值
    static {
        try {
            //实例化对象
            props = new Properties();
            //获取Properties文件的流对象
            InputStream in = BeanFactory.class.getClassLoader()
            .getResourceAsStream("bean.properties");
            props.load(in);

            //产生单例对象的方式
            //实例化容器
            beans = new HashMap<String,Object>();
            //取出配置文件中的所有的key
            Enumeration keys = props.keys();
            //遍历枚举中的值
            while(keys.hasMoreElements()){
                //取出每个key
                String key = keys.nextElement().toString();
                //根据key获取bean的路径
                String beanPath = props.getProperty(key);
                //反射创建对象
                Object value = Class.forName(beanPath).newInstance();
                //把key和value存入容器中
                beans.put(key,value);
            }
        }catch (Exception e){
            throw new ExceptionInInitializerError("初始化Properties对象失败");
        }

    }

    /**
     *根据bean的名称获取bean对象
     * @param beanName
     * @return
     */
    //此时获取的对象是单例的
    public static Object getBean(String beanName){
    	//直接通过传入bean的名字来获取容器中的bean对象
        return beans.get(beanName);
    }
}

在resources目录下新建一个bean的配置文件bean.properties:

accountService=com.nick.service.impl.AccountServiceImpl
accountDao=com.nick.dao.impl.AccountDaoImpl

13
       接下来,把原来代码中使用new方式引入依赖的两行代码注释掉,改成使用工厂模式的方法,直接去获取容器中所需的对象。

com.nick.service.impl.AccountServiceImpl,代码如下:

package com.nick.service.impl;

import com.nick.dao.IAccountDao;
import com.nick.factory.BeanFactory;
import com.nick.service.IAccountService;

public class AccountServiceImpl implements IAccountService {

    //private IAccountDao accountDao = new AccountDaoImpl();
    IAccountDao accountDao = (IAccountDao) BeanFactory.getBean("accountDao");
    public void saveAccount(){
        accountDao.saveAccount();
    }
}

14
com.nick.ui.Client.java,代码如下:

package com.nick.ui;

import com.nick.factory.BeanFactory;
import com.nick.service.IAccountService;

/**
 * 模拟一个表现层,用于调用业务层
 *
 */

public class Client {

    public static void main(String[] args) {
        //测试循环创建的对象是单例还是多例
        for(int i=0;i<5;i++) {
            //IAccountService as = new AccountServiceImpl();
            IAccountService as = (IAccountService) BeanFactory.getBean("accountService");
            System.out.println(as);
            as.saveAccount();
        }
    }
}

15
       注:上述代码中的for循环只是为了证明程序中创建的对象是单例而不是多例的,这里可把for循环去掉。

运行结果如下:

16       看到这里,大家可能会有疑问,明明优化前的代码看起来更简洁更简单,反而使用工厂模式后代码量多了,获取对象的方式看起来也更复杂,感觉好像很没必要,这里大家不需要想太多,只需要记住,在实际开发中,我们力求低耦合,程序之间的耦合不可能完全消除的,使用Ioc的方式是为了减少程序之间的耦合,降低对象之间的依赖程度

使用 spring 的 IoC 解决程序耦合

       后面,我将会介绍获取IoC容器中bean的两种方式,分别是通过xml文件配置和通过注解的方式。在实际开发中,一般使用注解的方式比较多,下面我就只讲基于注解的IoC配置方式。

基于注解的IoC配置方式

       首先,重新创建一个新的Maven项目,然后配置pom.xml中的依赖,Spring依赖的配置如下,使用注解需要用到的jar包是aop包,如下图所示。

17
       注意:在配置pom.xml文件的时候,idea的右下角可能会弹出下图的提示,此时只需点击红色箭头指向的地方即可自动引入maven中配置的依赖jar包。

18
       该项目使用的例子与上面的项目的例子相似,这里只是使用了Spring IoC中的注解来获取bean对象而已。新项目目录结构如下图:

19
下面开始介绍各个注解:

首先,先上项目的代码:

这里表现层的com.nick.ui.Client.java的代码如下:

package com.nick.ui;

import com.nick.service.IAccountService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * 模拟一个表现层,用于调用业务层
 *
 */
public class Client {

    public static void main(String[] args) {

        //获取容器对象
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //
        IAccountService as = (IAccountService) ac.getBean("accountServiceImpl");

        System.out.println("对象为:"+as);
        //先不使用对象中的方法
        //as.saveAccount();

    }
}

20
持久层的com.nick.dao.IAccountDao.java代码如下:

package com.nick.dao;

public interface IAccountDao {

    void saveAccount();
}

21
持久层的实现类com.nick.dao.AccountDaoImpl.java代码如下:

package com.nick.dao.impl;

import com.nick.dao.IAccountDao;
import org.springframework.stereotype.Component;

@Component
public class AccountDaoImpl implements IAccountDao{

    public void saveAccount(){
        System.out.println("已经保存了账户");
    }
}

22
业务层的com.nick.service.IAccountService.java代码如下:

package com.nick.service;

public interface IAccountService {

    void saveAccount();
}

23
业务层的实现类com.nick.service.AccountServiceImpl.java代码如下:

package com.nick.service.impl;

import com.nick.dao.IAccountDao;
import com.nick.service.IAccountService;
import org.springframework.stereotype.Component;

@Component
public class AccountServiceImpl implements IAccountService {

    private IAccountDao accountDao;
    public AccountServiceImpl(){
        System.out.println("成功创建了对象");
    }

    public void saveAccount(){
        accountDao.saveAccount();
    }

}

24
其中,Spring的主配置文件bean.xml的配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <!--告知spring在创建容器时要扫描的包,配置所需要的标签不是在beans的约束中,而是一个名称为
    context名称空间和约束中-->
    <context:component-scan base-package="com.nick"></context:component-scan>

</beans>

       注:该配置文件的名字不是固定的,但有时候很多人习惯使用applicationContext.xml这个名字。

用于创建对象的注解

       它们的作用就和在XML配置文件中编写一个<bean>标签实现的功能是一样的。

  • @Component:

    作用:用于把当前类对象存入spring容器中。

    属性:

    value:用于指定bean的id。当我们不写时,它的默认值是当前类名,且首字母改小写。

  • @Controller:一般用在表现层

  • @Service:一般用在业务层

  • @Repository:一般用在持久层

例如,使用@Repository注解:

25
修改代码,执行结果如下:

26
       例如,在com.nick.service.AccountServiceImpl.java改为使用@Service注解:

27
       以上三个注解他们的作用和属性与@Component是一模一样的,它们三个是spring框架为我们提供明确的三层使用的注解,使我们的三层对象更加清晰,所以我们可以代码中的@component注解改成对应层的注解@Controller、@Service和@Repository。

       另外需要注意的是,使用@Component注解后,必须在bean.xml配置文件中添加以下配置:

    <context:component-scan base-package="com.nick"></context:component-scan>

       而且,在基于注解的IoC配置方式中的配置文件,其中的约束跟基于xml的IoC配置方式使用的约束是不一样的:

基于xml的IoC配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200724093940155.png?x-oss-proc![在这里插入图片描述](https://img-blog.csdnimg.cn/20200724094017397.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzNTQ2NTk2,size_16,color_FFFFFF,t_70)

ess=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzNTQ2NTk2,size_16,color_FFFFFF,t_70)

</beans>

基于注解的IoC配置

<?xml version="1.0" encoding="UTF-8"?>
<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"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

</beans>

上述代码运行结果如下:

28
       在上述代码中,我把Client.java中调用业务层对象的方法那一行的代码注释掉了,下面我把该注释去掉,看看结果会是怎么样:

29
       代码运行过程中,报错信息指向了业务层的实现类com.nick.service.AccountServiceImpl.java的18行,这里报错说是空指针异常:

30
       由上图代码可见,我们在第12行只定义了对象accountDao,但是这里并没有对该对象初始化,没有为它赋值。这时候我们需要用到一个新的注解@Autowired。

使用@Autowired注解后的com.nick.service.AccountServiceImpl.java代码如下:

31
然后重新运行,这时候成功调用了业务层对象的方法,结果如下:

32
用于注入数据的注解

       他们的作用就和在xml配置文件中的bean标签中写一个<property>标签的作用是一样的。

  • @Autowired

作用:
       自动按照类型注入。只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功。

如果ioc容器中没有任何bean的类型和要注入的变量类型匹配,则报错。
如果Ioc容器中有多个类型匹配时:见下面的讲解。

出现位置:
       可以是变量上,也可以是方法上

若容器中有两个及其以上的bean跟要注入的对象类型一致的情况

       为了说明,我在com.nick.dao.impl目录下新建一个AccountDaoImpl2.java文件:

33
同时,也修改原来的业务层的实现类中代码:

34
修改Client.java文件,然后运行结果如下:

35
​ 从上图的报错信息可知,在使用了注解@Autowired进行自动注入的时候,由于同时存在两个类型均为IAccountDao的bean对象,所以不知道注入容器中的哪个bean对象。

​ 这时候,我们通过修改需要被注入的对象变量的名称为注解@Repository中的value值时,发现终于可以注入成功,分别修改两次,然后运行结果如下:

36
37
由上述实验可总结:

​ 自动注入时先匹配类型,若容器中有两个及其以上的bean跟要注入的对象类型一致的话,此时则根据要注入的对象变量的名字跟容器中的bean的id(即用于创建对象的那几个注解中的value值)比较,若名字和id相同则完成注入。

38

  • @Qualifier

作用:在按照类中注入的基础之上再按照名称注入。它在给类成员注入时不能单独使用(必须和@Autowired一起使用),但是在给方法参数注入时可以。

属性:
value:用于指定注入bean的id(即用于创建对象的那几个注解中的value值)。

使用@Qualifier注解后的效果如下:
39
       此时,即使spring容器中有相同类型的bean,也不会再根据需要被注入对象变量的名字去匹配bean对象了,而是根据@Qualifier中指定的value值去注入相应id的bean对象。

  • @Resource

作用:
       通过指定bean的id值,直接根据bean的id注入。它可以独立使用,而不必跟@Autowired一起使用。

属性:
       name:用于指定bean的id。

40
       由上可见,当处理有多个类型相同的bean的对象的时候,使用了@Resource注解就不必同时使用@Autowired和@Qualifier了。

       注:以上三个注入都只能注入其他bean类型的数据,而基本类型和String类型无法使用上述注解实现。

另外,集合类型的注入只能通过XML来实现。

  • @Value

作用:
       用于注入基本类型和String类型的数据。

属性:
       value:用于指定数据的值。它可以使用spring中SpEL(也就是spring的el表达式)

SpEL的写法:${表达式}

       好了,到这里我大概介绍了Spring IoC框架中的部分注解,Spring框架中还有很多的其他注解,其具体的用法大家在审计代码的时候遇到了再到网上查询吧。

       此时,即使spring容器中有相同类型的bean,也不会再根据需要被注入对象变量的名字去匹配bean对象了,而是根据@Qualifier中指定的value值去注入相应id的bean对象。

  • @Resource

作用:
       通过指定bean的id值,直接根据bean的id注入。它可以独立使用,而不必跟@Autowired一起使用。

属性:
       name:用于指定bean的id。
41
       由上可见,当处理有多个类型相同的bean的对象的时候,使用了@Resource注解就不必同时使用@Autowired和@Qualifier了。

       注:以上三个注入都只能注入其他bean类型的数据,而基本类型和String类型无法使用上述注解实现。

另外,集合类型的注入只能通过XML来实现。

  • @Value

作用:
       用于注入基本类型和String类型的数据

属性:
       value:用于指定数据的值。它可以使用spring中SpEL(也就是spring的el表达式)

SpEL的写法:${表达式}

       好了,到这里我大概介绍了Spring IoC框架中的部分注解,Spring框架中还有很多的其他注解,其具体的用法大家在审计代码的时候遇到了再到网上查询吧。

AOP(面向切面编程)

       关于AOP的内容我在这里就不讲述了,想了解的可以自己在网上学习。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java代码审计入门基础之Spring 的相关文章

随机推荐

  • [系统

    系统环境说明 系统 Deepin V20 平台 amd64 参考文献 asdf java asdf document asdf vm安装 见多版本管理命令行工具asdf vm安装及使用 asdf vm安装java 签出asdf java代码
  • MATLAB下载安装教程(非常详细)从零基础入门到精通,看完这一篇就够了

    MATLAB下载 安装教程 说明 本文章提供的是MATLAB 2018b的下载及安装方式 下载2个DVD镜像然后对其进行安装操作 本人亲测有效 下载 提供的是百度网盘的下载 暂时没有找到其他途径的下载方式 后续若有其他下载方式在更新提供给各
  • arduino智能跟随小车

    前言 目前 机器人 已经成为生活中频繁出现的词汇 本课设所做的智能跟随机器人 属于反馈型移动机器人 反馈型移动机器人 可以简单定义为一种对外界信号进行处理反馈最终实现的方式将感知和动作连接在一起的可自移动设备 它必须具有在一个位置的环物独立
  • Unity出模型动画的序列帧(特效序列帧)

    模型动画的序列帧 我这里是通过Recorder和Timeline的结合操作 输出带有透明通道是序列帧图片 流程图 mermaid svg ig9s3Ys4ZkUqP2IW font family trebuchet ms verdana a
  • AI时代,三步走成为超级个体!

    什么是超级个体 超级个体要同时满足 专业技能 商业变现 脱离依附三大要素 跃迁 言简意赅一点就是具备脱离平台也能生存能力的人 通俗的说就是一个人也能赚钱的人 像职场达人 副业高手 创业者等 为什么在AI时代每个人都必须成为超级个体 1 因为
  • OpenCV-Python教程(5、初级滤波内容)

    本篇文章介绍如何用OpenCV Python来实现初级滤波功能 提示 转载请详细注明原作者及出处 谢谢 本文介绍使用OpenCV Python实现基本的滤波处理 本文不介绍滤波处理的详细概念 所以读者需要预先对其有一定的了解 简介 过滤是信
  • 数学,金融,计算机优秀博客

    数学 金融 计算机优秀博客 网址 http zhiqiang org blog link 阅微堂 阅微堂认同的优秀博客标准为 有更新 原创 一年以前的文章还有价值 数学 谢松 木遥 Han Yan 统计之都 张驰原 李淼 科学松鼠会 卢昌海
  • 理解图像卷积操作的意义

    参考 http blog csdn net chaipp0607 article details 72236892 locationNum 9 fps 1 理解图像卷积操作的意义 标签 图像处理图像卷积 2017 05 16 22 40 4
  • 压缩感知应用于核磁共振图像

    1 对于K空间的理解 https www douruixin com 2018 11 15 20181115
  • Flex builder的调试时需要flash player debug版本

    在adobe的网站上 有几种debug版本 http www adobe com support flashplayer downloads html 第一个是IE的插件 Download the Windows Flash Player
  • Android最常用命令-Adb

    adb 挂载及过滤 获得root权限后 获得写权限 adb disable verity 第一次 adb root 挂载 adb remount 重新挂载系统分区 使系统分区重新可写 只针对system分区 adb shell mount
  • Hadoop 启动,提示:JAVA_HOME is incorrectly set with hadoop

    编辑C hadoop hadoop 2 8 2 etc hadoop 目录下的hadoop env cmd文件时 修改JDK的安装目录 错误编辑文件 set JAVA HOME C Program Files Java jdk1 8 0 1
  • Java语法-向下转型、instanceof

    向上转型 左父右子 向下转型 子类类型名 变量名 子类类型名 之前的变量名 向下转型一定要进行 instanceof 判断 public class Dog extends Pet public Dog String name 继承必须调用
  • 【51单片机】串口通信/LED点阵

    需要云服务器等云产品来学习Linux的同学可以移步 gt 腾讯云 lt gt 阿里云 lt gt 华为云 lt 官网 轻量型云服务器低至112元 年 新用户首次下单享超低折扣 目录 一 串口介绍
  • Nacos 在 Apache APISIX API 网关中的服务发现实践

    Apache APISIX 是一个动态 实时 高性能的 API 网关 提供负载均衡 动态上游 灰度发布 服务熔断 身份认证 可观测性等丰富的流量管理功能 它不仅拥有众多实用的插件 而且支持插件动态变更和热插拔 同时在使用服务发现组件时 不仅
  • 指针与引用的区别,指针与引用的底层实现是否一样?

    指针与引用 在More Effective C 的条款一有详细讲述 条款一 指针与引用的区别 指针与引用看上去完全不同 指针用操作符 和 gt 引用使用操作符 但是它们似乎有相同的功能 指针与引用都是让你间接引用其他对象 你如何决定在什么时
  • 情感分析的分类,情感分析模型有哪些,情感分析的应用场景,情感分析的发展趋势

    1 情感分析的分类 1 基于情感极性的分类 将文本的情感分为正向 负向和中性三类 2 基于情感维度的分类 将文本的情感分为喜欢 愤怒 悲伤 惊喜等多个情感维度 2 情感分析模型 1 基于词典的情感分析模型 使用情感词典 根据文本中的情感词出
  • 深入理解java7解压密码_7.2.2 通过GzipResourceResolver压缩静态资源 -《SSM深入解析与项目实战》...

    7 2 2 通过GzipResourceResolver压缩静态资源 前面使用到了缓存 但是第一次加载文件是不会走缓存的 为了降低传输的数据量 这个情况下就需要进行资源的压缩了 现代浏览器 基本都是支持gzip协议的 支持客户端解压处理 主
  • Linux内核软中断softirq和小任务tasklet分析(六)

    1 概述 硬件的中断处理函数处于中断上半部分 在CPU关中断的状态下执行 中断线程 软中断 softirq 及小任务 tasklet 属于中断的下半部分 bottom half 在CPU开中断的状态下执行 小任务基于软中断实现 实质是对软中
  • Java代码审计入门基础之Spring

    Java代码审计入门基础之Spring 目前大部分的 Java 互联网项目 都是用 SSM Spring MVC Spring MyBatis 框架组合搭建的 当然现在SpringBoot也很流行 但是学习Java代码审计 先从SSM框架开