• Category Archives: Java

SpringBoot2+JPA引入对Redis的支持

在pom.xml加入:

        <!-- 对Redis的支持 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

增加配置文件RedisConfig.java:

package com.zero4j.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;

import java.lang.reflect.Method;

/**
 * Redis 缓存配置类(通用)
 * @author linhongcun
 *
 */
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {

    /**
     * 缓存对象集合中,缓存是以 key-value 形式保存的。当不指定缓存的 key 时,SpringBoot 会使用 SimpleKeyGenerator 生成 key。
     * @return
     */
    @Bean
    public KeyGenerator wiselyKeyGenerator() {
        return new KeyGenerator() {
            @Override
            public Object generate(Object target, Method method, Object... params) {
                StringBuilder sb = new StringBuilder();
                sb.append(target.getClass().getName());
                sb.append(method.getName());
                for (Object obj : params) {
                    sb.append(obj.toString());
                }
                return sb.toString();
            }
        };

    }

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
    	return RedisCacheManager.create(factory);
    }

    @Bean
    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
        StringRedisTemplate template = new StringRedisTemplate(factory);
        @SuppressWarnings({ "rawtypes", "unchecked" })
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        template.setValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
}

在application.properties中加入:

# Redis
spring.redis.host=127.0.0.1
spring.redis.port=6379

对Service类加入注解@CacheConfig和@Cacheable,如:

package com.zero4j.model.config;

import java.util.List;

import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;

@Repository
@CacheConfig(cacheNames = "configRepository")
public interface ConfigRepository extends JpaRepository<Config, String> {

	@Query("SELECT config FROM Config config")
	@Cacheable(value = "findAll",keyGenerator="wiselyKeyGenerator")
	List<Config> findAll();
	
}

 

研究SpringBoot与非SpringBoot项目以及搭配MyBatis/JPA/Hibernate的并发性能浅度测试

测试环境服务器参数:

CPU:4核8G带宽200M
RDS:1核1G
操作系统:Linux
JDK版本:1.8
WEB容器:Tomcat 8
压测工具:JMeter 3.2

每个组合分别的test内容基本如下(根据搭配的数据库不同,有些方法命名稍有差异):

@Controller
@RequestMapping("")
public class RootController {
	
	@Autowired
    private UserService userService;
	
	@RequestMapping(value={"","/","/index"})
	String view(Map<String, Object> map){
    	
    	System.out.println("首页(default)");
    	
    	map.put("name", "SpringBoot");
		map.put("date", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
    	
  		return "index.jsp";
  		
  	}
	
	@RequestMapping(value={"test1"})
	@ResponseBody
	String test() {

		String str = "(new Random()).nextInt() = "+(new Random()).nextInt();
		
		System.out.println(str);
		
		return str;
		
    }
	
	@RequestMapping(value="test2")
  	public void test2(HttpServletRequest request, HttpServletResponse response) {

		String str = "(new Random()).nextInt() = "+(new Random()).nextInt();
		
		System.out.println(str);
		
		ResponseStaticUtil.write(response, str);
  	}
	
	@RequestMapping(value={"get1"})
	@ResponseBody
	String get1() {

		User user = this.userService.get(1);
		String str = "user = "+user.getName()+" / (new Random()).nextInt() = "+(new Random()).nextInt();
		
		System.out.println(str);
		
		return str;
		
    }
	
  	@RequestMapping(value="get2")
  	public void get2(HttpServletRequest request, HttpServletResponse response) {
  		
  		User user = this.userService.get(1);
		String str = "user = "+user.getName()+" / (new Random()).nextInt() = "+(new Random()).nextInt();
		
		System.out.println(str);
  		
  		ResponseStaticUtil.write(response, str);
  	}
	
	@RequestMapping(value={"list1"})
	@ResponseBody
	String list() {

		List<User> users = this.userService.listAll();
		String str = "users.size()= "+users.size()+" / (new Random()).nextInt() = "+(new Random()).nextInt();
		System.out.println(str);
		
		return str;
		
    }
	
	@RequestMapping(value="list2")
  	public void list2(HttpServletRequest request, HttpServletResponse response) {

		List<User> users = this.userService.listAll();
		String str = "users.size()= "+users.size()+" / (new Random()).nextInt() = "+(new Random()).nextInt();
		System.out.println(str);
		
		ResponseStaticUtil.write(response, str);
		
    }
	
}

 

普通SpringMVC+Hibernate项目的各Controller压力测试结果:
123

SpringBoot+MyBatis项目的各Controller压力测试结果:
SpringBoot+Hibernate项目的各Controller压力测试结果

 

SpringBoot+JPA项目的各Controller压力测试结果:
123

SpringBoot+Hibernate项目的各Controller压力测试结果:
123

 

” SpringBoot+MyBatis / SpringBoot+JPA / SpringBoot+Hibernate ” 的get单个user对象压力测试对比:
" SpringBoot+MyBatis / SpringBoot+JPA / SpringBoot+Hibernate " 的get单个user对象压力测试对比

” SpringBoot+MyBatis / SpringBoot+JPA / SpringBoot+Hibernate ” 的获取user所有对象(只有3个数据)压力测试对比:
" SpringBoot+MyBatis / SpringBoot+JPA / SpringBoot+Hibernate " 的获取user所有对象(只有3个数据)压力测试对比

 

总结:

1、在写Controller方法时,普通SpringMVC框架在使用普通的response.write的方式时效率很高,但使用annotation的方式返回数据时效率十分低;反之,使用SpringBoot框架在使用annotation的方式返回数据时效率比传统的response.write方式的运行效率要高出一截。

2、在SpringBoot框架下搭配分别搭配MyBatis、JPA、Hibernate时,其运行效率的差异不大,JPA的方式稍弱一点点。

 

本文属于“cp锋”的原创,虽然内容不是十分精品,但还望尊重本人的研究成果,转发时请注明转载并带上本页面链接,感谢~

Spring boot设置文件上传大小限制

Spring Boot做文件上传时出现了报错

The field file exceeds its maximum permitted size of 1048576 bytes

显示文件的大小超出了允许的范围。查看了官方文档,原来Spring Boot工程嵌入的tomcat限制了请求的文件大小默认为1MB,单次请求的文件的总数不能大于10Mb.要更改这个默认值需要在配置文件(如application.properties)中加入两个配置.

springboot 1.4之前是这样的:

multipart.maxFileSize = 10485760       //单个文件的大小
multipart.maxRequestSize = 10485760     //单次请求的文件的总大小

springboot 1.4之后是这样的:

spring.http.multipart.maxFileSize = 10485760
spring.http.multipart.maxRequestSize = 10485760

springboot 2.0之后是这样的:

spring.servlet.multipart.max-file-size = 10485760
spring.servlet.multipart.max-request-size = 10485760

注意:很多其他网站是让你们用10Mb这样的形式去赋值的,但事实上这样会报错,这里的单位是以字节b为单位的,我们可以调成他原来的1048576 bytes的10倍,也就是10MB大概等于10485760

JAVA的各种HTTP/HTTPS模拟请求使用经验汇总

https:

try{
			

			
		StringBuffer reultBuffer = new StringBuffer();
			
		KeyStore keyStore  = KeyStore.getInstance("PKCS12");
		FileInputStream instream = new FileInputStream(new File("/home/certificate/apiclient_cert.p12"));
	        String passwordssss=mch_id;
	        keyStore.load(instream, passwordssss.toCharArray());	//证书密码
	        instream.close();
	        SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, passwordssss.toCharArray()).build();	//证书密码(初始是商户ID)
	        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext,new String[] { "TLSv1" },null,SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
	        
		HttpPost httpPost = new HttpPost("https://fraud.mch.weixin.qq.com/risk/getpublickey");
	        CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
	        httpPost.setHeader("Content-Type", "text/xml; charset=UTF-8");
	        httpPost.setEntity(new StringEntity(str_lingqian.toString(),"UTF-8"));
	        
	        CloseableHttpResponse response      = null;
	        InputStream inputStream		        = null;
	        InputStreamReader inputStreamReader = null;
	        BufferedReader bufferedReader       = null;
	        try {
	        	response = httpclient.execute(httpPost);
	        	HttpEntity entity = response.getEntity();
				if (entity!=null){
					inputStream = entity.getContent();
					inputStreamReader = new InputStreamReader(inputStream,"UTF-8");
					bufferedReader = new BufferedReader(inputStreamReader);
					String str = null;
					while ((str = bufferedReader.readLine()) != null) {
						reultBuffer.append(str);
						System.out.println(str);
					}
				}
			} catch (ClientProtocolException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}finally{

				httpclient.close();
				response.close();
				bufferedReader.close();
				inputStreamReader.close();
				inputStream.close();
				inputStream = null;
			}
	     
	        Document document = DocumentHelper.parseText(reultBuffer.toString());
	        Element rootElm = document.getRootElement();
	        if(rootElm.elementText("return_code").toString().equals("SUCCESS") && rootElm.elementText("result_code").toString().equals("SUCCESS") ){
	        	pub_key = rootElm.elementText("pub_key").toString();
	        	System.out.println("pub_key = "+pub_key);
	        }else{	        	
	        	return status;
	        }
			
}catch(Exception e){
	e.printStackTrace();
}

 

通过正则表达式在中设置不包含的页面

比如写如下的配置文:
<filter-mapping>
<filter-name>AdminFilter</filter-name>
<url-pattern>/admin/*</url-pattern>
</filter-mapping>
那么admin目录下所有的文件都将在访问前被过滤器拦截,包括login.jsp。

我们可以自己在 Filter 里用正则表达式进行二次过滤,过滤的正则表达式可以通过:

<filter>
	<filter-name>LoginFilter</filter-name>
	<filter-class>com.test.LoginFilter</filter-class>
	<init-param>
		<param-name>UrlRegx</param-name>
		<param-value><!--你的正则表达式--></param-value>
	</init-param>
</filter>

 

CAS返回更多用户信息

1.修改deployerConfigContext.xml文件

 

2.修改casServiceValidationSuccess.jsp文件

 

 

 

 

3.最后修改client端的web.xml文件:

	<!-- ======================== 单点登录开始 ======================== -->
	<!-- 用于单点退出,该过滤器用于实现单点登出功能,可选配置 -->
	<listener>
		<listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class>
	</listener>
	<filter>
		<filter-name>CAS Single Sign Out Filter</filter-name>
		<filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>CAS Single Sign Out Filter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

	<filter>
		<filter-name>CAS Filter</filter-name>
		<filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class>
		<init-param>
			<param-name>casServerLoginUrl</param-name>
			<param-value>http://localhost:8080/cas</param-value>
		</init-param>
		<init-param>
			<param-name>serverName</param-name>
			<param-value>http://localhost:8080</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>CAS Filter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

	<!-- 该过滤器负责对Ticket的校验工作,必须启用它 -->
	<filter>
		<filter-name>CAS Validation Filter</filter-name>
		<filter-class>
			org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter
		</filter-class>
		<init-param>
			<param-name>casServerUrlPrefix</param-name>
			<param-value>http://localhost:8080/cas</param-value>
		</init-param>
		<init-param>
			<param-name>serverName</param-name>
			<param-value>http://localhost:8080</param-value>
		</init-param>
		<init-param>
			<param-name>encoding</param-name>
			<param-value>UTF-8</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>CAS Validation Filter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

	<!-- 该过滤器负责实现HttpServletRequest请求的包裹, -->
	<!-- 比如允许开发者通过HttpServletRequest的getRemoteUser()方法获得SSO登录用户的登录名,可选配置。 -->

	<filter>
		<filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>
		<filter-class>
			org.jasig.cas.client.util.HttpServletRequestWrapperFilter
		</filter-class>
	</filter>
	<filter-mapping>
		<filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

	<!-- 该过滤器使得开发者可以通过org.jasig.cas.client.util.AssertionHolder来获取用户的登录名。 -->
	<!-- 比如AssertionHolder.getAssertion().getPrincipal().getName()。根据客户端获取的方式可以选择使用这两种 -->


	<!-- <filter> <filter-name>CAS Assertion Thread Local Filter</filter-name> 
		<filter-class>org.jasig.cas.client.util.AssertionThreadLocalFilter</filter-class> 
		</filter> <filter-mapping> <filter-name>CAS Assertion Thread Local Filter</filter-name> 
		<url-pattern>/*</url-pattern> </filter-mapping> -->

	<!-- ======================== 单点登录结束 ======================== -->

4.可以用以下jsp代码来验证是否成功获取用户的更多信息:

	<%
	out.write("request.getUserPrincipal()="+request.getUserPrincipal());
	%>
	
	<br/><br/>


<%
//HttpServletRequest request = ServletActionContext.getRequest();
/*获取单点登录服务器传递过来的用户信息*/
AttributePrincipal principal = (AttributePrincipal) request.getUserPrincipal();
if (principal!=null) {
	out.write("principal不为空...");
	Map<String, Object> attributes = principal.getAttributes();
	for (String key : attributes.keySet()) {
		System.out.println(key+"="+attributes.get(key));
	}
}else{
	out.write("principal为空...");
}
%>

 

取消CAS的HTTPS登陆

1、修改WEB-INF\deployerConfigContext.xml,加入

 p:requireSecure="false"

<property name="authenticationHandlers">
			<list>
				<!--
					| This is the authentication handler that authenticates services by means of callback via SSL, thereby validating
					| a server side SSL certificate.
					+-->
				<bean class="org.jasig.cas.authentication.handler.support.HttpBasedServiceCredentialsAuthenticationHandler"
					p:httpClient-ref="httpClient" p:requireSecure="false"/>
				<!--
					| This is the authentication handler declaration that every CAS deployer will need to change before deploying CAS 
					| into production.  The default SimpleTestUsernamePasswordAuthenticationHandler authenticates UsernamePasswordCredentials
					| where the username equals the password.  You will need to replace this with an AuthenticationHandler that implements your
					| local authentication strategy.  You might accomplish this by coding a new such handler and declaring
					| edu.someschool.its.cas.MySpecialHandler here, or you might use one of the handlers provided in the adaptors modules.
					+-->
				<bean
					class="org.jasig.cas.authentication.handler.support.SimpleTestUsernamePasswordAuthenticationHandler" />
			</list>
		</property>

2、修改WEB-INF\spring-configuration\ticketGrantingTicketCookieGenerator.xml,修改p:cookieSecure=”false”

	<bean id="ticketGrantingTicketCookieGenerator" class="org.jasig.cas.web.support.CookieRetrievingCookieGenerator"
		p:cookieSecure="false"
		p:cookieMaxAge="-1"
		p:cookieName="CASTGC"
		p:cookiePath="/cas" />

3、修改修改WEB-INF\spring-configuration\warnCookieGenerator.xml,修改p:cookieSecure=”false”

	<bean id="warnCookieGenerator" class="org.jasig.cas.web.support.CookieRetrievingCookieGenerator"
		p:cookieSecure="false"
		p:cookieMaxAge="-1"
		p:cookieName="CASPRIVACY"
		p:cookiePath="/cas" />

经过以上三步,cas server端修改完毕

CAS配合server4.0返回用户更多信息

打开deployerConfigContext.xml

<bean id="attributeRepository" class="org.jasig.services.persondir.support.StubPersonAttributeDao"
            p:backingMap-ref="attrRepoBackingMap" />

将以上代码替换成以下代码:

<bean id="attributeRepository" class="org.jasig.services.persondir.support.jdbc.SingleRowJdbcPersonAttributeDao">
		<!-- 指定使用的数据源,此处dataSource是已配置好的数据源 -->
		<constructor-arg index="0" ref="dataSource" />
		<!-- 从数据库中查询信息的SQL语句,通常只需要修改表名即可 -->
		<constructor-arg index="1" value="select * fromuserinfo where {0}" />
		<property name="queryAttributeMapping">
			<map>
				<!-- 上述查询的参数,将userName替换为表中表示用户名的字段名称 -->
				<entry key="username" value="userName" />
			</map>
		</property>
		<property name="resultAttributeMapping">
			<map>
				<!-- 需要返回给Web应用的其它信息,多个信息时可继续增加entry节点 -->
				<!--key值为数据表中的字段名称,value值为Client端取值时的名称标识 -->
				<entry key="address" value="address" />
			</map>
		</property>
	</bean>

 

 

接着在这个文件最后的 bean class=”org.jasig.cas.services.RegexRegisteredService” 标签内加上 p:ignoreAttributes=”true”

 

 

jsp中Java的运行环境版本等信息显示

		<table width="100%" cellpadding="5">
			<tr>
				<th width="170">Java的运行环境版本</th>
				<td width="*"><%=System.getProperties().getProperty("java.version")%></td>
			</tr>
			<tr>
				<th>Java的运行环境供应商</th>
				<td><%=System.getProperties().getProperty("java.vendor")%></td>
			</tr>
			<tr>
				<th>Java供应商的URL</th>
				<td><%=System.getProperties().getProperty("java.vendor.url")%></td>
			</tr>
			<tr>
				<th>Java的安装路径</th>
				<td><%=System.getProperties().getProperty("java.home")%></td>
			</tr>
			<tr>
				<th>Java的虚拟机规范版本</th>
				<td><%=System.getProperties().getProperty("java.vm.specification.version")%></td>
			</tr>
			<tr>
				<th>Java的虚拟机规范供应商</th>
				<td><%=System.getProperties().getProperty("java.vm.specification.vendor")%></td>
			</tr>
			<tr>
				<th>Java的虚拟机规范名称</th>
				<td><%=System.getProperties().getProperty("java.vm.specification.name")%></td>
			</tr>
			<tr>
				<th>Java的虚拟机实现版本</th>
				<td><%=System.getProperties().getProperty("java.vm.version")%></td>
			</tr>
			<tr>
				<th>Java的虚拟机实现供应商</th>
				<td><%=System.getProperties().getProperty("java.vm.vendor")%></td>
			</tr>
			<tr>
				<th>Java的虚拟机实现名称</th>
				<td><%=System.getProperties().getProperty("java.vm.name")%></td>
			</tr>
			<tr>
				<th>Java运行时环境规范版本</th>
				<td><%=System.getProperties().getProperty("java.specification.version")%></td>
			</tr>
			<tr>
				<th>Java运行时环境规范供应商</th>
				<td><%=System.getProperties().getProperty("java.specification.vender")%></td>
			</tr>
			<tr>
				<th>Java运行时环境规范名称</th>
				<td><%=System.getProperties().getProperty("java.specification.name")%></td>
			</tr>
			<tr>
				<th>Java的类格式版本号</th>
				<td><%=System.getProperties().getProperty("java.class.version")%></td>
			</tr>
			<tr>
				<th>Java的类路径</th>
				<td><%=System.getProperties().getProperty("java.class.path")%></td>
			</tr>
			<tr>
				<th>加载库时搜索的路径列表</th>
				<td><%=System.getProperties().getProperty("java.library.path")%></td>
			</tr>
			<tr>
				<th>默认的临时文件路径</th>
				<td><%=System.getProperties().getProperty("java.io.tmpdir")%></td>
			</tr>
			<tr>
				<th>一个或多个扩展目录的路径</th>
				<td><%=System.getProperties().getProperty("java.ext.dirs")%></td>
			</tr>
			<tr>
				<th>操作系统的名称</th>
				<td><%=System.getProperties().getProperty("os.name")%></td>
			</tr>
			<tr>
				<th>操作系统的构架</th>
				<td><%=System.getProperties().getProperty("os.arch")%></td>
			</tr>
			<tr>
				<th>操作系统的版本</th>
				<td><%=System.getProperties().getProperty("os.version")%></td>
			</tr>
			<tr>
				<th>文件分隔符</th>
				<td><%=System.getProperties().getProperty("file.separator")%></td>
			</tr>
			<tr>
				<th>路径分隔符</th>
				<td><%=System.getProperties().getProperty("path.separator")%></td>
			</tr>
			<tr>
				<th>行分隔符</th>
				<td><%=System.getProperties().getProperty("line.separator")%></td>
			</tr>
			<tr>
				<th>用户的账户名称</th>
				<td><%=System.getProperties().getProperty("user.name")%></td>
			</tr>
			<tr>
				<th>用户的主目录</th>
				<td><%=System.getProperties().getProperty("user.home")%></td>
			</tr>
			<tr>
				<th>用户的当前工作目录</th>
				<td><%=System.getProperties().getProperty("user.dir")%></td>
			</tr>
		</table>

 

close