Java

概述

性能调优准则

1. 不要在没有必要的时候做性能调优

在大多数情况下,过早进行性能优化会占用大量时间,并使代码难以阅读和维护。更糟糕的是,这些优化通常不会带来任何好处,因为您花费大量时间来优化应用程序的非关键部分。

2. 使用分析器来查找真正的瓶颈

3. 为整个应用程序创建一个性能测试SuitCase

4. 先进行最大的瓶颈上工作

5. 使用StringBuilder来连接字符串

6. 在一个语句中使用+连接字符串

7. 尽可能使用基本数据

8. 尽量避免使用BigInteger和BigDecimal

9. 检查当前日志级别

正确的写法应该是这样的:

1
2
3
if (log.isDebugEnabled()) {
log.debug("User [" + userName + "] called method X with [" + i + "]");
}

10. 使用Apache Commons的StringUtils.Replace来替代String.replace

11. 缓存开销量较大的资源,如数据库连接等

编程进阶

1. 阅读源代码

搞清楚以下4个问题:

1
2
3
4
A、源代码是要解决什么问题?
B、源代码是通过什么原理实现?
C、它采用了哪些接口、类?
D、为什么采用这些接口和类?

2. 多参与项目

通过项目的细节,你也可以查缺补漏,找到自己知识技能薄弱的环节。

3. 问题指引你进阶之路

尽量以问题为导向,这可以让你的思考和理解聚焦,而不至于分散。

4. 编程思想驾驭代码

可以边学边用《Effective Java》中的原理

5. Java程序员进阶书单

《Java编程思想》、《深入剖析Tomcat》、《深入理解Java虚拟机》、《JavaScript编程全解》、《Java程序性能优化》

Spring

Spring MVC

1. 原理

配置阶段

引入

1
2
3
4
5
6
7
8
web.xml
application.xml
@Controller
@Service
@Autowired
@RequestMapping
@RequestParmter
@ModelAndView

使用阶段

浏览器发送请求

1
2
3
doGet、doPost
根据url找对应的Method
通过反射调用和输出返回结果

2. 手写mvc代码

提升对框架设计原理的理解

提高解决问题的效率

web.xml

添加servlet

1
2
引入application.properties文件
scanPackage=com.xxx.demo //扫描包路径下的所有类

添加与新建servlet对应的servlet-mapping

servlet对应的java类

继承J2EE自带的HttpServlet

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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
public class DisptcherServlet extends HttpServlet {
private Properties p = new Properties();
private List<String> classNames = new ArrayList<String>();
private Map<String,Object> ioc = new HashMap<String,Object>();
private Map<String,Method> handlerMapping = new HashMap<String,Method>();


protected void doGet(HttpServletRequest req, HttpServletResponse resp){
this.doGet(req, resp);
}

protected void doPost(HttpServletRequest req, HttpServletResponse resp){
doDispatch(req, resp);
}

private void doDispatch(HttpServletRequest req, HttpServletResponse resp){
String url = req.getRequestURI();

String contextPath = req.getContextPath();
url = url.replace(contextPath,"").replaceAll("/*","/");

if(!handlerMapping.containsKey(url)){
resp.getWriter().write("404 Not Found");
return;
}

Method method = handlerMapping.get(url);

//第一个参数:表示这个方法所在的实例
//!!!需要一个Handler内部类获取方法对应的类实例和方法需要的参数
//method.invoke();


}

@Override
public void init(ServletConfig config) throws ServletException{

// 1.加载配置文件application.properties
doLoadConfig(config.getInitParameter("contextConfigLocation"););

// 2.扫描所有相关类,拿到基础包路径,然后递归扫描
doScanner(p.getProperty("scanPackage"));

// 3.把扫描到的类实例化,放到ioc容器中(自己实现ioc容器,也就是一个Map,key是类名,value是类实例)
doInstance();

// 4.检查依赖注入,只要加了@Autowired注解的字段,不论公私有都要给强制赋值
doAutowired();

// 5.获取用户请求,根据所请求的url找到其对应的方法,通过反射机制,去调用
// HandlerMapping 把这一一个冠以存放到HandlerMapping中去(key是url,value是方法)
initHandlerMapping();

// 6.等待请求,把反射调用的结果通过response写出到浏览器中
do;
}

private void doLoadConfig(String path){
InputStream is = this.getClass().getClassLoader().getResourceAsStream(path);
p.load(is);
is.close();
}

private void doScanner(String packageName){
URL url = this.getClass().getClassLoader().getResource("/" + packageName.replaceAll("\\.","/"));
File dir = new File(url.getFile());
for(File file : dir.listFiles()){
if(file.isDirectory()){
doScanner(packageName + "." + file.getName());
}else{
classNames.add(packageName + "." + file.getName().replace(".class",""));
}
}
}

private void doInstance(){
if(classNames.isEmpty())return;
for(String className : classNames){
Class<?> clazz = Class.forName(className);
// 不是所有的类都要初始化的
if(class.isAnnotationPresent(Controller.class)){
//<bean id="" name="" class="">
String beanName = clazz.getSimpleName();
ioc.put(beanName, clazz.newInstance());
}else if(class.isAnnotationPresent(Service.class)){
//1.如果自己起了名字,则优先使用自己的名字进行匹配并注入
//2.默认首字母小写(发生在不是接口的情况)
//3.如果注入的类型是接口,要自动找到其实现类的实例并注入
Service service = clazz.getAnnotation(Service.class);
String beanName = service.value();//如果设了值,则不为""
if("".equals(beanName.trim())){
ioc.put(beanName, clazz.newInstance());
}else{
beanName = lowerFirst(clazz.getSimpleName()); // String首字母小写方法,自己实现
ioc.put(beanName, clazz.newInstance());
}
class<?> interfaces = clazz.getInterfaces();
for(Class<?> i : interfaces){
ioc.put(i.getName(), clazz.newInstance());
}



}
}
}

private void doAutowired(){
if(ioc.isEmpty()) return;
for(Entry<String,Object> entry:ioc.entrySet()){
Field[] fields = entry.getValue().getClass().getDeclaredFields();
for(Field field: fields){
if(!field.isAnnotationPresent(Autowired.class)continue;
//如果注解加了自定义名字
Autowired autowired = field.getAnnotation(Autowired.class);
String beanName = autowired.value().trim();
//通过声明接口注入
if("".equals(beanName.trim())){
beanName = field.getType.getName();
}

//只要加了Autowired注解的,强制赋值
field.setAccessible(true);
field.set(entry.getValue(),ioc.get(beanName));
}


}


}

private void initHandlerMapping(){
if(ioc.isEmpty())return;
for(Entry<String,Object> entry : ioc.entrySet()){

// 非常含有技术含量的点,要用反射调用方法

// 把所有的RequestMapping扫描出来,然后读取其值,和Method关联上,放入handlerMapping中
Class<?> clazz = entry.getValue().getClass();
//只和Controller有关
if(clazz.isAnnotationPresent(Controller.class)){
continue;
}
String baseUrl = "";
if(clazz.isAnnotationPresent(RequestMapping.class)){
RequestMapping requestMapping = clazz.getAnnotation(RequestMapping.class);
baseUrl = requestMapping.value();

}

Method[] methods = clazz.getMethods();

for(Method method:methods){
if(!method.isAnnotationPresent(RequestMapping.class)continue;
RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
String mappingUrl = "/" + baseUrl + "/" + requestMapping.value().replaceAll("/*","/");
handlerMapping.put(mappingUrl, method);
System.out.println("Mapping : " + mappingUrl + method);
}





}



}


}

添加注解类

Controller

1
2
3
4
5
6
7
package com.xxx.annotation;
@Documented
@Target(ElementType.TYPE)//在类上用
@Retention(TetentionPolicy.RUNTIME)//
public @interface Controller {
String value() default "";
}

RequestMapping

1
2
3
4
5
6
@Documented
@Target({ElementType.TYPE,ElementType.METHOD})//在类上用,在方法上用
@Retention(TetentionPolicy.RUNTIME)//
public @interface RequestMapping {
String value() default "";
}

RequestParam

1
2
3
4
5
6
@Documented
@Target({ElementType.PARAMETER)//在参数上用
@Retention(TetentionPolicy.RUNTIME)//
public @interface RequestParam {
String value() default "";
}

Service

1
2
3
4
5
6
@Documented
@Target(ElementType.TYPE)//在类上用
@Retention(TetentionPolicy.RUNTIME)//
public @interface Service {
String value() default "";
}

Autowired

1
2
3
4
5
6
@Documented
@Target(ElementType.RIELD)//在字段上用
@Retention(TetentionPolicy.RUNTIME)//
public @interface Autowired {
String value() default "";
}

使用注解

DemoService

1
2
3
4
5
6
@Service
public class DemoService{
public String get(String name){
return name + "!!!!!!";
}
}

DemoAction

1
2
3
4
5
6
7
8
9
10
11
12
@Controller
@RequestMapping("/web")
public class DemoAction{
@Autowired DemoService demoService;

@RequestMapping("/get.json")
public void get(HttpServletRequest req, HttpServletResponse resp,
@RequestParam("name") String name){
String result = demoService.get(name);
resp.getWriter().write(result);
}
}

启动服务,访问http://localhost:8080/web/get.json?name=john

spring-boot

1. spring-boot-starter-security模块默认密码

Boot会为你提供一个默认的用户账号user和默认角色USER,并且会在应用启动的时候在控制台上输出随机生成的密码。

2. spring-boot-actuator

提供更多面向生产环境的支持,安全,日志,管理,审计。

1
2
3
4
5
6
7
8
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.jolokia</groupId>
<artifactId>jolokia-core</artifactId>
</dependency>
1
2
3
4
5
6
7
8
9
10
# MANAGEMENT HTTP SERVER (ManagementServerProperties)
management.port= # defaults to 'server.port'
management.address= # bind to a specific NIC
management.context-path= # default to '/'
management.add-application-context-header= # default to true
management.security.enabled=true # enable security
management.security.role=ADMIN # role required to access the management endpoint
management.security.sessions=stateless # session creating policy to use (always, never, if_required, stateless)

management.context-path=/actuator

3. spring-boot

  1. 下载eclipse mars

eclipse-jee-mars-R-win32-x86_64.zip

JDK1.8

  1. 参考
    http://spring.io/guides

  2. 运行,Goals
    spring-boot:run

  3. 断点调试,Profiles
    -Drun.jvmArguments=”-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005”

  4. hateoas
    Accept: application/json, application/xml
    返回例子:

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
{
"_embedded":{
"bookmarkResourceList":[
{
"bookmark":{
"id":213,
"uri":"http://bookmark.com/1/mpollack",
"description":"A description"
},
//整个系统中的所有其他 URI(包含所有会发生状态改变的)都是从分析这些表述中获得的
"_links":{
"bookmark-uri":{
"href":"http://bookmark.com/1/mpollack"
},
"bookmarks":{
"href":"http://localhost:9008/mpollack/bookmarks"
},
"self":{
"href":"http://localhost:9008/mpollack/bookmarks/213"
}
}
},
{
"bookmark":{
"id":214,
"uri":"http://bookmark.com/2/mpollack",
"description":"A description"
},
"_links":{
"bookmark-uri":{
"href":"http://bookmark.com/2/mpollack"
},
"bookmarks":{
"href":"http://localhost:9008/mpollack/bookmarks"
},
"self":{
"href":"http://localhost:9008/mpollack/bookmarks/214"
}
}
}
]
}
}

为什么HATEOAS?

1)降低客户端编程错误。如下:访问uri全路径都返回了,直接使用即可,不用自己拼uri了

1
2
3
"bookmarks":{
"href":"http://localhost:9008/mpollack/bookmarks"
},

2)降低无效的状态迁移请求。只有当前状态下有效的状态变迁请求的URI会返回,其他无效的不会返回

3)渐进试改进并且不破坏(非必要的)旧客户端。服务器端严格控制后加的功能不破坏之前的行为,你可以合理快速的改进 API而不破坏所有客户端

  1. Post模拟工具fiddler

spring boot 基础

1. aop

面向切面,提供切面注入的机制,通过这种方式,在业务运行中将定义好的切面通过切入点绑定到业务中,以实现将一些特殊的逻辑绑定到此业务中

aop名称

  • 切面(Aspect):一个关注点的模块化,这个关注点可能会横切多个对象。在Spring AOP中,切面可以使用基于模式或者基于@Aspect注解的方式来实现。

  • 连接点(Joinpoint):在程序执行过程中某个特定的点,比如某方法调用的时候或者处理异常的时候。在Spring AOP中,一个连接点总是表示一个方法的执行。

  • 通知(Advice):在切面的某个特定的连接点上执行的动作。其中包括了”around”、”before”和”after”等不同类型的通知。许多AOP框架(包括Spring)都是以拦截器做通知模型,并维护一个以连接点为中心的拦截器链。

  • 切入点(Pointcut):匹配连接点的断言。通知和一个切入点表达式关联,并在满足这个切入点的连接点上运行(例如,当执行某个特定名称的方法时)。切入点表达式如何和连接点匹配是AOP的核心:Spring缺省使用AspectJ切入点语法。

使用spring AOP实现一个拦截器

http://blog.csdn.net/clementad/article/details/52035199

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
1、引入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2、创建拦截器类
/**
* 拦截器:记录用户操作日志,检查用户是否登录……
*/
@Aspect
@Component
public class ControllerInterceptor {

/**
* 定义拦截规则:拦截com.xjj.web.controller包下面的所有类中,有@RequestMapping注解的方法。
*/
@Pointcut("execution(* com.xjj.web.controller..*(..)) and @annotation(org.springframework.web.bind.annotation.RequestMapping)")
public void controllerMethodPointcut(){}

/**
* 拦截器具体实现
* @param pjp
* @return JsonResult(被拦截方法的执行结果,或需要登录的错误提示。)
*/
@Around("controllerMethodPointcut()") //指定拦截器规则;也可以直接把"execution(* com.xjj.........)"写进这里
public Object Interceptor(ProceedingJoinPoint pjp){
long beginTime = System.currentTimeMillis();
MethodSignature signature = (MethodSignature) pjp.getSignature();
Method method = signature.getMethod(); //获取被拦截的方法
String methodName = method.getName(); //获取被拦截的方法名

Set<Object> allParams = new LinkedHashSet<>(); //保存所有请求参数,用于输出到日志中

logger.info("请求开始,方法:{}", methodName);

Object result = null;

Object[] args = pjp.getArgs();
for(Object arg : args){
//logger.debug("arg: {}", arg);
if (arg instanceof Map<?, ?>) {
//提取方法中的MAP参数,用于记录进日志中
@SuppressWarnings("unchecked")
Map<String, Object> map = (Map<String, Object>) arg;

allParams.add(map);
}else if(arg instanceof HttpServletRequest){
HttpServletRequest request = (HttpServletRequest) arg;
result = new JsonResult(ResultCode.NOT_LOGIN, "该操作需要登录!去登录吗?\n\n(不知道登录账号?请联系老许。)", null);

//获取query string 或 posted form data参数
Map<String, String[]> paramMap = request.getParameterMap();
if(paramMap!=null && paramMap.size()>0){
allParams.add(paramMap);
}
}else if(arg instanceof HttpServletResponse){
//do nothing...
}else{
//allParams.add(arg);
}
}

try {
if(result == null){
// 一切正常的情况下,继续执行被拦截的方法
result = pjp.proceed();
}
} catch (Throwable e) {
logger.info("exception: ", e);
result = new JsonResult(ResultCode.EXCEPTION, "发生异常:"+e.getMessage());
}

if(result instanceof JsonResult){
long costMs = System.currentTimeMillis() - beginTime;
logger.info("{}请求结束,耗时:{}ms", methodName, costMs);
}

return result;
}
}

2. IOC(DI)

控制反转(IOC)和依赖注入(DI)的区别

http://blog.csdn.net/doris_crazy/article/details/18353197

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
(1)参与者都有谁:
一般有三方参与者,一个是某个对象;一个是IoC/DI的容器;另一个是某个对象的外部资源。
又要名词解释一下,某个对象指的就是任意的、普通的Java对象; IoC/DI的容器简单点说就是指用来实现IoC/DI功能的一个框架程序;对象的外部资源指的就是对象需要的,但是是从对象外部获取的,都统称资源,比如:对象需要的其它对象、或者是对象需要的文件资源等等。
(2)谁依赖于谁:
当然是某个对象依赖于IoC/DI的容器
(3)为什么需要依赖:
对象需要IoC/DI的容器来提供对象需要的外部资源
(4)谁注入于谁:
很明显是IoC/DI的容器 注入 某个对象
(5)到底注入什么:
就是注入某个对象所需要的外部资源
(6)谁控制谁:
当然是IoC/DI的容器来控制对象了
(7)控制什么:
主要是控制对象实例的创建
(8)为何叫反转:
反转是相对于正向而言的,那么什么算是正向的呢?考虑一下常规情况下的应用程序,如果要在A里面使用C,你会怎么做呢?当然是直接去创建C的对象,也就是说,是在A类中主动去获取所需要的外部资源C,这种情况被称为正向的。那么什么是反向呢?就是A类不再主动去获取C,而是被动等待,等待IoC/DI的容器获取一个C的实例,然后反向的注入到A类中。

依赖注入和控制反转是对同一件事情的不同描述,从某个方面讲,就是它们描述的角度不同。
依赖注入是从应用程序的角度在描述,可以把依赖注入描述完整点:应用程序依赖容器创建并注入它所需要的外部资源;
而控制反转是从容器的角度在描述,描述完整点:容器控制应用程序,由容器反向的向应用程序注入应用程序所需要的外部资源。

反射

程序运行状态时,对于任意一个类,都能够知道这个类的所有属性和方法(包括私有),对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态类型信息及动态调用对象方法的功能叫做反射机制;

http://blog.csdn.net/it_man/article/details/4402245

通常,每个对象在使用他的合作对象时,自己均要使用像new object() 这样的语法来完成合作对象的申请工作。你会发现:对象间的耦合度高了。而IOC的思想是:Spring容器来实现这些相互依赖对象的创建、协调工作。

比如对象A需要操作数据库,以前我们总是要在A中自己编写代码来获得一个Connection对象,有了 spring我们就只需要告诉spring,A中需要一个Connection,至于这个Connection怎么构造,何时构造,A不需要知道。在系统运行时,spring会在适当的时候制造一个Connection,然后像打针一样,注射到A当中,这样就完成了对各个对象之间关系的控制。

依赖注入的思想也很简单,它是通过反射机制实现的,在实例化一个类时,它通过反射调用类中set方法将事先保存在HashMap中的类属性注入到类中。

首先实例化一个类

public static Object newInstance(String className) {
Class<?> cls = null;
Object obj = null;
try {
cls = Class.forName(className);
obj = cls.newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
}
return obj;
}

接着它将这个类的依赖注入进去

public static void setProperty(Object obj, String name, String value) {
Class<? extends Object> clazz = obj.getClass();
try {
String methodName = returnSetMthodName(name);
Method[] ms = clazz.getMethods();
for (Method m : ms) {
if (m.getName().equals(methodName)) {
if (m.getParameterTypes().length == 1) {
Class<?> clazzParameterType = m.getParameterTypes()[0];
setFieldValue(clazzParameterType.getName(), value, m,
obj);
break;
}
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}

最后它将这个类的实例返回给我们,我们就可以用了。

实战

spring boot 性能优化

http://blog.oneapm.com/apm-tech/385.html

1. 组件自动扫描带来的问题

有三方面的影响:

  1. 会导致项目启动时间变长。当启动一个大的应用程序,或将做大量的集成测试启动应用程序时,影响会特别明显。
  2. 会加载一些不需要的多余的实例(beans)。
  3. 会增加 CPU 消耗。

解决方案:

移除 @SpringBootApplication 和 @ComponentScan 两个注解来禁用组件自动扫描
然后在我们需要的 bean 上进行显式配置:

1
2
3
4
5
6
7
8
9
10
11
@Configuration
@EnableAutoConfiguration
public class SampleWebUiApplication {

// ...

// 用 @Bean 注解显式配置,以便被 Spring 扫描到
@Bean
public MessageController messageController(MessageRepository messageRepository) {
return new MessageController(messageRepository);
}

2. 避免组件自动扫描带来的问题

首先要知道我们需要的组件列表是哪些,可以用 -Ddebug 的方式来帮助我们明确地定位:

1
mvn spring-boot:run -Ddebug
1
2
3
4
5
6
7
8
9
10
11
12


=========================
AUTO-CONFIGURATION REPORT
=========================
Positive matches:
-----------------

DispatcherServletAutoConfiguration
- @ConditionalOnClass classes found: org.springframework.web.servlet.DispatcherServlet (OnClassCondition)
- found web application StandardServletEnvironment (OnWebApplicationCondition)
...

拷贝 Positive matches 中列出的信息:

1
2
3
4
5
6
7
DispatcherServletAutoConfiguration
EmbeddedServletContainerAutoConfiguration
ErrorMvcAutoConfiguration
HttpEncodingAutoConfiguration
HttpMessageConvertersAutoConfiguration
JacksonAutoConfiguration
JmxAutoConfiguration

更新项目配置,显式地引入这些组件,引入之后,再运行一下应用确保没有错误发生:

1
2
3
4
5
6
7
8
9
@Configuration
@Import({
DispatcherServletAutoConfiguration.class,
EmbeddedServletContainerAutoConfiguration.class,
ErrorMvcAutoConfiguration.class,
HttpEncodingAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class,
})
public class SampleWebUiApplication {

不需要 JMX 和 WebSocket 功能,我就删掉了它们。

3. 将Servlet容器变成Undertow

从依赖信息里移除 Tomcat 配置:

1
2
3
4
5
6
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>

然后添加 Undertow:

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>

spring boot 远程调试

1
-Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=8000,suspend=n 

spring boot 打war包后

application.properties 配置文件需要放到war包的同一目录下,否则报Db配置错误。

thymeleaf里面的th和spring security结合进行页面权限配置

1
2
3
4
<li th:if="${#authorization.expression('!isAuthenticated()')}">
<a href="#" data-toggle="modal"
data-target="#changePasswordModal">修改密码</a>
</li>

spring 集成 h2数据库

1. application.properties

增加配置

1
2
3
4
5
6
7
#./database 表示当前启动目录下的database数据库,文件为database.mv.db文件

spring.datasource.url=jdbc:h2:file:./database;AUTO_SERVER=TRUE;DB_CLOSE_DELAY=-1
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.hibernate.naming-strategy=org.hibernate.cfg.EJB3NamingStrategy
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect

2. maven 配置

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
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com</groupId>
<artifactId>lottery</artifactId>
<version>1.0</version>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.5.RELEASE</version>
</parent>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-messaging</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
</dependencies>

<properties>
<java.version>1.7</java.version>
</properties>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>

3. h2默认生成的表varchar是255长度

需要手工建立表

1
2
3
4
5
6
7
8
9
10
11
12
CREATE CACHED TABLE PUBLIC.TODAYINFO(
ID BIGINT NOT NULL auto_increment,
DATES VARCHAR(255) NOT NULL ,
FOCURS VARCHAR(5000),
FUNDL VARCHAR(5000),
SOIL VARCHAR(5000),
SOILADVICE VARCHAR(5000),
SSILVER VARCHAR(5000),
SSILVERADVICE VARCHAR(5000),
TECL VARCHAR(5000),
UPDATETIME VARCHAR(255)
)

使用工具:H2 Database Engine 1.4.185 绿色版

4. JpaRepository

1
2
3
4
5
6
7
8
public interface TodayInfoRepository extends JpaRepository<TodayInfo, Long> {

@Query("SELECT o FROM TodayInfo o")
Page<TodayInfo> getPage(Pageable page);

@Query(nativeQuery = true, value = "SELECT a.* FROM `TodayInfo` a WHERE a.`dates` = :dates LIMIT 1")
TodayInfo getUniqueByDates(@Param("dates") String dates);
}

5. service

1
2
PageRequest pageRequest = new PageRequest(currPage, pageSize, Sort.Direction.DESC, "dates");
Page<TodayInfo> pages = todayInfoRepository.getPage(pageRequest);

杂记

1. 加载到配置文件,给入口类添加@ImportResource(“applicationContext.xml”)

2. spring security 官方文档

http://docs.spring.io/spring-security/site/docs/3.2.8.RELEASE/reference/htmlsingle/

Druid

https://github.com/alibaba/druid

Druid 是阿里唯一使用的数据库连接池,支持双十一等最严苛的使用场景。是”为监控而生的数据库连接池”

1. 简介

https://www.cnblogs.com/niejunlei/p/5977895.html

1
2
3
4
5
6
7
8
9
10
11
Druid是Java语言中最好的数据库连接池。Druid能够提供强大的监控和扩展功能。

maven中央仓库: http://central.maven.org/maven2/com/alibaba/druid/

配置maven

<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid-version}</version>
</dependency>

Dubbo2

http://dubbo.io/

https://github.com/alibaba/dubbo

dubbo是一个分布式的服务架构,可直接用于生产环境作为SOA服务框架。

实战

Eclipse添加阿里巴巴Java开发规约插件

http://blog.csdn.net/jiliang272/article/details/78269600

java 代码库

1. 获取访问者IP

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
/**
* 获取访问者IP
*
* 在一般情况下使用Request.getRemoteAddr()即可,但是经过nginx等反向代理软件后,这个方法会失效。
*
* 本方法先从Header中获取X-Real-IP,如果不存在再从X-Forwarded-For获得第一个IP(用,分割),
* 如果还不存在则调用Request .getRemoteAddr()。
*
* @param request
* @return
*/
public static String getIpAddr(HttpServletRequest request) throws Exception{
String ip = request.getHeader("X-Real-IP");
if (!StringUtils.isBlank(ip) && !"unknown".equalsIgnoreCase(ip)) {
return ip;
}
ip = request.getHeader("X-Forwarded-For");
if (!StringUtils.isBlank(ip) && !"unknown".equalsIgnoreCase(ip)) {
// 多次反向代理后会有多个IP值,第一个为真实IP。
int index = ip.indexOf(',');
if (index != -1) {
return ip.substring(0, index);
} else {
return ip;
}
} else {
return request.getRemoteAddr();
}
}

2. Unicode转UTF-8代码

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
String str="\u7528\u6237\u540d\u6216\u5bc6\u7801\u4e0d\u6b63\u786e\uff0c\u8bf7\u91cd\u8bd5";
System.out.println(decodeUnicode(str));

private static String decodeUnicode(String theString) {
char aChar;
int len = theString.length();
StringBuffer outBuffer = new StringBuffer(len);
for (int x = 0; x < len;) {
aChar = theString.charAt(x++);
if (aChar == '\\') {
aChar = theString.charAt(x++);
if (aChar == 'u') {
// Read the xxxx
int value = 0;
for (int i = 0; i < 4; i++) {
aChar = theString.charAt(x++);
switch (aChar) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
value = (value << 4) + aChar - '0';
break;
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
value = (value << 4) + 10 + aChar - 'a';
break;
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
value = (value << 4) + 10 + aChar - 'A';
break;
default:
throw new IllegalArgumentException(
"Malformed \\uxxxx encoding.");
}

}
outBuffer.append((char) value);
} else {
if (aChar == 't')
aChar = '\t';
else if (aChar == 'r')
aChar = '\r';
else if (aChar == 'n')
aChar = '\n';
else if (aChar == 'f')
aChar = '\f';
outBuffer.append(aChar);
}
} else
outBuffer.append(aChar);
}
return outBuffer.toString();
}

问题汇总

1. mp4视频在html里,ios设备浏览器、微信等无法播放视频问题

不能用spring-boot来当容器启动服务,需要单独html,直接放到tomcat中,不用spring-boot项目

1
2
3
4
<video id="video" style="position: absolute; left: 63.3%;"  controls="controls"
poster="resources/images/H5-2.jpg">
<source src="resources/video/meidui3.mp4" type='video/mp4;' />
</video>

2. 关于在打包Jar文件时遇到的资源路径问题

http://www.cnblogs.com/fjdingsd/p/4680922.html

如果将资源同代码一起打包进Jar包中,当程序打包成可执行Jar包时

1
2
URL picUrl = this.getClass().getResource("/palette.gif");
Image image = new ImageIcon(picUrl).getImage();

如果资源路径前没有加”/“,则无论怎么点Jar包都不会有反应。

3. java代码写的中文(非从数据库读的中文)返回给客户端变乱码问题。

maven配置文件里要设置成用utf-8编译java代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>tomcat-maven-plugin</artifactId>
<version>1.0</version>
<configuration>
<!-- 这里就是所要配置的 端口号 -->
<port>9109</port>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>utf8</encoding>
</configuration>
</plugin>
</plugins>