ElasticSearch 笔记 – 单机伪集群搭建

网上看了一下, 大都需要把elasticsearch程序拷贝多份, 然后修改配置.

程序有500多m, 蛮大的, 所以我想有没有只修改配置的方式…..

主要是三个地方:

  1. bin/elasticsearch.bat
  2. bin/elasticsearch-env.bat
  3. config文件夹

1.config文件夹(这里只做3个节点)

拷贝config文件夹, 命名为config-1, config-2, config-3
修改config/elasticsearch.yml
----------------------------------------------------------
#统一集群的名称(3个配置一样)
cluster.name: my-app
#当前节点名称
node.name: node-1
#当前节点数据
path.data: /data/elastic/data/1
#当前节点日志目录
path.data: /data/elastic/log/1
#当前节点端口(3个节点不同端口)
http.port: 9201
#指定初始的主节点(这里默认第一个)
cluster.initial_master_nodes: ["node-1"]
----------------------------------------------------------
其他两个文件夹做类似的修改

2.bin/elasticsearch-env.bat

找到设置"配置文件"的代码:
......
set HOSTNAME=%COMPUTERNAME%
if not defined ES_PATH_CONF (
  set ES_PATH_CONF=!ES_HOME!\config
)
......
把"set ES_PATH_CONF=!ES_HOME!\config" 修改为"set ES_PATH_CONF=!ES_HOME!\config-1", 另存为: bin/elasticsearch-env-1.bat
其他两个类似

3.bin/elasticsearch.bat

找到设置"环境变量"的代码:
......
CALL "%~dp0elasticsearch-env.bat" || exit /b 1
......
把"elasticsearch-env.bat" 修改为"elasticsearch-env-1.bat", 另存为: bin/elasticsearch-1.bat
其他两个类似

这样, 启动elasticsearch-1.bat/ elasticsearch-2.bat / elasticsearch-3.bat

就能建立单机集群.

Spring–AOP原理及拦截器

原理

AOP(Aspect Oriented Programming),也就是面向方面编程的技术。AOP基于IoC基础,是对OOP的有益补充。

  AOP将应用系统分为两部分,核心业务逻辑(Core businessconcerns)及横向的通用逻辑,也就是所谓的方面Crosscutting enterprise concerns,例如,所有大中型应用都要涉及到的持久化管理(Persistent)、事务管理(TransactionManagement)、安全管理(Security)、日志管理(Logging)和调试管理(Debugging)等。

  AOP正在成为软件开发的下一个光环。使用AOP,你可以将处理aspect的代码注入主程序,通常主程序的主要目的并不在于处理这些aspect。AOP可以防止代码混乱。

  Springframework是很有前途的AOP技术。作为一种非侵略性的、轻型的AOP framework,你无需使用预编译器或其他的元标签,便可以在Java程序中使用它。这意味着开发团队里只需一人要对付AOP framework,其他人还是像往常一样编程。

  AOP概念

  让我们从定义一些重要的AOP概念开始。

  — 方面(Aspect):一个关注点的模块化,这个关注点实现可能另外横切多个对象。事务管理是J2EE应用中一个很好的横切关注点例子。方面用Spring的Advisor或拦截器实现。

  — 连接点(Joinpoint):程序执行过程中明确的点,如方法的调用或特定的异常被抛出。

  — 通知(Advice):在特定的连接点,AOP框架执行的动作。各种类型的通知包括“around”、“before”和“throws”通知。通知类型将在下面讨论。许多AOP框架包括Spring都是以拦截器做通知模型,维护一个“围绕”连接点的拦截器链。

  — 切入点(Pointcut):指定一个通知将被引发的一系列连接点的集合。AOP框架必须允许开发者指定切入点,例如,使用正则表达式。

  — 引入(Introduction):添加方法或字段到被通知的类。Spring允许引入新的接口到任何被通知的对象。例如,你可以使用一个引入使任何对象实现IsModified接口,来简化缓存。

  — 目标对象(Target Object):包含连接点的对象,也被称作被通知或被代理对象。

  — AOP代理(AOP Proxy):AOP框架创建的对象,包含通知。在Spring中,AOP代理可以是JDK动态代理或CGLIB代理。

  — 编织(Weaving):组装方面来创建一个被通知对象。这可以在编译时完成(例如使用AspectJ编译器),也可以在运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。

  各种通知类型包括:

  —  Around通知:包围一个连接点的通知,如方法调用。这是最强大的通知。Aroud通知在方法调用前后完成自定义的行为,它们负责选择继续执行连接点或通过返回它们自己的返回值或抛出异常来短路执行。

  —  Before通知:在一个连接点之前执行的通知,但这个通知不能阻止连接点前的执行(除非它抛出一个异常)。

  —  Throws通知:在方法抛出异常时执行的通知。Spring提供强制类型的Throws通知,因此你可以书写代码捕获感兴趣的异常(和它的子类),不需要从Throwable或Exception强制类型转换。

  —  After returning通知:在连接点正常完成后执行的通知,例如,一个方法正常返回,没有抛出异常。

  Around通知是最通用的通知类型。大部分基于拦截的AOP框架(如Nanning和Jboss 4)只提供Around通知。

  如同AspectJ,Spring提供所有类型的通知,我们推荐你使用最为合适的通知类型来实现需要的行为。例如,如果只是需要用一个方法的返回值来更新缓存,你最好实现一个after returning通知,而不是around通知,虽然around通知也能完成同样的事情。使用最合适的通知类型使编程模型变得简单,并能减少潜在错误。例如,你不需要调用在around通知中所需使用的MethodInvocation的proceed()方法,因此就调用失败。

  切入点的概念是AOP的关键,它使AOP区别于其他使用拦截的技术。切入点使通知独立于OO的层次选定目标。例如,提供声明式事务管理的around通知可以被应用到跨越多个对象的一组方法上。 因此切入点构成了AOP的结构要素。

 拦截器(也称拦截机)

    拦截机 (Interceptor), 是 AOP (Aspect-Oriented Programming) 的另一种叫法。AOP本身是一门语言,只不过我们使用的是基于JAVA的集成到Spring 中的 SpringAOP。同样,我们将通过我们的例子来理解陌生的概念。

   接口类

 

package com.test.TestSpring3; 

 

public interface UserService // 被拦截的接口 

...{ 

   public void printUser(String user); 

} 

 实现类

 

package com.test.TestSpring3; 

 

public class UserServiceImp implements UserService// 实现UserService接口 

...{ 

   public void printUser(String user) ...{ 

       System.out.println("printUser user:" + user);// 显示user 

   } 

} 

  

 AOP拦截器

package com.test.TestSpring3; 

 

import org.aopalliance.intercept.MethodInterceptor; 

importorg.aopalliance.intercept.MethodInvocation; 

 

public class UserInterceptor implementsMethodInterceptor 

// AOP方法拦截器 

...{ 

 

   public Object invoke(MethodInvocation arg0) throws Throwable ...{ 

 

       try ...{ 

 

           if(arg0.getMethod().getName().equals("printUser")) 

           // 拦截方法是否是UserService接口的printUser方法 

           ...{ 

                Object[] args =arg0.getArguments();// 被拦截的参数 

               System.out.println("user:" + args[0]); 

               arg0.getArguments()[0] ="hello!";// 修改被拦截的参数 

 

           } 

 

           System.out.println(arg0.getMethod().getName() + "---!"); 

           return arg0.proceed();// 运行UserService接口的printUser方法 

 

       } catch (Exception e) ...{  

           throw e; 

       } 

   } 

}

 

 测试类

 

package com.test.TestSpring3; 

 

importorg.springframework.beans.factory.BeanFactory; 

 

importorg.springframework.beans.factory.xml.XmlBeanFactory; 

import org.springframework.context.ApplicationContext; 

importorg.springframework.context.support.ClassPathXmlApplicationContext; 

importorg.springframework.context.support.FileSystemXmlApplicationContext; 

importorg.springframework.core.io.ClassPathResource; 

import org.springframework.core.io.Resource; 

importorg.springframework.web.context.support.WebApplicationContextUtils; 

 

public class TestInterceptor ...{ 

 

   public static void main(String[] args) ...{ 

       ApplicationContext ctx = new FileSystemXmlApplicationContext( 

               "classpath:applicationContext.xml"); 

//       ApplicationContext ctx = newClassPathXmlApplicationContext("applicationContext.xml");     

         

       UserService us = (UserService)ctx.getBean("userService"); 

       us.printUser("shawn"); 

 

   } 

}

 

 配置文件

<?xml version="1.0"encoding="UTF-8"?> 

<!DOCTYPE beans PUBLIC"-//SPRING//DTD BEAN//EN""http://www.springframework.org/dtd/spring-beans.dtd"> 

<beans> 

   <bean id="userServiceImp" 

       class="com.test.TestSpring3.UserServiceImp" /> 

 

   <bean id="userInterceptor"class="com.test.TestSpring3.UserInterceptor" /> 

 

   <bean id="userService" 

       class="org.springframework.aop.framework.ProxyFactoryBean"> 

     <!-- 代理接口--> 

        <propertyname="proxyInterfaces"> 

           <value>com.test.TestSpring3.UserService</value> 

       </property> 

      <!-- 目标实现类--> 

       <property name="target"> 

           <ref local="userServiceImp" />  

     </property> 

        <!-- 拦截器 --> 

       <property name="interceptorNames"> 

           <list> 

               <value>userInterceptor</value> 

           </list> 

       </property> 

   </bean> 

 

</beans>

  

 输出:

 user:shawn

  printUser—!

 printUser user:hello!

 

结论:调用方法的时候 传入的值被拦截修改了.

拦截器中的事务管理(事务拦截机)

 如果不采用拦截机的机制时,在使用JDBC进行数据库访问时,存在两种情况:

自动提交        这是JDBC驱动默认的模式,每次数据库操作(CRUD)成功完成后,都作为一个单独的事务自动提交,如果未成功完成,即抛出了 SQLException 的话,仅最近的一个操作将回滚。

非自动提交    这是想更好的控制事务时需要程序地方式进行控制:

在进行该事务单元的任何操作之前 setAutoCommit(false)

在成功完成事务单元后commit()

在异常发生后rollback()

自动提交模式是不被推荐的,因为每个操作都将产生一个事务点,这对于大的应用来说性能将受到影响;再有,对于常见的业务逻辑,这种模式显得无能为力。比如:

转帐,从A帐户取出100元,将其存入B帐户;如果在这两个操作之间发生了错误,那么用户A将损失了100元,而本来应该给帐户B的,却因为失败给了银行。

所以,建议在所有的应用中,如果使用 JDBC 都将不得不采用非自动提交模式(你们要能发现了在我们的 JDBC 那个例子中,我们采用的就是自动提交模式,我们是为了把精力放在JDBC上,而不是事务处理上),即我们不得不在每个方法中:

try {     

 // 在获得连接后,立即通过调用 setAutoCommit(false) 将事务处理置为非自动提交模式  // Prepare Query to fetch the userInformation        

    pst = conn.prepareStatement(findByName);                  

      // ...           conn.commit();        

 } catch(Exception ex) {        

    conn.rollback();       

     throw ex;        

 }finally {    

        try {     

          // Close Result Set and Statement   

         if (rset != null) rset.close();                

        if (pst != null) pst.close();                         

    }catch (Exception ex) {                

      ex.printStackTrace();                 

      throw new Exception("SQL Error while closing objects = " +ex.toString());             

}   

}

  

 这样代码在AOP的倡导者看来是“肮脏”的代码。他们认为,所有的与事务有关的方法都应当可以集中配置(见声明性事务控制),并自动拦截,程序应当关心他们的主要任务,即商业逻辑,而不应和事务处理的代码搅和在一起。

 

我先看看 Spring 是怎么做到拦截的:

Spring 内置支持的事务处理拦截机

这里因为要用到JpetStore项目中的代码,我们将 applicationContext.xml 全部内容列出:

 

<?xml version="1.0" encoding="UTF-8"?>

<!--

  -Application context definition for JPetStore's business layer.

  -Contains bean references to the transaction manager and to the DAOs in

  -dataAccessContext-local/jta.xml (see web.xml's"contextConfigLocation").
   Jpetstore 的应用上下文定义,包含事务管理和引用了在 dataAccessContext-local/jta.xml(具体使用了哪个要看 web.xml 中的 'contextConfigLocation' 的配置)中注册的DAO

-->

<beans xmlns="http://www.springframework.org/schema/beans"

      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

      xmlns:aop="http://www.springframework.org/schema/aop"

      xmlns:tx="http://www.springframework.org/schema/tx"

      xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-2.0.xsd

      http://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop-2.0.xsd

http://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx-2.0.xsd">

   <!-- ========================= GENERAL DEFINITIONS========================= -->

 

   <!-- Configurer that replaces ${...} placeholders with values fromproperties files

        占位符的值将从列出的属性文件中抽取出来

    -->

   <!-- (in this case, mail and JDBC related properties) -->

   <bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">

       <property name="locations">

           <list>

               <value>WEB-INF/mail.properties</value>

               <value>WEB-INF/jdbc.properties</value>

           </list>

       </property>

   </bean>

   <!-- MailSender used by EmailAdvice

        指定用于发送邮件的javamail 实现者,这里使用了spring 自带的实现。此 bean 将被 emailAdvice 使用
    -->

   <bean id="mailSender"class="org.springframework.mail.javamail.JavaMailSenderImpl">

       <property name="host" value="${mail.host}"/>

   </bean>
 

   <!-- ========================= BUSINESS OBJECT DEFINITIONS======================== -->

 

   <!-- 不需要,因为被SpringMVC 的实现使用 Genericvalidator for Account objects, to be used for example by the Spring web tier-->

   <bean id="accountValidator"class="org.springframework.samples.jpetstore.domain.logic.AccountValidator"/>
  

   <!-- 不需要,因为被SpringMVC 的实现使用 Genericvalidator for Order objects, to be used for example by the Spring web tier-->

   <bean id="orderValidator"class="org.springframework.samples.jpetstore.domain.logic.OrderValidator"/>
   

   <!--

       主要的商业逻辑对象,即我们所说的门面对象

       注入了所有的DAO,这些DAO是引用了 dataAccessContext-xxx.xml 中定义的DAO

       门面对象中的所有方法的事务控制将通过下面的 aop:config 来加以控制

     - JPetStore primary business object (default implementation).

     - Transaction advice gets applied through the AOP configuration below.

  -->

   <bean id="petStore"class="org.springframework.samples.jpetstore.domain.logic.PetStoreImpl">

       <property name="accountDao" ref="accountDao"/>

       <property name="categoryDao"ref="categoryDao"/>

       <property name="productDao" ref="productDao"/>

       <property name="itemDao" ref="itemDao"/>

       <property name="orderDao" ref="orderDao"/>

   </bean>
  

   <!-- ========================= ASPECT CONFIGURATION======================== -->

   <!-- AOP配置,用来控制哪些方法将需要进行事务处理,采用了AspectJ 的语法 -->

   <aop:config>

       <!--

       This definition creates auto-proxy infrastructure based on the givenpointcut,

       expressed in AspectJ pointcut language. Here: applying the advice named

       "txAdvice" to all methods on classes named PetStoreImpl.
   -->

       <!-- 指出在PetStoreFacade 的所有方法都将采用txAdvice(在紧接着的元素中定义了)事务方针,注意,我们这里虽然指定的是接口 PetStoreFacace, 但其暗示着其所有的实现类也将

        同样具有这种性质,因为本身就是实现类的方法在执行的,接口是没有方法体的。 -->

       <aop:advisor pointcut="execution(**..PetStoreFacade.*(..))" advice-ref="txAdvice"/>

       

       <!--

           This definition creates auto-proxy infrastructure based on the givenpointcut,

           expressed in AspectJ pointcut language. Here: applying the advice named

           "emailAdvice" to insertOrder(Order) method of PetStoreImpl

   -->

       <!-- 当执行PetStoreFacade.insertOrder方法,该方法最后一个参数为Order类型时(其实我们的例子中只有一个 insertOrder 方法,但这告诉了我们,当我们的接口或类中有重载了的方法,

        并且各个重载的方法可能使用不同的拦截机机制时,我们可以通过方法的参数加以指定),将执行emailAdvice(在最后定义的那个元素)-->

       <aop:advisor pointcut="execution(**..PetStoreFacade.insertOrder(*..Order))" advice-ref="emailAdvice"/>
   </aop:config>

   <!--
  事务方针声明,用于控制采用什么样的事务策略

       Transaction advice definition, based on method name patterns.

       Defaults to PROPAGATION_REQUIRED for all methods whose name starts with

       "insert" or "update", and to PROPAGATION_REQUIREDwith read-only hint

       for all other methods.
   -->

   <tx:advice id="txAdvice">

       <tx:attributes>

           <tx:method name="insert*"/>

           <tx:method name="update*"/>

           <tx:method name="*" read-only="true"/>

       </tx:attributes>

   </tx:advice>


   <!-- 拦截机,用于在适当的时机(通过AOP配置,如上面)在方法执行成功后发送邮件

     AOP advice used to send confirmation email after order has beensubmitted -->

   <!-- -->

   <bean id="emailAdvice" class="org.springframework.samples.jpetstore.domain.logic.SendOrderConfirmationEmailAdvice">

       <property name="mailSender" ref="mailSender"/>

   </bean>

   <!-- ========================= 忽略 REMOTE EXPORTER DEFINITIONS ======================== -->

</beans>

 

这个配置比想象的要简单的多:

<aop:config>        

 <!-- This definition creates auto-proxyinfrastructure based on the given pointcut, expressed in AspectJ pointcutlanguage.  

Here: applying the advice named        "txAdvice" to all methods onclasses named PetStoreImpl. 指出在 PetStoreFacade  

的所有方法都将采用txAdvice(在紧接着的元素中定义了)事务方针,注意,我们这里虽然指定的是接口 PetStoreFacace,         

 但其暗示着其所有的实现类也将同样具有这种性质,因为本身就是实现类的方法在执行的,接口是没有方法体的。    -->    

      <aop:advisor pointcut="execution(* *..PetStoreFacade.*(..))"advice-ref="txAdvice"/>                

 <!-- 其它拦截机-->   

</aop:config>

 

1. 所有的拦截机配置都放在 <aop:config> 配置元素中.

2. 下面还是需要理解一下几个有关AOP的专用名词,不过,是挺抽象的,最好能会意出其的用意

pointcut 切入点,比如:updateAccount 方法需要进行事务管理,则这个切入点就是“执行方法体”(execution)。Spring 所有支持的切入点类型在都在 Spring reference: 6.2.3.1. Supported Pointcut Designators 中列出了。

advice   要对这个切入点进行什么操作,比如事务控制

advisor  Spring 特有的概念,将上两个概念合到一个概念中来,即一个 advisor 包含了一个切入点及对这个切入点所实施的操作。

因为 方法执行切入点 execution 为最常见的切入点类型,我们着重介绍一下,execution 的完全形式为:

execution(modifiers-pattern?ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)throws-pattern?)

这是一个正则表达式,其中由’?’ 结尾的部分是可选的。翻译过来就是:

执行(方法访问修饰符? 方法返回类型 声明类型? 方法名(方法参数类型) 抛出异常?)

所有的这些都是用来定义执行切入点,即那些方法应该被侯选为切入点:

方法访问修饰符   即 public, private 等等

方法返回类型       即方法返回的类型,如 void, String 等等

声明类型                1.5的语法,现在可以先忽略它

方法名                    方法的名字

方法参数类型       方法的参数类型

抛出异常                方法声明的抛出的异常

 

例如,所有dao代码被定义在包 com.xyz.dao 及子包 com.xyz.dao.hibernate, 或者其它,如果还有的话,子包中, 里面定义的是提供DAO功能的接口或类,那么表达式:

execution(* com.xyz.dao..*.*(..))

表示切入点为:执行定义在包com.xyz.dao 及其子包(因为 .. 所致) 中的任何方法

 

详细情况可以参见Spring refernce: 6.2.3.4. Examples

 

因此这个表达式为执行定义在类PetStoreFacade 及其实现类中的所有方法,采取的动作定义在 txAdvice 中. 关于该 advice 的定义,(见声明性事务控制)一节

<aop:advisor pointcut=”execution(**..PetStoreFacade.*(..))” advice-ref=”txAdvice”/>

 

Spring 自定拦截机

来为了进行事务控制,我们只需简单地配置几下,所有的工作都由 Spring 来做。这样固然很好,但有时我们需要有我们特有的控制逻辑。因为Spring 不可能包含所有人需要的所有拦截机。所以它提供了通过程序的方式加以定制的方式。我们的项目中就有这么一个拦截机,在用户确认付款后,将定单信息通过 email 的方式发送给注册用户的邮箱中。

 

<aop:config>

  ...

 

       <!-- 当执行 PetStoreFacade.insertOrder方法,该方法最后一个参数为Order类型时(其实我们的例子中只有一个 insertOrder 方法,但这告诉了我们,当我们的接口或类中有重载了的方法,

        并且各个重载的方法可能使用不同的拦截机机制时,我们可以通过方法的参数加以指定),将执行emailAdvice(在最后定义的那个元素)-->

       <aop:advisor pointcut="execution(**..PetStoreFacade.insertOrder(*..Order))"advice-ref="emailAdvice"/>

       

   </aop:config>

红色的注释已经说的很清楚这个Advisor 了,它的切入点(pointcut)为PetStoreFacade 的 voidinsertOrder(Order order) 方法,采取的动作为引用的 emailAdvice, 下面我们就来看看 emailAdvice:

 

   <bean id=”emailAdvice”class=”org.springframework.samples.jpetstore.domain.logic.SendOrderConfirmationEmailAdvice”>

       <property name=”mailSender” ref=”mailSender”/>

   </bean>

它给了这个 advice 的实现类为 logic 包中 SendOrderConfirmationEmailAdvice,该Bean 引用了我们前面定义的邮件发送器(一个 Spring 内置的邮件发送器).

 

下面看看这个实现类:

 

public classSendOrderConfirmationEmailAdvice implements AfterReturningAdvice,InitializingBean {

   // user jes on localhost

   private static final String DEFAULT_MAIL_FROM ="test@pprun.org";

   

   private static final String DEFAULT_SUBJECT = "Thank you for yourorder!";

   

   private final Log logger = LogFactory.getLog(getClass());

   

   private MailSender mailSender;

   

   private String mailFrom = DEFAULT_MAIL_FROM;

   

   private String subject = DEFAULT_SUBJECT;

   

   public void setMailSender(MailSender mailSender) {

       this.mailSender = mailSender;

    }

   

   public void setMailFrom(String mailFrom) {

       this.mailFrom = mailFrom;

    }

   

   public void setSubject(String subject) {

       this.subject = subject;

    }

    

   public void throws Exception {

       if (this.mailSender == null) {

           throw new IllegalStateException("mailSender is required");

       }

    }

   

   /**

    *

    * @param returnValue 被拦截的方法的返回值

    * @param m 被拦截的方法的所有信息(Method类封装了这些信息)

    * @param args 被拦截的方法的所有参数组成的数组

    * @param target 目标对象,对于方法执行来说,即是方法所在的类的实例(与 this 同,批当前对象)

    * @throws java.lang.Throwable

    */

   public void afterReturning(Object returnValue, Method m, Object[] args,Object target) throws Throwable {

       // 我们被拦截的方法为 voidinsertOrder(Order order),方法只有一个参数,所以可知数据的第1个元素即是被传进的 order对象

       // 得到了order 对象,就可以将 order 对应的帐户名及帐单号发送到邮件中,以便确认无误。

       Order order = (Order) args[0];

       Account account = ((PetStoreFacade) target).getAccount(order.getUser().getUsername());

       

       // don't do anything if email address is not set

       if (account.getEmail() == null || account.getEmail().length() == 0) {

           return;

       }

       

       StringBuffer text = new StringBuffer();

       text.append("Dear ").append(account.getFirstname()).

                append('').append(account.getLastname());

       text.append(", thank your for your order from JPetStore. " +

                "Please note that yourorder number is ");

        text.append(order.getId());

       

       SimpleMailMessage mailMessage = new SimpleMailMessage();

       mailMessage.setTo(account.getEmail());

       mailMessage.setFrom(this.mailFrom);

       mailMessage.setSubject(this.subject);

       mailMessage.setText(text.toString());

       try {

           this.mailSender.send(mailMessage);

       } catch (MailException ex) {

           // just log it and go on

           logger.warn("An exception occured when trying to send email",ex);

       }

    }

   

}

 

1. 红色的内容即为反向注入的 mailSender 属性

 

2. 蓝色的内容为 Spring Bean 的一个通用的接口 InitializingBean ,实现类需要实现该接口定义的方法 afterPropertiesSet() ,该方法中一般是在Bean 被初始化后并设置了所有的setter 注入后调用的。所以这里是保证邮件发送器配置正确。因为如果没有配置正确,下面的工作是无法进行的,所以与其等那时抛出异常,还不如早早地在部署时就告知(通过抛出 IllegalStateException 来提示)

 

3. 绿色的内容为这个 Advise 的核心,即在切入点被切入后将采用的动作。因为 Advise 也同样有多种类型,比如我们这里的“方法正常返回”,“方法执行前”,“方法执行后”,“环绕在方法执行前后”,“方法抛出异常时”等等(详情参见 SpringReference: 6.2.4. Declaring advice)。但是我们的逻辑为在用户确认定单并且执行成功(所谓的成功是指将这一定单插入到了表 Order 中了)后,将发送一确认信。所以”方法正常返回“完全符合我们的要求。

接口AfterReturningAdvice即是 Spring中表示”方法正常返回“ 这一语义的 Advice, 所以我们实现这个接口及其必须的方法 afterReturning.

方法代码的工作其实并不重要,只要我们理解这些“魔法”一样的技术后,实现代码是很简单的。值得提及的是这个方法的参数,这些参数是封装了切入点的所有信息,请见上面的注释。在我们的实现中只使用了被拦截方法的参数,在复杂的 Advice 实现中可能会用到切入点所有信息。

关于一次JVM宕机的问题排查

描述

情况是这样的,tomcat服务刚启动访问正常, 但是过不了一会儿,访问就N慢,到最后服务直接宕机.

处理

1. 先打印tomcat日志,看看什么情况: tail -200f logs/proj.log

   堆内存溢出,JVM荣幸挂了.
2.接下来: 看看内存的使用情况
    先找到java线程pid, 然后执行: jmap -heap [pid]

    JVM总共分配了3G内存, 怎么一下子就耗光了, Eden space/Old Generation都是满的.
    所以断定: 再往上加内存是没用了, 肯定是代码问题, 某个方法一次性加载了大量的数据导致.
    于是,我们就要做dump分析了
3.DUMP内存情况
    >jmap -dump:live,format=b,file=/tmp/dump20170603.bin [pid]
    dump文件一共3G,还好不大.
4.分析dump文件
    我用MAT(memory analyzer tool)分析工具: 先设置-Xmx4g,要不然撑不住.
   
    DubboServerHandler占了1.0G
    JDBC4ResultSet占了1.4G
    看了一下DubboServerHandler,  里面最大的对象也是JDBC4ResultSet.
    所以, 这是典型的SQL结果集太大, 没做分页处理.  到底是那个SQL???
    
    再分析ResultSet所属的Statement对象,终于找到了这个罪犯: select * from 用户表;
    后面居然没有加任何条件,没有加!!! 把所有用户都查出来…..顿时吴宇森!
    之后,在分析这个线程具体调用的堆栈信息,发现是: 查询别的业务时,附带查询的用户信息.
    
    用户查询本身有加条件做为输入参数,但是方法里面, 并没有为条件做空判断.
    导致: 条件为空时,便查询了所有用户.  

总结

缺少一个条件判断,导致整个服务宕机. 多么狗血的事实.

常用SQL

1. 行列转换--普通

假设有张学生成绩表(CJ)如下
Name Subject Result
张三 语文 80
张三 数学 90
张三 物理 85
李四 语文 85
李四 数学 92
李四 物理 82

想变成
姓名 语文 数学 物理
张三 80 90 85
李四 85 92 82

declare @sql varchar(4000)
set @sql = 'select Name'
select @sql = @sql + ',sum(case Subject when '''+Subject+''' then Result end) ['+Subject+']'
from (select distinct Subject from CJ) as a
select @sql = @sql+' from test group by name'
exec(@sql)

2. 行列转换--合并

有表A,
id pid
1 1
1 2
1 3
2 1
2 2
3 1
如何化成表B:
id pid
1 1,2,3
2 1,2
3 1

创建一个合并的函数
create function fmerg(@id int)
returns varchar(8000)
as
begin
declare @str varchar(8000)
set @str=''
select @str=@str+','+cast(pid as varchar) from 表A where id=@id set @str=right(@str,len(@str)-1)
return(@str)
End
go

--调用自定义函数得到结果
select distinct id,dbo.fmerg(id) from 表A

3. 如何取得一个数据表的所有列名

方法如下:先从SYSTEMOBJECT系统表中取得数据表的SYSTEMID,然后再SYSCOLUMN表中取得该数据表的所有列名。
SQL语句如下:
declare @objid int,@objname char(40)
set @objname = 'tablename'
select @objid = id from sysobjects where id = object_id(@objname)
select 'Column_name' = name from syscolumns where id = @objid order by colid

是不是太简单了? 呵呵 不过经常用阿.

4. 通过SQL语句来更改用户的密码

修改别人的,需要sysadmin role
EXEC sp_password NULL, 'newpassword', 'User'

如果帐号为SA执行EXEC sp_password NULL, 'newpassword', sa

5. 怎么判断出一个表的哪些字段不允许为空?

select COLUMN_NAME from INFORMATION_SCHEMA.COLUMNS where IS_NULLABLE='NO' and TABLE_NAME=tablename

6. 如何在数据库里找到含有相同字段的表?
a. 查已知列名的情况
SELECT b.name as TableName,a.name as columnname
From syscolumns a INNER JOIN sysobjects b
ON a.id=b.id
AND b.type='U'
AND a.name='你的字段名字'

b. 未知列名查所有在不同表出现过的列名
Select o.name As tablename,s1.name As columnname
From syscolumns s1, sysobjects o
Where s1.id = o.id
And o.type = 'U'
And Exists (
Select 1 From syscolumns s2
Where s1.name = s2.name
And s1.id <> s2.id
)

7. 查询第xxx行数据

假设id是主键:
select *
from (select top xxx * from yourtable) aa
where not exists(select 1 from (select top xxx-1 * from yourtable) bb where aa.id=bb.id)

如果使用游标也是可以的
fetch absolute [number] from [cursor_name]
行数为绝对行数

8. SQL Server日期计算
a. 一个月的第一天
SELECT DATEADD(mm, DATEDIFF(mm,0,getdate()), 0)
b. 本周的星期一
SELECT DATEADD(wk, DATEDIFF(wk,0,getdate()), 0)
c. 一年的第一天
SELECT DATEADD(yy, DATEDIFF(yy,0,getdate()), 0)
d. 季度的第一天
SELECT DATEADD(qq, DATEDIFF(qq,0,getdate()), 0)
e. 上个月的最后一天
SELECT dateadd(ms,-3,DATEADD(mm, DATEDIFF(mm,0,getdate()), 0))
f. 去年的最后一天
SELECT dateadd(ms,-3,DATEADD(yy, DATEDIFF(yy,0,getdate()), 0))
g. 本月的最后一天
SELECT dateadd(ms,-3,DATEADD(mm, DATEDIFF(m,0,getdate())+1, 0))
h. 本月的第一个星期一
select DATEADD(wk, DATEDIFF(wk,0,
dateadd(dd,6-datepart(day,getdate()),getdate())
), 0)
i. 本年的最后一天
SELECT dateadd(ms,-3,DATEADD(yy, DATEDIFF(yy,0,getdate())+1, 0))。
9.获取行号
mysql:
SELECT @rowno:=@rowno + 1 AS rowno,a.* FROM tableName a,(SELECT @rowno:=0) b
oracle:
SELECT rownum,a.* FROM tableName a

感谢那些网上提供相关SQL的作者

ElasticSearch 笔记 – 高级查询API

通用查询: match/multi_match
无论是全文搜索还是精确查询 它将用正确的分析器去分析查询字符串

范围查询: range

{
    "range": {
        "字段": {
            "gt":  20,
            "lt":   30
        }
    }
}

精确查询 term/terms

{ "term": { "age":    26 }}
term 查询对于输入的文本不分析 ,所以它将给定的值进行精确查询(包括在大小写、重音、空格等方面的差异)。

非空和空查询: exists/missing

{
    "exists":   {
        "field":    "字段"
    }
}
这与SQL中的 IS_NULL (missing) 和 NOT IS_NULL (exists) 在本质上具有共性

组合查询: bool

{
    "bool": {
        "must":     { "match": { "title": "how to make millions" }},
        "must_not": { "match": { "tag":   "spam" }},
        "should": [
            { "match": { "tag": "starred" }},
            { "range": { "date": { "gte": "2014-01-01" }}}
        ],
        "filter": {
          "range": { "date": { "lt": "2014-08-01" }} 
        }
    }
}
must
文档 必须 匹配这些条件才能被包含进来。
must_not
文档 必须不 匹配这些条件才能被包含进来。
should
如果满足这些语句中的任意语句,将增加 _score ,否则,无任何影响。它们主要用于修正每个文档的相关性得分。
filter
必须 匹配,但它以不评分、过滤模式来进行。这些语句对评分没有贡献,只是根据过滤标准来排除或包含文档。

constant_score 查询

{
    "constant_score":   {
        "filter": {
            "term": { "category": "ebooks" } 
        }
    }
}
它被经常用于你只需要执行一个 filter 而没有其它查询(例如,评分查询)的情况下

查询分析: 在查询URL后加上explain

GET /_validate/query?explain
{
   "query": {
      "match" : {
         "tweet" : "really powerful"
      }
   }
}

如何选择查询与过滤编辑

通常的规则是,使用 查询(query)语句来进行 全文 搜索或者其它任何需要影响 相关性得分 的搜索。除此以外的情况都使用过滤(filters)。

ElasticSearch 笔记 – 基础API

#新增文档(可以指定id, 或者由es生成)
POST .kibana_1/_doc/[id]
{
  "visualization" : {
      "title" : "First ES Doc",
      "visState" : "this is my first es document2.",
      "uiStateJSON" : "{}",
    },
    "type" : "visualization",
    "updated_at" : "1556582400000"
}

#修改文档
PUT .kibana_1/_doc/466x1GoBzyYXTMCaEtuq
{
  "visualization" : {
      "title" : "First ES Doc",
      "visState" : "this is my first es document2.",
      "uiStateJSON" : "{}",
      "description" : "",
      "version" : 1,
      "kibanaSavedObjectMeta" : {
        "searchSourceJSON" : """{"query":{"query":"","language":"kuery"},"filter":[]}"""
      }
    },
    "type" : "visualization",
    "references" : [ ],
    "migrationVersion" : {
      "visualization" : "7.0.1"
    },
    "updated_at" : "1556582400000"
}

#id查找
GET .kibana_1/_doc/466x1GoBzyYXTMCaEtuq

#指定字段返回
GET .kibana_1/_doc/466x1GoBzyYXTMCaEtuq?_source=visualization.title,visualization.visState,type,migrationVersion

#分页查询size=多少, from从哪里开始
GET .kibana_1/_doc/_search?size=1&from=4000
{
  "query": {
    "match_all": {}
  }
}

#只返回内容
GET .kibana_1/_doc/466x1GoBzyYXTMCaEtuq/_source

#检查是否存在
HEAD .kibana_1/_doc/466x1GoBzyYXTMCaEtuq

#请求参数方式搜索
GET kibana_sample_data_ecommerce/_doc/_search?q=category:Men's Clothing+products.price:>700

#访问映射
GET kibana_sample_data_ecommerce/_mapping

# 分析器演示
GET /_analyze
{
  "analyzer": "english",
  "text": ["This is my house. i'm living here",
          "every one know that",
          "don't you know"]
}

#定义索引映射
PUT kibana_sample_data_ecommerce/_mapping
{
  "properties": {
      "category":{
        "type": "text",
        "index": true
      }
  }
}

#更新索引映射
es中想要给一个已经建好映射的索引改变映射结果,即使改变一个字段类型都是不支持的,
需要重新建立索引以及映射结构,然后把以前的数据导入到新建的索引结构中去,完成改变映射结构的目的。
步骤:
1.给已有的索引定一个别名,并指向该别名
2.新建一个新的索引,新的映射结构
3.将别名指向新的索引,取消旧的索引与别名之间的关联
通过这几部即可达到重新改变映射结构的内容,例如我们想改变library01的映射中price字段的类型由现有的double变为integer类型。

一个简单的例子:

#删除索引
DELETE products

#建立索引
PUT products
{
  "settings": {
    "number_of_shards": 3,
    "number_of_replicas": 1
  }
}
#建立映射
PUT products/_mapping
{
  "properties": {
    "category":{
      "type": "text",
      "index": true
    },
    "id":{
      "type": "text",
      "index": true
    },
    "name":{
      "type": "text",
      "analyzer": "standard"
    },
    "description":{
      "type": "text",
      "analyzer": "standard"
    },
    "price":{
      "type": "double"
    },
    "detail":{
      "type": "text",
      "analyzer": "standard"
    },
    "enabled":{
      "type": "boolean"
    },
    "picture":{
      "type": "text"
    }
  }
}
#查询映射
GET products/_mapping
#写入数据
POST /products/_doc
{
  "category": "HaiLan Home",
  "id": "A92934JSDFIQ9234SJDF",
  "name": "男裤",
  "description": "纯棉 男裤 夏季 清凉一夏 直筒",
  "price": 201.50,
  "enabled": true,
  "picture": "http://img.alicdn.com/tfscom/i4/1692495776/TB2vLAKaDMG5uJjSZFAXXbmspXa_%21%211692495776.jpg",
  "detail": "裤子是人们下体所穿的主要服饰。原写作“绔”、“袴”。从出土文物及传世文献来看,早在春秋时期,人们的下体己穿著裤,不过那时的裤子不分男女,都只有两只裤管,其形制和后世的套裤相似,无腰无裆,穿时套在胫上,即膝盖以下的小腿部分,所以这种裤子又被称为“胫衣”。左右各一,分衣两胫。因其只有两只裤管,所以裤的计数与鞋袜相同,都用“两”字来计,居延汉简中就有这样的情况。穿著这种裤子,其目的是为了遮护胫部,尤其在冬天,可以起到保暖的作用,至于膝盖以上部分则无遮护。"
}
#查询数据
GET products/_doc/_search

备注: 随着 7.0 版本的发布,type 的移除也是越来越近了,在 6.0 的时候,已经默认只能支持一个索引一个 type 了,7.0 版本新增了一个参数 include_type_name ,即让所有的 API 是 type 相关的,这个参数在 7.0 默认是 true,不过在 8.0 的时候,会默认改成 false,也就是不包含 type 信息了,这个是 type 用于移除的一个开关。 让我们看看最新的使用姿势吧,当 include_type_name 参数设置成 false 后:

 索引操作:
PUT {index}/{type}/{id}需要修改成PUT {index}/_doc/{id}
Mapping 操作:
PUT {index}/{type}/_mapping 则变成 PUT {index}/_mapping
所有增删改查搜索操作返回结果里面的关键字 _type 都将被移除, 父子关系使用 join 字段来构建

Linux下nohup日志输出过大问题解决方案

  1、nohup命令解释:

  a、语法:nohup [command] [args] [&]

  b、说明:nohup 命令运行由 Command 参数和任何相关的 Arg 参数指定的命令,忽略所有挂断信号。在注销后使用 nohup 命令运行后台中的程序。要运行后台中的 nohup 命令,添加 & ( 表示“and”的符号)到命令的尾部,如果不指定重定向,则日志默认输出到当前目录下nohup.out文件中,

  一般提交如 :nohup ./execute.sh &  这样日志或输出当前运行目下.nohup.out中

  重定向: nohup ./execute.sh >  /home/xxx/log.log 2>&1 & :这样日志会重定向到指定目录下
  0: 表示标准输入
  1: 标准输出,在一般使用时,默认的是标准输出
  2: 标准错误信息输出
     可以用来指定需要重定向的标准输入或输出。例如,将某个程序的错误信息输出到log文件中:./program 2>log。这样标准输出还是在屏幕上,但是错误信息会输出到log文件中。另外,也可以实现0,1,2之间的重定向。2>&1:将错误信息重定向到标准输出。
  关于/dev/null文件
  Linux下还有一个特殊的文件/dev/null,它就像一个无底洞,所有重定向到它的信息都会消失得无影无踪。这一点非常有用,当我们不需要回显程序的所有信息时,就可以将输出重定向到/dev/null。

2 、切分nohup.out,同时不让它无限增长

 我这里用的一般提交命令:nohup ./execute.sh &,这样在当前目录就有nohup.out文件了,这时候可以想办法定时将nohup.out切分成,多个小文件,但同时又要使nohup.out不会无限增长下去(一般情况下是程序不能中断的):

    a、每天(根据需要设置时间),定时切分前一天的日志,(比如每天大概1g,那么可以么次切分100m左右),

    b、切分完后将nohup.out文件情况,保证新的输出日志会不停的继续输出到nohup.out

  以上在shell中

  current_date=`date -d "-1 day" "+%Y%m%d"`

   split  -b 65535000 -d -a 4  nohup.out  ./log/log_${current_date}_   这里使用split命令,将nouhup文件按指定大小切分(65535000b 大概60多M吧,可以自定义大小 ),并分成指定格式(-d -a 4以4位数字形式为后缀以从0000开始,具体可以百度split命令用法),最终输出格式为log_20160610_0001

  cat /dev/null > nohup.out  (该命令会瞬间清空nohup.out文件,后续会继续写该文件),将日志定向到/dev/null中

使用重定向输出一样可以这样,只不过换成重定向的文件名即可

将这些命令定义在一个shell文件每天定时运行即可,这样每天日志会被分成若干份,排查也方便,而且如果日志积压过大的话。可以定时删除历史的日志,保留近几天即可

整体代码如下:

this_path=$(cd `dirname $0`;pwd)

cd $this_path
echo $this_path
current_date=`date -d "-1 day" "+%Y%m%d"`
echo $current_date
split -b 65535000 -d -a 4 /home/.../nohup.out   /home/.../log/log_${current_date}_

cat /dev/null > nohup.out

https://blog.csdn.net/shawnhu007/article/details/50971084

Kafka 监控

KafkaOffsetMonitor托管在Github上,可以通过Github下载。
下载地址:https://github.com/quantifind/KafkaOffsetMonitor/releases

启动命令:

java -cp KafkaOffsetMonitor-assembly-0.2.1.jar com.quantifind.kafka.offsetapp.OffsetGetterWeb --zk 127.0.0.1:2181,127.0.0.1:2182 --port 6088  --refresh 5.seconds --retain 1.days
参数说明:

zk :zookeeper主机地址,如果有多个,用逗号隔开
port :应用程序端口
refresh :应用程序在数据库中刷新和存储点的频率
retain :在db中保留多长时间
dbName :保存的数据库文件名,默认为offsetapp
或者写一个shell脚本:
nohup java -Xms512M -Xmx512M -Xss1024K -XX:PermSize=256m -XX:MaxPermSize=512m  -cp KafkaOffsetMonitor-assembly-0.2.0.jar com.quantifind.kafka.offsetapp.OffsetGetterWeb \
--port 6088 \
--zk 127.0.0.1:2181,127.0.0.1:2182 \
--refresh 5.minutes \
--retain 1.day >/dev/null 2>&1 &

启动好后, 在浏览器中就能看见 Group, Topic, Consumer 等信息

备注: Kafka有一个坑,默认情况下Producer往一个不存在的Topic发送message时会自动创建这个Topic。 如果同时传递message和topic时, 传入的参数反了,将会在Kafka集群中自动创建Topic。在正常情况下,应该是先把Topic根据需要创建好,然后Producer往该Topic发送Message,最好把Kafka这个默认自动创建Topic的功能关掉。 
  那么,假设真的不小心创建了多余的Topic,在删除时,会出现“marked for deletion”提示,只是将该topic标记为删除,使用list命令仍然能看到。如果需要调整这两个功能的话,在server.properties中配置如下两个参数:

参数 默认值 作用
auto.create.topics.enable true Enable auto creation of topic on the server
delete.topic.enable false Enables delete topic. Delete topic through
the admin tool will have no effect
if this config is turned off

kafkaOffsetMonitor:程序一个jar包的形式运行,部署较为方便。只有监控功能,使用起来也较为安全。
除了KafkaOffsetMonitor,Kafka监控工具还有另外两款:
Kafka Web Console:监控功能较为全面,可以预览消息,监控Offset、Lag等信息,但存在bug,不建议在生产环境中使用。
Kafka Manager:偏向Kafka集群管理,若操作不当,容易导致集群出现故障。对Kafka实时生产和消费消息是通过JMX实现的。没有记录Offset、Lag等信息。

linux 添加自签名证书/Nginx 配置Https

自签名证书做法

1.先生成一对秘钥,把公钥做成证书 server.key

openssl  genrsa [-des3] -out server.key 2048       生成一个 2048 位的 私钥

说明:生成rsa私钥,des3算法,2048位强度,server.key是秘钥文件名。
注意:生成私钥,需要提供一个至少4位的密码, 如果不需要, 直接去掉-des3选项
这个密码会带来一个副作用,那就是在每次Apache启动Web服务器时,都会要求输入密码,这显然非常不方便。要删除私钥中的密码,操作如下:
cp server.key server.key.org
openssl rsa -in server.key.org -out server.key

我们可以 输出它的公钥看看

openssl  rsa -in server.key -pubout

2. 生成跟CA并自签(证书签名请求)

生成私钥之后,便可以创建csr文件了。
此时可以有两种选择。理想情况下,可以将证书发送给证书颁发机构(CA),CA验证过请求者的身份之后,会出具签名证书(很贵)。另外,如果只是内部或者测试需求,也可以使用OpenSSL实现自签名,具体操作如下:

openssl req -new -x509 -key server.key -out server.csr
国家 Country Name:  CN   
省    Stat or Province Name   Shanghai
市   Locality Name     Shanghai
公司  Organization Name :  HUPU
部门 Organizational Unit   Tech
主机名 Common Name    www.zmz8.com
邮件  Email Address    279861795@qq.com

3.生成证书CRT server.crt

openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt
备注: crt 转 p12
openssl x509 -in server.crt -inform der -out server.pem
openssl pkcs12 -export -inkey server.key-in server.pem -out server.p12

我们可以查看证书内容

openssl  x509  -text -in server.crt

4.配置nginx

证书路径 /usr/local/nginx/ssl/server.crt

私钥路径 /usr/local/nginx/ssl/server.key

nginx添加配置
server {
    listen 80;
    server_name     www.zmz8.com;
    #rewrite ^/(.*)$ https://www.zmz8.com/$1 permanent;
    return 307 https://$host$request_uri;
}
server {
  listen 443;
  server_name  www.zmz8.com;
  ssl on;
  ssl_certificate   ../ssl/server.crt;
  ssl_certificate_key  ../ssl/server.key;
  ssl_session_timeout 5m;
  ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
}

最后如果可以的话 ,可以配置一个服务器签署

openssl req -new -out server.csr -key server.key -config /etc/pki/tls/openssl.cnf 
OpenSSL工具说明:
OpenSSL   ssl的开源实现,几乎实现了市面上所有的加密
libcrypto: 通用加密库, 任何软件要实现加密功能 链接调用这个库
libssl:   TLS/SSL 加密库  
openssl: 命令行工具   多功能多用途工具  实现私有证书颁发机构
子命令:
genrsa    [-out filename]    [-passout arg] [numbits]
generate an  RSA  private key  
生成一个 RSA 的私钥 (公钥是从私钥中提取的,有了私钥 就有公钥)
openssl  rsa -in ca.key -pubout  提取私钥
创建证书的基本流程是这样:
生成自己的服务端私钥     Server Key
输入基本信息并用私钥签名生成CSR    证书签名请求
提交CSR给证书机构CA(免费或商业证书)签名生成CRT,或自己做CA签名生成CRT(自签名证书)
生成RSA服务器私钥:
openssl genrsa -out server.key 4096 
输出的server.key文件就是服务器私钥,4096是密钥长度,要求不高的话用2048也可。
生成CSR:
执行命令  openssl req -new -nodes -sha256 -newkey rsa:2048 -keyout myprivate.key -out mydomain.csr生成 CSR 文件。
其中,
-new 指定生成一个新的CSR。
-nodes 指定私钥文件不被加密。
-sha256 指定摘要算法。
-keyout 生成私钥文件。
-newkey rsa:2048 指定私钥类型和长度。 
因为sha1已经不安全,所以这里用了sha256,可能太旧的客户端(比如win98?)会不支持。
yoursite.csr就是生成的CSR,yoursite建议用你的网站名标识会比较方便识别。
然后按提示输入:
国家
省
市
公司
部门
通用名(即网站域名,这个必须准确,有些商业证书支持在这里用带www的域名后签发出同时支持不带www的域名)
email
密码(可选,设置的话以后重启webserver都需要输入密码)

参考连接:

https://www.jianshu.com/p/44a3efae1d84

https://blog.csdn.net/yongf_xu/article/details/85006854

Maven和Findbugs集成

1.配置pom.xml

<build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>findbugs-maven-plugin</artifactId>
                <version>3.0.4</version>
                <configuration>
                    <!--配置文件-->
                    <configLocation>${basedir}/findbugs-config.xml</configLocation>
                    <!--排除的BUG规则-->
                    <excludeFilterFile>custom-findbugs-exclude.xml</excludeFilterFile>
                    <!--包含的BUG规则-->
                    <includeFilterFile>custom-findbugs-include.xml</includeFilterFile>
                    <!--检查等级-->
                    <threshold>High</threshold>
                    <effort>Default</effort>
                    <!-- 输出配置 -->
                    <findbugsXmlWithMessages>true</findbugsXmlWithMessages>
                    <findbugsXmlOutput>true</findbugsXmlOutput>
                    <xmlOutput>true</xmlOutput>
                    <findbugsXmlOutputDirectory>target/site</findbugsXmlOutputDirectory>
                </configuration>
            </plugin>
        </plugins>
    </build>

custom-findbugs-exclude.xml内容:

<FindBugsFilter>
    <Match>
        <Bug pattern="DLS_DEAD_LOCAL_STORE"/>
    </Match>

    <Match>
        <Bug pattern="DM_BOXED_PRIMITIVE_FOR_PARSING"/>
    </Match>

    <Match>
        <Bug pattern="EI_EXPOSE_REP"/>
    </Match>

    <Match>
        <Bug pattern="EI_EXPOSE_REP2"/>
    </Match>

    <Match>
        <Bug pattern="ME_ENUM_FIELD_SETTER"/>
    </Match>
</FindBugsFilter>

2. 运行findbugs任务(先运行“mvn package”编译工程 )

mvn findbugs:help      
查看findbugs插件的帮助  
mvn findbugs:check      
检查代码是否通过findbugs检查,如果没有通过检查,检查会失败,但检查不会生成结果报表  
mvn findbugs:findbugs   
检查代码是否通过findbugs检查,如果没有通过检查,检查不会失败,会生成结果报表保存在target/findbugsXml.xml文件中  
mvn findbugs:gui        
检查代码并启动gui界面来查看结果  

具体fndbugs插件的配置项可以参考http://mojo.codehaus.org/findbugs-maven-plugin/findbugs-mojo.html