Java


概述

性能调优准则

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

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

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

  3. 为整个应用程序创建一个性能测试SuitCase
  4. 先进行最大的瓶颈上工作
  5. 使用StringBuilder来连接字符串
  6. 在一个语句中使用+连接字符串
  7. 尽可能使用基本数据
  8. 尽量避免使用BigInteger和BigDecimal
  9. 检查当前日志级别

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

    if (log.isDebugEnabled()) {
        log.debug(“User [” + userName + “] called method X with [” + i + “]”);
    }
    
  10. 使用Apache Commons的StringUtils.Replace来替代String.replace

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

编程进阶

  1. 阅读源代码

    搞清楚以下4个问题:

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

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

  3. 问题指引你进阶之路

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

  4. 编程思想驾驭代码

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

  5. Java程序员进阶书单

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

Spring

Spring MVC

  1. 原理
  • 配置阶段

    引入

    web.xml
    application.xml
    @Controller
    @Service
    @Autowired
    @RequestMapping
    @RequestParmter
    @ModelAndView
    
  • 使用阶段

    浏览器发送请求

    doGet、doPost
    根据url找对应的Method
    通过反射调用和输出返回结果
    
  1. 手写mvc代码

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

    提高解决问题的效率

  • web.xml

    添加servlet

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

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

  • servlet对应的java类

    继承J2EE自带的HttpServlet

    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

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

    RequestMapping

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

    RequestParam

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

    Service

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

    Autowired

    @Documented
    @Target(ElementType.RIELD)//在字段上用
    @Retention(TetentionPolicy.RUNTIME)//
    public @interface Autowired {
        String value() default "";
    }
    
  • 使用注解

    DemoService

    @Service
    public class DemoService{
        public String get(String name){
            return name + "!!!!!!";
        }
    }
    

    DemoAction

    @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

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

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
        <groupId>org.jolokia</groupId>
        <artifactId>jolokia-core</artifactId>
    </dependency>
    
    # 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

    2、参考
    http://spring.io/guides

    3、运行,Goals
    spring-boot:run

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

    5、hateoas
    Accept: application/json, application/xml
    返回例子:
    {

    "_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了

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

    2)降低无效的状态迁移请求。只有当前状态下有效的状态变迁请求的URI会返回,其他无效的不会返回
    3)渐进试改进并且不破坏(非必要的)旧客户端。服务器端严格控制后加的功能不破坏之前的行为,你可以合理快速的改进 API而不破坏所有客户端

6、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、引入依赖:
    <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;
        }
    }
  1. IOC(DI)

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

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

    (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 上进行显式配置:
    @Configuration
    @EnableAutoConfiguration
    public class SampleWebUiApplication {
    
    // ...
    
    // 用 @Bean 注解显式配置,以便被 Spring 扫描到
    @Bean
    public MessageController messageController(MessageRepository messageRepository) {
        return new MessageController(messageRepository);
    }
    
  2. 避免组件自动扫描带来的问题

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

    mvn spring-boot:run -Ddebug
    
    …
    
    =========================
    AUTO-CONFIGURATION REPORT
    =========================
    Positive matches:
    -----------------
    
    DispatcherServletAutoConfiguration
    - @ConditionalOnClass classes found: org.springframework.web.servlet.DispatcherServlet (OnClassCondition)
    - found web application StandardServletEnvironment (OnWebApplicationCondition)
    ...
    

    拷贝 Positive matches 中列出的信息:

    DispatcherServletAutoConfiguration
    EmbeddedServletContainerAutoConfiguration
    ErrorMvcAutoConfiguration
    HttpEncodingAutoConfiguration
    HttpMessageConvertersAutoConfiguration
    JacksonAutoConfiguration
    JmxAutoConfiguration
    

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

    @Configuration
    @Import({
    DispatcherServletAutoConfiguration.class,
    EmbeddedServletContainerAutoConfiguration.class,
    ErrorMvcAutoConfiguration.class,
    HttpEncodingAutoConfiguration.class,
    HttpMessageConvertersAutoConfiguration.class,
    })
    public class SampleWebUiApplication {
    

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

  3. 将Servlet容器变成Undertow

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

    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
    

    然后添加 Undertow:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-undertow</artifactId>
    </dependency>
    

spring boot 远程调试

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

spring boot 打war包后

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

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

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

spring 集成 h2数据库

  1. application.properties

    增加配置

    #./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
    
  1. maven 配置

    <?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>
    
  1. h2默认生成的表varchar是255长度

    需要手工建立表

    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 绿色版

  2. JpaRepository

    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);
    }
    
  3. service

    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

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

    /**
    * 获取访问者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代码

    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项目

    <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>
    
  1. 关于在打包Jar文件时遇到的资源路径问题

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

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

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

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

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

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

    <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>
    

a