Java笔记
目录:
- 1. 配置
- 2. Spring
- 3. Shiro
- 4. 集成Swagger
- 5. 常见问题
- 1. IDEA 2016 使用junit4
- 2. tomcat无响应
- 3. Maven项目(spring, mybatis…)在eclipse中可以运行,无法在Idea中运行
- 4. 使用Idea时提示无法找到配置文件
- 5. 引入第三方库
- 6. lib已存在但抛出java.lang.NoClassDefFoundError
- 7. jetty多实例部署
- 8. linux环境下调用so库
- 9. Spring MVC get请求带中文乱码
- 10. Maven同时编译Jar和War包
- 11. Spring MVC中Cacheable未生效
- 12. 同一对象的两个synchronized方法不能同时被访问
- 13. Maven项目依赖的”scope”项配置为”system”时的问题
- 14. Spring MVC 打印请求日志
- 15. MySql 索引未生效问题
- 16. 动态修改 Java Class
- 17. Mysql 字符串类型数据大小写敏感问题
- 18. OpenJDK FontConfiguration.getVersion() 抛出空指针异常
- 19. Spring 范型不一致时会导致 Bean 注入失败
- 20. Driver 自动注册机制
- 21. Stream#reduce 使用
- 22. RestTemplate 请求数据类型为 String 时返回中文乱码
- 23. 通过反射构造内部非静态类实例
- 参考资料
环境:
- 主机: ubuntu 16.04 LTS
- 服务器: Tomcat8, Jetty 9.4
- VM: OpenJDK 8
- IntelliJ IDEA Ultimate
1. 配置
1. Ubuntu安装Tomcat
Ubuntu 默认把 Tomcat 分到2个目录, Eclipse 无法识别安装的 Tomcat。 解决方案:(注意下面路径有些有空格)
sudo ln -s /var/lib/tomcat7/conf /usr/share/tomcat7/conf
sudo ln -s /etc/tomcat7/policy.d/03catalina.policy /usr/share/tomcat7/conf/catalina.policy
sudo ln -s /var/log/tomcat7 /usr/share/tomcat7/log
sudo ln -s /var/lib/tomcat7/webapps /usr/share/tomcat7/
sudo chmod -R 777 /usr/share/tomcat7/conf
2.Centos安装mysql
1. 安装
2. 配置
1. 开启MySql远程连接
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'youpassword' WITH GRANT OPTION;
重载授权表:
FLUSH PRIVILEGES;
NOTE: 如果是ubuntu的话需要更改配置文件,取消登录ip绑定。
如果使用的是 MariaDB,默认登录为unix_socket
,想使用账号密码登录,需把mysql.user.plugin
改为mysql_native_password
。
2. 安装配置tomcat
- 准备Java环境: 安装JDK
- 下载tomcat到指定的目录
- 解压,执行
./bin/startup.sh
- 启动成功之后,可查看8080端口是否被监控:
netstat -antp
NOTE: 如果提示“org.apache.catalina.core.AprLifecycleListener.lifecycleEvent The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: /usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/lib”,可到apache官网下载APR来安装,重启即可。
3. centos6配置iptables
- 添加允许访问的端口
/sbin/iptables -I INPUT -p tcp --dport 80 -j ACCEPT /sbin/iptables -I INPUT -p tcp --dport 3306 -j ACCEPT /sbin/iptables -I INPUT -p tcp --dport 22 -j ACCEPT /sbin/iptables -I INPUT -p tcp --dport 8080 -j ACCEPT
- 保存
service iptables save
- 重启服务
service iptables restart
4. Tomcat 在linux下开机自启动
- 修改/etc/rc.d/rc.local
vim /etc/rc.d/rc.local
- 添加下面两行脚本
export JAVA_HOME=$JAVA_PATH$ /usr/local/tomcat/bin/startup.sh start
- 修改rc.local文件为可执行
chmod +x rc.local
5. Apache映射到Tomcat
- 搭建Apache虚拟主机
<VirtualHost *:8080>
DocumentRoot /opt/tomcat7/webapps/rd
ServerName lb.test.com:8080
</VirtualHost>
- 映射到Tomcat使用的8080端口
6. https
- 生成证书
keytool -genkey -v -alias tomcat -keyalg RSA -keystore tomcat.keystore -validity 36500
# 导出cer证书
keytool -keystore tomcat.keystore -export -alias tomcat -file tomcat.cer
- 配置https
# 拷贝第一步生成的tomcat.keystore文件到${TOMCAT_HOME}/conf目录下
# 编辑server.xml
sudo vim ${TOMCAT_HOME}/conf/server.xml
# 内容如下
<Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"
maxThreads="150" scheme="https" secure="true"
clientAuth="false" sslProtocol="TLS"
keystoreFile="${TOMCAT_HOME}/conf/tomcat.keystore" keystorePass="${PASSWD}"
truststoreFile="${TOMCAT_HOME}/conf/tomcat.keystore" truststorePass="${PASSWD}" />
- 编辑web.xml,http自动跳转为https
sudo vim ${TOMCAT_HOME}/conf/web.xml
# 内容如下
<security-constraint>
<web-resource-collection >
<web-resource-name >SSL</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
- 对于沃通的证书,由于支持的加密解密方式不同,需要自己添加加密方式:
# 错误提示
ERR_SSL_VERSION_OR_CIPHER_MISMATCH
# 解决办法
# 在Connector节点下添加
ciphers="TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
TLS_ECDHE_RSA_WITH_RC4_128_SHA,
TLS_RSA_WITH_AES_128_CBC_SHA256,
TLS_RSA_WITH_AES_128_CBC_SHA,
TLS_RSA_WITH_AES_256_CBC_SHA256,
TLS_RSA_WITH_AES_256_CBC_SHA,
SSL_RSA_WITH_RC4_128_SHA"
- 沃通的证书使用
Oracle JDK
,使用OpenJDK时https
无法通过验证
2. Spring
1. IOC
1. 注解
- 通过
@Autowired
获取Bean
时需根据接口来引用变量, 因为自动注入策略默认为byType
- 多个类实现同一接口,并通过
@Autowired
获取Bean
,出现异常expected single matching bean but found 2
,需通过实现类的注解指定该Bean的变量名,可通过@Resource(name="xxx")
的形式来引用该实例,也可使用@Qualifier
。
// 实现类
@Component("XXX")
// 引用
@Resource(name="XXX")
- 使用
@Autowired
时,可通过@Qualifier
设置为自动注入策略byName
。
@Autowired
@Qualifier("userServiceImpl")
private UserService userService;
2. AOP
1. 配置
启用:
在spring配置文件的scheme中引入
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"
启用@Aspect
<aop:aspectj-autoproxy />
3. MVC
1. 基本配置
启用
<!-- web.xml -->
<context-param>
<param-name>contextConfigLocation</param-name>
<!-- context配置文件路径 -->
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<!-- MVC配置文件路径,如果在WEB-INF目录下,且文件名为<servlet-name>-servlet.xml,可省略contextConfigLocation配置 -->
<param-value>/WEB-INF/dispatcher-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
默认编码设置:
<!-- web.xml -->
<filter>
<filter-name>encodeFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodeFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
applicationContext.xml
<context:annotation-config />
注意 ⚠️:
<context:annotation-config>
是用于激活那些已经在spring容器里注册过的bean(无论是通过xml的方式还是通过package sanning的方式)上面的注解,是一个注解处理工具。
<context:component-scan>
除了具有<context:annotation-config>
的功能之外,<context:component-scan>
还可以在指定的package下扫描以及注册javabean 。
dispatcher-servlet.xml
<context:component-scan base-package="net.cofcool.mvc" />
2. 启用jackson解析JSON
配置:
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/html; charset=UTF-8</value>
</list>
</property>
</bean>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>application/json;charset=UTF-8</value>
</list>
</property>
<property name="prettyPrint">
<value>true</value>
</property>
<property name="objectMapper">
<bean class="com.fasterxml.jackson.databind.ObjectMapper">
<property name="serializationInclusion">
<value>NON_NULL</value>
</property>
</bean>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
在代码中使用@ResponseBody
标注,会自动把Map,Array等数据类型转为JSON。
4. 异常监测,统一管理
-
开发中经常需要捕获异常避免直接抛出给调用者,但是代码中充斥的try-catch异常的难看,可使用
ExceptionHandler
来统一捕捉异常。 -
Spring MVC
action filter:
- 简单示例:
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="org.apache.shiro.authz.UnauthorizedException">nopermission</prop>
<prop key="IOException">iopage</prop>
<prop key="NumberFormatException">numberpage</prop>
</props>
</property>
<property name="statusCodes">
<props>
<prop key="org.apache.shiro.authz.UnauthorizedException">401</prop>
</props>
</property>
</bean>
5. 跨域
添加配置:
<mvc:cors>
<mvc:mapping
path="/**"
allowed-origins="*"
allow-credentials="true"
max-age="1800"
allowed-methods="GET,POST"
/>
</mvc:cors>
3. Shiro
1. 依赖Jar包
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>${shiro.version}</version>
</dependency>
<!-- 扩展模块 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-aspectj</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>${shiro.version}</version>
</dependency>
2. 配置
Shiro配置
<!-- 自定义加密方式,继承自CredentialsMatcher,也可使用org.apache.shiro.authc.credential包下的其它CredentialsMatcher -->
<bean id="myCredentialsMatcher" class="net.cofcool.mvc.shiro.MyCredentialsMatcher" />
<!-- 自定义授权验证 -->
<bean id="myRealm" class="net.cofcool.mvc.shiro.AuthRealm" >
<property name="credentialsMatcher" ref="myCredentialsMatcher" />
</bean>
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="myRealm"/>
</bean>
<!-- 路径配置 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<property name="loginUrl" value="/login/login" />
<property name="filterChainDefinitions" >
<value>
/** = authc,validateFilter,perms,roles
/login/login = anon
</value>
</property>
<property name="filters" ref="filters" />
</bean>
<util:map id="filters">
<entry key="validateFilter" value-ref="validateFilter" />
</util:map>
<!-- 接口访问过滤器,继承自AccessControlFilter -->
<bean id="validateFilter" class="net.cofcool.mvc.shiro.ValidateFilter" />
Shiro-Spring 配置
<!--为了让Shiro的注解生效,必须保证以下Bean在LifecycleBeanPostProcessor创建之后创建-->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"/>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
自定义Realm
public class AuthRealm extends AuthorizingRealm {
// 权限验证
// 在做权限验证的时候会调用此方法,包括角色和具体权限,如下
// 1. SecurityUtils.getSubject().isPermitted("user:visit");
// 2. @RequiresPermissions("user:visit")
// 注意,使用注解验证权限时,如果验证不符合,则会抛出异常
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
// 登录授权验证
// 在调用Subject对象的login()方法时调用该方法来验证登录信息
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)
throws AuthenticationException {
return null;
}
}
自定义AccessControlFilter
public class ValidateFilter extends AccessControlFilter {
// 权限允许时调用
@Override
protected boolean isAccessAllowed(javax.servlet.ServletRequest servletRequest,
javax.servlet.ServletResponse servletResponse, Object o) throws Exception {
return false;
}
// 权限拒绝时调用
@Override
protected boolean onAccessDenied(javax.servlet.ServletRequest servletRequest,
javax.servlet.ServletResponse servletResponse) throws Exception {
return false;
}
// 跨域设置
@Override
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
HttpServletResponse servletResponse = (HttpServletResponse) response;
HttpServletRequest servletRequest = (HttpServletRequest) request;
servletResponse.setHeader("Access-Control-Allow-Origin", "*");
servletResponse.setHeader("Access-Control-Allow-Headers", "Origin, No-Cache, X-Requested-With, If-Modified-Since, Pragma, Last-Modified, Cache-Control, Expires, Content-Type, api_key, Authorizationh");
servletResponse.setHeader("Access-Control-Allow-Methods", "GET, POST, HEAD");
if (servletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
servletResponse.setStatus(HttpStatus.OK.value());
return false;
}
return super.preHandle(request, servletResponse);
}
}
4. 集成Swagger
1. 依赖
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.7.0</version>
</dependency>
2. 示例代码
配置类:
@EnableWebMvc
@EnableSwagger2
@ComponentScan(basePackages = "net.cofcool.mvc")
@Configuration
public class Swagger2Spring extends WebMvcConfigurerAdapter {
@Bean
public Docket petApi() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build()
.pathMapping("/")
.genericModelSubstitutes(ResponseEntity.class)
.useDefaultResponseMessages(false)
.globalResponseMessage(RequestMethod.GET,
newArrayList(new ResponseMessageBuilder()
.code(500)
.message("500 message")
.responseModel(new ModelRef("Error"))
.build()))
.securitySchemes(newArrayList(apiKey()))
.securityContexts(newArrayList(securityContext()))
.globalOperationParameters(
newArrayList(new ParameterBuilder()
.name("authorize")
.description("Description of someGlobalParameter")
.modelRef(new ModelRef("string"))
.parameterType("query")
.required(true)
.build()))
.tags(new Tag("Pet Service", "All apis relating to pets"))
;
}
private ApiKey apiKey() {
return new ApiKey("mykey", "api_key", "header");
}
private SecurityContext securityContext() {
return SecurityContext.builder()
.securityReferences(defaultAuth())
.forPaths(PathSelectors.regex("/anyPath.*"))
.build();
}
List<SecurityReference> defaultAuth() {
AuthorizationScope authorizationScope
= new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
return newArrayList(
new SecurityReference("mykey", authorizationScopes));
}
@Bean
SecurityConfiguration security() {
return new SecurityConfiguration(
"test-app-client-id",
"test-app-client-secret",
"test-app-realm",
"test-app",
"apiKey",
ApiKeyVehicle.HEADER,
"api_key",
"," /*scope separator*/);
}
@Bean
UiConfiguration uiConfig() {
return new UiConfiguration(
"validatorUrl",// url
"none", // docExpansion => none | list
"alpha", // apiSorter => alpha
"schema", // defaultModelRendering => schema
UiConfiguration.Constants.DEFAULT_SUBMIT_METHODS,
false, // enableJsonEditor => true | false
true, // showRequestHeaders => true | false
60000L); // requestTimeout => in milliseconds, defaults to null (uses jquery xh timeout)
}
// 引入UI
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}
在Spring XML配置文件中创建该Bean:
<bean class="net.cofcool.mvc.conf.Swagger2Spring"/>
即可通过http://{IP:PORT}/swagger-ui.html
访问Swagger文档页面。
3. 集成UI
- 下载
swagger-ui
git clone https://github.com/swagger-api/swagger-ui.git
- 把
dist
目录引入项目 - MVC配置文件中添加静态资源
<mvc:resources mapping="/ui/**" location="/ui/" />
5. 常见问题
1. IDEA 2016 使用junit4
IDEA 集成了junit4,因此进行测试时,只需添加junit的jar包到工程中即可。
- 添加jar包
- 新建类
新建一个测试类,该类继承 junit.framework.TestCase
,在需要测试的方法前添加test
前缀,开始测试时该方法会自动运行。
package net.cofcool.junit;
import junit.framework.TestCase;
/**
* Created by CofCool on 4/6/16.
*/
public class JunitTest extends TestCase {
public void testAdd() {
int result = Demo.add(2, 5);
assertEquals(7, result);
}
public void testFail() {
fail("test failure");
}
/*
* do something after test
* */
@Override
protected void tearDown() throws Exception {
super.tearDown();
}
/*
* do something before test
* */
@Override
protected void setUp() throws Exception {
super.setUp();
}
}
- 添加测试配置:
更多信息参考junit。
2. tomcat无响应
- 数据库连接处于等待中
- 连接数过多
- 内存不足 ……
3. Maven项目(spring, mybatis…)在eclipse中可以运行,无法在Idea中运行
IDEA的maven项目中,默认源代码目录下的xml等资源文件并不会在编译的时候一块打包进classes文件夹。为了保证mybatis的XML
映射文件打包进classes
文件夹中,可做如下修改:
pom.xml:
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>
4. 使用Idea时提示无法找到配置文件
Idea在编译打包时并没有把某些资源文件包含进去,因此需手动指定。
5. 引入第三方库
- 添加文件: 在WEB_INF目录下,新建lib目录,把需要的jar包添加到该目录中
- 添加为库:在该jar包上右键点击Add As Library..,
6. lib已存在但抛出java.lang.NoClassDefFoundError
环境:
- Idea
- Tomcat
- Java Web项目
开发过程中,虽然在pom.xml
中添加了lib,并没有引入到发布环境的路径中, 虽然在编译时无错误,但在运行时会因为缺少必要的类库而抛出异常。
如下图所示,类库lib
虽然包含在项目依赖库中,但是并不在classes/WEB-INF/lib
目录中。
要想包含进去,只需在Project Structure(Ctrl+Alt+Shift+S)
下的Artifacts
菜单中把该lib引入WEB-INF/lib
即可。
7. jetty多实例部署
从jetty
的启动文件jetty.sh
可以得到的基本信息:
- 变量
NAME
的值为该脚本的文件名(不包含后缀)
NAME=$(echo $(basename $0) | sed -e 's/^[SK][0-9]*//' -e 's/\.sh$//')
- 通过
findDirectory
找到可写的目录,并在该目录下创建jetty
文件夹, 最后得到的目录即为JETTY_RUN
if [ -z "$JETTY_RUN" ]
then
JETTY_RUN=$(findDirectory -w /var/run /usr/var/run $JETTY_BASE /tmp)/jetty
[ -d "$JETTY_RUN" ] || mkdir $JETTY_RUN
fi
- 每次jetty启动时会检测当前的
JETTY_RUN
目录时候包含文件名为${NAME}.pid
的文本文件, 如存在,即不会创建新的jetty实例.
#####################################################
# Find a pid and state file
#####################################################
if [ -z "$JETTY_PID" ]
then
JETTY_PID="$JETTY_RUN/${NAME}.pid"
fi
if [ -z "$JETTY_STATE" ]
then
JETTY_STATE=$JETTY_BASE/${NAME}.state
fi
综上, 可以的出结论: 只要保证每个jetty拥有唯一的pid
文件即可.
解决:
- 重命名
jetty.sh
文件 - 保证jetty只对自己当前的
JETTY_BASE
目录拥有写权限, 没有对系统目录/var/run, /usr/var/run, /tmp
的写权限, 可通过创建jetty用户
指定权限来实现
8. linux环境下调用so库
通过System.loadLibrary()
来引入so库时, so库的文件名不需要加lib
前缀.在载入so库时,public static String mapLibraryName(String libname)
会根据系统的特性来对so库的文件名进行处理, 生成适合特定系统的文件名,然后根据该名称去寻找so库.
9. Spring MVC get请求带中文乱码
修改项目下的web.xml
不会对GET
请求生效,要处理GET请求需要设置Tomcat
中的server.xml
下的Connector
:
<Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443" URIEncoding="true"/>
10. Maven同时编译Jar和War包
<packaging>war</packaging>
......
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>jar</goal>
</goals>
<configuration>
<finalName>${project.artifactId}-${project.version}</finalName>
<outputDirectory>${project.build.directory}</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-install-plugin</artifactId>
<executions>
<execution>
<phase>install</phase>
<goals>
<goal>install-file</goal>
</goals>
<!-- 引用编译完成的jar包,并安装到mvn依赖系统中 -->
<configuration>
<packaging>jar</packaging>
<artifactId>${project.artifactId}</artifactId>
<groupId>${project.groupId}</groupId>
<version>${project.version}</version>
<file>
${project.build.directory}/${project.artifactId}-${project.version}.jar
</file>
</configuration>
</execution>
</executions>
</plugin>
11. Spring MVC中Cacheable未生效
@Cacheable
标注的方法,如果其所在的类实现了某一个接口,那么该方法必须在接口中定义。Spring是根据AOP来实现缓存功能,如果该类的接口未定义该方法,导致代理中也没有该方法,所以缓存并没有生效。注意: 调用内部方法并不会触发,原因是通过原始类调用的该方法,而不是通过代理类。
12. 同一对象的两个synchronized方法不能同时被访问
当synchronized
修饰同一类的多个方法时,同一时间只有一个线程允许访问被修饰的方法,synchronized
会锁住对象本身,因为只有一个方法的锁被释放之后才可继续访问其它同步方法。当线程访问一个对象的同步方法时,可以调用这个对象的其它同步方法,也包含正在执行的方法,而不必重新获取方法的访问权。也就是说同一对象的两个同步方法不能同时被多个线程访问。为了更好的性能,临界区的访问时间应该尽可能的短。
13. Maven项目依赖的”scope”项配置为”system”时的问题
根据 Introduction to the Dependency Mechanism可知,system
和provided
功能类似,在编译阶段需要依赖,但是运行时依赖由 JDK 或运行环境提供
14. Spring MVC 打印请求日志
在 5.1 版本中FrameworkServlet
添加了enableLoggingRequestDetails
API,通过该 API 可打印请求参数和”header”信息。
Spring Boot 项目配置:
spring.http.log-request-details=true
15. MySql 索引未生效问题
MySql 会把查询条件进行切分,如果某个条件命中索引,即会直接定位到该记录,会导致没有按照预设的索引进行查询,可使用 USE INDEX ([INDEX_NAME])
(参考Index Hints)指定使用某个索引。
16. 动态修改 Java Class
除了 ASM, cglib, javassist 等字节码修改工具外,也可使用 Java Agent, 接口定义等在 java.lang.instrument
下。另外可参考 invokedynamic
等指令的相关资料,了解 MethodHandle
, VarHandle
等相关类, 这些类直接通过虚拟机指令实现方法动态调用,因此比通过反射方式调用的性能更好。
17. Mysql 字符串类型数据大小写敏感问题
Mysql 设置数据字符集时常用utf8
, 这时默认的”collation”为utf8_general_ci
,即大小写不敏感, 可通过collation()
函数查看指定字段的字符集,建表时通过COLLATE
指定需要的字符集。
- utf8_bin 区分大小写
- utf8_genera_ci 大小写不敏感
18. OpenJDK FontConfiguration.getVersion() 抛出空指针异常
OpenJDK 11
中没带FontConfig
服务,导致调用java.desktop
包的绘制服务时抛出空指针异常,按如下安装即可(Centos)。
yum install fontconfig
19. Spring 范型不一致时会导致 Bean 注入失败
20. Driver 自动注册机制
JDBC 4 以后可通过 SPI 机制实现驱动自动注入,参考 DriverManager.loadInitialDrivers()
方法,DriverManager
自动注册描述如下
The DriverManager methods getConnection and getDrivers have been enhanced to support the Java Standard Edition Service Provider mechanism. JDBC 4.0 Drivers must include the file META-INF/services/java.sql.Driver. This file contains the name of the JDBC drivers implementation of java.sql.Driver. For example, to load the my.sql.Driver class, the META-INF/services/java.sql.Driver file would contain the entry: my.sql.Driver
21. Stream#reduce 使用
Optional<T> java.util.stream.Stream#reduce(java.util.function.BinaryOperator<T>)
返回 Optional 类型的原因是 Stream 内部可能无元素
22. RestTemplate 请求数据类型为 String 时返回中文乱码
注意 RestTemplate 中的 StringHttpMessageConverter 使用无参构造方法,默认使用 ‘ISO-8859-1’,因此中文会乱码
23. 通过反射构造内部非静态类实例
内部非静态类反射构造实例失败, 调用 BeanUtils.instantiateClass(clazz.getDeclaredConstructor(Demo.class), (Object) this)
创建, 失败原因:
If this Class object represents an inner class declared in a non-static context, the formal parameter types include the explicit enclosing instance as the first parameter.
24. 使用 API 根据域名获取 IP 地址
java.net.InetAddress#getByName(java.lang.String)
java.net.NetworkInterface#getByName0
NetworkInterface.c Java_java_net_NetworkInterface_getByName0 static jobject createNetworkInterface(JNIEnv *env, netif *ifs)
25. 修改 JVM 时区
JVM 获取时区信息流程:
user.timezone
系统属性- 硬件时区 ID
- 如果硬件时区获取失败,使用 GMT
因此修改 user.timezone
系统属性即可, 如改为中国上海时区: -Duser.timezone=Asia/Shanghai
。Clock 类内部使用 System.currentTimeMillis()
等方法获取时间,再根据时区信息转换为相应时区的时间,这样就不会出去更改了时区导致时间错乱问题