
java架构
猿灯塔
这个作者很懒,什么都没留下…
展开
-
聊聊Dubbo(二):简单入门
准备以上两点准备,不是本文重点,不做详细介绍,安装比较简单,自行查阅相关资料安装学习。1 服务端1.2 接口定义1.创建Maven模块:msa-demo-apimsa-demo-api2.msa-demo-api:配置pom.xml以上POM配置,从dubbox-2.8.4开始,所有依赖库的使用方式将和dubbo原来的一样:即如果要使用REST、Kyro、FST、Jackson等功能,需要用户自行手工添加相关的依赖。3.定义接口:UserS...原创 2020-07-16 17:36:04 · 300 阅读 · 0 评论 -
java 后端博客系统文章系统——No6
工具IDE为idea16 JDK环境为1.8 gradle构建,版本:2.14.1 Mysql版本为5.5.27 Tomcat版本为7.0.52 流程图绘制(xmind) 建模分析软件PowerDesigner16.5 数据库工具MySQLWorkBench,版本:6.3.7build本期目标mybatis一些简单的进阶使用 日志功能的记录 文章发布 草稿保存上期我们通过对数据库的分析,仔仔细细的探索了一遍WordPress程序运行时候文章提交和草稿提交...原创 2020-07-15 17:42:48 · 257 阅读 · 0 评论 -
聊聊Dubbo(一):为何选择
1. 前言随着现在互联网行业的发展,越来越多的框架、中间件、容器等开源技术不断地涌现,更好地来服务于业务,实现业务并解决问题。然而面对众多的技术选择,我们要如何甄别出适合自己团队业务的技术呢?对于人来说,鞋子过大,可能影响奔跑的速度,鞋子过小,可能影响身体的成长。技术对于业务也是如此的关系。所以,相对于技术的学习、搭建、使用、运维等技能,我们对技术的甄别选择更是重中之重。那么本文要讲的Dubbox框架,又是如何在众多的服务框架中脱颖而出,被团队选中践行服务之路?2. 服务2.1 为什么.原创 2020-07-15 16:13:48 · 307 阅读 · 0 评论 -
java 后端博客系统文章系统——No5
工具IDE为idea16 JDK环境为1.8 gradle构建,版本:2.14.1 Mysql版本为5.5.27 Tomcat版本为7.0.52 流程图绘制(xmind) 建模分析软件PowerDesigner16.5 数据库工具MySQLWorkBench,版本:6.3.7build本期目标根据WordPress的工作进行程序设计分析 完成文章保存和草稿保存相关程序流程分析首先我们打开WordPress登录到控制台后随便保存草稿和文章,然后导出数据库中po...原创 2020-07-14 19:46:22 · 275 阅读 · 0 评论 -
啃碎并发(11):内存模型之重排序
前言在很多情况下,访问一个程序变量(对象实例字段,类静态字段和数组元素)可能会使用不同的顺序执行,而不是程序语义所指定的顺序执行。具体几种情况,如下:例如,如果一个线程写入值到字段a,然后写入值到字段b,而且b的值不依赖于a的值,那么,处理器就能够自由的调整它们的执行顺序,而且缓冲区能够在a之前刷新b的值到主内存。有许多潜在的重排序的来源,例如编译器,JIT以及缓冲区。所以,从Java源码变成可以被机器(或虚拟机)识别执行的程序,至少要经过编译期和运行期。在这两个期间,重排序分为..原创 2020-07-14 14:20:33 · 283 阅读 · 0 评论 -
java 后端博客系统文章系统——No4
这一期我们的目标是完成后端博客系统的博客发布功能。按照我们前面的设定,我们的后端博客系统需要完成最简单的博文发布,我们也得有后台管理界面,同时需要将用户权限这些都附带上,但是由于时间关系,我们后端默认账户就是管理员吧,毕竟这一期的重点是实现博客的发布。我们需要发布博文,那么后端必不可少的是登录和发布系统,至于其他的我们可以先缓一缓,毕竟我也没想好后端页面怎么设计,嘿嘿。前面我看了一下,确实是完美兼容WordPress还是有很多难度,毕竟很多技术细节我们并不知道,不过,至少说...原创 2020-07-13 19:47:55 · 334 阅读 · 0 评论 -
啃碎并发(10):内存模型之内部原理
前言如上一篇文章所述,Java内存模型规范了Java虚拟机与计算机内存是如何协同工作的。Java虚拟机是一个完整计算机的模型,因此,这个模型自然会包含一个内存模型—又称为Java内存模型。如果你想设计表现良好的并发程序,理解Java内存模型是非常重要的。Java内存模型规定了如何和何时可以看到由其他线程修改过后的共享变量的值,以及在必须时如何同步的访问共享变量。1 Java内存模型我们先来看看Java 线程运行内存示意图,如下图所示: ...原创 2020-07-13 15:51:37 · 241 阅读 · 0 评论 -
java 后端博客系统文章系统——No3
工具IDE为idea16 JDK环境为1.8 gradle构建,版本:2.14.1 Mysql版本为5.5.27 Tomcat版本为7.0.52 流程图绘制(xmind) 建模分析软件PowerDesigner16.5 数据库工具MySQLWorkBench,版本:6.3.7build本期目标1.文章阅读前端页面全部完成2.根据页面框架进行解耦 页面附属信息 文章信息文章系统前端页面文章系统作为我们博客系统中重要的一环,...原创 2020-07-11 18:11:35 · 239 阅读 · 0 评论 -
啃碎并发(九):内存模型之基础概述
前言在并发编程中,需要解决两个关键问题:线程之间如何通信;线程之间如何同步;线程通信是指线程之间以何种机制来交换信息。在命令式编程中,线程之间的通信机制有两种:共享内存和消息传递。在共享内存的并发模型里,线程之间共享程序的公共状态,线程之间通过写-读内存中的公共状态来隐式进行通信。在消息传递的并发模型里,线程之间没有公共状态,线程之间必须通过明确的发送消息来显式进行通信。线程同步是指程序用于控制不同线程之间操作发生相对顺序的机制。在共享内存的并发模型里,..原创 2020-07-11 15:03:27 · 202 阅读 · 0 评论 -
啃碎并发(八):深入分析wait¬ify原理
前言上一节讲了Synchronized关键词的原理与优化分析,而配合Synchronized使用的另外两个关键词wait¬ify是本章讲解的重点。最简单的东西,往往包含了最复杂的实现,因为需要为上层的存在提供一个稳定的基础,Object作为Java中所有对象的基类,其存在的价值不言而喻,其中wait¬ify方法的实现多线程协作提供了保证。1 源码今天我们要学习或者说分析的是 Object 类中的 wait¬ify 这两个方法,其实说是两个方法,这两个方法包括他们的重载原创 2020-07-10 20:36:34 · 316 阅读 · 0 评论 -
Java 后端博客系统文章系统——No2
今天这一篇,主要是关于上一张的编码实现。为什么我要单路分离出来?因为做事要分先后,明白道理,执行才能确定无误。wordpress做的文章存储在上次我们已经看过了wordpress的数据库模型(有朋友问我什么是逆向分析,拿着别人的产品逆向推导这就是逆向分析),我们可以很清楚的看到数据库关于文章存储的两张表,它们分别存储了文章的主体信息和文章的其他信息,具体的我们再看看数据库模型:wordpress数据库模型在上面的途中,我们很明显的看到数据库关于文章的存储主要分为两张表:wp_p.原创 2020-07-10 17:05:10 · 484 阅读 · 0 评论 -
啃碎并发(七):深入分析Synchronized原理
前言记得开始学习Java的时候,一遇到多线程情况就使用synchronized,相对于当时的我们来说synchronized是这么的神奇而又强大,那个时候我们赋予它一个名字“同步”,也成为了我们解决多线程情况的百试不爽的良药。但是,随着学习的进行我们知道在JDK1.5之前synchronized是一个重量级锁,相对于j.u.c.Lock,它会显得那么笨重,以至于我们认为它不是那么的高效而慢慢摒弃它。不过,随着Javs SE 1.6对synchronized进行的各种优化后,synchroniz.原创 2020-07-09 17:45:27 · 292 阅读 · 0 评论 -
Java 后端博客系统文章系统——No1
这一章我们主要介绍文章系统。毕竟博客系统的核心就是文章的发布和阅读。闲话不多说,老规矩走起来。工具IDE为idea16JDK环境为1.8gradle构建,版本:2.14.1Mysql版本为5.5.27Tomcat版本为7.0.52流程图绘制(xmind)建模分析软件PowerDesigner16.5首先我们需要先了解我们这个文章系统的整个功能模块组合,也就是我们的思维导图,只有这样才能实现整体功能的架设。下面如图所示:其实在上面的系统中,我...原创 2020-07-09 15:52:59 · 521 阅读 · 0 评论 -
啃碎并发(六):Java 线程同步与实现
前言为何要使用Java线程同步?Java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时,将会导致数据不准确,相互之间产生冲突,因此加入同步锁以避免在该线程没有完成操作之前,被其他线程的调用,从而保证了该变量的唯一性和准确性。但其并发编程的根本,就是使线程间进行正确的通信。其中两个比较重要的关键点,如下:Java中提供了很多线程同步操作,比如:synchronized关键字、wait/notifyAll、ReentrantLock、Condition、一些并发包下的工具类、1原创 2020-07-08 17:44:47 · 909 阅读 · 0 评论 -
啃碎并发(五):Java线程安全特性与问题
前言在单线程中不会出现线程安全问题,而在多线程编程中,有可能会出现同时访问同一个共享、可变资源的情况,这种资源可以是:一个变量、一个对象、一个文件等。特别注意两点:简单的说,如果你的代码在单线程下执行和在多线程下执行永远都能获得一样的结果,那么你的代码就是线程安全的。那么,当进行多线程编程时,我们又会面临哪些线程安全的要求呢?又是要如何去解决的呢?1 线程安全特性1.1 原子性跟数据库事务的原子性概念差不多,即一个操作(有可能包含有多个子操作)要么全部执行(生效),要么全部都不执..原创 2020-07-08 15:05:34 · 247 阅读 · 0 评论 -
啃碎并发(四):Java线程Dump分析
1 Thread Dump介绍1.1 什么是Thread DumpThread Dump是非常有用的诊断Java应用问题的工具。每一个Java虚拟机都有及时生成所有线程在某一点状态的thread-dump的能力,虽然各个 Java虚拟机打印的thread dump略有不同,但是大多都提供了当前活动线程的快照,及JVM中所有Java线程的堆栈跟踪信息,堆栈信息一般包含完整的类名及所执行的方法,如果可能的话还有源代码的行数。1.2 Thread Dump特点1.3 Thread Du.原创 2020-07-07 19:51:32 · 324 阅读 · 0 评论 -
啃碎并发(三):Java线程上下文切换
前言在过去单CPU时代,单任务在一个时间点只能执行单一程序。之后发展到多任务阶段,计算机能在同一时间点并行执行多任务或多进程。虽然并不是真正意义上的“同一时间点”,而是多个任务或进程共享一个CPU,并交由操作系统来完成多任务间对CPU的运行切换,以使得每个任务都有机会获得一定的时间片运行。再后来发展到多线程技术,使得在一个程序内部能拥有多个线程并行执行。一个线程的执行可以被认为是一个CPU在执行该程序。当一个程序运行在多线程下,就好像有多个CPU在同时执行该程序。多线程比多任务更加有挑战。多线.原创 2020-07-07 15:32:03 · 268 阅读 · 0 评论 -
啃碎并发(二):Java线程的生命周期
前言当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。在线程的生命周期中,它要经过新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和死亡(Dead)5种状态。尤其是当线程启动以后,它不可能一直"霸占"着CPU独自运行,所以CPU需要在多条线程之间切换,于是线程状态也会多次在运行、阻塞之间切换。线程状态转换关系1 新建(New)状态当程序使用new关键字创建了一个线程之后,该线程就处于新建状态,此时的线程情况如下..原创 2020-07-06 21:20:36 · 221 阅读 · 0 评论 -
啃碎并发(一):Java线程总述与概念
1 前言在JDK5之前,Java多线程以及其性能一直是个软肋,只有synchronized、Thread.sleep()、Object.wait/notify这样有限的方法,而synchronized的效率还特别地低,开销比较大。在JDK5之后,相对于前面版本有了重大改进,不仅在Java语法上有了很多改进,包括:泛型、装箱、for循环、变参等,在多线程上也有了彻底提高,其引进了并发编程大师Doug Lea的java.util.concurrent包(后面简称J.U.C),支持了现代CPU的CAS原语原创 2020-07-06 16:49:35 · 293 阅读 · 0 评论 -
Java架构-Apache POI Excel
相信在项目中,对数据进行动态导出这是一个比较常见的功能。对于数据导出我们可以使用Apache-POI这个框架来帮我来进行Excel的写入与读取。下面就用代码来实现Apache POI写入与读取excel文件。1、Apache POI基本概念下面将简单的描述一下当进行Excel读取与写入的时候要使用到的基本类。HSSF 为前缀的类名表示操作的是Microsoft Excel 2003文件。XSSF 为前缀的类名表示操作的是Microsoft Excel 2007或以后的版本XSSFWork原创 2020-07-04 20:03:25 · 254 阅读 · 0 评论 -
解读 java 并发队列 BlockingQueue
点击添加图片描述(最多60个字)编辑今天呢!灯塔君跟大家讲:解读 java 并发队列 BlockingQueue最近得空,想写篇文章好好说说 java 线程池问题,我相信很多人都一知半解的,包括我自己在仔仔细细看源码之前,也有许多的不解,甚至有些地方我一直都没有理解到位。说到线程池实现,那么就不得不涉及到各种 BlockingQueue 的实现,那么我想就 BlockingQueue 的问题和大家分享分享我了解的一些知识。本文没有像之前分析 AQS 那样一行一行源码分析了,不..原创 2020-07-04 17:20:38 · 390 阅读 · 0 评论 -
猿灯塔:最详细Dubbo相关面试题
1、Dubbo是什么?Dubbo是阿里巴巴开源的基于 Java 的高性能 RPC 分布式服务框架,现已成为 Apache 基金会孵化项目。面试官问你如果这个都不清楚,那下面的就没必要问了。官网:http://dubbo.apache.org2、为什么要用Dubbo?因为是阿里开源项目,国内很多互联网公司都在用,已经经过很多线上考验。内部使用了 Netty、Zookeeper,保证了高性能高可用性。使用 Dubbo 可以将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,可用于提高业务复用原创 2020-07-03 15:49:14 · 192 阅读 · 0 评论 -
猿灯塔:关于Java面试,你应该准备这些知识点
自天子以至于庶人,壹是皆以修身为本 《礼记·大学》马老师说过,员工的离职原因很多,只有两点最真实:钱,没给到位心,受委屈了当然,我是想换个平台,换个方向,想清楚为什么要跳槽,如果真的要跳槽,想要拿到一个理想的offer,除了运气,基本功也要足够的扎实,希望下面的面试经验能给你们能够提供一些帮助。项目经验面试官在一开始会让你进行自我介绍,主要是想让你介绍一下自己做过的一些项目,看看你对这些项目的了解程度,因为很多人简历上写的项目并非都是从头到尾都参与的,有些只是参与并实现了其中的一些模块而已,或是原创 2020-06-30 15:54:43 · 190 阅读 · 0 评论 -
猿灯塔:一份还热乎的蚂蚁金服面经(已拿Offer)!附答案!!
面试原创 2020-06-29 20:50:29 · 481 阅读 · 0 评论 -
JVM源码分析之堆内存的初始化
原创申明:本文由公众号【猿灯塔】原创,转载请说明出处标注“365篇原创计划”第十五篇。今天呢!灯塔君跟大家讲:JVM源码分析之堆内存的初始化堆初始化Java堆的初始化入口位于Universe::initialize_heap方法中,实现如下:其中UseParallelGC、UseG1GC、UseConcMarkSweepGC都可以通过启动参数进行设置,整个初始化过程分成三步:1、初始化GC策略;2、初始化分代生成器;3、初始化Java堆管理器;...原创 2020-05-22 15:26:43 · 394 阅读 · 0 评论 -
JVM源码分析之JVM启动流程
原创申明:本文由公众号【猿灯塔】原创,转载请说明出处标注“365篇原创计划”第十四篇。今天呢!灯塔君跟大家讲:JVM源码分析之JVM启动流程前言:执行Java类的main方法,程序就能运行起来,main方法的背后,虚拟机究竟发生了什么?如果你对这个感兴趣,相信本文会给你一个答案,本文分析的openjdk版本为openjdk-7-fcs-src-b147-27classBootStrap{publicstaticvoidmain(String[]arg...转载 2020-05-21 16:50:18 · 236 阅读 · 0 评论 -
JVM源码分析之synchronized实现
原创申明:本文由公众号【猿灯塔】原创,转载请说明出处标注“365篇原创计划”第十二篇。今天呢!灯塔君跟大家讲:JVM源码分析之synchronized实现java内部锁synchronized的出现,为多线程的并发执行提供了一个稳定的环境,有效的防止多个线程同时执行同一个逻辑,其实这篇文章应该写在JVM源码分析之Object.wait/notify实现机制之前,本文不会讲如何使用synchronized,以HotSpot1.7的虚拟机为例,对synchronized的实现进行...原创 2020-05-20 17:17:41 · 342 阅读 · 0 评论 -
JVM源码分析之Java对象头实现
原创申明:本文由公众号【猿灯塔】原创,转载请说明出处标注“365篇原创计划”第十一篇。今天呢!灯塔君跟大家讲:JVM源码分析之Java对象头实现HotSpot虚拟机中,对象在内存中的布局分为三块区域:对象头、实例数据和对齐填充。对象头对象头包括两部分:Mark Word 和 类型指针。Mark WordMark Word用于存储对象自身的运行时数据,如哈希码(HashCode)、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等等,占用内存大小与虚拟机位长一致。类型指..原创 2020-05-18 16:06:36 · 251 阅读 · 0 评论 -
JVM源码分析之深入分析Object类finalize()方法的实现原理
“365篇原创计划”第十篇。今天呢!灯塔君跟大家讲:深入分析Object类finalize()方法的实现原理finalize如果类中重写了finalize方法,当该类对象被回收时,finalize方法有可能会被触发,下面通过一个例子说明finalize方法对垃圾回收有什么影响。public class FinalizeCase { private static Block holder = null; public static void main(String[] args) .原创 2020-05-15 10:49:08 · 297 阅读 · 0 评论 -
猿灯塔-Phaser 使用介绍
原创申明:本文由公众号【猿灯塔】原创,转载请说明出处标注本文将介绍 java.util.concurrent.Phaser,一个常常被大家忽略的并发工具。它和 CyclicBarrier 以及 CountDownLatch 很像,但是使用上更加的灵活,本文会进行一些对比介绍。和之前的文章不同,本文不写源码分析了,就只是从各个角度介绍下它是怎么用的。本文比较简单,我觉得对于初学者大概需要 20 分钟左右吧。其实我对这个需要多少时间很没概念,有没有读者愿意记录下所花费的时间,在评论区反馈一下。使用示.原创 2020-05-14 11:20:55 · 257 阅读 · 0 评论 -
Netty 源码解析(九): connect 过程和 bind 过程分析
原创申明:本文由公众号【猿灯塔】原创,转载请说明出处标注今天是猿灯塔“365篇原创计划”第九篇。接下来的时间灯塔君持续更新Netty系列一共九篇Netty 源码解析(一): 开始Netty 源码解析(二): Netty 的 ChannelNetty 源码解析(三): Netty的 Future 和 PromiseNetty 源码解析(四): Netty 的 ChannelPipelineNetty 源码解析(五): Netty 的线程池分析Netty 源码解析(六): Channel 的 r原创 2020-05-13 14:47:19 · 745 阅读 · 0 评论 -
Netty 源码解析(八): 回到 Channel 的 register 操作
今天是猿灯塔“365篇原创计划”第八篇。接下来的时间灯塔君持续更新Netty系列一共九篇Netty 源码解析(一): 开始Netty 源码解析(二): Netty 的 ChannelNetty 源码解析(三): Netty 的 Future 和 PromiseNetty 源码解析(四): Netty 的 ChannelPipelineNetty 源码解析(五): Netty 的线程池分析Netty 源码解析(六): Channel 的 register 操作Netty 源码解析(七): N.原创 2020-05-09 11:13:47 · 698 阅读 · 0 评论 -
Netty 源码解析(七): NioEventLoop 工作流程
今天是猿灯塔“365篇原创计划”第六篇。接下来的时间灯塔君持续更新Netty系列一共九篇Netty 源码解析(一): 开始Netty 源码解析(二): Netty 的 ChannelNetty 源码解析(三): Netty 的 Future 和 PromiseNetty 源码解析(四): Netty 的 ChannelPipelineNetty 源码解析(五): Netty 的线程池分...原创 2020-05-08 09:54:53 · 392 阅读 · 0 评论 -
Netty 源码解析(六): Channel 的 register 操作
今天是猿灯塔“365篇原创计划”第六篇。接下来的时间灯塔君持续更新Netty系列一共九篇Netty 源码解析(一 ):开始Netty 源码解析(二): Netty 的 ChannelNetty 源码解析(三): Netty 的 Future 和 PromiseNetty 源码解析(四): Netty 的 ChannelPipelineNetty 源码解析(五): Netty 的线程池...原创 2020-05-07 09:55:30 · 1098 阅读 · 0 评论 -
Netty 源码解析(五): Netty 的线程池分析
今天是猿灯塔“365篇原创计划”第五篇。 接下来的时间灯塔君持续更新Netty系列一共九篇Netty 源码解析(一): 开始Netty 源码解析(二): Netty 的 ChannelNetty 源码解析(三): Netty的 Future 和 PromiseNetty 源码解析(四): Netty 的 ChannelPipeline当前:Netty 源码解析(五): Netty 的线...原创 2020-05-04 16:08:41 · 684 阅读 · 0 评论 -
Netty 源码解析(四): Netty 的 ChannelPipeline
今天是猿灯塔“365篇原创计划”第四篇。接下来的时间灯塔君持续更新Netty系列一共九篇Netty 源码解析(一): 开始Netty 源码解析(二): Netty 的 ChannelNetty 源码解析(三): Netty 的 Future 和 Promise当前:Netty 源码解析(四): Netty 的 ChannelPipelineNetty 源码解析(五): Netty 的线...原创 2020-05-02 10:07:41 · 430 阅读 · 0 评论 -
Netty 源码解析(三): Netty 的 Future 和 Promise
今天是猿灯塔“365篇原创计划”第三篇。接下来的时间灯塔君持续更新Netty系列一共九篇Netty 源码解析(一): 开始Netty 源码解析(二): Netty 的 Channel当前:Netty 源码解析(三): Netty 的 Future 和 PromiseNetty 源码解析(四): Netty 的 ChannelPipelineNetty 源码解析(五): Netty 的...原创 2020-04-29 09:59:50 · 447 阅读 · 0 评论 -
这样基于Netty重构RPC框架你不可能知道
原创申明:本文由公众号【名师猿灯塔】原创,转载请说明出处标注今天是猿灯塔“365天原创计划”第6天。今天呢!灯塔君跟大家讲: 基于Netty重构RPC框架一.CyclicBarrier方法说明1.单一应用架构当网站流量很小时,只需一个应用,将所有功能都部署在一起,以减少部署节点和成本。。(最开始58 同城的站点架构用一个词概括就是“ALL IN ONE”。就...原创 2020-04-01 09:02:43 · 280 阅读 · 0 评论 -
从CAS讲起,真正高性能解决并发编程的原子操作
今天是猿灯塔“365天原创计划”第1天。一.原子性操作原子性操作:原子性在一个操作是不可中断的,要么全部执行成功要么全部执行失败,有着“同生共死”的感觉。及时在多个线程一起执行的时候,一个操作一旦开始,就不会被其他线程所干扰。深入源码底层发现它的方法源于:继续深入:发现实际上这个方法的实现原理就是CAS。二.CAScompare and swap 比较和交换属于硬件同步...原创 2020-03-27 09:09:57 · 269 阅读 · 0 评论 -
Netty 源码解析(二):Netty 的 Channel
今天是猿灯塔“365篇原创计划”第十四篇。接下来的时间灯塔君持续更新Netty系列一共九篇Netty源码解析(一):开始当前:Netty 源码解析(二): Netty 的 ChannelNetty 源码解析(三): Netty 的 Future 和 PromiseNetty 源码解析(四): Netty 的 ChannelPipelineNetty 源码解析(五): Netty 的线程...原创 2020-04-25 10:13:52 · 346 阅读 · 0 评论