-
Notifications
You must be signed in to change notification settings - Fork 1
/
search.xml
123 lines (123 loc) · 61.8 KB
/
search.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title><![CDATA[Ajax的应用及原理]]></title>
<url>%2F2019%2F09%2F14%2FAjax%E7%9A%84%E5%BA%94%E7%94%A8%E5%8F%8A%E5%8E%9F%E7%90%86%2F</url>
<content type="text"><![CDATA[Ajax的应用及原理 AJAX即“Asynchronous JavaScript and XML”(异步的JavaScript与XML技术),指的是一套综合了多项技术的浏览器端网页开发技术。 什么是异步?举个例子: 假如你要做两件事,烧水、刷牙同步:你烧水,等水烧开了你再去刷牙异步:你烧水,不等水烧开就去刷牙了,水烧开了会发出声音告诉你(callback),然后你再处理水烧开之后的事情 为什么需要Ajax? 传统的web前端与后端的交互中,浏览器直接访问服务器来获取数据。服务器处理之后把数据发送给浏览器**。 使用AJAX之,浏览器是先把请求发送到XMLHttpRequest异步对象之中,异步对象对请求进行封装,然后再与发送给服务器。服务器并不是以转发的方式响应,而是以流的方式把数据返回给浏览器 XMLHttpRequest异步对象会不停监听服务器状态的变化,得到服务器返回的数据,就写到浏览器上【因为不是转发的方式,所以是无刷新就能够获取服务器端的数据 传统方式每当访问服务器都要刷新网页,如果只是需要获取部分数据的刷新,则需要重新请求整个网页内容,在资源上来说是一种浪费,同样浪费的还有时间。 Ajax的优点和缺点?优点1、无刷新更新数据,响应迅捷,减少用户等待时间。2、异步与服务器通信,不需要打断用户的操作减少不必要的数据传输、时间及降低网络上数据流量。3、前端和后端负载平衡。减轻服务器的负担,AJAX的原则是“按需取数据”,可以最大程度的减少冗余请求和响应对服务器造成的负担,提升站点性能。4、基于标准并被各大浏览器广泛支持,但需要客户允许JavaScript在浏览器上执行。5、界面与应用分离(也可以说是数据与呈现分离),有利于分工合作。 缺点1、AJAX干掉了Back和History功能,即对浏览器机制的破坏。在动态更新页面的情况下,用户无法回到前一个页面状态,因为浏览器仅能记忆历史记录中的静态页面。一个被完整读入的页面与一个已经被动态修改过的页面之间的差别非常微妙;用户通常会希望单击后退按钮能够取消他们的前一次操作,但是在Ajax应用程序中,这将无法实现。后退按钮是一个标准的web站点的重要功能,但是它没法和js进行很好的合作。这是Ajax所带来的一个比较严重的问题,因为用户往往是希望能够通过后退来取消前一次操作的。那么对于这个问题有没有办法?答案是肯定的,用过Gmail的知道,Gmail下面采用的Ajax技术解决了这个问题,在Gmail下面是可以后退的,但是,它也并不能改变Ajax的机制,它只是采用的一个比较笨但是有效的办法,即用户单击后退按钮访问历史记录时,通过创建或使用一个隐藏的IFRAME来重现页面上的变更。(例如,当用户在Google Maps中单击后退时,它在一个隐藏的IFRAME中进行搜索,然后将搜索结果反映到Ajax元素上,以便将应用程序状态恢复到当时的状态。)但是,虽然说这个问题是可以解决的,但是它所带来的开发成本是非常高的,并与Ajax框架所要求的快速开发是相背离的。这是Ajax所带来的一个非常严重的问题。一个相关的观点认为,使用动态页面更新使得用户难于将某个特定的状态保存到收藏夹中。该问题的解决方案也已出现,大部分都使用URL片断标识符(通常被称为锚点,即URL中#后面的部分)来保持跟踪,允许用户回到指定的某个应用程序状态。(许多浏览器允许JavaScript动态更新锚点,这使得Ajax应用程序能够在更新显示内容的同时更新锚点。)这些解决方案也同时解决了许多关于不支持后退按钮的争论。2、AJAX的安全问题。AJAX技术给用户带来很好的用户体验的同时也对IT企业带来了新的安全威胁,Ajax技术就如同对企业数据建立了一个直接通道。这使得开发者在不经意间会暴露比以前更多的数据和服务器逻辑。Ajax的逻辑可以对客户端的安全扫描技术隐藏起来,允许黑客从远端服务器上建立新的攻击。还有Ajax也难以避免一些已知的安全弱点,诸如跨站点脚步攻击、SQL注入攻击和基于Credentials的安全漏洞等等。3、对搜索引擎支持较弱。对搜索引擎的支持比较弱。如果使用不当,AJAX会增大网络数据的流量,从而降低整个系统的性能。4、破坏程序的异常处理机制。至少从目前看来,像Ajax.dll,Ajaxpro.dll这些Ajax框架是会破坏程序的异常机制的。关于这个问题,曾在开发过程中遇到过,但是查了一下网上几乎没有相关的介绍。后来做了一次试验,分别采用Ajax和传统的form提交的模式来删除一条数据……给我们的调试带来了很大的困难。5、违背URL和资源定位的初衷。例如,我给你一个URL地址,如果采用了Ajax技术,也许你在该URL地址下面看到的和我在这个URL地址下看到的内容是不同的。这个和资源定位的初衷是相背离的。6、AJAX不能很好支持移动设备。一些手持设备(如手机、PDA等)现在还不能很好的支持Ajax,比如说我们在手机的浏览器上打开采用Ajax技术的网站时,它目前是不支持的。7、客户端过肥,太多客户端代码造成开发上的成本。编写复杂、容易出错 ;冗余代码比较多(层层包含js文件是AJAX的通病,再加上以往的很多服务端代码现在放到了客户端);破坏了Web的原有标准。 5.AJAX注意点及适用和不适用场景(1).注意点Ajax开发时,网络延迟——即用户发出请求到服务器发出响应之间的间隔——需要慎重考虑。不给予用户明确的回应,没有恰当的预读数据,或者对XMLHttpRequest的不恰当处理,都会使用户感到延迟,这是用户不希望看到的,也是他们无法理解的。通常的解决方案是,使用一个可视化的组件来告诉用户系统正在进行后台操作并且正在读取数据和内容。 (2).Ajax适用场景1、表单驱动的交互2、深层次的树的导航3、快速的用户与用户间的交流响应4、类似投票、yes/no等无关痛痒的场景5、对数据进行过滤和操纵相关数据的场景6、普通的文本输入提示和自动完成的场景(3).Ajax不适用场景1、部分简单的表单2、搜索3、基本的导航4、替换大量的文本5、对呈现的操纵]]></content>
<categories>
<category>JAVA</category>
</categories>
<tags>
<tag>博客</tag>
</tags>
</entry>
<entry>
<title><![CDATA[BIO-NIO-AIO]]></title>
<url>%2F2019%2F09%2F13%2FJava%2FBIO-NIO-AIO%2F</url>
<content type="text"><![CDATA[熟练掌握 BIO,NIO,AIO 的基本概念以及一些常见问题是你准备面试的过程中不可或缺的一部分,另外这些知识点也是你学习 Netty 的基础。 BIO,NIO,AIO 总结 1. BIO (Blocking I/O) 1.1 传统 BIO 1.2 伪异步 IO 1.3 代码示例 1.4 总结 2. NIO (New I/O) 2.1 NIO 简介 2.2 NIO的特性/NIO与IO区别 1)Non-blocking IO(非阻塞IO) 2)Buffer(缓冲区) 3)Channel (通道) 4)Selectors(选择器) 2.3 NIO 读数据和写数据方式 2.4 NIO核心组件简单介绍 2.5 代码示例 3. AIO (Asynchronous I/O) 参考 BIO,NIO,AIO 总结 Java 中的 BIO、NIO和 AIO 理解为是 Java 语言对操作系统的各种 IO 模型的封装。程序员在使用这些 API 的时候,不需要关心操作系统层面的知识,也不需要根据不同操作系统编写不同的代码。只需要使用Java的API就可以了。 在讲 BIO,NIO,AIO 之前先来回顾一下这样几个概念:同步与异步,阻塞与非阻塞。 同步与异步 同步: 同步就是发起一个调用后,被调用者未处理完请求之前,调用不返回。 异步: 异步就是发起一个调用后,立刻得到被调用者的回应表示已接收到请求,但是被调用者并没有返回结果,此时我们可以处理其他的请求,被调用者通常依靠事件,回调等机制来通知调用者其返回结果。 同步和异步的区别最大在于异步的话调用者不需要等待处理结果,被调用者会通过回调等机制来通知调用者其返回结果。 阻塞和非阻塞 阻塞: 阻塞就是发起一个请求,调用者一直等待请求结果返回,也就是当前线程会被挂起,无法从事其他任务,只有当条件就绪才能继续。 非阻塞: 非阻塞就是发起一个请求,调用者不用一直等着结果返回,可以先去干其他事情。 举个生活中简单的例子,你妈妈让你烧水,小时候你比较笨啊,在那里傻等着水开(同步阻塞)。等你稍微再长大一点,你知道每次烧水的空隙可以去干点其他事,然后只需要时不时来看看水开了没有(同步非阻塞)。后来,你们家用上了水开了会发出声音的壶,这样你就只需要听到响声后就知道水开了,在这期间你可以随便干自己的事情,你需要去倒水了(异步非阻塞)。 1. BIO (Blocking I/O)同步阻塞I/O模式,数据的读取写入必须阻塞在一个线程内等待其完成。 1.1 传统 BIOBIO通信(一请求一应答)模型图如下(图源网络,原出处不明): 采用 BIO 通信模型 的服务端,通常由一个独立的 Acceptor 线程负责监听客户端的连接。我们一般通过在while(true) 循环中服务端会调用 accept() 方法等待接收客户端的连接的方式监听请求,请求一旦接收到一个连接请求,就可以建立通信套接字在这个通信套接字上进行读写操作,此时不能再接收其他客户端连接请求,只能等待同当前连接的客户端的操作执行完成, 不过可以通过多线程来支持多个客户端的连接,如上图所示。 如果要让 BIO 通信模型 能够同时处理多个客户端请求,就必须使用多线程(主要原因是socket.accept()、socket.read()、socket.write() 涉及的三个主要函数都是同步阻塞的),也就是说它在接收到客户端连接请求之后为每个客户端创建一个新的线程进行链路处理,处理完成之后,通过输出流返回应答给客户端,线程销毁。这就是典型的 一请求一应答通信模型 。我们可以设想一下如果这个连接不做任何事情的话就会造成不必要的线程开销,不过可以通过 线程池机制 改善,线程池还可以让线程的创建和回收成本相对较低。使用FixedThreadPool 可以有效的控制了线程的最大数量,保证了系统有限的资源的控制,实现了N(客户端请求数量):M(处理客户端请求的线程数量)的伪异步I/O模型(N 可以远远大于 M),下面一节”伪异步 BIO”中会详细介绍到。 我们再设想一下当客户端并发访问量增加后这种模型会出现什么问题? 在 Java 虚拟机中,线程是宝贵的资源,线程的创建和销毁成本很高,除此之外,线程的切换成本也是很高的。尤其在 Linux 这样的操作系统中,线程本质上就是一个进程,创建和销毁线程都是重量级的系统函数。如果并发访问量增加会导致线程数急剧膨胀可能会导致线程堆栈溢出、创建新线程失败等问题,最终导致进程宕机或者僵死,不能对外提供服务。 1.2 伪异步 IO为了解决同步阻塞I/O面临的一个链路需要一个线程处理的问题,后来有人对它的线程模型进行了优化一一一后端通过一个线程池来处理多个客户端的请求接入,形成客户端个数M:线程池最大线程数N的比例关系,其中M可以远远大于N.通过线程池可以灵活地调配线程资源,设置线程的最大值,防止由于海量并发接入导致线程耗尽。 伪异步IO模型图(图源网络,原出处不明): 采用线程池和任务队列可以实现一种叫做伪异步的 I/O 通信框架,它的模型图如上图所示。当有新的客户端接入时,将客户端的 Socket 封装成一个Task(该任务实现java.lang.Runnable接口)投递到后端的线程池中进行处理,JDK 的线程池维护一个消息队列和 N 个活跃线程,对消息队列中的任务进行处理。由于线程池可以设置消息队列的大小和最大线程数,因此,它的资源占用是可控的,无论多少个客户端并发访问,都不会导致资源的耗尽和宕机。 伪异步I/O通信框架采用了线程池实现,因此避免了为每个请求都创建一个独立线程造成的线程资源耗尽问题。不过因为它的底层仍然是同步阻塞的BIO模型,因此无法从根本上解决问题。 1.3 代码示例下面代码中演示了BIO通信(一请求一应答)模型。我们会在客户端创建多个线程依次连接服务端并向其发送”当前时间+:hello world”,服务端会为每个客户端线程创建一个线程来处理。代码示例出自闪电侠的博客,原地址如下: https://www.jianshu.com/p/a4e03835921a 客户端 123456789101112131415161718192021222324252627/** * * @author 闪电侠 * @date 2018年10月14日 * @Description:客户端 */public class IOClient { public static void main(String[] args) { // TODO 创建多个线程,模拟多个客户端连接服务端 new Thread(() -> { try { Socket socket = new Socket("127.0.0.1", 3333); while (true) { try { socket.getOutputStream().write((new Date() + ": hello world").getBytes()); Thread.sleep(2000); } catch (Exception e) { } } } catch (IOException e) { } }).start(); }} 服务端 1234567891011121314151617181920212223242526272829303132333435363738394041/** * @author 闪电侠 * @date 2018年10月14日 * @Description: 服务端 */public class IOServer { public static void main(String[] args) throws IOException { // TODO 服务端处理客户端连接请求 ServerSocket serverSocket = new ServerSocket(3333); // 接收到客户端连接请求之后为每个客户端创建一个新的线程进行链路处理 new Thread(() -> { while (true) { try { // 阻塞方法获取新的连接 Socket socket = serverSocket.accept(); // 每一个新的连接都创建一个线程,负责读取数据 new Thread(() -> { try { int len; byte[] data = new byte[1024]; InputStream inputStream = socket.getInputStream(); // 按字节流方式读取数据 while ((len = inputStream.read(data)) != -1) { System.out.println(new String(data, 0, len)); } } catch (IOException e) { } }).start(); } catch (IOException e) { } } }).start(); }} 1.4 总结在活动连接数不是特别高(小于单机1000)的情况下,这种模型是比较不错的,可以让每一个连接专注于自己的 I/O 并且编程模型简单,也不用过多考虑系统的过载、限流等问题。线程池本身就是一个天然的漏斗,可以缓冲一些系统处理不了的连接或请求。但是,当面对十万甚至百万级连接的时候,传统的 BIO 模型是无能为力的。因此,我们需要一种更高效的 I/O 处理模型来应对更高的并发量。 2. NIO (New I/O)2.1 NIO 简介 NIO是一种同步非阻塞的I/O模型,在Java 1.4 中引入了NIO框架,对应 java.nio 包,提供了 Channel , Selector,Buffer等抽象。 NIO中的N可以理解为Non-blocking,不单纯是New。它支持面向缓冲的,基于通道的I/O操作方法。 NIO提供了与传统BIO模型中的 Socket 和 ServerSocket 相对应的 SocketChannel 和 ServerSocketChannel 两种不同的套接字通道实现,两种通道都支持阻塞和非阻塞两种模式。阻塞模式使用就像传统中的支持一样,比较简单,但是性能和可靠性都不好;非阻塞模式正好与之相反。对于低负载、低并发的应用程序,可以使用同步阻塞I/O来提升开发速率和更好的维护性;对于高负载、高并发的(网络)应用,应使用 NIO 的非阻塞模式来开发。 2.2 NIO的特性/NIO与IO区别如果是在面试中回答这个问题,我觉得首先肯定要从 NIO 流是非阻塞 IO 而 IO 流是阻塞 IO 说起。然后,可以从 NIO 的3个核心组件/特性为 NIO 带来的一些改进来分析。如果,你把这些都回答上了我觉得你对于 NIO 就有了更为深入一点的认识,面试官问到你这个问题,你也能很轻松的回答上来了。 1)Non-blocking IO(非阻塞IO)IO流是阻塞的,NIO流是不阻塞的。 Java NIO使我们可以进行非阻塞IO操作。比如说,单线程中从通道读取数据到buffer,同时可以继续做别的事情,当数据读取到buffer中后,线程再继续处理数据。写数据也是一样的。另外,非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。 Java IO的各种流是阻塞的。这意味着,当一个线程调用 read() 或 write() 时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了 2)Buffer(缓冲区)IO 面向流(Stream oriented),而 NIO 面向缓冲区(Buffer oriented)。 Buffer是一个对象,它包含一些要写入或者要读出的数据。在NIO类库中加入Buffer对象,体现了新库与原I/O的一个重要区别。在面向流的I/O中·可以将数据直接写入或者将数据直接读到 Stream 对象中。虽然 Stream 中也有 Buffer 开头的扩展类,但只是流的包装类,还是从流读到缓冲区,而 NIO 却是直接读到 Buffer 中进行操作。 在NIO厍中,所有数据都是用缓冲区处理的。在读取数据时,它是直接读到缓冲区中的; 在写入数据时,写入到缓冲区中。任何时候访问NIO中的数据,都是通过缓冲区进行操作。 最常用的缓冲区是 ByteBuffer,一个 ByteBuffer 提供了一组功能用于操作 byte 数组。除了ByteBuffer,还有其他的一些缓冲区,事实上,每一种Java基本类型(除了Boolean类型)都对应有一种缓冲区。 3)Channel (通道)NIO 通过Channel(通道) 进行读写。 通道是双向的,可读也可写,而流的读写是单向的。无论读写,通道只能和Buffer交互。因为 Buffer,通道可以异步地读写。 4)Selectors(选择器)NIO有选择器,而IO没有。 选择器用于使用单个线程处理多个通道。因此,它需要较少的线程来处理这些通道。线程之间的切换对于操作系统来说是昂贵的。 因此,为了提高系统效率选择器是有用的。 2.3 NIO 读数据和写数据方式通常来说NIO中的所有IO都是从 Channel(通道) 开始的。 从通道进行数据读取 :创建一个缓冲区,然后请求通道读取数据。 从通道进行数据写入 :创建一个缓冲区,填充数据,并要求通道写入数据。 数据读取和写入操作图示: 2.4 NIO核心组件简单介绍NIO 包含下面几个核心的组件: Channel(通道) Buffer(缓冲区) Selector(选择器) 整个NIO体系包含的类远远不止这三个,只能说这三个是NIO体系的“核心API”。我们上面已经对这三个概念进行了基本的阐述,这里就不多做解释了。 2.5 代码示例代码示例出自闪电侠的博客,原地址如下: https://www.jianshu.com/p/a4e03835921a 客户端 IOClient.java 的代码不变,我们对服务端使用 NIO 进行改造。以下代码较多而且逻辑比较复杂,大家看看就好。 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384/** * * @author 闪电侠 * @date 2019年2月21日 * @Description: NIO 改造后的服务端 */public class NIOServer { public static void main(String[] args) throws IOException { // 1. serverSelector负责轮询是否有新的连接,服务端监测到新的连接之后,不再创建一个新的线程, // 而是直接将新连接绑定到clientSelector上,这样就不用 IO 模型中 1w 个 while 循环在死等 Selector serverSelector = Selector.open(); // 2. clientSelector负责轮询连接是否有数据可读 Selector clientSelector = Selector.open(); new Thread(() -> { try { // 对应IO编程中服务端启动 ServerSocketChannel listenerChannel = ServerSocketChannel.open(); listenerChannel.socket().bind(new InetSocketAddress(3333)); listenerChannel.configureBlocking(false); listenerChannel.register(serverSelector, SelectionKey.OP_ACCEPT); while (true) { // 监测是否有新的连接,这里的1指的是阻塞的时间为 1ms if (serverSelector.select(1) > 0) { Set<SelectionKey> set = serverSelector.selectedKeys(); Iterator<SelectionKey> keyIterator = set.iterator(); while (keyIterator.hasNext()) { SelectionKey key = keyIterator.next(); if (key.isAcceptable()) { try { // (1) // 每来一个新连接,不需要创建一个线程,而是直接注册到clientSelector SocketChannel clientChannel = ((ServerSocketChannel) key.channel()).accept(); clientChannel.configureBlocking(false); clientChannel.register(clientSelector, SelectionKey.OP_READ); } finally { keyIterator.remove(); } } } } } } catch (IOException ignored) { } }).start(); new Thread(() -> { try { while (true) { // (2) 批量轮询是否有哪些连接有数据可读,这里的1指的是阻塞的时间为 1ms if (clientSelector.select(1) > 0) { Set<SelectionKey> set = clientSelector.selectedKeys(); Iterator<SelectionKey> keyIterator = set.iterator(); while (keyIterator.hasNext()) { SelectionKey key = keyIterator.next(); if (key.isReadable()) { try { SocketChannel clientChannel = (SocketChannel) key.channel(); ByteBuffer byteBuffer = ByteBuffer.allocate(1024); // (3) 面向 Buffer clientChannel.read(byteBuffer); byteBuffer.flip(); System.out.println( Charset.defaultCharset().newDecoder().decode(byteBuffer).toString()); } finally { keyIterator.remove(); key.interestOps(SelectionKey.OP_READ); } } } } } } catch (IOException ignored) { } }).start(); }} 为什么大家都不愿意用 JDK 原生 NIO 进行开发呢?从上面的代码中大家都可以看出来,是真的难用!除了编程复杂、编程模型难之外,它还有以下让人诟病的问题: JDK 的 NIO 底层由 epoll 实现,该实现饱受诟病的空轮询 bug 会导致 cpu 飙升 100% 项目庞大之后,自行实现的 NIO 很容易出现各类 bug,维护成本较高,上面这一坨代码我都不能保证没有 bug Netty 的出现很大程度上改善了 JDK 原生 NIO 所存在的一些让人难以忍受的问题。 3. AIO (Asynchronous I/O)AIO 也就是 NIO 2。在 Java 7 中引入了 NIO 的改进版 NIO 2,它是异步非阻塞的IO模型。异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。 AIO 是异步IO的缩写,虽然 NIO 在网络操作中,提供了非阻塞的方法,但是 NIO 的 IO 行为还是同步的。对于 NIO 来说,我们的业务线程是在 IO 操作准备好时,得到通知,接着就由这个线程自行进行 IO 操作,IO操作本身是同步的。(除了 AIO 其他的 IO 类型都是同步的,这一点可以从底层IO线程模型解释,推荐一篇文章:《漫话:如何给女朋友解释什么是Linux的五种IO模型?》 ) 查阅网上相关资料,我发现就目前来说 AIO 的应用还不是很广泛,Netty 之前也尝试使用过 AIO,不过又放弃了。 参考 《Netty 权威指南》第二版 https://zhuanlan.zhihu.com/p/23488863 (美团技术团队)]]></content>
<categories>
<category>JAVA</category>
</categories>
<tags>
<tag>博客</tag>
</tags>
</entry>
<entry>
<title><![CDATA[反射之基础篇]]></title>
<url>%2F2019%2F09%2F12%2FJava%2F%E5%8F%8D%E5%B0%84%E4%B9%8B%E5%9F%BA%E7%A1%80%E7%AF%87%2F</url>
<content type="text"><![CDATA[反射之基础篇本篇主要对反射的概念和常见用法进行介绍,未涉及底层源码分析。 JAVA反射机制是在运行状态中,对于任意一个实体类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。 反射常见的三种操作方式,假设有一个实体类Apple: 第一种方式(也是最常用): Class cla = Class.forName(“com.zs.reflect.Apple”); 第二种方式: Apple.class 该种方式主要适合当做实参来传递,从而进行对类的一系列操作 第三种方式: Class cla = Class.forName(“com.zs.reflect.Apple”); 用法与第二种类似 12345678910111213141516171819202122232425262728package com.zs.reflect;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;public class TestReflect { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException { //第一种较常用 //Class cla = Class.forName("com.zs.reflect.Apple"); test(cla); //第二种 test(Apple.class); //第三种 //Class cla = new Apple().getClass();} public static void test(Class cla) throws InstantiationException, IllegalAccessException { System.out.println("类名:"+cla.getName()); Field[] files = cla.getDeclaredFields(); for (Field field : files) { System.out.println("属性名:"+field ); } System.out.println("包:"+cla.getPackage()); }} 以上三种获得的是Apple的类对象, 请注意,是类对象而不是类的对象,这意味着可以通过类对象cla调用反射底层所提供的方法,比如: 类对象并不能直接访问调用类里的方法和属性,必须要根据类对象创建类的对象 cla.getName(); //获得该类的包名加类名 cla.getDeclaredFields(); // 返回数组Field对象,表示此类或接口声明的所有字段类对象 cla.getPackage();//返回此类的包 反射学习:获取类对象 Class.forName(“类的全限定路径”); 创建类对象使用较多 类名.class 操作反射方法较多 对象名.getClass() 操作反射方法较多 注意: 一个类只有一个类对象 操作类属性 获取类对象 获取类属性 getFields() 获取所有的公共字段包括父类 返回Field[] getDeclaredFields() 获取所有声明的字段(不包括父类) 返回Field[] getField(String name) 获取指定的公共字段包括父类 返回Field getDeclaredField(String name) 获取指定的声明的字段(不包括父类) 返回Field 操作静态属性 类属性对象.get(null) 返回静态属性的值 类属性对象.set(null,”值”) 赋值 操作非静态属性 类属性对象.get(Object obj); 类属性对象.set(Object obj,”值”); 操作方法: 获取类对象 获取方法对象 getMethods() 获取所有的公共方法包括父类 getDeclaredMethods() 获取所有声明的方法不包括父类 getMethod(String name,Class…cla) 获取指定的公共方法 String name 表示方法名 Class…cla 表示方法接收的参数类型的类对象 getDeclaredMethod(String name,Class…cla) 获取指定的声明方法 String name 表示方法名 Class…cla 表示方法接收的参数类型的类对象 静态方法方法对象.invoke(null,参数值1,参数值2,….); 方法对象.invoke(null,null); 非静态方法Object obj=cla.newInstance(); 方法对象.invoke(obj,参数值1,参数值2,….) 方法对象.invoke(obj,null) 操作构造器: 获取类对象 获取构造器对象 操作构造器对象 代码示例: 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103*/public class TestReflect {public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException, SecurityException, NoSuchMethodException, IllegalArgumentException, InvocationTargetException { //反射操作类属性 //operField(); //反射操作类方法 //operMethod(); //反射操作构造器 operConstructor();}//反射操作构造器private static void operConstructor() throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchFieldException { //获取类对象 Class cla=Class.forName("com.bjsxt.pojo.Student"); //获取构造器方法对象 Constructor[] cs=cla.getConstructors(); for(Constructor c:cs){ System.out.println(c.getName()); } //获取指定的构造器 Constructor c=cla.getConstructor(String.class); //创建实例化对象 Object obj= c.newInstance("女"); System.out.println(cla.getDeclaredMethod("getSsex",null).invoke(obj,null)); Student s=new Student("女"); System.out.println(s.getSsex()); }//操作方法private static void operMethod() throws ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, InstantiationException { //获取类对象 Class cla=Class.forName("com.bjsxt.pojo.Student"); //获取类方法对象 //获取所有的公共方法包括父类 Method[] ms=cla.getMethods(); for(Method m:ms){ System.out.println("获取方法名--->"+m.getName()); } System.out.println("************************************"); //获取所有声明的方法不包括父类 Method[] ms2=cla.getDeclaredMethods(); for(Method m:ms2){ System.out.println("获取方法名--->"+m.getName()); } //获取指定的公共方法包括父类 Method m=cla.getMethod("pHi", int.class,String.class); System.out.println(m.getReturnType()); //获取指定的声明的方法,不包括父类 Method m2=cla.getDeclaredMethod("sHello",null); System.out.println(m2.getName()); //执行方法 //静态方法 Method m3=cla.getDeclaredMethod("sHi",String.class); m3.invoke(null, "今天学了反射,好开心"); //非静态 Method m4=cla.getDeclaredMethod("sHi",int.class,String.class); m4.invoke(cla.newInstance(), 3,"反射功能好强大");}public static void operField() throws ClassNotFoundException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, InstantiationException{ //获取类对象 Class cla=Class.forName("com.bjsxt.pojo.Student"); //获取反射类属性 //获取类及其父类的公共字段 Field[] fds = cla.getFields(); for(Field f:fds){ System.out.println("获取属性名------>"+f.getName()); System.out.println("获取修饰符------>"+f.getModifiers()); System.out.println("获取类型------>"+f.getType());//返回的是类型的Class对象 } System.out.println("******************************"); //获取类声明的所有字段 Field[] fds2=cla.getDeclaredFields(); for(Field f:fds2){ System.out.println("获取属性名----->"+f.getName()); System.out.println("获取修饰符------>"+f.getModifiers()); System.out.println("获取类型------>"+f.getType());//返回的是类型的Class对象 } System.out.println("******************************"); //获取指定的字段 Field f=cla.getField("pname");//指定获取类及其父类的公共字段 System.out.println(f.getName()); Field f2=cla.getDeclaredField("money");//指定获取类的所有字段 System.out.println(f2.getName()); Field f3=cla.getSuperclass().getDeclaredField("pname");//指定获取父类声明的字段 System.out.println(f3.getName()); //操作字段值 System.out.println("************操作静态字段**********************"); //操作静态属性 Field fs=cla.getDeclaredField("money"); fs.set(null,2000); System.out.println(fs.get(null)); System.out.println("************操作非静态字段**********************"); //操作非静态属性 Field fd=cla.getDeclaredField("sname"); Object obj=cla.newInstance(); fd.set(obj, "李四"); System.out.println(fd.get(obj)); //暴力反射操作私有化属性(了解) Field fd2=cla.getDeclaredField("ssex"); fd2.setAccessible(true);//暴力反射,操作私有化属性,不安全 Object obj2=cla.newInstance();//获取实例化对象 System.out.println(fd2.get(obj2)); }]]></content>
<categories>
<category>JAVA</category>
</categories>
<tags>
<tag>博客</tag>
</tags>
</entry>
<entry>
<title><![CDATA[IoC容器之注解]]></title>
<url>%2F2019%2F09%2F10%2FJava%2FIoC%E5%AE%B9%E5%99%A8%E4%B9%8B%E6%B3%A8%E8%A7%A3%2F</url>
<content type="text"><![CDATA[IoC容器之注解如果你厌倦配置复杂的applicationContext.xml文件,我们可以找到另外一种方法去替代它:注解 注解技术是在jdk5.0版本出现的,注解是一种符号标记,可以写在类定义前,方法定义前,成员变量定义前。格式是:@标记名,常见的有@Test,@Service,@Override等标记。现在注解配置在主流的框架如struts,hibernate,spring中得到广泛的应用,原因是注解方式直观方便,而且很多元素都不需要在Spring配置文件中定义了,这样大大简化了xml配置内容。注解名在框架底层代码定义,通过反射技术,读取注解符号运行底层代码,实现相应的功能。 首先使用注解,xml文件需增加 1xmlns:context="http://www.springframework.org/schema/context" 使用注解定义bean:通过注解的形式 将bean以及相应的属性值 放入ioc容器 1<context:component-scan base-package="类名全路径">/context:component-scan> Spring在启动的时候,会根据base-package在 该包中扫描所有类,查找这些类是否有注解@Component(“studentDao”),如果有,则将该类 加入spring Ioc容器。 @Component(范围太广,可以使用,但一般细化为三个方面): dao层注解:@Repositoryservice层注解:@Service控制器层注解:@Controller 这样一来不同的层次有惯用的注解,代码可读性增强。]]></content>
<categories>
<category>JAVA</category>
</categories>
<tags>
<tag>博客</tag>
</tags>
</entry>
<entry>
<title><![CDATA[依赖注入的三种方式]]></title>
<url>%2F2019%2F09%2F10%2FJava%2F%E4%BE%9D%E8%B5%96%E6%B3%A8%E5%85%A5%E7%9A%84%E4%B8%89%E7%A7%8D%E6%96%B9%E5%BC%8F%2F</url>
<content type="text"><![CDATA[依赖注入的三种方式在ioc容器中中定义bean的前提:该bean的类 必须提供了 无参构造! 这是实体类定义的属性,都有对应的get/set方法,为节省篇幅没有写。 123private String name;private Teacher teacher;private int age; 1、属性注入的两种方式(property)12345678910111213141516<bean id="person" class="com.zs.test.Person"> <property name="name" value="张三"></property> <property name="age" value="18"></property> <property name="teacher" ref="teacher"> </bean><!--其实它还可以这样写--> <bean id="person" class="com.zs.test.Person"> <property name="name"> <value>张三</value> </property> <property name="age" > <value>18</value> </property> <property name="teacher"> <ref>teacher</ref></bean> 注意: 基本数据类型加String注入用value,对象注入采用ref 不仅仅可以通过name来注入,也可以用索引位置index,数据类型type等等都可以,也可以联合使用 如果没有指定name,那么value会默认注入String类型(因为都加引号),如果想给其他类型注入最好加上name来识别。 属性注入两种方式的区别: 使用子元素value注入 使用value属性注入 参数值位置 写在首尾标签()的中间(不加双引号) 写在value的属性值中(必须加双引号) type属性 有(可选)可以通过type属性指定数据类型 无 参数值包含特殊字符(<, &)时的处理方法 两种处理方法。一、使用标记,二、使用XML预定义的实体引用 一种处理方法。即使用XML预定义的实体引用 实体引用 表示的符号 < < & & > > 给对象类型赋值null : 123<property name="name" > <null/> -->注意 没有<value></property> 赋空值 “” 123<property name="name" > <value></value> </property> 2、构造器注入(constructor)12345<bean id="person" class="com.zs.test.Person"> <constructor-arg name="name" value="李四"></constructor-arg> <constructor-arg name="age" value="30"></constructor-arg> <constructor-arg name="age" ref="teacher"></constructor-arg></bean> 注意: 最好按构造器参数顺序来赋值 其余注意与属性赋值类似 3、p命名空间注入需要在文件顶部添加 1xmlns:p="http://www.springframework.org/schema/p" 注入方法 1<bean id="person" class="com.zs.test.Person" p:name="王五" p:age="90" p:teacher-ref="teacher></bean> 注意: 直接在bean标签内赋值 注入基本数据类型为:p:属性名=”值”,注入对象为p:对象名-ref=”对象” 多个注入之间必须以空格分隔 集合类型注入:1、数组注入: 123456789<bean id="collectionDemo" class="org.lanqiao.entity.AllCollectionType" > <property name="arrayElement"> <array> <value>足球1</value> <value>篮球1</value> <value>乒乓球1</value> </array> </property></bean> 2、List、Set集合注入: 123456789101112131415161718<bean id="collectionDemo" class="org.lanqiao.entity.AllCollectionType" > <property name="listElement"> <list> <value>足球</value> <value>篮球</value> <value>乒乓球</value> </list> </property> <constructor-arg name="listElement"> <list> <value>足球xx</value> <value>篮球xx</value> <value>乒乓球xxx</value> </list> </constructor-arg></bean> 3、Map注入 1234567891011121314151617181920212223242526<bean id="collectionDemo" class="org.lanqiao.entity.AllCollectionType" > <property name="mapElement"> <map> <entry> <key> <value>foot</value> </key> <value>足球3</value> </entry> <entry> <key> <value>basket</value> </key> <value>篮球3</value> </entry> <entry> <key> <value>pp3</value> </key> <value>乒乓球</value> </entry> </map> </property></bean> 4、property注入 123456789<bean id="collectionDemo" class="org.lanqiao.entity.AllCollectionType" ><property name="propsElement"> <props> <prop key="foot4">足球4</prop> <prop key="basket4">篮球4</prop> <prop key="pp4">乒乓球4</prop> </props> </property></bean> 注:set、list、数组 各自都有自己的标签 ,但是也可以混着用 自动装配(只适用于ref类型)1<bean ...class="org.lanqiao.entity.Course" autowire="byName|byType|constructor|no"> 也就是给bean标签添加属性autowire,可选值有byName、byType、constructor byName本质是byIdbyName: 自动寻找:其他bean的id值=该Course类的属性名,不用写注入的代码byType: 其他bean的类型(class) 是否与 该Course类的ref属性类型一致 (注意,此种方式 必须满足:当前Ioc容器中 只能有一个Bean满足条件 )constructor: 其他bean的类型(class) 是否与 该Course类的构造方法参数 的类型一致;此种方式的本质就是byType 还可以在头文件beans标签中中 一次性将该ioc容器的所有bean 统一设置成自动装配:加入 default-autowire=”byName” 注意:自动装配虽然可以减少代码量,但是会降低程序的可读性,使用时需要谨慎。]]></content>
<categories>
<category>JAVA</category>
</categories>
<tags>
<tag>博客</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Spring如何理解IoC(控制反转)与DI(依赖注入)?]]></title>
<url>%2F2019%2F09%2F09%2FJava%2FSpring%E5%A6%82%E4%BD%95%E7%90%86%E8%A7%A3ioc%EF%BC%88%E6%8E%A7%E5%88%B6%E5%8F%8D%E8%BD%AC%EF%BC%89%E4%B8%8EDI%EF%BC%88%E4%BE%9D%E8%B5%96%E6%B3%A8%E5%85%A5%EF%BC%89%EF%BC%9F%2F</url>
<content type="text"><![CDATA[1、如何理解IoC(控制反转)?1.1、IoC是什么 Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想。在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。如何理解好Ioc呢?理解好Ioc的关键是要明确“谁控制谁,控制什么,为何是反转(有反转就应该有正转了),哪些方面反转了”,那我们来深入分析一下: ●谁控制谁,控制什么:传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对 象的创建;谁控制谁?当然是IoC 容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是对象包括比如文件等)。 ●为何是反转,哪些方面反转了:有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。 用图例说明一下,传统程序设计如图2-1,都是主动去创建相关对象然后再组合起来: 当有了IoC/DI的容器后,在客户端类中不再主动去创建这些对象了,如图2-2所示: 1.2、IoC能做什么 IoC 不是一种技术,只是一种思想,一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是 松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。 其实IoC对编程带来的最大改变不是从代码上,而是从思想上,发生了“主从换位”的变化。应用程序原本是老大,要获取什么资源都是主动出击,但是在IoC/DI思想中,应用程序就变成被动的了,被动的等待IoC容器来创建并注入它所需要的资源了。 IoC很好的体现了面向对象设计法则之一—— 好莱坞法则:“别找我们,我们找你”;即由IoC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。 2、如何理解DI(依赖注入)? DI—Dependency Injection,即“依赖注入”:组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。 理解DI的关键是:“谁依赖谁,为什么需要依赖,谁注入谁,注入了什么”,那我们来深入分析一下: ●谁依赖于谁:当然是应用程序依赖于IoC容器; ●为什么需要依赖:应用程序需要IoC容器来提供对象需要的外部资源; ●谁注入谁:很明显是IoC容器注入应用程序某个对象,应用程序依赖的对象; ●注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)。 IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的。比如对象A需要操作数据库,以前我们总是要在A中自己编写代码来获得一个Connection对象,有了 spring我们就只需要告诉spring,A中需要一个Connection,至于这个Connection怎么构造,何时构造,A不需要知道。在系统运行时,spring会在适当的时候制造一个Connection,然后像打针一样,注射到A当中,这样就完成了对各个对象之间关系的控制。A需要依赖 Connection才能正常运行,而这个Connection是由spring注入到A中的,依赖注入的名字就这么来的。那么DI是如何实现的呢? Java 1.3之后一个重要特征是反射(reflection),它允许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性,spring就是通过反射来实现注入的。 理解了IoC和DI的概念后,一切都将变得简单明了,剩下的工作只是在spring的框架中堆积木而已。 3、IoC和DI由什么关系呢?其实它们是同一个概念的不同角度描述,由于控制反转概念比较含糊(可能只是理解为容器控制对象这一个层面,很难让人想到谁来维护对象关系),所以2004年大师级人物Martin Fowler又给出了一个新的名字:“依赖注入”,相对IoC 而言,“依赖注入”明确描述了“被注入对象依赖IoC容器配置依赖对象”。 文章转载自:谈谈对Spring IoC的理解 个人感觉这篇文章真的是对新手特别友好,通读易懂,简直不要太舒服。]]></content>
</entry>
<entry>
<title><![CDATA[Spring入门]]></title>
<url>%2F2019%2F09%2F09%2FJava%2FSpring%E5%85%A5%E9%97%A8%2F</url>
<content type="text"><![CDATA[JAVA框架之Spring基础环境搭建 Spring框架是 Java 平台的一个开源的全栈(Full-stack)应用程序框架和控制反转容器实现,一般被直接称为 Spring。该框架的一些核心功能理论上可用于任何 Java 应用,但 Spring 还为基于Java企业版平台构建的 Web 应用提供了大量的拓展支持。虽然 Spring 没有直接实现任何的编程模型,但它已经在 Java 社区中广为流行,基本上完全代替了企业级JavaBeans(EJB)模型 1.搭建Spring环境下载jar包http://maven.springframework.org/release/org/springframework/spring/根据版本选择,这里我选择的是spring-framework-4.3.9.RELEASE-dist.zip开发spring至少需要使用的jar(5个+1个):spring-aop.jar 开发AOP特性时需要的JARspring-beans.jar 处理Bean的jar spring-context.jar 处理spring上下文的jar spring-core.jar spring核心jarspring-expression.jar spring表达式第三方提供的日志jarcommons-logging.jar 日志 下载完之后集体加载到类路径,也就是Build Path。 2.编写配置文件为了编写时有一些提示、自动生成一些配置信息,我们需要安装sts插件:方式一:增加sts插件可以给eclipse增加 支持spring的插件:spring tool suite(https://spring.io/tools/sts/all)下载(根据自己的eclipse版本选择)springsource-tool-suite-3.9.4.RELEASE-e4.7.3a-updatesite.zip, 然后在Eclipse中安装:Help->Install new SoftWare-> Add 方式二:直接下载sts工具(相当于一个集合了Spring tool suite的Eclipse): https://spring.io/tools/sts/,下载完成之后打开运行。它相当于一个自带sts的eclipse。 新建配置文件: new File ->搜索spring或bean->选择Spring Bean Configuration File->起名为:applicationContext.xml(大家公认写法) 配置xml文件,新增bean标签,书写如下 12345<!--属性注入--><bean id="person" class="com.zs.test.Person"> <property name="name" value="张三"></property> <property name="age" value="18"></property></bean> id :唯一标识符(也就是实例名) class:类型(也就是类的路径) property:代表属性注入(还有另外一种构造器注入,这里先不做介绍) name :属性名 value:赋值 3.开发Spring程序(IOC-控制反转),编写测试类TestSpring 自行写好实体类Student。 测试类方法里输入: 1234ApplicationContext conext = new ClassPathXmlApplicationContext("applicationContext.xml") ;//执行从springIOC容器中获取一个 id为student的对象Student student = (Student)conext.getBean("student") ;System.out.println(student.getName); springioc容器 会自动帮我们new了对象,并且给对象赋了值 查看输出结果:张三 此篇主要概括spring项目搭建的大致流程,具体细节会在后面一一介绍。]]></content>
<categories>
<category>JAVA</category>
</categories>
<tags>
<tag>博客</tag>
</tags>
</entry>
<entry>
<title><![CDATA[前言]]></title>
<url>%2F2019%2F09%2F08%2F%E5%89%8D%E8%A8%80%2F</url>
<content type="text"><![CDATA[你迷茫的原因在于读书太少而想的太多。 你问我有哪些进步?我开始成为我自己的朋友。 如果每个人都能理解你,那你得普通成什么样子。 若能避开猛烈的狂喜,自然不会有悲痛来袭。 你看到的世界其实就是你自己的样子。]]></content>
</entry>
<entry>
<title><![CDATA[Maven环境配置(基于Eclipse)]]></title>
<url>%2F2019%2F09%2F07%2FJava%2FMaven%E7%8E%AF%E5%A2%83%E9%85%8D%E7%BD%AE-%E5%9F%BA%E4%BA%8EEclipse%2F</url>
<content type="text"><![CDATA[Apache Maven,是一个软件(特别是Java软件)项目管理及自动构建工具,由Apache软件基金会所提供。基于项目对象模型(缩写:POM)概念,Maven利用一个中央信息片断能管理一个项目的构建、报告和文档等步骤。 Maven也可被用于构建和管理各种项目,例如C#,Ruby,Scala和其他语言编写的项目。Maven曾是Jakarta项目的子项目,现为由Apache软件基金会主持的独立Apache项目。 一、安装Maven1、Maven下载、解压2、设置环境变量 121、新建变量M2_HOME,值为Maven的目录X:\XXX\apache-maven-XXX2、Path变量:将%M2_HOME%\bin添加到Path变量下 3、运行CMD,输入mvn -v后可以看到Maven的版本信息等则表示安装成功 4、报错信息处理(如果正常输出版本信息则省略) 1234567891.不是内部或外部命令 解决方案: 编辑环境变量Path,以前是(%MAVEN_HOME%\bin\),改为E:\Mavenaven\apache-maven-3.5.0\bin(也就是bin目录的绝对路径) 重启DOS窗口。2.报错 Exception in thread “main” java.lang.UnsupportedClassVersionError 这个错误是因为maven版本和jdk版本不兼容,需要更换jdk或maven版本 二、修改本地仓库的位置:(X:\XXX\apache-maven-3.5.3指的是maven解压之后的路径,即环境变量M2_HOME的值)X:\XXX\apache-maven-3.5.3\conf\settings.xml文件:添加如下语句 1<localRepository>D:/Java/maven/repository</localRepository> 三、Maven镜像更换为阿里云中央仓库:X:\XXX\apache-maven-3.5.3\conf\settings.xml文件:在标签下添加如下语句: 123456<mirror> <id>alimaven</id> <name>aliyun maven</name> <url>http://maven.aliyun.com/nexus/content/groups/public/</url> <mirrorOf>central</mirrorOf></mirror> 四、Eclipse中配置maven、创建maven项目配置maven:https://www.cnblogs.com/pengyan-9826/p/7767070.html创建maven项目:https://blog.csdn.net/u012081441/article/details/75201197 五、安装spring-tool-suit插件https://www.cnblogs.com/MrYoodb/p/7574566.html]]></content>
<categories>
<category>JAVA</category>
</categories>
<tags>
<tag>博客</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Servlet+Jsp项目实战]]></title>
<url>%2F2019%2F09%2F07%2FJava%2FServlet-Jsp%E9%A1%B9%E7%9B%AE%E5%AE%9E%E6%88%98%2F</url>
<content type="text"><![CDATA[信息管理系统技术需求 Servlet+jsp+mvc+jdbc 软件需求: 开发工具:myEclipse 数据库:MYSQL 服务器:Tomcat 浏览器:Firefox 硬件需求: 一台电脑 功能需求: 完成用户登录 完成用户注册 完成用户退出 完成查看个人信息 完成修改密码 完成查询所有用户信息 数据库设计:创建用户表:t_user 表结构设计: 字段名 类型 约束 uid int(10) 主键、非空、自增 uname varchar(50) 非空 pwd varchar(50) 非空 sex char(2) 非空 age int(3) birth date 代码规范:命名规范: 包名:com.zs.* 类名:首字母大写,见名知意 变量名和方法名:驼峰原则,见名知意 注释规范: 方法功能注释 方法体核心位置必须有说明注释 日志规范: 使用log4j进行日志输出]]></content>
<categories>
<category>JAVA</category>
</categories>
<tags>
<tag>博客</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Jsp知识点整合]]></title>
<url>%2F2019%2F09%2F07%2FJava%2FJsp%E7%9F%A5%E8%AF%86%E7%82%B9%E6%95%B4%E5%90%88%2F</url>
<content type="text"><![CDATA[JSP 与 PHP、ASP、ASP.NET 等语言类似,运行在服务端的语言。 JSP(全称Java Server Pages)是由 Sun Microsystems 公司倡导和许多公司参与共同创建的一种使软件开发者可以响应客户端请求,而动态生成 HTML、XML 或其他格式文档的Web网页的技术标准。 JSP 技术是以 Java 语言作为脚本语言的,JSP 网页为整个服务器端的 Java 库单元提供了一个接口来服务于HTTP的应用程序。 JSP文件后缀名为 *.jsp 。 JSP开发的WEB应用可以跨平台使用,既可以运行在 Linux 上也能运行在 Windows 上。 Jsp的三种注释前端语言注释:会被转译,也会被发送java语言注释:会被转译,不会被servlet执行Jsp注释:不会被转译 Jsp的Page指令:1<%@page 属性名="属性值" 属性名="属性值" ...%> language:声明jsp要被转义的语言(其实只能java)。 import:导入java包,不同的包使用逗号隔开 pageEncoding:当前jsp文件所保存的编码格式。 session:设置转义的servlet中是否开启session支持 errorPage:设置jsp运行错误跳转的页面 extends:设置jsp转义的java文件要继承的父类(包名加类名) 作用: 配置jsp文件转译相关的参数,必须要有。 jsp的局部代码块: 声明java代码 1<% int i = 4;out.println(i);%> 特点:局部代码块中声明的java代码会被原样转译到对应servlet文件的_JspServlce方法中 缺点:使用局部代码快在jsp中进行逻辑判断,书写麻烦,阅读困难! jsp的全局代码块:123<%! public void test(){ System.out.println("全局代码块");}%> 特点:声明的java代码作为全局代码转译到sevlet中,不在servlce方法里。 注意:全局代码快声明的代码需要局部代码快调用。 jsp的脚本段语句: 特点:帮助我们快速的获取变量或者方法的返回值作为数据响应给浏览器 使用:<%=变量名或方法%>, 注意:不要在后面加分号 注意使用位置,不要瞎写。 jsp的静态引入和动态引入: 静态引入: 1<%@include file="要引入文件的相对路径" %> 特点: 会将引入的jsp文件和当前jsp文件转译成一个java文件使用。 注意: 静态引入的jsp文件不会单独转译成单个java(Servlet)文件。 不能有同名变量。 动态引入: 1<jsp:include page="要引入的jsp文件的相对路径"%> 特点: 会将引入法人jsp文件单独转译,也就是会新转译出一个java文件。 在当前转译好的java文件中调用引入的jsp文件的转译java文件,在网页中显示合并后的结果。 因为是两个文件,调用关系,所以可以存在同名变量。 注意: 动态引入允许声明同名变量引入的优点: 降低jsp代码的冗余,便于维护升级。 jsp的转发标签–forward:1<jsp:forward page="要转发的jsp文件的相对路径"></jsp:forward> 特点: 一次请求 地址栏信息不改变 注意: 转发标签的额两个标签之间不能有空格 但是可以写<jsp:param name=”str” value=”aaa” />,它会将数据以?的形式拼接在转发路径的后面 转发之后如何接收: 1<%=request.getParameter("str")%> 可以直接输出value的值。 jsp的九大内置对象: 内置对象:jsp文件在转移成对应的Servlet文件的时候,自动生成的并声明的变量。 注意: 内置对象在jsp页面中使用,使用局部代码快或脚本段语句来使用,不能再全局代码块中使用 九大对象: pageContext:页面上下文对象,封存了其他内置对象。 注意:每个Jsp文件单独拥有一个pageContext对象 作用域:当前页面。 request:封存当前请求数据的对象,由tomcat服务器创建,一次请求。 session:此对象用来存储用户的不同请求的共享数据的,一次会话。 application:也就是ServletContext对象,一个项目只有一个,存储用户共享数据的对象,以及完成其他操作。 response:响应对象,用来响应请求处理结果给浏览器的对象,设置响应头,重定向。 out:响应对象,Jsp内部使用,带有缓冲区的响应对象,效率高于response对象。 Page:代表当前Jsp的对象,相当于Java中的this(其实转译完之后就是this….)。 exception:异常对象,存储了当前的运行异常信息。 注意:使用此对象需要在Page中设置属性isErrorPage=”true”开启。 config:也就是ServletConfig,主要用来获取web.xml的配置数据,完成一些初始化数据的读取。 Jsp四大作用域: pageContext:当前页面,当前页面内的数据共享。 request:一次请求内的servlet的数据共享,通过请求转发,将数据流转给下一个servlet。 session:一次会话。一个用户的不同请求数据的共享。将数据从一次请求流转给其他请求。 application:项目内。不同用户的数据共享问题。将数据从一个用户流转给其他用户。 作用: 数据的流转! Jsp的路径: 在Jsp中资源路径可以使用相对路径完成跳转,但是: 问题一:资源的位置不可以随意更改。 问题二:需要使用../进行文件夹的跳出,使用麻烦,可读性较低。 使用绝对路径(开发最常用,必须会): /虚拟项目名/项目资源路径 “/“代表服务器根目录 相当于:ip:端口号 例如:localhost:8080 Jsp中自带的全局路径声明: 1234<% String path = request.getContextPath(); String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";%> 还有head标签下的: 1<base href="<%=basePath%>"> 作用:给资源全面加项目路径:http://127.0.0.1:8080/虚拟项目名/]]></content>
<categories>
<category>JAVA</category>
</categories>
<tags>
<tag>博客</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Servlet转发与重定向的区别和联系]]></title>
<url>%2F2019%2F09%2F06%2FJava%2FServlet%E8%BD%AC%E5%8F%91%E4%B8%8E%E9%87%8D%E5%AE%9A%E5%90%91%E7%9A%84%E5%8C%BA%E5%88%AB%2F</url>
<content type="text"><![CDATA[本人正在学习java,之前一直困扰二者有什么区别,经过一番查阅资料,终于恍然大悟。 Servlet的转发有两种:转发和重定向。 转发: 1httpServletRequest.getRequestDispatcher("资源路径").forward(httpServletRequest, httpServletResponse); 重定向: 1httpServletResponse.sendRedirect("/资源路径"); 转发的过程: 浏览器向服务器发送请求 服务器接收请求并处理请求 这时服务器发现有转发代码存在, 直接跳转到新的资源(注意:这个过程是连续的,在这个过程中session可以跟随传递) 网页显示返回结果,地址栏不会变化 重定向的过程: 浏览器向服务器发送请求 服务器接收请求并处理请求 这时服务器发现有重定向代码存在 服务器会立即通知浏览器,告诉它,你去访问这个资源 这时浏览器会对新资源重新发起访问(这个过程是断开的,中间不连续) 地址栏产生相应的变化 二者的区别: 转发地址栏不会变化,重定向会变化。 转发是一次请求,而重定向是两次。 转发速度较快,重定向较慢(因为浏览器要重新发起请求)。 由于重定向是重新对资源发起访问,而浏览器默认访问方式为get,所以对应的新响应要换成get,当然这是默认情况。注意一下即可。 转发不会造成信息丢失,而重定向则会造成信息丢失。 转发只能将请求转发给同一个WEB应用中的组件,重定向可以指向任何的资源,包括当前应用程序中的其他资源,同一个站点上的其他应用程序中的资源,其他站点的资源。** 如何选择转发还是重定向? 典型的应用场景: 转发:访问 Servlet 处理业务逻辑,然后 forward 到 jsp 显示处理结果,浏览器里 URL 不变 重定向:提交表单,处理成功后 redirect 到另一个 jsp,防止表单重复提交,浏览器里 URL 变了]]></content>
<categories>
<category>JAVA</category>
</categories>
<tags>
<tag>博客</tag>
</tags>
</entry>
</search>