记一次在CentOS 7上部署Java测试环境的过程

将之前配置LNMJ测试环境的过程归个档,方便日后查阅。

1) JDK环境

下载并安装
1
2
cd usr
mkdir java

当时不小心下到了JDKDemo的包,直接下JDK包时发现下载下来的是网页,于是只好手动下载再上传。

1
2
cd java
rpm -ivh jdk-8u131-linux-x64.rpm
配置环境
1
vi /etc/profile

按I进入编辑模式,将如下内容加入profile中:

1
2
export JAVA_HOME=/usr/java/jdk1.8.0_131
export CLASSPATH=.:$JAVA_HOME/jre/lib/rt.jar:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar

输入

1
source /etc/profile

命令,让配置生效。
最后和Windows平台一样,输入java -version来验证安装。

2)Tomcat的配置

下载并安装

wget下载安装包,并且解压。

1
tar -zxvf apache-tomcat-x.x.xx.tar.gz

修改文件夹名字方便后续使用。

配置环境变量

编辑/etc/profile,在末尾加上:

1
2
3
export CATALINA_HOME=/usr/apache-tomcat
export CATALINA_BASE=/usr/apache-tomcat
export PATH=$PATH:$JAVA_HOME/bin:$CATALINA_HOME/bin

刷新并运行Tomcat进行测试:

1
2
source /etc/profile
/usr/apache-tomcat/bin/startup.sh
将Tomcat配置为系统服务,方便用systemctrl管理:
  • 在Tomcat的bin目录下,新建setenv.sh脚本。Tomcat启动时会自动运行这个脚本。
    1
    CATALINA_PID="$CATALINA_BASE/tomcat.pid"
  • 使用vi编辑/usr/lib/systemd/system/tomcat.service文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    [Unit]
    Description=Tomcat
    After=syslog.target network.target remote-fs.target nss-lookup.target
    [Service]
    Type=forking
    PIDFile=/usr/apache-tomcat/tomcat.pid
    ExecStart=/usr/apache-tomcat/bin/startup.sh
    ExecReload=/bin/kill -s HUP $MAINPID
    ExecStop=/bin/kill -s QUIT $MAINPID
    PrivateTmp=true
    [Install]
    WantedBy=multi-user.target

然后就可以用systemctrl命令来管理Tomcat的开机启动等问题了。

当时出了一个很奇怪的bug,我本地运行没有问题的war包,上传到服务器上就是404,在Tomcat管理页面里也能看到这个WebAPP在运行。
最后我直接删掉了服务器Tomcat,将本地Tomcat直接上传上去,并把权限改为0777,最终解决问题。

3)MySQL

MySQL这事比较复杂,Oracle收购了MySQL之后,准备在MySQL6.x收费,然后社区开发了一个叫MariaDB的分支,采用GPL授权,以此应对。
当然我还是不准备采用这个分支,能求稳就别浪……

下载并安装
1
2
3
wget http://dev.mysql.com/get/mysql-community-release-el7-5.noarch.rpm
rpm -ivh mysql-community-release-el7-5.noarch.rpm
yum install mysql-community-server
启动

运行mysql -u root启动。
(值得注意的是,只有root账户没有密码的时候才能这么启动,否则返回Access denied,不会提示你输入密码。在有密码的时候需要加上-p参数。)
进入mysql命令行之后,运行SQL语句设置密码:

1
set password for 'root'@'localhost' =password('密码');

当时配置完成后死活连不上,最后发现CentOS不是用IPTABLE作为防火墙,而是使用了firewall。
更换为IPTABLE,并且开放端口后问题解决。

一些配置:
  • 更改编码:
    1
    vi /etc/my.cnf
    在文件末尾添加:
    1
    2
    [mysql]
    default-character-set =utf8
  • 设置其他IP可以连接:
    在MySQL命令行中执行:
    1
    grant all privileges on *.* to root@'%'identified by '你的密码';

4)nginx

nginx的安装给我一种印象,下载的似乎是源代码,还要下一个gcc的编译器,现场编译现场用。
(不知道是不是真的,如有谬误请指正)

安装依赖
1
2
3
4
yum install gcc-c++  
yum install pcre pcre-devel
yum install zlib zlib-devel
yum install openssl openssl--devel
安装nginx本体
1
2
wget http://nginx.org/download/nginx-1.7.12.tar.gz
tar -zxvf nginx-1.7.12.tar.gz

并重命名目录,去掉版本号,方便后续使用。
当时安装完后,因为firewall的问题(我看错,认为80端口是打开的)以为配置出了错,最后一怒之下关了防火墙一切正常了。
决定换回熟悉的iptable。
#####配置nginx代理Tomcat
编辑nginx.conf文件,在http-server-location-proxy_pass段中填入:

1
2
http://127.0.0.1:8080
#此处8080为Tomcat默认端口号
将nginx配置为服务
  • 先编辑nginx.conf文件:
    1
    vi /usr/local/ngnix/conf/nginx.conf
    将里面的pid段后面的路径复制出来。
  • 使用vi编辑/usr/lib/systemd/system/nginx.service
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    [Unit]
    Description=nginx - high performance web server
    Documentation=http://nginx.org/en/docs/
    After=network.target remote-fs.target nss-lookup.target
    [Service]
    Type=forking
    #与nginx.conf一致
    PIDFile=/usr/local/nginx/logs/nginx.pid
    #启动前检测配置文件 是否正确
    ExecStartPre=/usr/sbin/nginx -t -c /usr/local/nginx/conf/nginx.conf
    #启动
    ExecStart=/usr/sbin/nginx -c /usr/local/nginx/conf/nginx.conf
    #重启
    ExecReload=/bin/kill -s HUP $MAINPID
    #关闭
    ExecStop=/bin/kill -s QUIT $MAINPID
    PrivateTmp=true
    [Install]
    WantedBy=multi-user.target

至此,JDK Tomcat Nginx MySQL全部配置完成。
可以将war包上传到Tomcat的webapp目录下,Tomcat会自动部署。

学习JNDI时的一点感受。

关于JNDI,较为学术向的解释:

1
2
3
4
J2EE 规范要求所有 J2EE 容器都要提供 JNDI 规范的实现。
JNDI 在 J2EE 中的角色就是“交换机” —— J2EE 组件在运行时间接地查找其他组件、资源或服务的通用机制。
在多数情况下,提供 JNDI 供应者的容器可以充当有限的数据存储,这样管理员就可以设置应用程序的执行属性,并让其他应用程序引用这些属性。
在 J2EE 中,JNDI 是把 J2EE 应用程序合在一起的粘合剂,但还没有紧到无法让人很容易地把它们分开并重新装配。

在IBM DeveloperWork我看到了这样一个例子:
https://www.ibm.com/developerworks/cn/java/j-jndi/index.html

大致的内容就是,Dolly在编写JDBC程序时,将数据库驱动、连接池的数量等写死在程序中。
实际部署时,情况发生了变化,但是程序却很难修改。

文章中还提到,J2EE 规范把职责委托给多个开发角色:编写程序的人,打包为程序包的人,部署人员和运维人员。
(实际开发中,1、2的活经常一个人干,3、4则另一个人干, 就是我们熟悉的程序员和运维。)

J2EE中定义了JNDI规范,它的一个最简单的实例,上述例子中也有给出:

1
2
3
4
5
6
<resource-ref>
<description>Dollys DataSource</description>
<res-ref-name>jdbc/mydatasource</res-ref-name>
<res-ref-type>javax.sql.DataSource</res-ref-type>
<res-auth>Container</res-auth>
</resource-ref>

这是一段写在web.xml中的代码,它的作用是构建一个数据源:

1
java:comp/env/jdbc/mydatasource

以便在稍后的编码中使用它。

在查找参考资料时,我见到过一些早期的加载Spring框架的applicationcontext.xml的办法,就是在web.xml中用类似手段定义好一个资源,初始化Spring时,把它作为参数传入进去。

在培训班学习时,我一度认为J2EE是简陋的、原生的、被淘汰的技术。
在我的了解中,现在没有人直接用JSP、Servlet、JDBC开发Web应用,取而代之的是SpringMVC,分层开发,开源框架。
web.xml的作用仅仅是配置拦截器,以及初始化Spring容器,而JDBC和JPA早已不见踪影,EJB也仅仅存在于各种数年前的文档中。

在看完这篇文章后,我发现之前的认识过于浅薄。
J2EE并不是只有Servlet和JSP,EJB实际上是一套类似Spring的IOC/DI容器,它甚至也有AOP的功能。
JNDI接口就是一个用于解耦的规范,将部署人员和运维人员才需要配置的东西独立出去。上文中1和2只需要在程序内定义JNDI格式的数据源,而3和4根据文档配置这个数据源就能使用了。

那么问题来了,为什么Java会有同人逼死官方的现象出现呢……

面试题——强引用和Spring的模块

2018-10-19更新:关于Spring模块的理解

spring-expression是一套内置的EL表达式语言,参考文章
spring-instrument是早期版本Spring对老版本Tomcat等Web容器的支持,参考文章;
spring-tx事务相关也有用过了,@Transaction注解等
spring-messaging这块则是用于消息交互,现代似乎更倾向于REST,目前还没有用过

关于强引用:


强引用:

个人理解:强引用指向的对象,会霸占堆空间不走
学术解释:只要强引用还在,GC永远不会回收掉被引用的对象(via 深入理解JVM),如果内存不足就OOM。

1
Object o=new Object();

软引用:

个人理解:设置了软引用的对象,会在内存里苟且偷生到内存不足的时候,被用作缓存。
学术解释:用于描述非必需对象,在系统发生内存溢出异常之前,将会把软引用列入回收对象进行第二次回收,如果还是没有足够的内存才会抛出内存溢出。(via 深入理解JVM)

1
SoftReference<String> softRef=new SoftReference<String>(str); 

弱引用:

个人理解:与软引用类似,但是它只能活到下一次GC
学术解释:软引用、弱引用可以和一个引用队列联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。
(关于引用队列暂时理解不了,先死记下来)

虚引用:

个人理解:唯一的作用是,虚引用关联的对象被回收时会收到一个通知,用于细粒度的内存控制。
学术解释:有虚引用的对象必须联合引用队列使用,被回收时会将这个引用加入到关联的队列里。

Spring的模块


(稍微扯几句,是不是只有我一个人觉得java是同人逼死官方,Mybatis逼死了JPA,Spring逼死了EJB)

核心容器

  • spring-core提供了框架的基本组成部分,包括 IoC 和DI功能。(见名知义,核心)

  • spring-beans提供BeanFactory,它是一个工厂模式的复杂实现。(管理beans)

  • spring-context是访问定义和配置的任何对象的媒介。ApplicationContext 接口是上下文模块的重点。(用来处理XML配置文件)

  • spring-expression(Spring Expression Language ,SpEL)在运行时提供了查询和操作一个对象图的强大的表达式语言。(暂时没有用到过。)

数据访问/集成

  • spring-jdbc提供了删除冗余的 JDBC 相关编码的 JDBC 抽象层。(让jdbc简洁化,然而现在主流用Mybatis、Hibernate的多,一般还整合了连接池,这个似乎用不到)

  • spring-orm(Spring Object/Relational Mapping)为流行的对象关系映射 API,包括 JPA,JDO,Hibernate 和 iBatis,提供了集成层。(将数据库虚拟化为对象,)

  • spring-oxm(Spring Object/XML Marshalling)提供了抽象层,它支持对 JAXB,Castor,XMLBeans,JiBX 和 XStream 的对象/XML 映射实现。

  • spring-jms包含生产和消费的信息的功能。(之前尝试配置远程Tomcat时遇到过这块的错误,但是我没有去解决,目前是我的知识盲区,有待学习)

  • spring-tx模块为实现特殊接口的类及所有的 POJO 支持编程式和声明式事务管理。(暂时没用过Spring的事务管理……)

Web 层

  • spring-web提供了基本的面向 web 的集成功能,例如多个文件上传的功能和使用 servlet 监听器和面向 web 应用程序的上下文来初始化 IoC 容器。(就是把自己做成一个servlet,容器一启动Spring也就启动了)

  • spring-webmvc包含 Spring 的模型-视图-控制器(MVC),实现了 web 应用程序。(就是现在著名的SpringMVC)

  • spring-websocket为 WebSocket-based 提供了支持,而且在 web 应用程序中提供了客户端和服务器端之间通信的两种方式。(知识盲区,并没有用过_(:з」∠)_)

  • spring-webmvc-portlet提供了在 portlet 环境中实现 MVC,并且反映了 Web-Servlet 模块的功能。(知识盲区,并没有用过_(:з」∠)_)

其他

  • spring-aop提供了面向切面的编程实现,允许你定义方法拦截器和切入点对代码进行干净地解耦,它实现了应该分离的功能。(另一种解耦方式,单独开发,需要的时候插入)

  • spring-aspects提供了与 AspectJ 的集成,这是一个功能强大且成熟的面向切面编程(AOP)框架。(结合aop使用,实现织入)

  • spring-instrument在一定的应用服务器中提供了类 instrumentation的支持和类加载器的实现。(?????)

  • spring-messaging为 STOMP 提供了支持作为在应用程序中 WebSocket 子协议的使用。它也支持一个注解编程模型,它是为了选路和处理来自 WebSocket 客户端的 STOMP 信息。(?????)

  • spring-test支持对具有 JUnit 或 TestNG 框架的 Spring 组件的测试。(@Runwith注解,支持Test类中运行Spring框架,而不用启动web容器)

面试题——Java线程的状态

Java线程的状态

1)NEW

线程创建之后,启动之前的状态。

1
2
Thread thread = new Thread();
thread.getState();//NEW

2)RUNNABLE

启动完成,正常运行的线程。
(需要注意的是,线程等待系统资源时也会处于该状态,例如BIO等待网络资源的时候)

3)BLOCKED

被阻塞的状态,出现在多线程环境下。
线程A占用了一个synchronize的方法/代码块/对象,这时候线程B如果也想占用这个资源,就会处于BLOCKED状态

4)WAITING

等待中的状态,由于执行了Object.wait();或者Thread.join()并且没有指定Timeout,以及LockSupport.park()而导致。
如果调用了Object.notify()或者notifyAll()方法,等待状态就会结束。

5)TIMED_WAITING

如果调用了Thread.sleep();或者Object.wait(),Thread.join(),并指定了Timeout,
或者LockSupport的parkNanos(),parkUntil()方法,并指定了Timeout。线程就会进入TimedWaiting状态。

6)TERMINATED

线程的run方法执行完毕后,在虚拟机层面上的状态。