如何优雅的在weblogic上部署spring-boot

介绍

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.xmlweblogic.xml

IDEA里创建这两个文件方法如下:

  1. File->Project-Structure->Project Settinngs->Facets
  2. 点击如图可以创建web.xml

  1. 点击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上,后面的内容可以不看.上面的方式虽然解决了问题,但也存在很多不足之处

  1. 配置项略多,增加学习成本(实际上不多,但新人动手能力令人发指)
  2. 因为入口类继承了SpringBootServletInitializer本地运行缺少Servlet环境,无法运行,调试起来麻烦(不知道怎么解决,如果知道欢迎评论留言)

理想的情况是,通过SPRING INITIALIZR生成spring-boot项目,开发完后通过某种手段可以实现快速部署weblogic的目的.接下来就探讨解决方案

脚本打包

基本思路是

  1. 本地开发提交工程到svn或者git
  2. 使用maven进行编译
  3. 将以上配置项和编译好到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

思路就是将以下文件和用户编译好的文件一起重新打包,并保持目录结构

  1. SpringBootWebLogicApplication.class
  2. /WEB/INF/lib/*.jar
  3. web.xml
  4. 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.xmlwls: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上,实现整个过程自动化.

还有哪些需要做的

以上只是解决了部署过程中最核心的几个问题,但还有很多问题一定会碰到但未解决的:

  1. 怎么解决第三发依赖包的问题
  2. 如何做到版本管理,应用回滚
  3. 是否支持所有spring-boot特性
  4. 等等..

生产上的需求远远不是本地做个poc验证通过就可以解决的,但千里之行始于足下,走出第一步,就已经成功一半.

一些问题记录

  1. 编译jdk和weblogic 运行jdk需要1.8或以上版本
  2. 脚本在MacOS 10.12.4上运行通过,如果运行出错请自行根据操作系统版本做修改,一般是sed命令会跟操作系统版本有关.
  3. 运行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...

你可能感兴趣的:(jenkins,springboot,spring,weblogic)