重剑无锋,大巧不工 SpringBoot --- 环境集成

build-better-enterprise

SpringBoot文章推荐

SpringBoot项目实战

不啰嗦,直接上代码

集成Druid

DruidConfig:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/**
* Created by LiuFa on 2016/9/14.
* cn.lfdevelopment.www.sys.druid
* DevelopmentApp
*/
@Configuration
public class DruidConfig{

private Logger logger = LoggerFactory.getLogger(getClass());

@Bean(initMethod = "init", destroyMethod = "close")
@ConfigurationProperties(prefix="spring.datasource")
public DataSource druidDataSource(){
return new DruidDataSource() {
@Override
public void setUsername(String username) {
try {
username = ConfigTools.decrypt(username);
} catch (Exception e) {
e.printStackTrace();
}
super.setUsername(username);
}

@Override
public void setUrl(String jdbcUrl) {
try {
jdbcUrl = ConfigTools.decrypt(jdbcUrl);
} catch (Exception e) {
e.printStackTrace();
}
super.setUrl(jdbcUrl);
}
};
}
}

DruidStatViewConfig:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/**
* Created by LiuFa on 2016/8/8.
* cn.lfdevelopment.www.sys.druid
* DevelopmentApp
* 这样的方式不需要添加注解:@ServletComponentScan
*/
@Configuration
public class DruidStatViewConfig {

@Value("${spring.druid.loginUsername}")
private String loginUsername;

@Value("${spring.druid.loginPassword}")
private String loginPassword;

/**
* 注册一个StatViewServlet
* 使用Druid的内置监控页面
*/
@Bean
public ServletRegistrationBean DruidStatViewServlet() {
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(),
"/druid/*");
//添加初始化参数:initParams
//白名单
//ip配置规则
//配置的格式
//<IP> 或者 <IP>/<SUB_NET_MASK_size> 多个ip地址用逗号隔开
//其中
//128.242.127.1/24
//24表示,前面24位是子网掩码,比对的时候,前面24位相同就匹配。
//由于匹配规则不支持IPV6,配置了allow或者deny之后,会导致IPV6无法访问。
servletRegistrationBean.addInitParameter("allow", "");

//deny优先于allow,如果在deny列表中,就算在allow列表中,也会被拒绝。
//如果allow没有配置或者为空,则允许所有访问
//IP黑名单 (存在共同时,deny优先于allow) : 如果满足deny的话提示:Sorry, you are not permitted to view this page.
servletRegistrationBean.addInitParameter("deny", "");

//登录查看信息的账号密码.
try {
servletRegistrationBean.addInitParameter("loginUsername", ConfigTools.decrypt(loginUsername));
servletRegistrationBean.addInitParameter("loginPassword", ConfigTools.decrypt(loginPassword));
} catch (Exception e) {
e.printStackTrace();
}

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

return servletRegistrationBean;
}

/**
* 注册一个:filterRegistrationBean
* 内置监控中的Web关联监控的配置
*/
@Bean
public FilterRegistrationBean druidStatFilter() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new WebStatFilter());

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

//排除一些不必要的url
filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
//缺省sessionStatMaxCount是1000个,这里设置了3000个
filterRegistrationBean.addInitParameter("sessionStatMaxCount", "3000");
//可以配置principalCookieName,使得druid知道指定的sessionName是谁
// filterRegistrationBean.addInitParameter("principalSessionName", "sessionId");
//druid 0.2.7版本开始支持profile,配置profileEnable能够监控单个url调用的sql列表。
filterRegistrationBean.addInitParameter("profileEnable", "true");
return filterRegistrationBean;
}

/**
* 注册一个:druidStatInterceptor
*/
/*@Bean
public DruidStatInterceptor druidStatInterceptor(){
return new DruidStatInterceptor();
}*/

/**
* 注册一个:beanNameAutoProxyCreator
* 内置监控中的spring关联监控的配置
* 该方法使用的是按照BeanId来拦截配置,还有2种方法,分别是
* 按类型拦截配置
* 方法名正则匹配拦截配置
*/
/*@Bean
public BeanNameAutoProxyCreator beanNameAutoProxyCreator(){
BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator();
beanNameAutoProxyCreator.setProxyTargetClass(true);
beanNameAutoProxyCreator.setBeanNames("*Controller");
beanNameAutoProxyCreator.setInterceptorNames("druidStatInterceptor");
return beanNameAutoProxyCreator;
}*/
}

application-dev:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#druid配置
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=G11Jor+OrLz9MFztdkOfqRnrJKVrFCDdBbYJFmB0qGjUARxPr2tiyRzUn4xbnk/XqPgM8PMjdIJ/pO8UF4aeVg==
spring.datasource.username=bNVOqb7WKLX5Bjnw+LMv92taj25KOxDimXxILPQjw42wgv+1lHzOH8kr97xDwWdhpY67QuYCS7sWN4W46YbkFA==
spring.datasource.password=l65GeQaXVXxx2ogcQeZLAFM7VcPwgzc9202vxql4hjCbjM8dVm/sD4osdvaBdVkC+BiYdnYL2EzpaCysXAZ5Gw==


# 下面为连接池的补充设置,应用到上面所有数据源中
# 初始化大小,最小,最大
spring.datasource.initialSize=10
spring.datasource.minIdle=25
spring.datasource.maxActive=250

# 配置获取连接等待超时的时间
spring.datasource.maxWait=60000

# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
spring.datasource.timeBetweenEvictionRunsMillis=1200000

# 配置一个连接在池中最小生存的时间,单位是毫秒
spring.datasource.minEvictableIdleTimeMillis=1800000

spring.datasource.validationQuery=SELECT 'x'

#建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
spring.datasource.testWhileIdle=true

#申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
spring.datasource.testOnBorrow=false

#归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
spring.datasource.testOnReturn=false

# 打开PSCache,并且指定每个连接上PSCache的大小 如果用Oracle,则把poolPreparedStatements配置为true,mysql可以配置为false。分库分表较多的数据库,建议配置为false 在mysql5.5以下的版本中没有PSCache功能,建议关闭掉。5.5及以上版本有PSCache,建议开启。
spring.datasource.poolPreparedStatements=true

# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙,'stat'用于监控,‘log4j’用于日志,'config'是指ConfigFilter
spring.datasource.filters=wall,stat,config

# 通过connectProperties属性来打开mergeSql功能;慢SQL记录,超过3秒就是慢sql
spring.datasource.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=3000;config.decrypt=true

# 合并多个DruidDataSource的监控数据,缺省多个DruidDataSource的监控数据是各自独立的,在Druid-0.2.17版本之后,支持配置公用监控数据
spring.datasource.useGlobalDataSourceStat=true

#druid登陆用户名
spring.druid.loginUsername=lCzd9geWAuAuJtLhpaG/J+d28H8KiMFAWopxXkYpMNdUai6Xe/LsPqMQeg5MIrmvtMa+hzycdRhWs29ZUPU1IQ==

#druid登录密码
spring.druid.loginPassword=hf96/2MU+Q12fdb9oZN9ghub1OHmUBa8YuW7NJf8Pll/sawcaRVscHTpr4t5SB39+KbJn31Lqy76uEDvj+sgMw==

集成Mybatis

MyBatisConfig

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
/**
* Created by LiuFa on 2016/8/8.
* cn.lfdevelopment.www.sys.mybatis
* DevelopmentApp
* DataSource 交由Druid自动根据配置创建
*/
@Configuration
@EnableTransactionManagement
public class MyBatisConfig implements TransactionManagementConfigurer {

@Autowired
private DataSource dataSource;


@Bean
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setTypeAliasesPackage("cn.lfdevelopment.www.app.**.pojo");
//支持属性使用驼峰的命名,mapper配置不需要写字段与属性的配置,会自动映射。
org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
configuration.setMapUnderscoreToCamelCase(true);
bean.setConfiguration(configuration);

//分页插件
PageHelper pageHelper = new PageHelper();
Properties properties = new Properties();
/* 3.3.0版本可用 - 分页参数合理化,默认false禁用
启用合理化时,如果pageNum<1会查询第一页,如果pageNum>pages会查询最后一页
禁用合理化时,如果pageNum<1或pageNum>pages会返回空数据
在EXTjs里面配置与否无所谓,因为在前台传过来的分页数据已经进行合理化了 */
properties.setProperty("reasonable", "true");
properties.setProperty("supportMethodsArguments", "true");
properties.setProperty("returnPageInfo", "check");
/* 3.5.0版本可用 - 为了支持startPage(Object params)方法
增加了一个`params`参数来配置参数映射,用于从Map或ServletRequest中取值
可以配置pageNum,pageSize,count,pageSizeZero,reasonable,orderBy,不配置映射的用默认值
不理解该含义的前提下,不要随便复制该配置 -->*/

// properties.setProperty("params", "count=countSql");
pageHelper.setProperties(properties);

//添加插件
bean.setPlugins(new Interceptor[]{pageHelper});

//添加XML目录
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
try {
bean.setMapperLocations(resolver.getResources("classpath*:mapperxml/**/*Mapper.xml"));
return bean.getObject();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}

@Bean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}

@Bean
@Override
public PlatformTransactionManager annotationDrivenTransactionManager() {
try {
return new DataSourceTransactionManager(dataSource);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}

MyBatisMapperScannerConfig

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* MyBatis扫描接口,使用的tk.mybatis.spring.mapper.MapperScannerConfigurer,如果你不使用通用Mapper,可以改为org.xxx...
*/
@Configuration
//由于MapperScannerConfigurer执行的比较早
public class MyBatisMapperScannerConfig {

@Bean
public static MapperScannerConfigurer mapperScannerConfigurer() {
MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
mapperScannerConfigurer.setSqlSessionFactoryBeanName("sqlSessionFactory");
mapperScannerConfigurer.setBasePackage("cn.lfdevelopment.www.app.**.mapper");
Properties properties = new Properties();
properties.setProperty("mappers", "cn.lfdevelopment.www.sys.base.BaseMapper");
properties.setProperty("notEmpty", "false");
properties.setProperty("IDENTITY", "MYSQL");
mapperScannerConfigurer.setProperties(properties);
return mapperScannerConfigurer;
}

}

集成Redis

RedisConfig

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
    
/**
* Created by LiuFa on 2016/9/5.
* cn.lfdevelopment.www.sys.redis
* DevelopmentApp
*/
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
@Bean
public KeyGenerator keyGenerator() {
return (target, method, 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(RedisTemplate redisTemplate) {
return new RedisCacheManager(redisTemplate);
}

/**
* StringRedisTemplate
* @param factory
* @return
*/
@Bean
public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
StringRedisTemplate template = new StringRedisTemplate(factory);
Jackson2JsonRedisSerializer<Object> 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;
}


/**
* redisTemplateForShiro
* @param factory
* @return
*/
@Bean
public RedisTemplate<byte[], Object> redisTemplateForShiro(RedisConnectionFactory factory) {
RedisTemplate<byte[], Object> redisTemplateForShiro = new RedisTemplate<>();
redisTemplateForShiro.setConnectionFactory(factory);
return redisTemplateForShiro;
}

}

集成Shiro

ShiroConfiguration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
/**
* Created by LiuFa on 2016/9/13.
* cn.lfdevelopment.www.sys.shiro
* DevelopmentApp
*/
@Configuration
public class ShiroConfiguration {
/**
* FilterRegistrationBean
* @return
*/
@Autowired
private RedisTemplate redisTemplate;

@Bean
public FilterRegistrationBean filterRegistrationBean() {
FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
filterRegistration.setFilter(new DelegatingFilterProxy("shiroFilter"));
filterRegistration.addInitParameter("targetFilterLifecycle","true");
filterRegistration.addUrlPatterns("/*");
filterRegistration.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
// filterRegistration.setAsyncSupported(true);
filterRegistration.setDispatcherTypes(DispatcherType.REQUEST);
return filterRegistration;
}

/**
* @see org.apache.shiro.spring.web.ShiroFilterFactoryBean
* @return
*/
@Bean(name = "shiroFilter")
public ShiroFilterFactoryBean shiroFilter(){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
bean.setSecurityManager(securityManager());
bean.setLoginUrl("/login");
bean.setSuccessUrl("/main");
//验证不具备权限后的转向页面
bean.setUnauthorizedUrl("/main");

Map<String, Filter> filters = new LinkedHashMap<>();
filters.put("authc",shiroFormAuthenticationFilter());
filters.put("session",sessionFilter());
filters.put("rolesOr",rolesAuthorizationFilter());
bean.setFilters(filters);

Map<String, String> chains = new LinkedHashMap<>();
chains.put("/favicon.ico","anon");
chains.put("/","anon");
chains.put("/index","anon");
chains.put("/blog","anon");
chains.put("/blog/**","anon");
chains.put("/weixin","anon");
chains.put("/weixin/**","anon");
chains.put("/static/**", "anon");
chains.put("/getGifCode","anon");
chains.put("/404", "anon");
chains.put("/druid/**","anon");
chains.put("/logout", "logout");

chains.put("/login", "authc");
chains.put("/main","authc");
chains.put("/**", "session,user");
bean.setFilterChainDefinitionMap(chains);
return bean;
}


/**
* @see org.apache.shiro.mgt.SecurityManager
* @return
*/
@Bean(name="securityManager")
public DefaultWebSecurityManager securityManager() {
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setRealm(userRealm());
manager.setCacheManager(redisCacheManager());
manager.setSessionManager(defaultWebSessionManager());
return manager;
}

/**
* @see DefaultWebSessionManager
* @return
*/
@Bean(name="sessionManager")
public DefaultWebSessionManager defaultWebSessionManager() {
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
sessionManager.setCacheManager(redisCacheManager());
sessionManager.setGlobalSessionTimeout(1800000);
sessionManager.setDeleteInvalidSessions(true);
sessionManager.setSessionValidationSchedulerEnabled(true);
sessionManager.setSessionValidationInterval(600000);
sessionManager.setSessionIdUrlRewritingEnabled(false);
return sessionManager;
}


/**
* @return
*/
@Bean
@DependsOn(value={"lifecycleBeanPostProcessor", "shrioRedisCacheManager"})
public AuthorizingRealm userRealm() {
AuthorizingRealm userRealm = new AuthorizingRealm();
userRealm.setCacheManager(redisCacheManager());
userRealm.setCachingEnabled(true);
userRealm.setAuthenticationCachingEnabled(true);
userRealm.setAuthorizationCachingEnabled(true);
return userRealm;
}


@Bean(name="shrioRedisCacheManager")
@DependsOn(value="redisTemplate")
public ShrioRedisCacheManager redisCacheManager() {
ShrioRedisCacheManager cacheManager = new ShrioRedisCacheManager(redisTemplate);
return cacheManager;
}

@Bean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}

@Bean(name = "authcFilter")
public FormAuthenticationFilter shiroFormAuthenticationFilter(){
return new AuthcFilter();
}

@Bean
public SessionFilter sessionFilter(){
return new SessionFilter();
}


@Bean(name = "rolesOrFilter")
public RolesAuthorizationFilter rolesAuthorizationFilter(){
return new RolesAuthorizationFilter() {
@Override
public boolean isAccessAllowed(ServletRequest request,
ServletResponse response, Object mappedValue) throws IOException {
Subject subject = getSubject(request, response);
String[] rolesArray = (String[]) mappedValue;

if ((rolesArray == null) || (rolesArray.length == 0)) {
return true;
}
for (String aRolesArray : rolesArray) {
if (subject.hasRole(aRolesArray)) {
//用户只要拥有任何一个角色则验证通过
return true;
}
}
return false;
}
};
}
}

ShiroRedisCache

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
/**
* Created by LiuFa on 2016/9/13.
* cn.lfdevelopment.www.sys.shiro
* DevelopmentApp
*/
public class ShrioRedisCache<K, V> implements Cache<K, V> {
private org.slf4j.Logger log = LoggerFactory.getLogger(getClass());
@Autowired
private RedisTemplate<byte[], V> redisTemplate;
private String prefix = "shiro_redis:";

public ShrioRedisCache(RedisTemplate<byte[], V> redisTemplate) {
this.redisTemplate = redisTemplate;
}

public ShrioRedisCache(RedisTemplate<byte[], V> redisTemplate, String prefix) {
this(redisTemplate);
this.prefix = prefix;
}

@Override
public V get(K key) throws CacheException {
if(log.isDebugEnabled()) {
log.debug("Key: {}", key);
}
if(key == null) {
return null;
}

byte[] bkey = getByteKey(key);
return redisTemplate.opsForValue().get(bkey);
}

@Override
public V put(K key, V value) throws CacheException {
if(log.isDebugEnabled()) {
log.debug("Key: {}, value: {}", key, value);
}

if(key == null || value == null) {
return null;
}

byte[] bkey = getByteKey(key);
redisTemplate.opsForValue().set(bkey, value);
return value;
}

@Override
public V remove(K key) throws CacheException {
if(log.isDebugEnabled()) {
log.debug("Key: {}", key);
}

if(key == null) {
return null;
}

byte[] bkey = getByteKey(key);
ValueOperations<byte[], V> vo = redisTemplate.opsForValue();
V value = vo.get(bkey);
redisTemplate.delete(bkey);
return value;
}

@Override
public void clear() throws CacheException {
redisTemplate.getConnectionFactory().getConnection().flushDb();
}

@Override
public int size() {
Long len = redisTemplate.getConnectionFactory().getConnection().dbSize();
return len.intValue();
}

@SuppressWarnings("unchecked")
@Override
public Set<K> keys() {
byte[] bkey = (prefix+"*").getBytes();
Set<byte[]> set = redisTemplate.keys(bkey);
Set<K> result = new HashSet<>();

if(CollectionUtils.isEmpty(set)) {
return Collections.emptySet();
}

for(byte[] key: set) {
result.add((K)key);
}
return result;
}

@Override
public Collection<V> values() {
Set<K> keys = keys();
List<V> values = new ArrayList<>(keys.size());
for(K k: keys) {
byte[] bkey = getByteKey(k);
values.add(redisTemplate.opsForValue().get(bkey));
}
return values;
}

private byte[] getByteKey(K key){
if(key instanceof String){
String preKey = this.prefix + key;
return preKey.getBytes();
}else{
return SerializeUtils.serialize(key);
}
}

public String getPrefix() {
return prefix;
}

public void setPrefix(String prefix) {
this.prefix = prefix;
}
}

最后

  • 本文暂未完结,后续将持续集成更多第三方框架,或接着更新,或另起新篇
  • 详细代码内容可在GitHub上follow