SpringBoot 通用项目配置

1 环境配置

注册中心(服务治理:注册与发现nginx反向代理)

111.231.112.151:81	111.231.112.151:8001
	                123.207.218.250:8001

服务网关	
111.231.112.151:80	111.231.112.151:8002
	                123.207.218.250:8002

2 配置

  • 2.1 配置线程池/任务执行器

SpringBoot默认单线程执行,需要多线程异步执行,需要@EnableAsync并设置ThreadPoolTaskExecutor,仅仅开启@EnableAsync 1.4.X版本仍是单线程执行,1.5.X会报错提醒设置线程池!

//可以设置执行任务的线程池的数量。默认是单线程。
@Bean
public ThreadPoolTaskScheduler getDefaultThreadPoolScheduler(){
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setPoolSize(2);
threadPoolTaskScheduler.setThreadNamePrefix("scheduleMoon");
return threadPoolTaskScheduler;
}

//spingBoot默认单线程执行
@Bean
    public ThreadPoolTaskExecutor createThreadPoolTaskExecutor() {
        ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
        threadPoolTaskExecutor.setCorePoolSize(10);
        threadPoolTaskExecutor.setMaxPoolSize(20);
        return threadPoolTaskExecutor;
    }
  • 2.2 配置跨域
private CorsConfiguration buildConfig() {  
        CorsConfiguration corsConfiguration = new CorsConfiguration();  
        corsConfiguration.addAllowedOrigin("*");  
        corsConfiguration.addAllowedHeader("*");  
        corsConfiguration.addAllowedMethod("*");  
        return corsConfiguration;  
    }  
      
    /** 
     * 跨域过滤器 
     * @return 
     */  
    @Bean  
    public CorsFilter corsFilter() {  
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();  
        source.registerCorsConfiguration("/**", buildConfig()); // 4  
        return new CorsFilter(source);  
    }  
  • 2.3 配置文件上传
@Bean 
    public MultipartConfigElement multipartConfigElement() { 
        MultipartConfigFactory factory = new MultipartConfigFactory();
         设置文件大小限制 ,超了,页面会抛出异常信息,这时候就需要进行异常信息的处理了;
        factory.setMaxFileSize("400MB"); //KB,MB
        /// 设置总上传数据总大小
        factory.setMaxRequestSize("400MB"); 
        //Sets the directory location where files will be stored.
        //factory.setLocation("路径地址");
        return factory.createMultipartConfig(); 
    }  
	
  • 2.4 配置mybatis
/**
 * MyBatis 配置
 *
 */
@Configuration
@MapperScan(basePackages = "com.ttd.mapper")
public class MyBatisConfiguration {

	private static final Logger logger = LoggerFactory.getLogger(MyBatisConfiguration.class);
	
	@Bean
	public PageHelper pageHelper(DataSource dataSource) {
		logger.info("注册MyBatis分页插件PageHelper");
		PageHelper pageHelper = new PageHelper();
		Properties p = new Properties();
		p.setProperty("offsetAsPageNum", "true");
		p.setProperty("rowBoundsWithCount", "true");
		p.setProperty("reasonable", "true");
		pageHelper.setProperties(p);
		return pageHelper;
	}
	
}

3 多数据源实战

  • 3.1 配置application
# \u4e3b\u6570\u636e\u6e90\uff0c\u9ed8\u8ba4\u7684
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://*****/*****
spring.datasource.username=root
spring.datasource.password=****

# \u66f4\u591a\u6570\u636e\u6e90
custom.datasource.names=ds1,ds2
custom.datasource.ds1.type=com.alibaba.druid.pool.DruidDataSource
custom.datasource.ds1.driver-class-name=com.mysql.jdbc.Driver
custom.datasource.ds1.url=jdbc:mysql://*****/*****
custom.datasource.ds1.username=root
custom.datasource.ds1.password=*****

custom.datasource.ds2.type=com.alibaba.druid.pool.DruidDataSource
custom.datasource.ds2.driver-class-name=com.mysql.jdbc.Driver
custom.datasource.ds2.url=jdbc:mysql://*****/*****
custom.datasource.ds2.username=root
custom.datasource.ds2.password=******

# 下面为连接池的补充设置,应用到上面所有数据源中
spring.datasource.initialSize=5
spring.datasource.minIdle=5
spring.datasource.maxActive=20
# 配置获取连接等待超时的时间
spring.datasource.maxWait=60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
spring.datasource.timeBetweenEvictionRunsMillis=60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
spring.datasource.minEvictableIdleTimeMillis=300000
spring.datasource.validationQuery=SELECT 1 FROM DUAL
spring.datasource.testWhileIdle=true
spring.datasource.testOnBorrow=false
spring.datasource.testOnReturn=false
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
spring.datasource.filters=stat,wall,log4j
spring.datasource.logSlowSql=true
  • 3.2 注册定义数据源bean
定义bean实现ImportBeanDefinitionRegistrar并在程序入口导入@Import(DynamicDataSourceRegister.class)

@Override
	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
		
		System.out.println("####加载数据源####"+ defaultDataSource);
		
		Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
		// 将主数据源添加到更多数据源中
		targetDataSources.put("dataSource", defaultDataSource);
		DynamicDataSourceContextHolder.dataSourceIds.add("dataSource");
		// 添加更多数据源
		targetDataSources.putAll(customDataSources);
		for (String key : customDataSources.keySet()) {
			DynamicDataSourceContextHolder.dataSourceIds.add(key);
		}

		// 创建DynamicDataSource
		GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
		beanDefinition.setBeanClass(DynamicDataSource.class);
		beanDefinition.setSynthetic(true);
		MutablePropertyValues mpv = beanDefinition.getPropertyValues();
		mpv.addPropertyValue("defaultTargetDataSource", defaultDataSource);
		mpv.addPropertyValue("targetDataSources", targetDataSources);
		registry.registerBeanDefinition("dataSource", beanDefinition);

		logger.info("#############################Dynamic DataSource Registry########################");
	}
  • 3.3 多数据源aop切换
@Before("@annotation(ds)")
	public void changeDataSource(JoinPoint point, TargetDataSource ds) throws Throwable {
		String dsId = ds.name();
		if (!DynamicDataSourceContextHolder.containsDataSource(dsId)) {
			logger.error("数据源[{}]不存在,使用默认数据源 > {}", ds.name(), point.getSignature());
		} else {
			logger.debug("Use DataSource : {} > {}", ds.name(), point.getSignature());
			DynamicDataSourceContextHolder.setDataSourceType(ds.name());
		}
	}

	@After("@annotation(ds)")
	public void restoreDataSource(JoinPoint point, TargetDataSource ds) {
		logger.debug("Revert DataSource : {} > {}", ds.name(), point.getSignature());
		DynamicDataSourceContextHolder.clearDataSourceType();
	}
  • 3.4 druid数据源监控
/**
     * 注册一个StatViewServlet
     * #####此配置需要在入口程序添加@ServletComponentScan
     * @WebServlet(urlPatterns = "/druid/*", 
	 *	    initParams={
	 *	            @WebInitParam(name="allow",value="127.0.0.1"),// IP白名单 (没有配置或者为空,则允许所有访问)
	 *	            @WebInitParam(name="deny",value="192.168.16.111"),// IP黑名单 (存在共同时,deny优先于allow)
	 *	            @WebInitParam(name="loginUsername",value="wolf"),// 用户名
	 *	            @WebInitParam(name="loginPassword",value="wolf"),// 密码
	 *	            @WebInitParam(name="resetEnable",value="false")// 禁用HTML页面上的“Reset All”功能
	 *	    })
	 *	public class DruidStatViewServlet extends StatViewServlet {
	 *	
	 *	}
     * @return
     */
    @Bean
    public ServletRegistrationBean DruidStatViewServle(){
        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(),"/druid/*");


        //添加初始化参数:initParams
        //白名单:
//        servletRegistrationBean.addInitParameter("allow","127.0.0.1");
        
        //IP黑名单 (存在共同时,deny优先于allow) : 如果满足deny的话提示:Sorry, you are not permitted to view this page.
//        servletRegistrationBean.addInitParameter("deny","192.168.1.73");

        //登录查看信息的账号密码.
        servletRegistrationBean.addInitParameter("loginUsername", "wolf");
        servletRegistrationBean.addInitParameter("loginPassword", "wolf");

        //是否能够重置数据.
        servletRegistrationBean.addInitParameter("resetEnable", "false");

        return servletRegistrationBean;
    }

    /**
     * 注册一个:filterRegistrationBean
     * @WebFilter(filterName = "druidWebStatFilter", urlPatterns = "/*",
	 *	initParams = { @WebInitParam(name = "exclusions", value = "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid2/*") // 忽略资源
	 *	})
	 *	public class DruidStatFilter extends WebStatFilter {
	 *	
	 *	}
     * @return
     */
    @Bean
    public FilterRegistrationBean druidStatFilter(){

        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new WebStatFilter());

        //添加过滤规则.
        filterRegistrationBean.addUrlPatterns("/*");

        //添加不需要忽略的格式信息.
        filterRegistrationBean.addInitParameter("exclusions","*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid2/*");

        return filterRegistrationBean;
}

这里写图片描述
这里写图片描述

4 redis实战

  • 4.1 redis配置
   /**
     * 生成key的策略
     *
     * @return
     */
    @Bean
    public KeyGenerator keyGenerator() {
    	logger.info("########################redis 初始化配置!!!#################");
        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();
            }
        };
    }
    
    /**
     * 管理缓存
     *
     * @param redisTemplate
     * @return
     */
    @SuppressWarnings("rawtypes")
    @Bean
    public CacheManager cacheManager(RedisTemplate redisTemplate) {
        RedisCacheManager rcm = new RedisCacheManager(redisTemplate);
        //设置缓存过期时间
        // rcm.setDefaultExpiration(60);//秒
        //设置value的过期时间
        Map<String,Long> map=new HashMap();
        map.put("test",60L);
        rcm.setExpires(map);
        return rcm;
    }
    
    /**
     * RedisTemplate配置
     * @param factory
     * @return
     */
    @SuppressWarnings("rawtypes")
    @Bean
    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
        StringRedisTemplate template = new StringRedisTemplate(factory);
		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;
}
  • 4.2 redis utils操作
@Test
	public void testRedis() {
		/*redisUtil.set("testKey", "spring:session:expirations:1499333280000");
		System.out.println(redisUtil.get("testKey"));
		System.out.println(redisUtil.exists("testKey"));*/
	}
  • 4.3 redis spring业务缓存
@Service("cacheForemanHouseService")
@CacheConfig(cacheNames={"cacheForemanHouse"})
public class CacheForemanHouseService extends BaseService<CacheForemanHouse, Long> {

}

5 安全控制

  • 5.1 request入参解密
 */
@WebFilter(filterName = "webAPPFilter", urlPatterns ={ "/foremanInfo/*", "/houseInfo/*", "/orderInfo/*", "/userInfo/*"}, initParams={@WebInitParam(name="excluds", value="/druid/*")})
public class WebAPPFilter implements Filter {
	protected final Logger logger = LoggerFactory.getLogger(getClass());
	private final String iv = DigestUtils.sha256Hex("Totodi!@#").substring(0, 16);
	private String[] excluds = null; 
	

	@Override
	public void destroy() {
		excluds = null;
	}
	
	private void handleParamter(String queryString, Map<String, String[]> retParams) {
		String[] params = queryString.split("&");
		for (int i = 0; i < params.length; i++) {
			int splitIndex = params[i].indexOf("=");
			if (splitIndex == -1) {
				continue;
			}
			String key = params[i].substring(0, splitIndex);

			if (splitIndex < params[i].length()) {
				String value = params[i].substring(splitIndex + 1);
				retParams.put(key, new String[] {value});
			}
		}
	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response,	FilterChain chain) throws IOException, ServletException {
		HttpServletResponse res = (HttpServletResponse) response;
		HttpServletRequest req = (HttpServletRequest) request;

		//调试不需要进行加密
        if (StringUtils.equalsIgnoreCase(req.getParameter("skipEncryption"), "true") || StringUtils.contains(req.getQueryString(), "skipEncryption")
        	|| StringUtils.contains(req.getRequestURI(), "favicon.ico")) {
            chain.doFilter(req, res);
            return;
        }
        
		// 解决跨域访问
		res.addHeader("Access-Control-Allow-Origin", "*");
		res.addHeader("Access-Control-Allow-Methods", "GET");
		res.addHeader("Access-Control-Allow-Methods", "POST");
		res.addHeader("Access-Control-Allow-Headers", "x-requested-with,content-type");

		// 判断是否OPTIONS请求
		if (req.getMethod().equals("OPTIONS")) {
			res.setStatus(HttpStatus.OK.value());
			return;
		} else {
			try {
				 Map<String, String[]> retParams = Maps.newHashMap();
				 
				 //request key读写
				 Map<String, String[]> params = new HashMap<String,String[]>(request.getParameterMap());
				 for (Entry<String, String[]> item : params.entrySet()) {
					 logger.info(">>>拦截前参数:"+item.getValue()[0]);
					 String newVal = AESUtils.decrypt(item.getValue()[0], iv);
					 logger.info(">>>拦截后参数:"+newVal);	
					 handleParamter(newVal, retParams);
				 } 
				 
				 //requet body流读写
				/* byte[] buf = new byte[1024];
				 while (req.getInputStream().readLine(buf, 0, 1024) > 0) {
					 String tem = new String(buf);
					 logger.info(">>>拦截前参数:" + tem);
					 String newVal = AESUtils.decrypt(tem, iv);
					 logger.info(">>>拦截后参数:" + newVal);	
					 handleParamter(newVal, retParams);
				}*/
				 
				req = new  ParameterRequestWrapper((HttpServletRequest)req, retParams);
				chain.doFilter(req, res);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	@Override
	public void init(FilterConfig config) throws ServletException {
	
		String vals = config.getInitParameter("excluds");
		excluds = vals.split(",");
	}

}
  • 5.2 response解密

/**
     * 输出文本
     * @param txt
     * @param contextType
     */
    protected void write(String txt, String contextType) {
        try {
            if(Strings.isNullOrEmpty(txt)){
            	return;
            }
            
            getResponse().setContentType(contextType);
            if (StringUtils.isNotBlank(getRequest().getParameter("skipEncryption")) && getRequest().getParameter("skipEncryption").equals("true")) {
            	LOGGER.info("response skipEncryption:" + txt);
            	StreamUtil.writeData(txt.getBytes("UTF-8"), getResponse().getOutputStream());
            } else {
            	LOGGER.info("response 明文:" + txt);
            	String encryString = AESUtils.aesEncipherString(AESUtils.KEY, AESUtils.IV, txt);
            	
            	LOGGER.info("response 密文:" + encryString);
            	StreamUtil.writeData(encryString.getBytes("UTF-8"), getResponse().getOutputStream());
            }
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
}
  • 5.3 配置全局拦截器

@Override
	public void addInterceptors(InterceptorRegistry registry) {
		// addPathPatterns 用于添加拦截规则
		// excludePathPatterns 用户排除拦截
		registry.addInterceptor(new GlobalInterceptor()).addPathPatterns("/**").excludePathPatterns("/common/**");
		
//		registry.addInterceptor(new MyInterceptor2()).addPathPatterns("/**");
		super.addInterceptors(registry);
	}
  • 5.4 移动端请求生成唯一hash
//移动设备:mac、os、version、timestamp全局hash校验
private void refreHash(ServletContextUtil context, boolean refree) {
		HttpServletRequest request = context.getRequest();
		String mac = context.getParameter("mac");
		String os = context.getParameter("os");
		String version = context.getParameter("version");
				
		String hashKey = EncryptUtils.encodeMD532(mac + os + version, null);
		BeanFactory factory = WebApplicationContextUtils.getRequiredWebApplicationContext(request.getServletContext()); 
		RedisUtil redisUtil = (RedisUtil) factory.getBean("redisUtil");
		
		if (refree) {
			if (redisUtil.exists(hashKey)) { 
				redisUtil.remove(hashKey);
				logger.info("|----------释放hash-----------");
			}
		} else {
			redisUtil.set(hashKey, true);
			logger.info("|----------存储hash-----------");
		}
	}

@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
		ServletContextUtil context = ServletContextUtil.getContext();
		// 设置本地线程变量
		context.setRequest(request);
		context.setResponse(response);
		HandlerMethod method = (HandlerMethod) handler;

		Object[] params = new Object[] { request.getRequestURI(), request.getParameterMap() };
		logger.info("request:请求地址:{}-请求参数-{}", params);

		boolean ret = authentication(method.getMethodAnnotation(Authentication.class), context);
		if (ret) {
			refreHash(context, false);
		}
		return ret;
	}
  • 5.5 释放hash
    如支付/订单发起申请,由于网络或者被攻击或者延迟未响应,提示客户端请求处理中,同一设备处理挂起操作。
@Override
	public void afterCompletion(HttpServletRequest request,	HttpServletResponse response, Object handler, Exception ex) throws Exception {
		ServletContextUtil context = ServletContextUtil.getContext();
		refreHash(context, true);
	}

6 监听器

  • 6.1 spring application context
public class ApplicationStartUpListener implements ApplicationListener<ApplicationStartingEvent>{
	 	
	@Override
    public void onApplicationEvent(ApplicationStartingEvent event) {
        PropertyUtil.loadAllProperties();
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>ApplicationStartUpListener EXEC");
    }
}
  • 6.2 servlet context
@WebListener
public class MyServletContextListener implements ServletContextListener {

	@Override
	public void contextInitialized(ServletContextEvent sce) {
		System.out.println("ServletContex初始化");
		System.out.println(sce.getServletContext().getServerInfo());
	}

	@Override
	public void contextDestroyed(ServletContextEvent sce) {
		System.out.println("ServletContex销毁");
	}

}

7 请求aop日志切割

@Aspect
@Component
public class LogOrderAspect {
	private static final Logger logger = LoggerFactory.getLogger(LogOrderAspect.class);
	@Resource private LogOrderOperationService logOrderService;

	@Before("@annotation(orderAspect)")
	public void gennerateLog(JoinPoint point, LogOrderAnnotation orderAspect) throws Throwable {
		logger.info(">>>>>>订单操作开始aspect");
		ServletContextUtil context = ServletContextUtil.getContext();
		HttpServletRequest request = context.getRequest();
		
		LogOrderOperation log = new LogOrderOperation();
		log.setAction(orderAspect.action());
		log.setUserId(LongUtils.parseLong(request.getParameter("userId")));
		Map<String, String[]> params = Maps.newHashMap(request.getParameterMap());
		params.put("url", new String[]{request.getRequestURI()});
		log.setParams(JSON.toJSONString(params));
		log.setCreated(new Date());
		
		logOrderService.insertEntry(log);
	}

	@After("@annotation(orderAspect)")
	public void endLog(JoinPoint point, LogOrderAnnotation orderAspect) {
		logger.info(">>>>>>订单操作执行完aspect");
	}
	
	@AfterReturning("@annotation(orderAspect)")
	public void returnLog(JoinPoint point, LogOrderAnnotation orderAspect) {
		logger.info(">>>>>>订单操作执行完aspect,return.................");
	}
	
	
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值