一、安装与环境
1.idea中tomcat乱码:
a. file - settings - 搜File Encodings,改为utf-8
b.打开idea工作目录,在idea64.exe.vmoptions和idea.exe.vmoptions最后追加-Dfile.encoding=UTF-8
c.配置tomcat的页面中:VM option设置:-Dfile.encoding=UTF-8
2.热部署问题(jsp+java)
Update:更新操作(经我测试,很多时候无效)
Frame:idea失去焦点时触发
推荐选项:
Update:任意
Frame:update classes and resources
idea:热部署,如果是run启动,仅JSP等静态资源有效
如果是debug启动,java和jsp等均有效
总结 热部署:
a. Frame:update classes and resources
b. 以debug模式启动
注意:编写servlet前 需要先加入tomcat环境
3.JNDI:java命名与目录接口
pageContext < request< session< application(一个项目运行期间都有效)
String jndiName = "jndiValue" ;
abc 对象(资源....)
jndi:将某一个资源(对象),以配置文件(tomcat/conf/context.xml)的形式写入;
实现步骤:
tomcat/conf/context.xml配置:
<Environment name="jndiName" value="jndiValue" type="java.lang.String" />
jsp中用:
<%
Context ctx = new InitialContext() ;
String testJndi = (String)ctx.lookup("java:comp/env/jndiName");
out.print(testJndi);
%>
4.连接池
常见连接池:Tomcat-dbcp、dbcp、c3p0、druid
可以用数据源(javax.sql.DataSource)管理连接池
Tomcat-dbcp:
a.类似jndi,在context.xml中配置数据库
<Resource name="student" auth="Container" type="javax.sql.DataSource"
maxActive="400"
maxIdle="20" maxWait="5000" username="scott" password="tiger"
driverClassName="oracle.jdbc.driver.OracleDriver" url="jdbc:oracle:thin:@127.0.0.1:1521:ORCL" />
b.在项目的web.xml中
<resource-ref>
<res-ref-name>student</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
c.使用数据源
更改 连接对象Connection的获取方式 :
传统Jdbc方式
connection = DriverManager.getConnection(URL,USERNAME,PASSWORD);
数据源方式:
Context ctx = new InitialContext() ;//context.xml
DataSource ds = (DataSource)ctx.lookup("java:comp/env/student") ;
connection = ds.getConnection();
tomcat-dbcp数据源总结: a.配置数据源(context.xml) b.指定数据源(web.xml) c.用数据源 :通过数据库获取Connection
5.dbcp连接池
连接池:
怎么用?
不用连接池
Class.forName();
Connection connection = DriverManager.getConnection();//连接指向数据库
用连接池的核心:将连接的指向改了,现在指向的是数据源 而不是数据库。
....-> DataSource ds = ....
Connection connection =ds.getConnection();//指向的是数据源的连接
数据库访问核心 -> pstmt/stmt ->connection ->1.直接数据库 2.数据源 ds.getConnection()
PreparedStatemnt pstmt = connection.preparedStatement();
commons-dbcp-1.4.jar、commons-pool.jar
dbcp获取ds: BasicDataSource、BasicDataSourceFactory
BasicDataSource方式(硬编码):DasicDataSource对象设置各种数据
BasicDataSourceFactory方式: 配置方式(.properties文件, 编写方式key=value)
dbcpconfig.properties
BasicDataSourceFactory.createDataSource(props )
通用:NoClassDefFoundError异常 说明少jar
DataSource 是所有sql数据源的上级类。BasicDataSource是dbcp类型的数据源,ComboPooledDataSource是c3p0类型的数据源;XxxDataSource...
jar包问题:
Eclipse 中的web项目引入jar:只需要 jar 放入web-info/lib
idea中web项目jar包问题:
a.web-info/lib :在编写时不需要使用,在部署运行时使用(ojdbc.jar)
b.加到类路径中(Module-dependencies...) :在编写代码时,需要使用的java类(在写代码时用到BasicDataSource类,而该类存在于commons-dbcp.jar中,因此commons-dbcp.jar就要放在类路径中)
6.c3p0连接池 :ComboPooledDataSource
两种方式
硬编码
配置文件
->合二为一 ,通过ComboPooledDataSource的构造方法参数区分:如果无参,硬编码;有参,配置文件
c3p0.jar c3p0-oracle-thin-extras.jar
a.无参,硬编码
ComboPooledDataSource c3p0 = new ComboPooledDataSource();
c3p0.set...();
b.有参,配置文件(c3p0-config.xml)
oralce修改密码:
管理员状态 sqlplus / as sysdba
alter user scott identified by tiger ;
所有连接池的思路:
a.硬编码,某个连接池数据源的 对象 ds = new XxxDataSource(); ds.setXxx(); return ds ;
b.配置文件, ds = new XxxDataSource();加载配置文件 ,return ds ;
7.数据源工具类 dbcp、c3p0, druidApache DBUtils
下载commons-dbutils-1.7.jar,其中包含以下几个重点类:
DbUtils、QueryRunner、ResultSetHandler
1.
DbUtils:辅助
2.QueryRunner:增删改查
update()
query()
oracle:dml,commit
mysql:dml自动提交
3.如果是查询,则需要ResultSetHandler接口,有很多实现类,一个实现类对应于一种 不同的查询结果类型
select * from student -> List<T>
select count(1) from student ; int
-- Object[] , {1,zs}
实现类ArrayHandler :返回结果集中的第一行数据,并用Object[]接收
Object[] = {1 ,zs,23};
实现类ArrayListHandler:返回结果集中的多行数据, List<Object[]>
--Student (1 ,zs)
BeanHandler :返回结果集中的第一行数据,用对象(Student)接收
BeanListHandler:返回结果集中的多行数据, List<Student> students, stu stu2 stu3
BeanMapHandler: 1:stu,2:stu2, 3:stu3
//反射会通过无参构造来创建对象
-- Map
MapHandler::返回结果集中的第一行数据
{id=1 ,name=zs}
String,int
String,String
-->
String,Object
MapListHandler:返回结果集中的多行数据
{{id=2 ,name=ls},{id=3 ,name=ww}}
KeyedHanlder:
{ls={id=2 ,name=ls},ww={id=3 ,name=ww}}
-->
ColumnListHander :把結果集中的某一列 保存到List中
2,ls
3,ww
{ls,ww}
-->
select count(1) from xxx ; 23
select name from student where id =2 ;
ScalarHandler :单值结果
问题:查询的实现类的参数问题
query(..., Object... params )
其中Object... params代表可变参数: 既可以写单值,也可以写一个数组
runner.query("... where id = ? and name like ? " ,new ArrayHandler(),new Object[]{1,"%s%"}) ;
runner.query("... where id = ?" ,new ArrayHandler(),1) ;
ORA-00904: "NAMEIKE": 标识符无效 : SQL关键字写错, ojdbc.jar版本问题
4.apache dbutils 增删改
自动提交事务 update(sql,参数);update(sql);
手动提交事务 update(connection ,sql,参数);
5.手动提交事务
转账: zs -1000 -> ls +1000
基础知识:
①
如果既要保证数据安全,又要保证性能,可以考虑ThreadLocal
ThreadLocal:可以为每个线程 复制一个副本。每个线程可以访问自己内部的副本。 别名 线程本地变量
set():给tl中存放一个 变量
get():从tl中获取变量(副本),
remove();删除副本
②对于数据库来说,一个连接 对应于一个事务 ,一个事务可以包含多个DML操作
Service(多个原子操作) -> Dao(原子操作)
转账
Service(转账) -> Dao(a.减少 b.增加)
Service ->(connection ->psmst ->update)update (connection ->psmst ->update)update : 如果给每个 dao操作 都创建一个connection,则 多个dao操作对应于多个事务;
但是 一般来讲,一个业务(service) 中的多个dao操作 应该包含在一个事务中。
->解决,ThreadLocal, 在第一个dao操作时 真正的创建一个connection对象,然后在其他几次dao操作时,借助于tl本身特性 自动将该connection复制多个(connection只创建了一个,因此该connection中的所有操作 必然对应于同一个事务; 并且tl将connection在使用层面复制了多个,因此可以同时完成多个dao操作)
事务流程: 开启事务(将自动提交->手工提交) ->进行各种DML ->正常,将刚才所有DML全部提交 (全部成功)
->失败(异常),将刚才所有DML全部回滚(全部失败)
事务的操作 全部和连接Connection密切相关
元数据(MetaData):描述数据的数据
三类:
数据库元数据 、参数元数据、结果集元数据
1.数据库元数据 DataBaseMetaData
Connection -> DataBaseMetaData -> .
2.参数元数据 ParameterMetaData
pstmt -> ParameterMetaData -> .
3.结果集元数据 ResultSetMetaData
ResultSet -> ResultSetMetaData
自定义标签
1.步骤:a.编写标签处理类 b.编写标签描述符 c.导入并使用
a.编写标签处理类
传统方式(JSP1.1):实现javax.servlet.jsp.tagext.Tag接口 doStartTag()
简单方式(JSP2.0):实现javax.servlet.jsp.tagext.SimpleTag接口 doTag()
如果jsp在编译阶段 发现了自定义标签<xx:yyy > ,就会交给doStartTag()或doTag()
b.编写标签描述符 tld
编写建议:可以仿照一个 其他标签语言(el jstl ) 的tld文件
c.导入并使用
位置:myTag.tld导入 WEB-INF或子目录下WEB-INF/abc/ (特例排除,不能是WEB-INF/lib、WEB-INF/classes )
使用:
引入具体要使用的.tld文件
<%@ taglib uri="..." prefix="d" %>
uri:每个tld文件的 唯一描述符
prefix:使用tld标签时 的前缀
myTag1.tld
myTag2.tld
myTag3.tld
具体的使用:
a.空标签(没有标签体的标签)
<d:foreach></d:foreach>
<d:foreach />
b.带标签体
<d:foreach> xxx </d:foreach>
c.带属性
<d:foreach 属性名="属性值" > xxx </d:foreach>
<d:foreach collection="${students}" > xxx </d:foreach>
d.嵌套
<d:foreach>
<d:foreach> xxx </d:foreach>
</d:foreach>
<d:foreach>
xxx
<d:...>
<%xxx%>
实际开发步骤:
a.编写标签处理类
先确保项目有tomcat环境
Tag接口:
doStartTag():标签处理类的核心方法 (标签体的执行逻辑)
该方法有以下2个返回值:0/1
int SKIP_BODY = 0; 标签体不会被执行
int EVAL_BODY_INCLUDE = 1; 标签体会被执行
doEndTag():标签执行完毕之后 的方法.例如可以让 标签在执行完毕后,再执行一次
int SKIP_PAGE = 5;后面的JSP页面内容不被执行
int EVAL_PAGE = 6;后面的JSP页面内容继续执行
Tag接口中的所有方法执行顺序:
jsp - servlet
当JSP容器(Tomcat、jetty)在将.jsp翻译成.servlet(.java)的时候 ,如果遇到JSP中有标签,
就会依次执行 setPageContext() setParent() doStartTag() doEndTag realease(),用于解析标签的执行逻辑。
javax.servlet.jsp.tagext.IterationTag接口:(是Tag的子接口)
如果有循环:IterationTag,
没有循环:Tag
IterationTag接口中存在以下方法:
doAfterBody():当标签体执行完毕之后的操作 ,通过返回值决定 :
(EVAL_BODY_AGAIN = 2)要么重复执行 ;
(SKIP_BODY=0):要么不再执行
目标:遍历3次 (hello)
<xxx>hello<xxx>
执行一次,并重复两次
b.编写标签描述符
myTag.tld
c.导入并使用
<%@ taglib uri="http://www.yanqun.com" prefix="yq" %>
..
<body>
<yq:mytag num="3">
hello
</yq:mytag>
</body>
jstl: <c:foreach ....> </...> 使用
<yq:mytag num="3"> 处理类,tld,使用
BoyTag接口:如果在标签体 被显示之前,进行一些其他的“额外”操作
hello hello hello -> HELLO HELLO HELLO
包含属性:
int EVAL_BODY_BUFFERED = 2, 是doStartTag()的第三个返回值 ,代表一个缓冲区(BodyContent)。
BodyContent是abstract,具体使用时需要使用实现类 BodyContentImpl(再引入jasper.jar)
缓冲区(BodyContent)的使用用途:hello ->HELLO
如果返回值是EVAL_BODY_BUFFERED ,则服务器会自动 将标签体需要显示的内容 放入缓冲区中(BodyContent)
。因此,如果要更改最终显示结果,只需要从 缓冲区 获取原来的数据 进行修改即可。
如何修改、获取 缓冲区:详见BodyContent的方法,具体就是 通过getXxx()获取原来的数据(hello),自己修改(HELLO),输出getEnclosingWriter();
a.编写标签处理类
b.编写标签描述符 c.导入并使用
简单方式
SimpleTag
最大的简化:
将传统方式的doStartTag() doEndTag() doafterBody()等方法 简化成了一个通用的 doTag()方法。
doTag() :传统方式 可以对标签的最终显示 进行修改, hello ->HELLO ,核心是有一个缓冲区。
但是简单方式 没有“缓冲区”。 如何修改显示内容? 流
javax.servlet.jsp.tagext.JspFragment类 :代表一块JSP元素(该块 不包含scriptlet,因此简单方式的tld文件中<body-content>不能是JSP )
JspFragment中有一个invoke(Writer var1) 方法,入参是 “流”,即如果要修改显示内容,只需要修改此 流
invoke(Writer var1) :每调用一次invoke()方法,会执行一次 标签体。
SimpleTagSupport的 getJspBody()可以获取JspFragment对象。
SimpleTagSupport的 getJspContext()方法 可以获取 jsp一些内置对象:getJspContext()的返回值是JspContext对象 是JSP内置对象的入口对象PageContext 的父类。
简单标签方式 ,获取JSP内置对象: getJspContext() -> JspContext ->转成子类PageContext ->PageContext就是所有JSP内置对象的入口,即可以获取一切JSP内置对象
条件选择:
当某一个条件满足时,再执行某个标签体
传统标签:如果条件不满足,让doStartTag()的返回值为0;
简单标签(不允许写scriplet <% ...%>): 如果条件不满足,不调用invoke()即可。
集群:apache、
nginx
tomcat:理论上 单节点tomcat能够稳定的 处理请求并发量200-300;
负载均衡
失败迁移
服务端集群:
a.水平集群 :将服务器安装在 各个不同的计算机上 (失败迁移)
b.垂直集群 :将多个服务器,安装在同一个计算机上 (负载均衡)
水平+垂直
搭建集群:
apache :特点是处理静态资源(html 图片 js) .这里的apache是一个服务工具,不是 之前理解的 基金组织。
tomcat:特点 可以处理动态资源
apache+tomcat:动静分离
apache:请求的分流操作
下载apache服务器工具
https://www.apachehaus.com/cgi-bin/download.plx?dli=QYGxmNRVVQ08ERjtWZVp1cKVlUGR1UwllWUJVe
配置:
conf/http.conf
Define SRVROOT "D:\dev\cluster\Apache24"
看一看将apache配置成windows服务:
管理员身份打开cmd,通过命令注册apache服务
注册成功后,启动,localhost 查看是否成功访问
准备tomcat:复制两份,改端口
i.规划并修改端口: server.xml
server端口号 http协议端口 ajp协议端口号
tomcat-a: 1005 1080 1009
tomcat-b: 2005 2080 2009
ii.配置引擎Engine : server.xml
增加jvmRoute
<Engine name="Catalina" defaultHost="localhost" jvmRoute="tomcat-b">
iii.打開集群开关 server.xml
打开以下注释
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
tomcat集群配置搞定,apache启动
->结合apache+tomcat :mod_jk.so
http://archive.apache.org/dist/tomcat/tomcat-connectors/jk/binaries/windows/ 中的httpd.zip
配置mod_jk.so:
a.存放位置,\Apache24\modules\mod_jk.so
b.配置\Apache24\conf\workers.properties
分布式session策略:
1.sticky:固定将每一个用户的请求 分给特定的服务器,后期的请求不会分给其他服务器
2.session广播(自动同步session): 自动同步session,
弊端:如果服务器太多,可能造成广播风暴(将一个服务器的session,需要同步到其他所有的服务器中)
3.集中管理方式(推荐):将各个服务器的session集群存储到一个 数据库中
c.配置\Apache24\conf\mod_jk.conf (用于加载mod_jk.so和workers.properties)
其中JkMount /* controller,表示拦截一切请求。也可以之拦截jsp: /*.jsp
mod_jk.so,workers.properties ->mod_jk.conf -> apache程序会自动加载httpd.conf
配置httpd.conf: 在apache启动时 自动加载mod_jk.conf:
追加include conf/mod_jk.conf
集群:应用阶段、部署实施
CATALINA_HOME会使 启动tomcat时 自动开启CATALINA_HOME指定的tomcat。而集群中 需要开启多个不同的tomcat,因此 在单机环境下,需要删除CATALINA_HOME。
依次启动apache、tomcata、tomcatb
原因:
apache之前已经成功运行,但是重启时 失败
分析思路:
成功运行的时机:第一把apche下载完后
失败时机: apache+tomcat整合
得出结论: 整合
测试:失败迁移 负载均衡 session共享
解决bug
细节:
apache:
worker.list=controller,tomcata,tomcatb
tomcat配置:(只需要保证 所有tomcat的jvmRoute不能重名即可 ,可以和apache中的命名不一致)
jvmRoute="tomcat-a"
jvmRoute="tomcat-b"
流程:apache->workers.properties中配置的 ip:端口 找到具体的tomcat服务(与tomcat中的jvmRoute="tomcat-a"的没有关系)
tomcat服务的目录名:(任意)
tomcata
tomcatb