4

介绍

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

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.boostrdev.legacy.weblogic</groupId>
    <artifactId>spring-boot-legacy-weblogic</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>

    <name>Spring Boot Legacy WebLogic</name>
    <description>Demo project for deploying a Spring Boot to a legacy (10.3.5) weblogic environment using servlet 2.5</description>

    <properties>
        <!-- Overrides the spring.version in the parent pom -->
        <spring.version>4.2.5.RELEASE</spring.version>
        <spring.boot.version>1.1.12.RELEASE</spring.boot.version>
    </properties>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <!-- Using version 1.1.12 for Java SE 6 compatibility -->
        <version>1.1.12.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-legacy</artifactId>
            <version>1.0.2.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>${spring.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <configuration>
                    <attachClasses>true</attachClasses>
                    <archive>
                        <manifestEntries>
                            <Weblogic-Application-Version>${project.version}</Weblogic-Application-Version>
                        </manifestEntries>
                        <manifest>
                            <addClasspath>true</addClasspath>
                            <addClasspath>lib/</addClasspath>
                        </manifest>
                    </archive>
                    <webResources>
                        <resource>
                            <directory>${project.basedir}/src/main/resources/static</directory>
                        </resource>
                        <resource>
                            <directory>${project.basedir}/src/main/webapp</directory>
                        </resource>
                    </webResources>
                    <warName>${project.artifactId}</warName>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring.boot.version}</version>
                <configuration>
                    <classifier>BOOT</classifier>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>


</project>
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

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>com.yaya.SpringBootWebLogicApplication</param-value>
    </context-param>

    <listener>
        <listener-class>org.springframework.boot.legacy.context.web.SpringBootContextLoaderListener</listener-class>
    </listener>

    <filter>
        <filter-name>metricFilter</filter-name>
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>

    <filter-mapping>
        <filter-name>metricFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <servlet>
        <servlet-name>appServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextAttribute</param-name>
            <param-value>org.springframework.web.context.WebApplicationContext.ROOT</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>appServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

</web-app>

weblogic.xml

<?xml version="1.0" encoding="UTF-8"?>
<wls:weblogic-web-app
        xmlns:wls="http://xmlns.oracle.com/weblogic/weblogic-web-app"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="
        http://xmlns.oracle.com/weblogic/weblogic-web-app
        http://xmlns.oracle.com/weblogic/weblogic-web-app/1.4/weblogic-web-app.xsd">
    <wls:context-root>/spring-boot-weblogic-app</wls:context-root>
    <wls:container-descriptor>
        <wls:prefer-application-packages>
            <wls:package-name>org.slf4j.*</wls:package-name>
            <wls:package-name>org.springframework.*</wls:package-name>
        </wls:prefer-application-packages>
    </wls:container-descriptor>
</wls:weblogic-web-app>

此时目录结构

.
├── 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用参数代替

<?xml version="1.0" encoding="UTF-8"?>
<wls:weblogic-web-app
        xmlns:wls="http://xmlns.oracle.com/weblogic/weblogic-web-app"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="
        http://xmlns.oracle.com/weblogic/weblogic-web-app
        http://xmlns.oracle.com/weblogic/weblogic-web-app/1.4/weblogic-web-app.xsd">
    <wls:context-root>/{app-name}</wls:context-root>
    <wls:container-descriptor>
        <wls:prefer-application-packages>
            <wls:package-name>org.slf4j.*</wls:package-name>
            <wls:package-name>org.springframework.*</wls:package-name>
        </wls:prefer-application-packages>
    </wls:container-descriptor>
</wls:weblogic-web-app>

修改脚本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...


DQuery
300 声望93 粉丝

幸福是奋斗出来的