JavaWeb的IDEA开发/数据连接池/验证码/集群

一、安装与环境

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


 

    

    


 

发布了18 篇原创文章 · 获赞 4 · 访问量 5256

猜你喜欢

转载自blog.csdn.net/Zzw1129/article/details/104481166