Spring boot开发笔记

15 篇文章 1 订阅
  1. 自动扫描配置不需要扫描某个包或者某个类,下面的写法不会扫描org.xxx.yyy.*下的包以及MyClassToExclude.class

 

@ComponentScan(            
        excludeFilters = {
                @ComponentScan.Filter(type = FilterType.REGEX, pattern = "org.xxx.yyy.*"),
                @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = MyClassToExclude.class) })

 

  2.  Multipart上传文件,过一段时间以后将会出现org.springframework.web.multipart.MultipartException,解决办法在大神的博客中,增加-Djava.io.tmpdir启动参数,或者修改程序new MultipartConfigFactory().setLocation("/app/pttms/tmp"),我采用的是在配置文件中增加server.tomcat.basedir=${user.home}/deployer/tomcat

 

3.弹出浏览器自带的用户名和密码框, 输入用户名和密码以后每次请求在Header中都会自动带上Authorization: Basic Base64.encode("username:password")

	public static void ret401(HttpServletRequest request, HttpServletResponse response){
		String serverName = request.getServerName();
		response.setStatus(401);
		response.setHeader("Cache-Control", "no-store");
		response.setDateHeader("Expires", 0);
		response.setHeader("WWW-authenticate", "Basic Realm=\"" + serverName + "\"");
	}

4.RestTemplate的坑

RestTemplate会对url会自动进行编码,如果url的参数已经编码过就会出现重复编码, 并且带有特殊符号的参数自动编码的结果和URLEncoder.encode编码的结果并不是完全一致

 解决办法如下: 如果url的参数已经Encode过了则不再重新编码, UricomponentsBuilder.fromHttpUrl(url).build(true).toUri()或者new URI(url)

UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
// url的参数已经编码过了,所以不需要再次编码
ResponseEntity<String> responseEntity = restTemplate.getForEntity(builder.build(true).toUri(), String.class);

或者

ResponseEntity<String> responseEntity = restTemplate.getForEntity(new URI(url), String.class);

5. 日志冲突问题

经常出现log4j和logback产生冲突的错误,看以下几篇文章基本都能解决

最终只能使用一种具体日志框架打印slf4j接口日志,排除多余的日志框架然后用log4j-over-slf4、jcl-over-slf4j等依赖包将依赖jar包中的日志转接到slf4j中进行打印

Logback统一配置及环境变量加载问题

Slf4j官方冲突文档

slf4j、jcl、jul、log4j1、log4j2、logback大总结

 

6. logback.xml根据环境切换日志配置

需求背景: 公司要求把所有的错误日志统一搜集到阿里云日志loghub中,但是开发环境明显会有很多错误不需要上传

方法一: 创建不同环境的文件夹,每个文件夹下都有一个logback.xml,用maven的resources配置引入logback.xml

    <build>
		<resources>
			<resource>
				<filtering>true</filtering>
				<directory>src/main/resources/${profiles.active}</directory>
			</resource>
        </resources>
   ...
   </build>

方法二:使用logback-spring.xml

application.properties文件中加入

logging.config=classpath:logback-spring.xml

logback-spring.xml

    <springProfile name="prod">
        <root>
            <level value="INFO"/>
            <appender-ref ref="STDOUT" />
            <appender-ref ref="FILE" />
            <appender-ref ref="loghub" />
        </root>
    </springProfile>
    <springProfile name="dev,test,uat">
        <root>
            <level value="INFO"/>
            <appender-ref ref="STDOUT" />
            <appender-ref ref="FILE" />
        </root>
    </springProfile>

方法三: 自定义LoggerStartupListener类, 缺点是还需要配置系统变量或者环境变量

参考文章: 根据环境变量切换logback配置

System.getenv 获取系统变量

System.getProperties获取环境变量,启动命令中-Dspring.profiles.active=dev可以通过getProperties拿到

--spring.profiles.active=dev 只能通过main方法的args拿到

可以在main方法中把启动变量塞到环境变量中,然后在LoggerStartupListener通过System.getProperties("spring.profiles.active")拿到当前环境变量

	public static void main(String[] args) {
		for(String arg: args){
			if(arg.startsWith("--spring.profiles.active")){
				System.getProperties().put("spring.profiles.active", arg.replace("--spring.profiles.active=",""));
			}
		}
        ...
	}
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.spi.LoggerContextListener;
import ch.qos.logback.core.Context;
import ch.qos.logback.core.spi.ContextAwareBase;
import ch.qos.logback.core.spi.LifeCycle;
import org.apache.commons.lang.StringUtils;

public class LoggerStartupListener extends ContextAwareBase implements LoggerContextListener, LifeCycle {
    private boolean isStarted = false;

    @Override
    public void start() {
        if(isStarted){
            return;
        }
        String profile = System.getProperty("spring.profiles.active","dev");

        Context context = getContext();
        context.putProperty("ACTIVE_PROFILE", profile);

        isStarted = true;
    }

}

方法四:使用logback.{env}.xml, 参照一种Spring下的logback多profile配置的暴力流解决方案

方法五:使用logback.{env}.xml, 参照一种相对优雅的Spring下logback多profile配置解决方案

方法六: 推荐,一个logback.xml,一个logback-loghub.xml文件, loghub的配置在logback-loghub.xml文件中

只需要在application-prod.properties中增加一条配置:logging.config=classpath:logback-loghub.xml

7.Memcached连接池问题

com.danga.Memcached和spymemcached的key哈希规则不一样,同一个公司的系统最好统一使用一种连接池框架

com.danga.Memcached的默认哈希规则比较简单,就是key.hashCode()%serverCount

python可以实现和java一样的hashCode方法

 private final long getBucket(String key, Integer hashCode) {
		long hc = getHash(key, hashCode);

		if (this.hashingAlg == CONSISTENT_HASH) {
			return findPointFor(hc);
		} else {
			long bucket = hc % buckets.size();
			if (bucket < 0)
				bucket *= -1;
			return bucket;
		}
	}

	private final long getHash(String key, Integer hashCode) {

		if (hashCode != null) {
			if (hashingAlg == CONSISTENT_HASH)
				return hashCode.longValue() & 0xffffffffL;
			else
				return hashCode.longValue();
		} else {
			switch (hashingAlg) {
			case NATIVE_HASH:
				return (long) key.hashCode();
			case OLD_COMPAT_HASH:
				return origCompatHashingAlg(key);
			case NEW_COMPAT_HASH:
				return newCompatHashingAlg(key);
			case CONSISTENT_HASH:
				return md5HashingAlg(key);
			default:
				// use the native hash as a default
				hashingAlg = NATIVE_HASH;
				return (long) key.hashCode();
			}
		}
	}

8.获取resources下静态文件的问题

打包成jar包使用内嵌tomcat容器的时候,只能拿到某个文件的文件流InputStream,拿不到文件对象File

// 方法一
ClassPathResource classPathResource = new ClassPathResource("1.txt");
InputStream inputStream = classPathResource.getInputStream();// 能够拿到文件流,但是classPathResource.getFile()拿不到文件
// 方法二
InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("1.txt");
// 方法三
InputStream inputStream = this.getClass().getResourceAsStream("1.txt");

// 如果要遍历一个目录下所有的文件,先从InputStream中得到所有的文件名,拼接每个文件的完整路径,再重新获取每个文件的文件流
String directoryPath = "dict";
List<String> files = IOUtils.readLines(new ClassPathResource(directoryPath).getInputStream()); // 返回的是目录下所有文件或者文件夹的名字
for(String fileName:files){
   InputStream fileInputStream = new ClassPathResource(directoryPath+File.separator+fileName).getInputStream()
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值