一些零碎知识点

MD5

MD5信息摘要算法(MD5 Message-Digest Algorithm),一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。

前后端分离开发

以 Java Web 项目为例,在传统的开发模式中,前端代码(Html、js、css)写在 JSP 中,甚至 JSP 中嵌入 Java 代码。当用户访问网站时,页面数据也就是 Html 文档,由 Servlet 容器将 jsp 编译成 Servlet,然后将 jsp 中的 html,css,js 代码输出到浏览器,这个过程需要经过很多步骤,才能响应用户的请求。这个过程非常繁琐,效率低下,直接造成了页面响应速度慢的效果。从项目维护的角度上,传统的开发模式,前端代码和后端代码耦合在一起,导致代码混乱不堪,极大的降低了项目的可维护性,增加了维护成本。从开发角度来看,研发人员在开发过程中,不仅要设计后端架构还要兼顾前端展示,导致开发效率低下,延长开发周期。

在前后端分离的开发模式中,后端返回前端所需的数据,前端负责渲染 HTML 页面,后端不再控制前端的效果,用户看到什么样的效果,从后端请求的数据如何加载到前端中,都由前端自己决定,后端仅仅需要提供一套逻辑对外提供数据即可,并且前端与后端的耦合度相对较低,在这种模式中,我们通常将后端开发的每个视图都成为一个接口或者API,前端通过访问接口来对数据进行增删改查。总结一句话,后台负责提供数据,前端负责数据展示,职责分离,分工明确。

session 机制

服务器状态管理技术,将用户状态信息保存在服务器端,是 SUN 公司定义的一个接口。

session 机制是用来解决 hTTP 无状态的问题。本质上是在服务器端开辟一个空间(容器),这个容器中有一个唯一标识送给客户端,客户端每次请求都会携带这个标识。

类似于一个散列表文件(一个 Map),里面的 key 存储的是用户的 session 标识 sessionId 。

用户向服务器发起请求时,服务器检测是否带有 sessionId,若没有则创建容器,再把这个 sessionId 送回给客户端;若有则去检测容器并给客户端使用。

同一个客户端(同一个浏览器)在一段时间内 session 标识是不变的。

20220311104413.png

20220311104813.png

session 过期

客户端与服务器端连接时,长时间没有动作或超过程序员设置的时间戳,此时会话 Session 会被清空或回收,从服务器内存中清除,之后该 Session 就失效了,Tomcat中默认失效时间是 20 分钟。

如何获取 session

Request.getSession() ,该方法有两个含义:1.服务器发现是一个新的请求则开辟一个新的 session;2.若请求中有 session 标识,判断 session 标识是否失效,若失效则返回一个新的 session,否则返回原来的 session。

如何销毁 session

  1. 主动销毁:session.invalidate();
  2. 设置生命时间周期:设置无操作间隔时间,超时则销毁session.setMaxInactiveInterval(1000*5*60);
  3. 当用户关闭浏览器时,sessionId 的信息就会丢失,虽然服务 session 还在,但是无法访问到 session 中的数据。

session 有哪些主要的方法 API

  1. setAttribute(String key,Object value):采用 key / value 存储模式
  2. Object getAttribute(String key):获取值
  3. removeAttribute(String key):移除

Cookie 机制

在 Session 出现之前基本上所有网站都采用 Cookie 来跟踪会话(一个用户的所有请求操作都属于同一个会话)。因此,Cookie 也是解决 HTTP 协议无状态的问题(一旦数据交换完毕,客户端与服务器端的连接就会关闭,再次交换数据需要建立新的连接,也就是无法跟踪会话)。

Cookie 实际上就是一小段的文本信息,我们可以用加密来解决它的信息不安全问题。Cookie 对象使用 key-value 的形式保存用户状态,一个 Cookie 对象保存一个属性对,一个 request 或 response 同时使用多个 Cookie。

Cookie 生命周期案例:可以永久保存(永久登录)。方法是把登录信息如账号、密码等保存在 Cookie 中,并控制 Cookie 的有效期,下次访问时再验证 Cookie 中的登录信息即可。缺点是与数据库相比,不安全。

transient 变量

只要一个对象实现了 Serilizable 接口,该对象就能被序列化。

对象的某些属性需要序列化,而有些属性不需要被序列化,例如,账号密码不想被序列化,这时候就可以在变量前面加上 transient 关键字。这个字段的周期仅存在于调用者内存中而不会被写到磁盘里持久化。

总结:一旦被 transient 声明的变量, 将不是对象持久化的一部分,该变量内容在序列化后无法获得访问。

transient 只能修饰变量,不能修饰方法和类。

注意:本地变量是不能被 transient 修饰的,如果变量是用户自定义的变量,则该类需要实现 serializable 接口。被 transient 的变量不能被序列化,一个静态变量不管是否被 transient 修饰,均不能被序列化。

Java 程序种类

  1. Application:Java 应用程序,是可以由 Java 解释器直接运行的程序。

  2. Applet:Java 小应用程序,是可随网页下载到客户端由浏览器解释执行的 Java 程序。

    Appletviewer 是运行 applet 的,applet 不用 main 方法,继承 applet 类即可。

  3. Servlet:Java 服务器端小程序,由 Web 服务器(容器)中配置运行的 Java 程序。

拦截器

Spring Web MVC 的处理器拦截器类似于 Servlet 开发中的过滤器,用于对处理器进行预处理和后处理。

实现:实现 HandlerInterceptor 接口

public class HandlerInterceptor1 implements HandlerInterceptor{
    /**
    *Controller执行前会调用此方法
    *返回true表示继续执行,否则终止执行
    *可以用于登录校验、权限拦截
    */
    @Override
    public boolean preHandle(HttpServletRequest request,HttpServletResponse response,Object handler) throws Exception{
        return false;
    }
    
    /**
    *Controller执行后,但未返回视图前调用此方法
    *可以在返回用户前对模型数据进行加工,比如加入公共信息以便页面显示
    */
    @Override
    public void postHandle(HttpServletRequest request,HttpServletResponse response,Object handler,ModelAndView modleAndView) throws Exception{
        
    }
    
    /**
    *Controller执行后且视图返回后,调用此方法
    *可得到执行Controller时的异常信息、记录操作日志、资源清理等
    */
    public void afterComletion(HttpServletRequest request,HttpServletResponse response,Object handler,Exception e) throws Exception{
        
    }
}

配置

  1. 针对某种 mapping 配置拦截器

    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
    	<property name="interceptors">
        	<list>
            	<ref bean="handlerInterceptor1"/>
                <ref bean="handlerInterceptor2"/>
            </list>
        </property>
    </bean>
    <bean id="handlerInterceptor1" class="springmvc.interceptor.HandlerInterceptor1"/>
    <bean id="handlerInterceptor2" class="springmvc.interceptor.HandlerInterceptor2"/>
    
  2. 针对所有 mapping 配置拦截器

    <mvc:interceptors>
    	<mvc:interceptor>
        	<mvc:mapping path="/**"/>
            <bean class="com.test.springmvc.filter.HandlerInterception1"></bean>
        </mvc:interceptor>
        <mvc:interception>
        	<mvc:mapping path="/**"/>
            <bean class="com.test.springmvc.filter.HandlerInterception2"></bean>
        </mvc:interception>
    </mvc:interceptors>
    

文件上传下载

配置在 beans.xml 中:

<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> 
      	<property name="maxUploadSize" value="102400000"/>    <!--设置文件上传最大值字节-->
</bean>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>文件上传</title>
</head>
<body>
    <form action="file/upload.do" method="post" enctype="multipart/form-data">
     	上传文件名:<input type="file" name="file"/>
       	<input type="submit" value="上传" />
    </form>
</body>
</html>

#{} VS ${}

#{}:不用写引号。表示一个占位符,可以实现 prepareStatement 向占位符注入值,自动进行 Java 类型和 JDBC 类型转换,有效防止 SQL 注入。可以接收简单类型值或 poji 属性值,若 parameterType 传输单个简单类型值,#{}括号中可以是 value 或其他名称。

${}:要写引号,‘ ${} ’。表示拼接字符串,可以将 parameterType 传入的内容拼接在 SQL 中且不进行 JDBC 类型转换。可以接收简单类型值或 poji 属性值,若 parameterType 传输单个简单类型值,${}括号中只能是 value 。

Spring 循环依赖怎么解决

是什么?

假如我们有两个 bean,A 和 B,代码如下:

@Bean
public class A{
    @Autowire
    private B b;
}

@Bean
public class B{
    @Autowire
    private A a;
}

有上述代码可知:我们需要在 A 中注入 B,在 B 中注入 A。

那么 Spring 在创建 A 的时候可能会出现这种现象:创建 A 实例后,在依赖注入时(给 Bean 的属性赋值时)需要 B ,然后就去创建 B 实例,这时发现又需要依赖注入 A ,这样就导致了循环依赖。

20220311180653.png

怎么办?

  • 宏观思想:Spring 创建的过程可以简单概括为实例化 –> 依赖注入 –> 初始化。而 Spring 解决循环依赖的方法就是在实例化之后,注入依赖之前,将实例化对象放到缓存中进行提前曝光,后面的对象则在实例化前,先到缓存中查找有无对应的实例化对象即可。

    https://i-blog.csdnimg.cn/blog_migrate/f48d55aa1f29ba9ca75e2e2880c1731a.png

    该流程就是解决循环依赖的宏观流程,接下来具体说说细节三级缓存(将 A 放到缓存和从缓存获取 A 的部分)。

  • 实现细节(源码),博主还在学习。

垃圾回收机制

Java 提供了一个系统级的线程,即垃圾回收器线程。用来对每一个分配出去的内存空间进行跟踪,当 JVM 空闲时,自动回收每块可能被回收的内存,GC 是完全自动的,不能被强制执行。程序员最多只能用System.gc() 来建议执行垃圾回收器回收内存,但是具体的回收时间是不可知的。当对象的引用变量被赋值为 null,可能被当成垃圾。

Java 内存溢出

原因

  1. 内存中加载的数据量过于庞大,如一次性从数据库中取出过多的数据;
  2. 集合类中有指向对象的引用,使用完后未清空,使 JVM 不能回收;
  3. 代码存在死循环或循环产生过多重复的对象实体;
  4. JVM 的启动参数设定过小;

解决

  1. 修改 JVM 的启动参数,直接增加内存(使用 -Xms 表示初始堆大小、-Xmx 表示最大堆大小);
  2. 检查错误日志,看看 OutOfMemory 错误之前是否发生其他异常
  3. 排查以上四种错误原因
  4. 使用内存查看工具动态查看内存使用情况

Java 的四种引用

  1. 强引用:用的最多

    声明格式:String str=“abc”;

    只要某个对象与强引用关联,那么在内存不足的情况下,宁愿抛出 OutOfMemoryError 错误,也不会回收此类对象;

    把 str 置为 null。

  2. 软引用

    SoftReference<String> str = new SoftReference<String>(new String("abc"));
    

    只要某个对象与软引用关联,JVM 只会在内存不足的情况下回收该对象。利用这个特性,软引用可以用来做缓存

    软引用适合做缓存,在内存足够时,直接通过软引用取值,无需从真实来源中查询数据,可以显著提升网站性能。当内存不足时,让 JVM 进行内存回收,从而删除缓存,这时候只能从真实来源查询数据。

  3. 弱引用

    WeakReference<String> str = new WeakReference<String>(new String("abc"));
    

    如果某个对象与弱引用关联,那么当 JVM 在进行垃圾回收时,无论内存是否充足,都会回收此类对象。

  4. 虚引用

    ReferenceQueue<String> queue = new ReferenceQueue<>();
    PhantomReference<String> str = new PhantomReference<String>("abc", queue);
    

    若某个对象与虚引用关联,那么在任何时候都可能被 JVM 回收掉。虚引用不能单独使用,必须配合引用队列一起使用。

String、StringBuffer、StringBuilder

String 类:静态的类,先于对象存在,在程序初始化时就会加载。值是不可变的,改变的是赋值地址而不是值,所以每次对 String 的操作都会生成新的 String 对象,效率低,浪费内存空间。

对于经常要改变值的字符串应该使用 StringBuffer、StringBuilder。

StringBuffer 类 VS StringBuilder 类:功能基本相似,主要区别是 StringBuffer 类的方法是多线程的、安全的,StringBuilder 不是线程安全的,所以效率相对高。

MySQL

  1. 内连接(inner join):只取出两张表中匹配到的数据

  2. 外连接(outer join):取出连接表中匹配到的数据,匹配不到的会保留,值为 null

    • 左外连接(left outer join):以左边的表为主表
    • 右外连接(right outer join):以右边的表为主表

    通俗点讲:先取出主表所有的数据,然后关联到外表去找有没有符合关联条件的数据,如果有就正常显示,没有就显示 null。

Java 技术栈

  • 语言相关:核心开发语言 Java 、数据库查询语言 SQL

    语法必须熟悉,知道一些语法错误并且修正。

    API

  • 开发工具:Eclipse、Idea、Java 工程构建 Maven、源码仓库管理 git

    熟悉常用工程工具:Jenkins, Maven/Sbt/Npm/Ant,Git/SVN,Eclipse/IntelliJ,等等

  • 开发框架:Spring 框架(Spring MVC 、Spring Boot )、ORM 框架(MyBatis)、测试框架( Junit 、TestNG、Mockito)

  • 数据库 / 中间件:关系型数据库(MySQL、Oracle)、key - value 存储系统(Redis、memcache)、分布式对象存储系统(网易 nos 、阿里云 oos 等)、消息队列(rabbitmq、kafka)、Java Web 容器(tomcat、jetty等)、HTTP 和反向代理服务器(nginx)

  • 其他:权限模型、安全相关、日志相关(Log4J、LogBack、SLF4J 等)、异常相关( Java 异常处理)、Restful(接口规范)、多线程编程、设计原则和设计模式、分布式系统设计相关

API(Application Programming Interface,应用程序编程接口):

java.lang 包、java.util 包、java.io 包、java.math 包、java.net 包、java.text 包、Java.security 包、JDBC 包下的类要熟悉使用

Git / SVN

  1. Git :是一种分布式版本管理系统,作用是对文件进行版本管理,在团队协作中很好地整合代码、更新版本、回退版本。一个系统的开发可以分成多个模块,每个模块一个小团队开发,小团队开发完通过 git 命令,便可以轻松合并。

    有三个特点:云的概念(代码可以存在云端 git 网站上);社交属性(代码开源);团队合作(支持多人多用户协作,并且可以标示别人的修改和备注,最后还可以合并)

    GitHub :是基于 git 的代码托管站,类似于一个程序员保存源代码的网盘,是一个开源代码库,支持在线修改源代码。

  2. SVN :同 Git

跨域问题

概念

一个 url 由三部分组成:协议、域名、端口,当三者之间任意一个与当前页面的 url 不同,即为跨域(当前页面 url 和被请求页面 url 不同)。

产生原因

由于浏览器的同源策略限制。同源策略是一种约定,是浏览器核心也是最基本的安全功能,它会阻止一个域的 JS 脚本和另外一个域的内容进行交互,如果没有同源策略,浏览器会受到 XSS、CSFR 等攻击。

XSS 攻击:跨站脚本攻击。指利用网页开发时留下的漏洞,通过巧妙的方法注入恶意代码到网页,使客户端用户加载并执行攻击者恶意制造的网页程序。这些恶意的网页程序通常是 JS,攻击成功后,攻击者可能得到更高的权限、私密网页内容、会话和 cookie 等内容。

CSFR:跨站请求伪造。攻击者通过一些技术手段欺骗用户浏览器去访问一个曾经认证过的网站,并运行一些操作。

解决方法

  • 在 Controller 方法上加上注解 @CrossOrigin(origins=“*”,maxAge=3600),编写一个配置类

  • 利用 HttpComponent 技术:先在前端 JS 代码块 xmlhttp.open 的本系统 url 后面加上请求访问的另外一个 web系统的 url ,再在后端本系统 url 里加上 HttpComponentUtil.get()。

    IMG_0316_20220306-223706_.PNG

  • 通过 jsonp :把静态资源分离到另一台独立域名的服务器上,HTML 页面中再通过相应的标签从不同域名下加载静态资源,而被浏览器允许。

项目问题

问题1:后端向前端传 JSON 格式数据,前端无法接收(显示 undefined)

原因:单词拼错,前端找错很麻烦,注意变量名的对应。

问题2:网页 500,Invalid bound statement (not found):业务层调用的持久层的方法

原因:Dao 层(持久层)中,MyBatis 的 mapper 类与 mapper.xml 中的绑定映射出错,mapper 中的方法名与 xml 文件中的 id 内容要完全一致。名称对应起来就没有问题了。

问题3:后端生成得到的 ID ,没法以 JSON 格式数据(name:“value”)传到前端

原因:controller 控制层中的方法返回值写的是 String,即使用了注解 @Responsebody 也不能向前端传递 JSON 格式的数据。所以我用了 VO,把后端得到的 ID 放进 VO 的 message ,这样能够以 JSON 格式数据传回前端,前端再调用 message。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值