最近出现一个问题,部署在weblogic的war包,启动后,除了Spring的标示日志,就没有其他日志。
百思不得其解,就开始排查

  1. 开始排查了logback.xml,以为是配置问题 -> 使用可正常打印的war包的logback.xml配置:排除这个问题
  2. 后面排查weblogic.xml,以为是包优先加载的问题 -> 加入对应包设置,依旧无效:排除问题
  3. 开始怀疑是maven包引用问题,排查pom.xml
    发现包里有一个
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.3</version>
</dependency>
取消引入,解决

按道理,微服务应该jar包+docker/k8s的方式运行,但总有这么个操蛋时候需要通过war包运行
那这时候小葵花妈妈课堂开课啦~
踩过的坑就要为后面的铺平道路

Eureka Client配置更改

# Tomcat/Weblogic的服务端口
server.port=8080
# 注册的服务名,这个要和你的war包名称一致
# 比如文件是demo.war,那么这里就是demo
wl.name=demo
# 应用名称
spring.application.name=${wl.name}
# 指定服务的请求路径前缀
server.servlet.context-path=/${wl.name}
eureka.instance.prefer-ip-address=true
# 设置eureka server的注册地址
# 需要注意的是,后面的‘/eureka/eureka/’,
# 这里前面的/eureka/是固定写法,后面的‘eureka/’是eureka server部署在容器里面的context-path
eureka.client.service-url.defaultZone=http://10.1.24.227:${server.port}/eureka/eureka/
eureka.instance.hostname=${wl.name}
# 微服务路径前缀
eureka.instance.home-page-url-path=/${wl.name}
# 设置eureka client的地址
eureka.instance.instance-id=${spring.cloud.client.ip-address}:${server.port}/${wl.name}
#心跳时间/服务续约间隔时间(默认30s)
eureka.instance.lease-renewal-interval-in-seconds=10
#发呆(无心跳淘汰时间)时间/服务续约到期时间(默认90s)
eureka.instance.lease-expiration-duration-in-seconds=20
#健康检查
eureka.client.healthcheck.enabled=true

Feign 配置

@FeignClient(
    # 服务名 spring.application.name
    name = "demo",
    # 服务路径前缀 server.servlet.context-path
    path = "/demo")
public interface DemoFeign {
    // xxxx实际业务
}
重要提醒,如果你的war包没有设置context-root,那么你的war包名称就要和spring.application.name保持一致
这样就可以正常被访问了
下课~

最近项目在迁移weblogic(12c), 就遇到了多多少少的问题

这次遇到: @WebFilter不生效: 是整个不生效

// 源代码
@Slf4j
@ServletComponentScan
@Component
@WebFilter(filterName = "jwtFilter", urlPatterns = {"/register"})
public class JwtFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        // 业务逻辑省略
        log.info("--- 日志打印 ---");
    }

}
这样启动之后, 就发现控制台完全没有相关日志打印

踩坑过程

1. 搜索出来的结果主要是说需要配置web.xml: 但是@WebFilter本来就是web.xml的升级版??
2. 再搜索说的是urlPatterns不生效: 但我这个是压根没反应
下面是不明所以的解决方法
    // 加init即可
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        
    }
有明白为什么的欢迎留言告诉我= =

早些前写了Cacheable的自定义缓存啥的, 这次优化了一下Cacheable注解错误时缓存

需缓存的方法

@Cacheable(key = "#cardNo + '_' + #unifTranId", cacheNames = "bill.usTrade")
public TradeSkinResDto<StmtTradeResDto> usTrade(String cardNo, String unifTranId) {
    return null;
}

AOP

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * redis缓存调用切面
 * <p>
 * 由于服务调用时, 返回的可能是错误的信息, 因此需要将错误信息也缓存起来
 * <p>
 * 方法中使用这个 @Cacheable 注解, 就会处理返回的参数
 *
 * @author roothk
 * @date 2020/4/23 8:53
 */
@Slf4j
@Order(1)
@Aspect
@Component
public class CacheableHandlerAspect {

    @Autowired
    private CacheableUtil cacheableUtil;

    @Pointcut("@annotation(org.springframework.cache.annotation.Cacheable)")
    public void feignAspectPointCut() {
    }

    /**
     * 环绕通知 @Around  , 当然也可以使用 @Before (前置通知)  @After (后置通知)
     *
     * @param point
     * @return
     * @throws Throwable
     */
    @Around("feignAspectPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        Cacheable cacheable = cacheableUtil.getCacheable(point);
        String cacheName = cacheable.cacheNames()[0];
        String key = cacheableUtil.getValueByKey(cacheable.key(), point);

        try {
            // 错误处理
            handlerException(key, cacheName);

            Object o = point.proceed();
            // 其他正常返回, 如空对象
            if (EsbThreadLocal.get()) {
                // 也依旧缓存, 防止缓存穿透
                cacheableUtil.saveCache(o, key, cacheName);
            }
            return o;
        } catch (EsbException e) {
            cacheableUtil.saveCache(e, key, cacheName);
            throw e;
        }
    }

    /**
     * 如果缓存报错的是Exception, 则直接抛出Exception
     *
     * @param key
     * @param cacheName
     */
    private void handlerException(Object key, String cacheName) {
        Object o = cacheableUtil.getCache(key, cacheName);
        if (o == null) {
            return;
        }
        // 如果是错误就直接输出
        if (o instanceof EsbException) {
            throw (EsbException) o;
        } else if (o instanceof EsbHystrixException) {
            throw (EsbHystrixException) o;
        } else if (o instanceof EsbServiceException) {
            throw (EsbServiceException) o;
        }
    }

}

CacheableUtil工具类

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * @author RootHK
 */
@Slf4j
@Component
public class CacheableUtil {

    private final SpelExpressionParser parserSpel = new SpelExpressionParser();
    private final DefaultParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();

    // 错误时的缓存时间
    @Value("${esb.cache.not.result.time}")
    private Long exceptionTimeout;

    @Autowired
    private CacheManager cacheManager;
    @Autowired
    private RedisCacheManager redisCacheManager;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 获取方法上的 @Cacheable
     *
     * @param point
     * @return
     */
    public Cacheable getCacheable(ProceedingJoinPoint point) {
        Signature signature = point.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method targetMethod = methodSignature.getMethod();
        return targetMethod.getAnnotation(Cacheable.class);
    }

    /**
     * 获取Spring Sl语法的值
     * @param key
     * @param pjp
     * @return
     */
    public String getValueByKey(String key, ProceedingJoinPoint pjp) {
        MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
        return getValueByKey(key, methodSignature.getMethod(), pjp.getArgs());
    }

    /**
     * 获取Spring Sl语法的值
     * @param key
     * @param m
     * @param args
     * @return
     */
    public String getValueByKey(String key, Method m, Object[] args) {
        Expression expression = parserSpel.parseExpression(key);
        EvaluationContext context = new StandardEvaluationContext();
        String[] paramNames = parameterNameDiscoverer.getParameterNames(m);
        for (int i = 0; i < args.length; i++) {
            if (paramNames == null) {
                continue;
            }
            context.setVariable(paramNames[i], args[i]);
        }
        Object o = expression.getValue(context);
        return o != null ? o.toString() : null;
    }

    public Object getCache(Object key, String cacheName) {
        // 获取指定命名空间的cache
        Cache cache = cacheManager.getCache(cacheName);
        if (cache == null) {
            if (log.isDebugEnabled()) {
                log.debug("------ 获取缓存: {} 失败", cacheName);
            }
            return null;
        }
        Cache.ValueWrapper wrapper = cache.get(key);
        if (wrapper == null) {
            // 空的
            return null;
        }
        return wrapper.get();
    }

    public Cache getCache(String cacheName) {
        return cacheManager.getCache(cacheName);
    }

    public void saveCache(Object o, Object key, String cacheName) {
        // 获取指定命名空间的cache
        Cache cache = this.getCache(cacheName);
        if (cache == null) {
            log.warn("获得cacheName失败, {}", cacheName);
            return;
        }
        // 加入缓存
        cache.put(key, o);
        try {
            // 获取redis的配置
            RedisCacheConfiguration configuration = redisCacheManager.getCacheConfigurations().get(cacheName);
            String redisKey = configuration.getKeyPrefixFor(cacheName).concat(key.toString());
            log.info("save exception|not result redis, key:{} timeout:{}", redisKey, exceptionTimeout);
            stringRedisTemplate.expire(redisKey, exceptionTimeout, TimeUnit.SECONDS);
        } catch (Exception e) {
            e.printStackTrace();
            log.info("获取RedisCacheConfiguration失败", e);
        }
    }
}

缓存配置 CacheConfig


import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.time.Duration;
import java.util.Objects;

/**
 * redis 缓存设置
 * @author RootHK
 */
@Configuration
@EnableCaching
public class CacheConfig extends CachingConfigurerSupport {

    // 正常缓存的时间
    @Value("${esb.cache.result.time}")
    private Long timeout;

    /**
     * 设置缓存策略
     *
     * @param redisTemplate
     * @return
     */
    @Bean
    public CacheManager cacheManager(StringRedisTemplate redisTemplate) {
        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
                .prefixCacheNameWith(SystemConstant.REDIS_PREFIX)
                .entryTtl(Duration.ofSeconds(timeout));
        RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(Objects.requireNonNull(redisTemplate.getConnectionFactory()));
        return new RedisCacheManager(redisCacheWriter, redisCacheConfiguration);
    }

}

Spring Boot: 2.1.9.RELEASE
Spring Cloud: Greenwich.SR3
Weblogic: 12c(12.2.1.3.0)

重点

MAVEN POM文件

<packaging>war</packaging>
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-tomcat</artifactId>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        <exclusions>
            <exclusion>
                <groupId>com.sun.jersey</groupId>
                <artifactId>jersey-client</artifactId>
            </exclusion>
            <exclusion>
                <groupId>com.sun.jersey</groupId>
                <artifactId>jersey-core</artifactId>
            </exclusion>
            <exclusion>
                <groupId>com.sun.jersey.contribs</groupId>
                <artifactId>jersey-apache-client4</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>javax.ws.rs</groupId>
        <artifactId>jsr311-api</artifactId>
        <version>1.1.1</version>
        <scope>provided</scope>
    </dependency>
</dependencies>
<build>
    <finalName>${project.artifactId}</finalName>
</build>

SpringBootServletInitializer

public class ServletInitializer extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(EurekaClientApplication.class);
    }

}

weblogic.xml

<?xml version="1.0" encoding="UTF-8"?>
<weblogic-web-app
        xmlns="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.9/weblogic-web-app.xsd">
    <context-root>/client</context-root>
    <container-descriptor>
        <prefer-application-packages>
            <package-name>net.minidev.json.*</package-name>
            <package-name>com.jayway.*</package-name>
            <package-name>org.slf4j.*</package-name>
            <package-name>com.sun.jersey.*</package-name>
            <package-name>org.springframework.*</package-name>
            <package-name>aj.org.objectweb.*</package-name>
            <package-name>antlr.*</package-name>
            <package-name>antlr.ASdebug.*</package-name>
            <package-name>antlr.actions.cpp.*</package-name>
            <package-name>antlr.actions.csharp.*</package-name>
            <package-name>antlr.actions.java.*</package-name>
            <package-name>antlr.actions.python.*</package-name>
            <package-name>antlr.build.*</package-name>
            <package-name>antlr.collections.*</package-name>
            <package-name>antlr.collections.impl.*</package-name>
            <package-name>antlr.debug.*</package-name>
            <package-name>antlr.debug.misc.*</package-name>
            <package-name>antlr.preprocessor.*</package-name>
            <package-name>com.ctc.wstx.*</package-name>
            <package-name>com.fasterxml.classmate.*</package-name>
            <package-name>com.fasterxml.jackson.*</package-name>
            <package-name>com.google.common.*</package-name>
            <package-name>com.google.thirdparty.*</package-name>
            <package-name>com.sun.research.*</package-name>
            <package-name>com.sun.ws.*</package-name>
            <package-name>javax.annotation.*</package-name>
            <package-name>javax.annotation.security.*</package-name>
            <package-name>javax.annotation.sql.*</package-name>
            <package-name>javax.inject.*</package-name>
            <package-name>javax.validation.*</package-name>
            <package-name>javax.validation.bootstrap.*</package-name>
            <package-name>javax.validation.constraints.*</package-name>
            <package-name>javax.validation.constraintvalidation.*</package-name>
            <package-name>javax.validation.executable.*</package-name>
            <package-name>javax.validation.groups.*</package-name>
            <package-name>javax.validation.metadata.*</package-name>
            <package-name>javax.validation.spi.*</package-name>
<!--            <package-name>javax.ws.rs.*</package-name>-->
            <package-name>jersey.repackaged.org.*</package-name>
            <package-name>org.antlr.runtime.*</package-name>
            <package-name>org.aopalliance.aop.*</package-name>
            <package-name>org.aopalliance.intercept.*</package-name>
            <package-name>org.apache.commons.*</package-name>
            <package-name>org.bouncycastle.*</package-name>
            <package-name>org.bouncycastle.asn1.*</package-name>
            <package-name>org.bouncycastle.cert.*</package-name>
            <package-name>org.bouncycastle.cms.*</package-name>
            <package-name>org.bouncycastle.crypto.*</package-name>
            <package-name>org.bouncycastle.dvcs.*</package-name>
            <package-name>org.bouncycastle.eac.*</package-name>
            <package-name>org.bouncycastle.i18n.*</package-name>
            <package-name>org.bouncycastle.jcajce.*</package-name>
            <package-name>org.bouncycastle.jce.*</package-name>
            <package-name>org.bouncycastle.math.*</package-name>
            <package-name>org.bouncycastle.mozilla.*</package-name>
            <package-name>org.bouncycastle.openssl.*</package-name>
            <package-name>org.bouncycastle.operator.*</package-name>
            <package-name>org.bouncycastle.pkcs.*</package-name>
            <package-name>org.bouncycastle.pkix.*</package-name>
            <package-name>org.bouncycastle.pqc.*</package-name>
            <package-name>org.bouncycastle.tsp.*</package-name>
            <package-name>org.bouncycastle.util.*</package-name>
            <package-name>org.bouncycastle.voms.*</package-name>
            <package-name>org.bouncycastle.x509.*</package-name>
            <package-name>org.codehaus.jettison.*</package-name>
            <package-name>org.codehaus.stax2.*</package-name>
            <package-name>org.hibernate.validator.*</package-name>
            <package-name>org.jboss.logging.*</package-name>
            <package-name>org.joda.time.*</package-name>
        </prefer-application-packages>
    </container-descriptor>
</weblogic-web-app>

jsr311-api-1.1.1.jar

将jsr311-api-1.1.1.jar放入WebLogic的公共模块目录中,具体位置为:
${WEBLOGIC_HOME}/Middleware/wlserver/modules/