介绍
spring-boot
应该是目前最火的java后台开发框架,现在java的简历里如果不贴个spring boo的标签都不好意思说自己是做java的,事实上现状就是如此,和当年ssh
盛行时一模一样.不过说归说,spring-boot
确实好用,学习成本低
,入门快
,开发效率高
,自启动
,天生适合容器化
.但要把spring-boot
部署到weblogic
上,尤其是低版本的weblogic上,还是要费点功夫.如果要求高一点,优雅的部署,则要费点脑筋,这里说的优雅的部署,是指开发人员不需要因为服务器的限制对程序做特殊的配置,增加其学习成本和工作量.本文则探讨几种我认为优雅的方式,如看官有更好的方案,欢迎留言一起讨论.
实现
环境
ide | IntelliJ IDEA 2017.2 |
weblogic | 10.3.6.0 |
weblogic-jdk | java version "1.8.0_171" |
传统实现方案
1. 创建一个支持(Support)maven
的工程
pom.xml
4.0.0
com.boostrdev.legacy.weblogic
spring-boot-legacy-weblogic
0.0.1-SNAPSHOT
war
Spring Boot Legacy WebLogic
Demo project for deploying a Spring Boot to a legacy (10.3.5) weblogic environment using servlet 2.5
4.2.5.RELEASE
1.1.12.RELEASE
org.springframework.boot
spring-boot-starter-parent
1.1.12.RELEASE
org.springframework.boot
spring-boot-legacy
1.0.2.RELEASE
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-tomcat
provided
javax.servlet
servlet-api
2.5
test
org.springframework.boot
spring-boot-starter-test
test
org.springframework
spring-web
${spring.version}
org.apache.maven.plugins
maven-war-plugin
true
${project.version}
true
lib/
${project.basedir}/src/main/resources/static
${project.basedir}/src/main/webapp
${project.artifactId}
org.springframework.boot
spring-boot-maven-plugin
${spring.boot.version}
BOOT
repackage
2. 创建spring boot
入口类和测试接口
SpringBootWebLogicApplication.java
package com.yaya;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.web.SpringBootServletInitializer;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.WebApplicationInitializer;
@EnableAutoConfiguration
@Configuration
@ComponentScan
public class SpringBootWebLogicApplication extends SpringBootServletInitializer implements WebApplicationInitializer {
public static void main(String[] args) {
SpringApplication.run(SpringBootWebLogicApplication.class, args);
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(SpringBootWebLogicApplication.class).showBanner(false);
}
}
TestService.java
package com.yaya;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* @Description:
* @author: jianfeng.zheng
* @since: 2018/7/20 上午9:58
* @history: 1.2018/7/20 created by jianfeng.zheng
*/
@RestController
public class TestService {
@RequestMapping("/welcome")
public String welcome(@RequestParam(name = "username") String username) {
return "welcome:" + username;
}
}
3. 创建web.xml
和 weblogic.xml
在IDEA
里创建这两个文件方法如下:
- File->Project-Structure->Project Settinngs->Facets
- 点击如图可以创建
web.xml
- 点击
Add Application Server specific...
选择weblogic
并指定版本创建weblogic.xml
.(IDEA这个功能简直太贴心)
web.xml
contextConfigLocation
com.yaya.SpringBootWebLogicApplication
org.springframework.boot.legacy.context.web.SpringBootContextLoaderListener
metricFilter
org.springframework.web.filter.DelegatingFilterProxy
metricFilter
/*
appServlet
org.springframework.web.servlet.DispatcherServlet
contextAttribute
org.springframework.web.context.WebApplicationContext.ROOT
1
appServlet
/
weblogic.xml
/spring-boot-weblogic-app
org.slf4j.*
org.springframework.*
此时目录结构
.
├── SpringBootWeblogicDemo.iml
├── WEB-INF
│ ├── web.xml
│ └── weblogic.xml
├── pom.xml
└── src
├── main
│ ├── java
│ │ └── com
│ │ └── yaya
│ │ ├── SpringBootWebLogicApplication.java
│ │ └── TestService.java
│ ├── resources
│ │ └── static
│ └── webapp
└── test
└── java
4. 构建war包
选择菜单Build->BuildArtifacts->SpringBootWeblogicDemo:war->Build
构建完的war包默认在target目录下spring-boot-weblogic.war
5. 部署war包
登录weblogic
控制台选择Deployments->Install
选择上面的war包部署.
6. 测试
curl http://10.1.11.118:7001/spring-boot-weblogic-app/welcome?username=jianfeng.zheng
welcome:jianfeng.zheng
存在的问题
如果你不嫌麻烦,那么到这里你已经把你的spring-boot部署到weblogic上,后面的内容可以不看.上面的方式虽然解决了问题,但也存在很多不足之处
- 配置项略多,增加学习成本(实际上不多,但新人动手能力令人发指)
- 因为入口类继承了
SpringBootServletInitializer
本地运行缺少Servlet环境,无法运行,调试起来麻烦(不知道怎么解决,如果知道欢迎评论留言)
理想的情况是,通过SPRING INITIALIZR生成spring-boot
项目,开发完后通过某种手段
可以实现快速部署weblogic的目的.接下来就探讨解决方案
脚本打包
基本思路是
- 本地开发提交工程到svn或者git
- 使用
maven
进行编译 - 将以上配置项和编译好到class文件一起重新打包
第3步详细讲下
可以将spring-boot-weblogic.war
解压开看下目录结构
.
├── ./META-INF
│ └── ./META-INF/MANIFEST.MF
└── ./WEB-INF
├── ./WEB-INF/classes
│ └── ./WEB-INF/classes/com
│ └── ./WEB-INF/classes/com/yaya
│ ├── ./WEB-INF/classes/com/yaya/SpringBootWebLogicApplication.class
│ └── ./WEB-INF/classes/com/yaya/TestService.class
├── ./WEB-INF/lib
│ ├── ./WEB-INF/lib/spring-aop-4.2.5.RELEASE.jar
│ ├── ./WEB-INF/lib/spring-beans-4.2.5.RELEASE.jar
.....
├── ./WEB-INF/web.xml
└── ./WEB-INF/weblogic.xml
思路就是将以下文件和用户编译好的文件一起重新打包,并保持目录结构
- SpringBootWebLogicApplication.class
- /WEB/INF/lib/*.jar
- web.xml
- weblogic.xml
可以将这些文件从war包里解压出来放到指定目录下,这里称这个目录为pre-compiled-path
shell
脚本如下:
spring-boot-war.sh
#!/bin/bash
#用户工程目录
v_s=$1
#脚本运行临时目录
v_d=$1/temp
#存放web.xml等文件目录
v_m=$2
rm -rf $v_d
mkdir -p $v_d
cp $v_s/pom.xml $v_d
cp -r $v_s/src $v_d
cd $v_d
#编译代码(跳过单元测试)
mvn compile -Dmaven.test.skip=true
mkdir -p $v_d/war
v_war=$v_d/war
cd $v_war
mkdir -p $v_war/WEB-INF
#拷贝编译好的class文件
cp -r $v_d/target/classes $v_war/WEB-INF/
#spring-boot入口类路径(可以写死)
v_app=$v_war/WEB-INF/classes/com/yaya/
mkdir -p $v_app
#拷贝入口类
cp $v_m/SpringBootWebLogicApplication.class $v_app
#拷贝web.xml和weblogic.xml
cp $v_m/web.xml $v_war/WEB-INF/
cp $v_m/weblogic.xml $v_war/WEB-INF/
#拷贝spring-boot依赖jar包
cp -r $v_m/lib $v_war/WEB-INF/lib
cd $v_war
#重新开始打包
jar -cvfM spring-boot-weblogic.war .
运行脚本,完成编译打包
./spring-boot.sh /you/spring-boot/source/path /you/pre-compiled/path
脚本运行成功后,在工程目录下,会创建一个temp
目录,目标war
包就在该目录下.
虽然完成了打包,并且也能成功部署到weblogic上运行但这个脚本目前context-root
和war包名称都是写死的,我们需要一个能动态命名的脚本.解决的方案是,用sed
命令对文本进行替换,名称由脚本参数指定.
修改weblogic.xml
将wls:context-root
用参数代替
/{app-name}
org.slf4j.*
org.springframework.*
修改脚本spring-boot-war.sh
#!/bin/bash
#用户工程目录
v_s=$1
#脚本运行临时目录
v_d=$1/temp
#存放web.xml等文件目录
v_m=$2
v_a=$3
rm -rf $v_d
mkdir -p $v_d
cp $v_s/pom.xml $v_d
cp -r $v_s/src $v_d
cd $v_d
#编译代码(跳过单元测试)
mvn compile -Dmaven.test.skip=true
mkdir -p $v_d/war
v_war=$v_d/war
cd $v_war
mkdir -p $v_war/WEB-INF
#拷贝编译好的class文件
cp -r $v_d/target/classes $v_war/WEB-INF/
#spring-boot入口类路径(可以写死)
v_app=$v_war/WEB-INF/classes/com/yaya/
mkdir -p $v_app
#拷贝入口类
cp $v_m/SpringBootWebLogicApplication.class $v_app
#拷贝web.xml和weblogic.xml
cp $v_m/web.xml $v_war/WEB-INF/
cp $v_m/weblogic.xml $v_war/WEB-INF/
#替换app-name
sed -i '' "s/{app-name}/${v_a}/g" $v_war/WEB-INF/weblogic.xml
#拷贝spring-boot依赖jar包
cp -r $v_m/lib $v_war/WEB-INF/lib
cd $v_war
#重新开始打包(文件名用变量替换)
jar -cvfM $v_a.war .
运行脚本
./spring-boot.sh /you/spring-boot/source/path /you/pre-compiled/path app-name
脚本会根据指定的app-name
生成名称不同,context-root不同的war包.
自动部署
可以借助wlst.sh
工具,将war
包自动部署到weblogic上.因为要使用wlst.sh
工具,所以服务器需要预先安装好weblogic
.脚本使用jython语言编写.
deployApp.py
print '\n Begin deploy application'
try:
mUsername=sys.argv[1]
mPassword=sys.argv[2];
mConsoleURL=sys.argv[3];
mAppName=sys.argv[4];
mAppPath=sys.argv[5];
mTarget=sys.argv[6];
print('\n mServerName:')
print(mTarget)
print('\n appName:')
print(mAppName)
print('\n appPath:')
print(mAppPath)
connect(mUsername,mPassword,mConsoleURL)
progress=deploy(mAppName,mAppPath,targets=mTarget,upload='true',timeout=10000000)
dumpStack()
progress.printStatus()
except Exception, ex:
print ex.getMessage()
print '#########################################################'
print '##### Deploy Failed #########'
print '##### Contact support with Exception Stack #########'
print '#########################################################'
exit()
mUsername:console用户名
mPassword:console密码
mConsoleURL:console路径(带端口)
mAppName:app name
mAppPath:war包路径
mTarget:目标服务器,可以是server和集群名称
修改脚本spring-boot-war.sh
#!/bin/bash
#用户工程目录
v_s=$1
#脚本运行临时目录
v_d=$1/temp
#存放web.xml等文件目录
v_m=$2
#app-name
v_a=$3
#WLS_HOME weblogic安装目录
v_wls=$4
rm -rf $v_d
mkdir -p $v_d
cp $v_s/pom.xml $v_d
cp -r $v_s/src $v_d
cd $v_d
#编译代码(跳过单元测试)
mvn compile -Dmaven.test.skip=true
mkdir -p $v_d/war
v_war=$v_d/war
cd $v_war
mkdir -p $v_war/WEB-INF
#拷贝编译好的class文件
cp -r $v_d/target/classes $v_war/WEB-INF/
#spring-boot入口类路径(可以写死)
v_app=$v_war/WEB-INF/classes/com/yaya/
mkdir -p $v_app
#拷贝入口类
cp $v_m/SpringBootWebLogicApplication.class $v_app
#拷贝web.xml和weblogic.xml
cp $v_m/web.xml $v_war/WEB-INF/
cp $v_m/weblogic.xml $v_war/WEB-INF/
#替换app-name
sed -i '' "s/{app-name}/${v_a}/g" $v_war/WEB-INF/weblogic.xml
#拷贝spring-boot依赖jar包
cp -r $v_m/lib $v_war/WEB-INF/lib
cd $v_war
#重新开始打包(文件名用变量替换)
jar -cvfM $v_a.war .
#开始部署
$v_wls/common/bin/wlst.sh /script/path/deployApp.py weblogic admin-password admin-host:7001 $v_a $v_war/$v_a.war app_server
这样就可以将war包自动部署到weblogic上,实现整个过程自动化.
还有哪些需要做的
以上只是解决了部署过程中最核心的几个问题,但还有很多问题一定会碰到但未解决的:
- 怎么解决第三发依赖包的问题
- 如何做到版本管理,应用回滚
- 是否支持所有spring-boot特性
- 等等..
生产上的需求远远不是本地做个poc验证通过就可以解决的,但千里之行始于足下,走出第一步,就已经成功一半.
一些问题记录
- 编译jdk和weblogic 运行jdk需要1.8或以上版本
- 脚本在
MacOS 10.12.4
上运行通过,如果运行出错请自行根据操作系统版本做修改,一般是sed
命令会跟操作系统版本有关. - 运行
spring-boot
的weblogic server不要部署任何oracle 中间件产品,可能会有jar包冲突
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private org.springframework.boot.autoconfigure.web.ServerProperties org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration.properties; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'serverProperties' defined in class path resource [org/springframework/boot/autoconfigure/web/ServerPropertiesAutoConfiguration.class]: Initialization of bean failed; nested exception is java.lang.AbstractMethodError: org.apache.openjpa.persistence.PersistenceProviderImpl.getProviderUtil()Ljavax/persistence/spi/ProviderUtil;
参考
https://github.com/bamiidowu/...
https://docs.oracle.com/cd/E1...