1 Star 0 Fork 0

维之/xiweile.github.io

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
search.xml 48.22 KB
一键复制 编辑 原始数据 按行查看 历史
维之 提交于 2019-11-15 16:49 . Site updated: 2019-11-15 16:49:00
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title><![CDATA[mybatis-plus代码生成maven插件]]></title>
<url>%2F2019%2F11%2F15%2F2019-11-15-mybatis-plus%E4%BB%A3%E7%A0%81%E7%94%9F%E6%88%90maven%E6%8F%92%E4%BB%B6%2F</url>
<content type="text"><![CDATA[为了在项目中快捷方便的代码生成,将mybatis-plus-generator封装为了一个maven的插件mybatis-plus-generator-maven-plugin,在要使用的项目pom文件引入该插件,执行mvn命令,即可直接生成代码到项目中,生成基于mybatis-plus的mapper、service、controller三层结构,包括entity实体类和mapper.xml文件,生成后直接能够满足基本的条件查询和分页查询。下面介绍该插件的使用步骤: 一、下载插件方式1:CSDN中下载:jar包地址:mybatis-plus-generator-maven-plugin-1.0.0.jar,pom文件地址:pom.xml方式2:将源代码导入项目工程中,执行mvn intall,源码地址:https://github.com/xiweile/mybatis-plus-generator-maven-plugin 二、插件上传至本地仓库在下载好的mybatis-plus-generator-maven-plugin-1.0.0.jar和pom.xml文件目录下打开命令行工具,执行下面命令-DpomFile为pom.xml所在目录,-Dfile是jar所在位置,-Dpackaging固定为 maven-plugin,其他参数此处不介绍。1mvn install:install-file -DpomFile=pom.xml -Dfile=mybatis-plus-generator-maven-plugin-1.0.0.jar -DgroupId=com.weiller -DartifactId=mybatis-plus-generator-maven-plugin -Dversion=1.0.0 -Dpackaging=maven-plugin 三、在pom中引入插件在要使用插件的工程pom文件中引入该插件,如下案例123456789101112131415161718&lt;build&gt; &lt;plugins&gt; &lt;plugin&gt; &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt; &lt;artifactId&gt;spring-boot-maven-plugin&lt;/artifactId&gt; &lt;/plugin&gt; &lt;!-- mybatis-plus generator 自动生成代码插件 --&gt; &lt;plugin&gt; &lt;groupId&gt;com.weiller&lt;/groupId&gt; &lt;artifactId&gt;mybatis-plus-generator-maven-plugin&lt;/artifactId&gt; &lt;version&gt;1.0.0&lt;/version&gt; &lt;configuration&gt; &lt;configurationFile&gt;$&#123;basedir&#125;/src/main/resources/generator/mp-code-generator-config.yaml&lt;/configurationFile&gt; &lt;/configuration&gt; &lt;/plugin&gt; &lt;!-- mybatis-plus generator 自动生成代码插件 --&gt; &lt;/plugins&gt; &lt;/build&gt; 注意configurationFile参数为 下一步中配置文件generator-config的位置,该文件类型为yaml。 四、填写配置文件配置完整案例1234567891011121314151617181920212223242526272829303132333435363738394041424344globalConfig: author: weiller open: false idType: INPUT dateType: ONLY_DATE enableCache: false activeRecord: false baseResultMap: true baseColumnList: true swagger2: false fileOverride: truedataSourceConfig: url: jdbc:mysql://localhost:3306/demo?useUnicode=true&amp;useSSL=false&amp;characterEncoding=utf8 driverName: com.mysql.jdbc.Driver username: root password: xiweilepackageConfig: parent: com.weiller moduleName: rest entity: model service: service serviceImpl: service.impl mapper: dao xml: mapper controller: controller pathInfo: entity_path: src\main\java\com\weiller\rest\model service_path: src\main\java\com\weiller\rest\service service_impl_path: src\main\java\com\weiller\rest\service\impl mapper_path: src\main\java\com\weiller\rest\dao xml_path: src\main\resources\com\weiller\rest\mapper controller_path: src\main\java\com\weiller\rest\controllerstrategyConfig: naming: underline_to_camel columnNaming: underline_to_camel entityLombokModel: true superMapperClass: com.baomidou.mybatisplus.core.mapper.BaseMapper superServiceClass: com.baomidou.mybatisplus.extension.service.IService superServiceImplClass: com.baomidou.mybatisplus.extension.service.impl.ServiceImpl controllerMappingHyphenStyle: true restControllerStyle: true tablePrefix: include: - t_user 配置项参数解释:https://mp.baomidou.com/config/generator-config.html#基本配置 五、运行maven命令在命令工具中,进入到要生成项目的根目录(即pom.xml目录),执行以下命令1mvn mybatis-plus-generator:generator 如果是使用InterlliJ IDEA工具,使用更加方便,步骤如下图: 生成结果如下:]]></content>
<categories>
<category>java</category>
</categories>
</entry>
<entry>
<title><![CDATA[Java设计模式:静态代理、JDK动态代理和cglib动态代理]]></title>
<url>%2F2019%2F09%2F15%2F2019-09-15-Java%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%EF%BC%9A%E9%9D%99%E6%80%81%E4%BB%A3%E7%90%86%E3%80%81JDK%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86%E5%92%8Ccglib%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86%2F</url>
<content type="text"><![CDATA[123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121静态代理、JDK动态代理和cglib动态代理/** * 静态代理案例:增强猫(Cat的代理类) * 利用装饰者模式 * 要求:1.委托类、代理类必须实现共同的接口 2.代理类需要获得委托类的对象的引用 * * @author weiller * @version 1.0,2016-11-27 14:11:36 */public class StaticProxyDemo implements Jump&#123; /** 委托类对象 */ private Cat cat; /** 委托类对象作为参数传入构造方法 */ public StaticProxyDemo(Cat cat)&#123; this.cat = cat; &#125; @Override public void jump() &#123; // 增强功能 System.out.println("准备起跳..."); // 原始方法调用 cat.jump(); // 增强功能 System.out.println("回落..."); &#125;&#125;/** * JDC动态代理案例 * 要求:目标类必须实现一个接口 * * @author weiller * @version 1.0,2016-11-27 08:34:05 */public class JDKProxyDemo &#123; /** * 获得代理对象 * * @param t 目标类对象实现的接口 * @return 代理对象 */ public static&lt;T&gt; T createProxyObject(T t)&#123; // 5.将被代理的对象修饰为final,便于匿名内部类使用 final Object obj = t; // 2.获取被代理对象的类加载器 ClassLoader loader = t.getClass().getClassLoader(); // 3.获取被代理对象的所有实现接口 Class&lt;?&gt;[] interfaces = t.getClass().getInterfaces(); // 4.创建调用处理对象 InvocationHandler h = new InvocationHandler() &#123; @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable &#123; // 7.增强功能 System.out.println("开启事务..."); // 6.原始方法调用 Object ret = method.invoke(obj, args); // 增强功能 System.out.println("关闭事务..."); return ret; &#125; &#125;; // 1.创建代理对象 T proxyObject = (T) Proxy.newProxyInstance(loader, interfaces, h); return proxyObject; &#125;&#125;/** * cglib动态代理案例 * cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强。 * 要求:CGLib由于是采用动态创建子类的方法,对于final方法,无法进行代理。 * * @author weiller * @version 1.0,2016-11-27 09:28:11 */public class CglibPorxyDemo&lt;T&gt; &#123; public T createProxyObject(Class clazz)&#123; // 1.在内存中创建动态的增强类 Enhancer enhancer = new Enhancer(); // 2.为该增强类设置父类,与目标类形成继承关系。此类最终完成对原始方法的功能,同时对其功能进行加强。 enhancer.setSuperclass(clazz); // 3.创建回调处理对象,对目标类的方法进行拦截 Callback callback = new MethodInterceptor() &#123; @Override /** * obj:目标类的实例 * method:被拦截的方法对象 * args:调用参数 * methodProxy:代理方法的对象(通过JDK代理得到) */ public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable &#123; // 增强功能 System.out.println("说相声..."); //System.out.println(obj); // 调用obj产生死循环,溢栈。 // 利用代理方法对象调用原始方法。注意:不能调用invoke()方法,否则产生死循环,溢栈。无法进行代理 // 代理是通过多态的形式进行的。 Object ret = methodProxy.invokeSuper(obj, args); return ret; &#125; &#125;; // 4.设置回调 enhancer.setCallback(callback); // 5.通过字节码技术动态创建该增强类的实例 T newObject = (T) enhancer.create(); return newObject; &#125;&#125;]]></content>
<categories>
<category>java</category>
</categories>
</entry>
<entry>
<title><![CDATA[springcloud zuul实践:自定义异常过滤器,统一异常响应格式]]></title>
<url>%2F2019%2F09%2F13%2F2019-09-13-springcloud-zuul%E5%AE%9E%E8%B7%B5%EF%BC%9A%E8%87%AA%E5%AE%9A%E4%B9%89%E5%BC%82%E5%B8%B8%E8%BF%87%E6%BB%A4%E5%99%A8%EF%BC%8C%E7%BB%9F%E4%B8%80%E5%BC%82%E5%B8%B8%E5%93%8D%E5%BA%94%E6%A0%BC%E5%BC%8F%2F</url>
<content type="text"><![CDATA[在springcloud项目中,网关发生异常时,响应内容并不是我们想要的格式,内容如下:1234567&#123; "timestamp": 1481674980376, "status": 500, "error": "Internal Server Error", "exception": "java.lang.RuntimeException", "message": "Exist some errors..."&#125; 上面的json则是内置异常过滤器封装的一种格式。我们现在想要修改她,就需要自定义异常过滤器。 首先继承抽象类ZuulFilter,实现filterType(),filterOrder(),shouldFilter(), run()四个抽象方法。前三个方法均使用父方法逻辑。仅修改run()中部分内容,主体逻辑步骤依然参考SendErrorFilter。 方法run()中重新定义异常响应格式,将自定义的响应体,设置到原有的响应中。 停用内置的默认异常处理器SendErrorFilter,在application.yml中设置zuul.SendErrorFilter.error.disable: true。 CustomSendErrorFilter在内置的默认异常处理器失效时生效。设置注解ConditionalOnProperty属性name=&quot;zuul.SendErrorFilter.error.disable&quot;。 过滤器详细介绍可参考往期文章:springcloud zuul源码分析:内置过滤器 下面是案例代码: 123456789101112131415161718192021222324252627282930313233343536373839@Component@ConditionalOnProperty(name="zuul.SendErrorFilter.error.disable")public class CustomSendErrorFilter extends SendErrorFilter &#123; @Override public String filterType() &#123; return super.filterType(); &#125; @Override public int filterOrder() &#123; return super.filterOrder(); &#125; @Override public boolean shouldFilter() &#123; return super.shouldFilter(); &#125; @Override public Object run() &#123; RequestContext ctx = RequestContext.getCurrentContext(); HttpServletResponse response = ctx.getResponse(); try &#123; int responseStatusCode = ctx.getResponseStatusCode(); // 此处自定义响应体start String cumstomBody = "&#123;&#125;";//内容省略... // 此处自定义响应体end response.setStatus(ctx.getResponseStatusCode()); response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE); response.getOutputStream().write(cumstomBody.getBytes()); &#125; catch (IOException e) &#123; ReflectionUtils.rethrowRuntimeException(e); &#125; finally &#123; ThreadLocalUtil.remove(); &#125; return null; &#125;&#125;]]></content>
<categories>
<category>springcloud</category>
</categories>
</entry>
<entry>
<title><![CDATA[springcloud zuul实践:自定义前置过滤器进行权限认证]]></title>
<url>%2F2019%2F09%2F11%2F2019-09-17-springcloud-zuul%E5%AE%9E%E8%B7%B5%EF%BC%9A%E8%87%AA%E5%AE%9A%E4%B9%89%E5%89%8D%E7%BD%AE%E8%BF%87%E6%BB%A4%E5%99%A8%E8%BF%9B%E8%A1%8C%E6%9D%83%E9%99%90%E8%AE%A4%E8%AF%81%2F</url>
<content type="text"><![CDATA[在springcloud项目中,权限认证逻辑一般放在请求路由之前,如果认证通过,则会执行route类型的过滤器,访问微服务获取数据。如果认证未通过,则要设置不进行路由,从而直接响应给客户端。 过滤器详细介绍可参考往期文章:springcloud zuul源码分析:内置过滤器 因此,我们通过自定义一个前置过滤器,来实现权限认证的逻辑。 首先继承抽象类ZuulFilter,实现filterType(),filterOrder(),shouldFilter(), run()四个抽象方法。 filterType() 返回 &quot;pre&quot;定义过滤器为前置过滤器。 方法run()中是执行逻辑,RequestContext.getCurrentContext()获取当前上下文实例ctx,如果认证不通过,可设置ctx.setSendZuulResponse(false)阻止请求进行路由。 前置过滤器内也可以对请求参数进行修改或添加。 下面是案例代码: 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667@Componentpublic class AccessFilter extends ZuulFilter &#123; /** * 返回一个字符串代表过滤器的类型,在zuul中定义了四种不同生命周期的过滤器类型: * pre:可以在请求被路由之前调用 * route:在路由请求时候被调用 * post:在route和error过滤器之后被调用 * error:处理请求时发生错误时被调用 * * @return */ @Override public String filterType() &#123; return "pre"; // 前置过滤器 &#125; @Override public int filterOrder() &#123; return 2; // 过滤器的执行顺序,数字越大优先级越低 &#125; @Override public boolean shouldFilter() &#123; return true;// 是否执行该过滤器,此处为true,说明需要过滤 &#125; @Override public Object run() &#123; RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest request = ctx.getRequest(); HttpServletResponse response = ctx.getResponse(); // 访问权限认证逻辑 boolean result = this.exeAuth(request); if(result ==true)&#123;//通过校验 // 添加额外的请求参数为可选逻辑... // 添加额外的请求参数start Map&lt;String, List&lt;String&gt;&gt; requestQueryParams = ctx.getRequestQueryParams(); List&lt;String&gt; params = new ArrayList&lt;String&gt;(); params.add("otherParamValue"); requestQueryParams.put("otherParamKey",params); ctx.setRequestQueryParams(requestQueryParams); // 添加额外的请求参数end &#125;else&#123;//校验未通过 ctx.setSendZuulResponse(false); // 过滤该请求,不进行路由 ctx.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value()); ctx.setResponseBody(JSON.toJSONString(res)); // 返回前端内容 response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE); &#125; return null; &#125; /** * 认证逻辑 * * @return * @throws ZuulException */ public boolean exeAuth(HttpServletRequest request)&#123; // 详细认证逻辑... // 省略... return false; &#125;&#125;]]></content>
<categories>
<category>springcloud</category>
</categories>
</entry>
<entry>
<title><![CDATA[springcloud zuul实践:自定义前置过滤器进行权限认证]]></title>
<url>%2F2019%2F09%2F11%2F2019-09-11-springcloud-zuul%E5%AE%9E%E8%B7%B5%EF%BC%9A%E8%87%AA%E5%AE%9A%E4%B9%89%E5%89%8D%E7%BD%AE%E8%BF%87%E6%BB%A4%E5%99%A8%E8%BF%9B%E8%A1%8C%E6%9D%83%E9%99%90%E8%AE%A4%E8%AF%81%2F</url>
<content type="text"><![CDATA[在springcloud项目中,权限认证逻辑一般放在请求路由之前,如果认证通过,则会执行route类型的过滤器,访问微服务获取数据。如果认证未通过,则要设置不进行路由,从而直接响应给客户端。 过滤器详细介绍可参考往期文章:springcloud zuul源码分析:内置过滤器 因此,我们通过自定义一个前置过滤器,来实现权限认证的逻辑。 首先继承抽象类ZuulFilter,实现filterType(),filterOrder(),shouldFilter(), run()四个抽象方法。 filterType() 返回 &quot;pre&quot;定义过滤器为前置过滤器。 方法run()中是执行逻辑,RequestContext.getCurrentContext()获取当前上下文实例ctx,如果认证不通过,可设置ctx.setSendZuulResponse(false)阻止请求进行路由。 前置过滤器内也可以对请求参数进行修改或添加。 下面是案例代码: 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667@Componentpublic class AccessFilter extends ZuulFilter &#123; /** * 返回一个字符串代表过滤器的类型,在zuul中定义了四种不同生命周期的过滤器类型: * pre:可以在请求被路由之前调用 * route:在路由请求时候被调用 * post:在route和error过滤器之后被调用 * error:处理请求时发生错误时被调用 * * @return */ @Override public String filterType() &#123; return "pre"; // 前置过滤器 &#125; @Override public int filterOrder() &#123; return 2; // 过滤器的执行顺序,数字越大优先级越低 &#125; @Override public boolean shouldFilter() &#123; return true;// 是否执行该过滤器,此处为true,说明需要过滤 &#125; @Override public Object run() &#123; RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest request = ctx.getRequest(); HttpServletResponse response = ctx.getResponse(); // 访问权限认证逻辑 boolean result = this.exeAuth(request); if(result ==true)&#123;//通过校验 // 添加额外的请求参数为可选逻辑... // 添加额外的请求参数start Map&lt;String, List&lt;String&gt;&gt; requestQueryParams = ctx.getRequestQueryParams(); List&lt;String&gt; params = new ArrayList&lt;String&gt;(); params.add("otherParamValue"); requestQueryParams.put("otherParamKey",params); ctx.setRequestQueryParams(requestQueryParams); // 添加额外的请求参数end &#125;else&#123;//校验未通过 ctx.setSendZuulResponse(false); // 过滤该请求,不进行路由 ctx.setResponseStatusCode(HttpStatus.UNAUTHORIZED.value()); ctx.setResponseBody(JSON.toJSONString(res)); // 返回前端内容 response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE); &#125; return null; &#125; /** * 认证逻辑 * * @return * @throws ZuulException */ public boolean exeAuth(HttpServletRequest request)&#123; // 详细认证逻辑... // 省略... return false; &#125;&#125;]]></content>
<categories>
<category>springcloud</category>
</categories>
</entry>
<entry>
<title><![CDATA[springcloud zuul源码分析:内置过滤器]]></title>
<url>%2F2019%2F09%2F10%2F2019-09-10-springcloud%20zuul%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90%EF%BC%9A%E5%86%85%E7%BD%AE%E8%BF%87%E6%BB%A4%E5%99%A8%2F</url>
<content type="text"><![CDATA[springcloud项目中我们常常使用zuul作为网关,用它做一些路由分发、权限校验、统一异常处理、日志收集等等工作。而实现这些功能的重要组件就是它的过滤器ZuulFilter,本篇文章介绍zuul中内置过滤器。 1.ZuulFilter介绍我们自定义的每个过滤器都需要继承ZuulFilter才能被加载生效。它有四个重要的抽象方法需要重写。1234567public abstract String filterType();public abstract int filterOrder();boolean shouldFilter();Object run(); 四个方法介绍: filterType 返回值标识过滤器类型。 pre 在请求被路由之前调用。 routing 路由时被调用。 post 在routing或error过滤器之后被调用。 error 在请求发送异常时被调用。 filterOrder 返回int值标识过滤器执行顺序,数值越小优先级越高。 shouldFilter 返回boolean类型确定是否执行该过滤器。 2.内置过滤器介绍1)按filterType分类图示 2)各过滤器功能详细介绍pre过滤器 ServletDetectionFilter:它的执行顺序为-3,是最先被执行的过滤器。该过滤器总是会被执行,主要用来检测当前请求是通过Spring的DispatcherServlet处理运行,还是通过ZuulServlet来处理运行的。它的检测结果会以布尔类型保存在当前请求上下文的isDispatcherServletRequest参数中,这样在后续的过滤器中,我们就可以通过RequestUtils.isDispatcherServletRequest()和RequestUtils.isZuulServletRequest()方法判断它以实现做不同的处理。一般情况下,发送到API网关的外部请求都会被Spring的DispatcherServlet处理,除了通过/zuul/路径访问的请求会绕过DispatcherServlet,被ZuulServlet处理,主要用来应对处理大文件上传的情况。另外,对于ZuulServlet的访问路径/zuul/,我们可以通过zuul.servletPath参数来进行修改。 Servlet30WrapperFilter:它的执行顺序为-2,是第二个执行的过滤器。目前的实现会对所有请求生效,主要为了将原始的HttpServletRequest包装成Servlet30RequestWrapper对象。 FormBodyWrapperFilter:它的执行顺序为-1,是第三个执行的过滤器。该过滤器仅对两种类请求生效,第一类是Content-Type为application/x-www-form-urlencoded的请求,第二类是Content-Type为multipart/form-data并且是由Spring的DispatcherServlet处理的请求(用到了ServletDetectionFilter的处理结果)。而该过滤器的主要目的是将符合要求的请求体包装成FormBodyRequestWrapper对象。 DebugFilter:它的执行顺序为1,是第四个执行的过滤器。该过滤器会根据配置参数zuul.debug.request和请求中的debug参数来决定是否执行过滤器中的操作。而它的具体操作内容则是将当前的请求上下文中的debugRouting和debugRequest参数设置为true。由于在同一个请求的不同生命周期中,都可以访问到这两个值,所以我们在后续的各个过滤器中可以利用这两值来定义一些debug信息,这样当线上环境出现问题的时候,可以通过请求参数的方式来激活这些debug信息以帮助分析问题。另外,对于请求参数中的debug参数,我们也可以通过zuul.debug.parameter来进行自定义。 PreDecorationFilter:它的执行顺序为5,是pre阶段最后被执行的过滤器。该过滤器会判断当前请求上下文中是否存在forward.to和serviceId参数,如果都不存在,那么它就会执行具体过滤器的操作(如果有一个存在的话,说明当前请求已经被处理过了,因为这两个信息就是根据当前请求的路由信息加载进来的)。而它的具体操作内容就是为当前请求做一些预处理,比如:进行路由规则的匹配、在请求上下文中设置该请求的基本信息以及将路由匹配结果等一些设置信息等,这些信息将是后续过滤器进行处理的重要依据,我们可以通过RequestContext.getCurrentContext()来访问这些信息。另外,我们还可以在该实现中找到一些对HTTP头请求进行处理的逻辑,其中包含了一些耳熟能详的头域,比如:X-Forwarded-Host、X-Forwarded-Port。另外,对于这些头域的记录是通过zuul.addProxyHeaders参数进行控制的,而这个参数默认值为true,所以Zuul在请求跳转时默认地会为请求增加X-Forwarded-*头域,包括:X-Forwarded-Host、X-Forwarded-Port、X-Forwarded-For、X-Forwarded-Prefix、X-Forwarded-Proto。我们也可以通过设置zuul.addProxyHeaders=false关闭对这些头域的添加动作。 route过滤器 RibbonRoutingFilter:它的执行顺序为10,是route阶段第一个执行的过滤器。该过滤器只对请求上下文中存在serviceId参数的请求进行处理,即只对通过serviceId配置路由规则的请求生效。而该过滤器的执行逻辑就是面向服务路由的核心,它通过使用Ribbon和Hystrix来向服务实例发起请求,并将服务实例的请求结果返回。 SimpleHostRoutingFilter:它的执行顺序为100,是route阶段第二个执行的过滤器。该过滤器只对请求上下文中存在routeHost参数的请求进行处理,即只对通过url配置路由规则的请求生效。而该过滤器的执行逻辑就是直接向routeHost参数的物理地址发起请求,从源码中我们可以知道该请求是直接通过httpclient包实现的,而没有使用Hystrix命令进行包装,所以这类请求并没有线程隔离和断路器的保护。 SendForwardFilter:它的执行顺序为500,是route阶段第三个执行的过滤器。该过滤器只对请求上下文中存在forward.to参数的请求进行处理,即用来处理路由规则中的forward本地跳转配置。post过滤器 SendErrorFilter:它的执行顺序为0,是post阶段第一个执行的过滤器。该过滤器仅在请求上下文中包含error.status_code参数(由之前执行的过滤器设置的错误编码)并且还没有被该过滤器处理过的时候执行。而该过滤器的具体逻辑就是利用请求上下文中的错误信息来组织成一个forward到API网关/error错误端点的请求来产生错误响应。 SendResponseFilter:它的执行顺序为1000,是post阶段最后执行的过滤器。该过滤器会检查请求上下文中是否包含请求响应相关的头信息、响应数据流或是响应体,只有在包含它们其中一个的时候就会执行处理逻辑。而该过滤器的处理逻辑就是利用请求上下文的响应信息来组织需要发送回客户端的响应内容。 网关过滤器执行逻辑源码逻辑123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657public class ZuulServlet extends HttpServlet &#123; private ZuulRunner zuulRunner; public ZuulServlet() &#123; &#125; public void init(ServletConfig config) throws ServletException &#123; super.init(config); String bufferReqsStr = config.getInitParameter("buffer-requests"); boolean bufferReqs = bufferReqsStr != null &amp;&amp; bufferReqsStr.equals("true"); this.zuulRunner = new ZuulRunner(bufferReqs); &#125; public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException &#123; // 省略.... try &#123; this.preRoute(); &#125; catch (ZuulException var13) &#123; this.error(var13); this.postRoute(); return; &#125; try &#123; this.route(); &#125; catch (ZuulException var12) &#123; this.error(var12); this.postRoute(); return; &#125; try &#123; this.postRoute(); &#125; catch (ZuulException var11) &#123; this.error(var11); &#125; // 省略.... &#125; void postRoute() throws ZuulException &#123; this.zuulRunner.postRoute(); &#125; void route() throws ZuulException &#123; this.zuulRunner.route(); &#125; void preRoute() throws ZuulException &#123; this.zuulRunner.preRoute(); &#125; void init(HttpServletRequest servletRequest, HttpServletResponse servletResponse) &#123; this.zuulRunner.init(servletRequest, servletResponse); &#125; void error(ZuulException e) &#123; RequestContext.getCurrentContext().setThrowable(e); this.zuulRunner.error(); &#125;&#125; 流程分析:请求经ZuulServlet 进入内部service处理方法,依次经过preRoute,route,postRoute三组过滤器,如果任何一个过滤器中发生异常都会转发到error过滤器,而error最终也会再次将结果转发postRoute,有postRoute负责将结果给返回客户端。 执行流程图如下:]]></content>
<categories>
<category>springcloud</category>
</categories>
</entry>
<entry>
<title><![CDATA[在线HTML页面导出pdf或图片,支持模拟跳过登陆验证]]></title>
<url>%2F2019%2F09%2F10%2F2019-09-10-%E5%9C%A8%E7%BA%BFHTML%E9%A1%B5%E9%9D%A2%E5%AF%BC%E5%87%BApdf%E6%88%96%E5%9B%BE%E7%89%87%2C%E6%94%AF%E6%8C%81%E6%A8%A1%E6%8B%9F%E8%B7%B3%E8%BF%87%E7%99%BB%E9%99%86%E9%AA%8C%E8%AF%81%2F</url>
<content type="text"><![CDATA[项目经常用到把当前页面导出为pdf,或者生成某个页面或部分的快照。如果是简单的页面快照,不没有太多的渲染,可以用html2canvas.min.js导出图片或者html2pdf.bundle.min.js导出为pdf,仅仅在前端就可以完成。但是复杂的页面,以上的导出效果不好,质量也不高。因此我采用PhantomJS,用后台生成pdf、png等进行导出。 1.PhantomJS简介 PhantomJS是一个基于webkit的JavaScript API。它使用QtWebKit作为它核心浏览器的功能,使用webkit来编译解释执行JavaScript代码。任何你可以在基于webkit浏览器做的事情,它都能做到。它不仅是个隐形的浏览器,提供了诸如CSS选择器、支持Web标准、DOM操作、JSON、HTML5、Canvas、SVG等,同时也提供了处理文件I/O的操作,从而使你可以向操作系统读写文件等。PhantomJS的用处可谓非常广泛,诸如网络监测、网页截屏、无需浏览器的 Web 测试、页面访问自动化等。 2.下载及安装官方下载地址:http://phantomjs.org/download.html。目前官方支持三种操作系统,包括windows\Mac OS\Linux这三大主流的环境。根据运行环境选择要下载的包,下面以Windows7为例,我将phantomjs文件放置到D盘根目录下,里面的内容如下图。 3.如何使用1) 准备一个配置的js文件在D盘phantomjs目录下新建一个html2pdf.js 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778/** * phantomJs 脚本 html转图片 */var page = require('webpage').create(), system = require('system'), address, output, size,JSESSIONID,domain;if (system.args.length &lt; 3 || system.args.length &gt; 5) &#123; phantom.exit(1);&#125; else &#123; address = system.args[1]; output = system.args[2]; if(system.args.length&gt;3)&#123; JSESSIONID = system.args[3]; domain = system.args[4]; // 添加cookie phantom.addCookie(&#123; "name":"JSESSIONID", "value":JSESSIONID, "domain":domain, "path":"/", "httponly":false, "secure":false, "expires": "Fri, 01 Jan 2038 00:00:00 GMT" &#125;); &#125; //定义浏览器宽高 page.viewportSize = &#123; width : 2048, height : 768 &#125;; // 纸张尺寸 page.paperSize = &#123; format: 'A3', orientation: 'landscape',//横向 margin: &#123; left:"2cm", right:"2cm", top:"1cm", bottom:"1cm" &#125; &#125;; page.open(address, function(status) &#123; console.log('Status: ' + status); if (status !== 'success') &#123; console.log('Unable to load the address!'); phantom.exit(1); &#125; else &#123; var bb = page.evaluate(function() &#123; //var child=document.getElementById("printBt"); //child.parentNode.removeChild(child);//此外可以写一些对页面操作的代码,比如词句代码是删除页面上的打印按钮 return document.getElementsByTagName('html')[0].getBoundingClientRect();//返回内容就是要导出的范围(此处是HTML整页) &#125;); // 截取范围 page.clipRect = &#123; top : bb.top, left : bb.left, width : bb.width, height : bb.height &#125;; // 等待页面渲染 window.setTimeout(function() &#123; page.render(output, &#123;format: 'pdf', quality: '100'&#125;);//这里配置导出的格式pdf,png等,quality为导出质量 page.close(); console.log('渲染成功...'); phantom.exit(); &#125;, 2000); &#125; &#125;);&#125;address = system.args[1];//传入的URL地址output = system.args[2];//保存的图片路径 2)测试命令 例如:phantomjs文件夹放于D盘根目录,在phantomjs/bin 目录下(也可以配置环境变量)打开控制台。以我的CSDN博客页面https://blog.csdn.net/xiweiller导出为PDF为例。输入如下命令: 1phantomjs.exe D:/phantomjs/html2pdf.js https://blog.csdn.net/xiweiller D:/phantomjs/blog.pdf 其中 phantomjs.exe是执行命令文件, D:/phantomjs/screenshot.js是执行的配置js文件,https://blog.csdn.net/xiweiller是目标HTML,D:/phantomjs/blog.pdf是导出PDF的路径。 3) java中的使用,springboot项目为例 application.yml中添加配置,配置项为phantomjs执行文件路径 12345htmltopdf: path: windows: D:/phantomjs/bin/phantomjs.exe linux: /root/soft/phantomjs-2.1.1-linux-x86_64/bin/phantomjs mac: Html转Pdf工具类 参数: - htmlPath 目标网页访问地址 - pdfPath 导出pdf存放目录 - JSESSIONID 登陆认证时需要传递,用于cookie携带 - domain cookie中获取 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283/** * Html转Pdf工具类 * */@Componentpublic class HtmlToPdf &#123; @Value("$&#123;htmltopdf.path.windows&#125;") private String WINDOWS_WKHTMLTOPDF_PATH; @Value("$&#123;htmltopdf.path.mac&#125;") private String MAC_WKHTMLTOPDF_PATH; @Value("$&#123;htmltopdf.path.linux&#125;") private String LINUX_WKHTMLTOPDF_PATH; private static Logger log = LoggerFactory.getLogger(HtmlToPdf.class); /** * html转pdf核心方法 * @param htmlPath html路径(硬盘和网盘路径都支持) * @param pdfPath pdf存储路径 * @return 转换成功:true,失败:false */ public boolean convert(String htmlPath, String pdfPath,String JSESSIONID,String domain) &#123; File file = new File(pdfPath); File parent = file.getParentFile(); if (!parent.exists()) &#123;// 如果pdf保存路径不存在,则创建路径 parent.mkdirs(); &#125; String toolDir = null;// 转换工具在系统中的路径 if (System.getProperty("os.name").indexOf("Windows") != -1) &#123;//windows 系统 toolDir = WINDOWS_WKHTMLTOPDF_PATH; &#125;else if(System.getProperty("os.name").indexOf("Mac OS X") != -1)&#123;//mac 系统 toolDir = MAC_WKHTMLTOPDF_PATH; &#125;else if(System.getProperty("os.name").indexOf("Linux") != -1)&#123;//linux 系统 toolDir = LINUX_WKHTMLTOPDF_PATH; &#125; boolean result = true;//pdf创建成功标识,默认成功 String BLANK = " ";//空格 Process process = null;//图片输出路径 StringBuffer sbf = null; InputStream is = null; StringBuilder cmd = new StringBuilder(); cmd.append(toolDir) .append(BLANK) .append(new File(toolDir).getParent()).append(File.separator).append("html2pdf.js") .append(BLANK) .append(htmlPath) .append(BLANK) .append(pdfPath); if(JSESSIONID!=null)&#123;//添加cookie值 cmd.append(BLANK).append(JSESSIONID); cmd.append(BLANK).append(domain); &#125; try &#123; log.info("报表准备导出 " ); log.info("导出命令:&#123;&#125;",cmd.toString() ); process = Runtime.getRuntime().exec(cmd.toString());// 输出路径 is = process.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(is)); sbf = new StringBuffer(); String tmp = ""; while ((tmp = br.readLine()) != null) &#123; sbf.append(tmp); &#125; log.info("报表导出完成 " ); &#125; catch (IOException e) &#123; result = false; log.error("报表导出失败!", e); &#125; finally &#123; if (is != null) &#123; try &#123; is.close(); &#125; catch (IOException e) &#123; e.printStackTrace(); &#125; is = null; &#125; &#125; return result; &#125;&#125; 转换工具的调用 123456789101112131415public void test( HttpServletRequest request )&#123; // 注意不同部署环境,路径不同。 String htmlUrl = "D:/phantomjs/html2pdf.js"; String pdfPath = "D:/phantomjs/export.pdf" String JSESSIONID = ""; String domain = request.getServerName(); Cookie[] cookies = request.getCookies(); for(Cookie cookie :cookies)&#123; if("JSESSIONID".equals(cookie.getName()))&#123; JSESSIONID = cookie.getValue(); &#125; &#125; //将html转换为pdf文件 boolean isno =htmlToPdf.convert(htmlUrl,pdfPath,JSESSIONID,domain); &#125; 4) 导出效果图 4. 注意事项​ 在实际项目使用时,我们必须保证页面异步加载的速度比较快,否则会出现页面渲染不完整的问题。因此在项目中,被导出的目标页面最好能做到快速响应,尽量减少异步请求加载,或者保证异步接口快速响应。]]></content>
<categories>
<category>java</category>
</categories>
</entry>
<entry>
<title><![CDATA[简单的HTML快照生成,导出pdf或图片]]></title>
<url>%2F2019%2F09%2F01%2F2019-09-01-%E7%AE%80%E5%8D%95%E7%9A%84HTML%E5%BF%AB%E7%85%A7%E7%94%9F%E6%88%90%2C%E5%AF%BC%E5%87%BApdf%E6%88%96%E5%9B%BE%E7%89%87%2F</url>
<content type="text"><![CDATA[一、利用 html2canvas实现HTML页面截图 官方网址: https://html2canvas.hertzen.com/ GitHub:https://github.com/niklasvh/html2canvas 1.HTML页面引入 html2canvas.min.js2.定义一个截图的触发按钮1234&lt;button onclick="exprotImg();"&gt;导出图片&lt;/button&gt; &lt;a href="" download="canvas.png" id="save_href" style="display:none"&gt; &lt;img src="" id="save_img"/&gt; &lt;/a&gt; 3.js代码12345678910111213141516171819202122232425function exprotImg() &#123; // 要截图的元素 var element = document.getElementsByTagName('html')[0]; // 获取元素的大小及其相对于视口的位置等参数 var dd= element.getBoundingClientRect(); var opts = &#123; scale: 2, // 添加的scale 参数 // logging: true, //日志开关,便于查看html2canvas的内部执行流程 width: dd.width, //dom 原始宽度 height: dd.height, useCORS: true, // 【重要】开启跨域配置 allowTaint:true &#125;; html2canvas(element,opts).then(function(canvas) &#123; // 模拟的下载按钮 var svaeHref = document.getElementById("save_href"); var img = document.getElementById("save_img"); var tempSrc = canvas.toDataURL("image/png"); svaeHref.href=tempSrc; img.src=tempSrc; $(img).click(); &#125;);&#125; 二、利用 html2pdf 实现页面导出pdf GitHub :https://github.com/eKoopmans/html2pdf 1.页面导入 html2pdf.bundle.min.js2.导出按钮1&lt;button onclick="exprotPdf();"&gt;导出pdf&lt;/button&gt; 3.js代码123456789101112131415161718192021function exprotPdf() &#123; // 要截图的元素 var element = document.getElementsByTagName('html')[0]; // 获取元素的大小及其相对于视口的位置等参数 var opt = &#123; margin: 1, filename: 'myfile.pdf', image: &#123; type: 'png', quality: 0.98 &#125;, html2canvas: &#123; scale: 2, useCORS:true, // 【重要】开启跨域配置 allowTaint:true, width:dd.width, height:dd.height &#125;, jsPDF: &#123; unit: 'in', format: 'letter', orientation: 'portrait' &#125; &#125;; html2pdf().from(element).set(opt).save();&#125;]]></content>
<categories>
<category>javascript</category>
</categories>
</entry>
<entry>
<title><![CDATA[利用自定义注解,统计方法执行时间]]></title>
<url>%2F2019%2F08%2F15%2F2019-08-15-%E5%88%A9%E7%94%A8%E8%87%AA%E5%AE%9A%E4%B9%89%E6%B3%A8%E8%A7%A3%EF%BC%8C%E7%BB%9F%E8%AE%A1%E6%96%B9%E6%B3%95%E6%89%A7%E8%A1%8C%E6%97%B6%E9%97%B4%2F</url>
<content type="text"><![CDATA[项目中需要统计某些方法的执行时间,最简易的方式是方法执行前记录时间戳startTime,在方法结束前,用时间戳endTime-startTime得出该方法耗时。但是为了避免无代码侵入并实现通用,于是定义一个注解,如果要统计哪个方法,只需在方法上写上注解即可,通过注解可以获取到方法的参数、方法名、返回值等等信息。 下面是一个简单的时间统计实现: 1.定义一个注解TimeConsume该注解有一个默认的value属性,value值为方法名或自定义的描述1234567@Target(&#123;ElementType.METHOD&#125;)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface TimeConsume &#123; String value() default "方法";&#125; 2.使用Aspect定义该注解的切面TimeConsumeAspect定义好注解后,需要对该注解使用的类进行监听,利用Spring框架Aspect实现切面,定义环绕通知,获取到方法的参数、方法名等信息,便于统计所需。对执行方法进行异常捕获,在finally代码块中实现时间统计逻辑,避免方法异常无法统计。代码如下:1234567891011121314151617181920212223242526272829303132333435363738394041424344454647@Slf4j@Component@Aspectpublic class TimeConsumeAspect &#123; /** * 切点定义为注解@annotation(注解类路径) */ @Pointcut("@annotation(com.weiller.demo.common.annotation.TimeConsume)") public void consume()&#123; &#125; @Around("consume()") public &lt;T&gt; T around(ProceedingJoinPoint pjp) throws Throwable &#123; Long startTime = System.currentTimeMillis(); Object[] args = pjp.getArgs(); T result; Method methodClass; try &#123; result = (T)pjp.proceed(args);//执行方法 &#125;finally &#123; long endTime = System.currentTimeMillis(); Signature signature = pjp.getSignature(); String methodName = signature.getName(); Class&lt;?&gt; targetClass = pjp.getTarget().getClass(); Class[] parameterTypes = ((MethodSignature) pjp.getSignature()).getParameterTypes(); methodClass = targetClass.getMethod(methodName, parameterTypes); Annotation[] annotations = methodClass.getAnnotations(); for (Annotation annotation : annotations)&#123; Class&lt;? extends Annotation&gt; aClass = annotation.annotationType(); String simpleName = aClass.getSimpleName(); if("TimeConsume".equals(simpleName))&#123; TimeConsume timeConsume = (TimeConsume) annotation; String value = timeConsume.value(); log.info(value+"[&#123;&#125;] 执行耗时:&#123;&#125;ms",methodName,endTime-startTime); break; &#125; &#125; &#125; return result; &#125;&#125; 3.测试目标方法中使用123456789101112131415161718192021222324@RestController@RequestMapping("/test")public class testController &#123; @TimeConsume("测试") @GetMapping("info") public Object testInfo()&#123; Object parse = JSONObject.parse("&#123;\n" + "\t\t\"requestId\":\"\",\n" + "\t\t\"appId\":\"\",\n" + "\t\t\"nonce\":\"\",\n" + "\t\t\"timestamp\":12345676543,\n" + "\t\t\"signature\":\"\",\n" + "\t\t\"sjgsd\":\"61000\",\n" + "\t\t\"starTime\":12345676543\n" + "\t&#125;"); try &#123; Thread.sleep(new Random().nextInt(100));//随机时间休眠 &#125; catch (InterruptedException e) &#123; e.printStackTrace(); &#125; return parse ; &#125;&#125;]]></content>
<categories>
<category>java</category>
</categories>
</entry>
<entry>
<title><![CDATA[在线sql编辑查询工具sql-editor]]></title>
<url>%2F2019%2F07%2F19%2F2019-07-19-sql-editor%2F</url>
<content type="text"><![CDATA[sql-editor是基于CodeMirror的一个在线sql编辑工具,模仿navicat工具,开发的一个简易版。 目前只提供前端代码,由easy-mock提供json测试数据。 Github地址: https://github.com/xiweile/sql-editor 功能更新 左侧提供数据库表字段树形结构,可拖拽到sql编辑框。(2019-7-19新增) 编辑区和数据展示区上下拖动更改大小。(2019-7-19新增) 执行与中断功能。(2019-7-19新增) 自定义外部接口获取sql关键词、函数、表字段等,用于sql编辑时提示补全,键盘录入后自动提示,上下键选择,tab键或点击选中项自动补全。(2019-7-16) sql格式化。(2019-7-16) 一些截图:]]></content>
<categories>
<category>前端</category>
</categories>
</entry>
<entry>
<title><![CDATA[一个基于jquery的仿vue的简易js模板]]></title>
<url>%2F2019%2F06%2F26%2F2019-06-26-wuedemo%2F</url>
<content type="text"><![CDATA[现在的项目大部分都是前后端分离的模式,作为后端开发者,喜欢用一些成熟的前端框架(如:layui)方便开发,尤其在前端工程师紧缺的情况下,后端开发者可以容易入手,后来又引入了vue.js使原有的HTML数据渲染和js动态操作更加快捷。但是在一次政府项目中,要求浏览器支持到ie8,这样vue支持的不好,因此将原来的vue写法改成原始jquery写法,为了改动最小化,便写了一个简单js,用于支持模板替换、el表达式解析等功能,我将名字命名为wue.js 1. 支持的模板 v-text 标签内文本替换 v-value 标签中value值替换 v-each 标签内部节点遍历 i-value v-each使用标签中循环子节点中使用,替换内部子节点value值 i-text v-each使用循环子节点中使用,替换内部子节点内文本值 i-click v-each使用循环子节点中使用,为内部子节点添加点击事件 e-click 标签添加点击事件 2. HTML中模板的使用 页面引入jquery.min.js和wue.js 查看文件wue.js 定义一个wue实例,js标签写在HTML中的body内最底部。 1234567891011121314151617181920212223242526var vm_app = new Wue(&#123; el:"#app", data:&#123; title:"一个案例", user:&#123; name:"张三", age:25 &#125;, menu:[ &#123;"id":"bh1","name":"目录1"&#125;, &#123;"id":"bh2","name":"目录2"&#125;, &#123;"id":"bh3","name":"目录3"&#125;, ], &#125;, methods:&#123; init:function()&#123; this.replaceModel(); &#125;, show:function(p)&#123; alert(this.title+"--"+p) &#125; &#125;&#125;);vm_app.init(); 参数说明: el dom选择器,限定模板的解析范围 data 数据定义 methods 方法定义 init 页面加载完成后的执行入口方法 页面定义模板 模板均使用在HTML标签的属性中,模板名作为属性名,值中使用双大括号作为解析对象 v-text、v-value写法一样,例如v-text=&quot;姓名-&quot; ,双大括号中的内容,会使用wue实例中data内的值进行解析并替换,双大括号外的内容不解析。 e-click定义在属性中,为该标签添加点击事件,事件的函数可以使用多个参数,参数之间用,分隔,可以是变量取值,也可以是其他常量,例如e-click=&quot;show(user.name)&quot;,e-click=&quot;show(&#39;这是点击事件&#39;)&quot;,e-click=&quot;sayUser(&#39;姓名&#39;,user.name,user.id)&quot;。 v-each写在循环体元素的父标签上,写法为v-each=&quot;m,item in menu&quot;或v-each=&quot;m in menu&quot;,字母in 前后分别为遍历的变量名、被遍历的数据集合,与in分别严格用一个空格隔开,变量名部分可以用,分隔,左侧为数据变量名,右侧为元数据对象,里面包含:索引值index,序号:num,总行数count。 内部循环体元素定义时,需要添加隐藏属性style=&quot;display:none&quot;,用于循环的 参照模板,它的属性中使用i-text、i-value进行文本和value属性赋值,取值对象则为自定义的循环变量或元数据变量,例如i-text=&quot;&quot;,i-text=&quot;&quot;,如果添加点击事件则使用i-click,例如i-click=&quot;show(m.id)&quot;,i-click=&quot;show(&#39;我是循环元素的点击事件&#39;)&quot;,事件的函数定义方式和e-click相同。 双括号内表达式支持自定义的函数。函数可以定义在当前wue实例methods中,也可以定义在页面全局window域下。书写方式如:myfun(user.name) 使用wue的实例名,调用init()方法进行初始化,init方法内部为页面加载完成后要执行的逻辑,this.replaceModel()调用时执行模板解析和替换逻辑,replaceModel()方法可以携带一个参数,参数为dom选择器,如replaceModel(&#39;#table&#39;),注意该选择器必须在el内部,表示仅解析或替换指定范围的模板。调用该方法根据逻辑需要,放于数据返回并已赋值给wue实例之后,赋值方法如this.resultdata = r.data。 3. 完整案例123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263&lt;!DOCTYPE html&gt;&lt;html&gt;&lt;head&gt; &lt;meta charset="utf-8"&gt; &lt;title&gt;el解析案例&lt;/title&gt; &lt;!-- 引入jQuery --&gt; &lt;script src="js/jquery.min.js"&gt;&lt;/script&gt; &lt;!-- 引入自定义的wue.js --&gt; &lt;script src="js/wue.js"&gt;&lt;/script&gt;&lt;/head&gt;&lt;body &gt; &lt;div id="app"&gt; &lt;div&gt; v-text属性:&lt;span v-text="这是标题:&#123;&#123;title&#125;&#125;"&gt;这是标题:&lt;/span&gt; &lt;/div&gt; &lt;div&gt; v-value属性:&lt;input type="text" v-value="&#123;&#123;user.name&#125;&#125;"&gt;&lt;/input&gt; &lt;/div&gt; &lt;div&gt; e-click属性:&lt;button e-click="show('点击事件')"&gt;点击按钮&lt;/button&gt; &lt;/div&gt; &lt;div&gt; v-each:属性 &lt;ul v-each="m,item in menu"&gt; &lt;li style="display:none" &gt; &lt;span i-text="&#123;&#123;item.num&#125;&#125;"&gt;&lt;/span&gt; &lt;a href="javascript:;" i-click="show(m.id)" i-text="&#123;&#123;m.name&#125;&#125;"&gt;&lt;/a&gt; &lt;/li&gt; &lt;/ul&gt; &lt;/div&gt; &lt;/div&gt; &lt;script&gt; var vm_app = new Wue(&#123; el:"#app", data:&#123; title:"一个案例", user:&#123; name:"张三", age:25 &#125;, menu:[ &#123;"id":"bh1","name":"目录1"&#125;, &#123;"id":"bh2","name":"目录2"&#125;, &#123;"id":"bh3","name":"目录3"&#125;, ], &#125;, methods:&#123; init:function()&#123; this.replaceModel(); &#125;, show:function(p)&#123; alert(this.title+"--"+p) &#125; &#125; &#125;); vm_app.init(); &lt;/script&gt;&lt;/body&gt;&lt;/html&gt; 页面效果:]]></content>
<categories>
<category>前端</category>
</categories>
</entry>
<entry>
<title><![CDATA[从这里开始]]></title>
<url>%2F2019%2F03%2F14%2F2019-03-14-hello-world%2F</url>
<content type="text"><![CDATA[我的个人博客搭建好了,先简单尝试发表一篇吧,在后续有空时间里,我将写一些JAVA学习总结,以及自己平时生活爱好随笔,欢迎大家随时访问,记住我的博客域名 www.weiller.cn 。下面分享一篇美文。 轻轻晃动手中地茶杯,看淡绿色地茶或针或片,忽上忽下,簇拥着,沉沉浮浮,变换着不同地位置,试图寻找-个属于自己地最佳平衡点。心急地我常常等不得茶泡好,就轻吹杯口,带动-漾-漾地茶涡,看茶叶聚聚散散,无奈分离。呷-小口茶,任清清浅浅地苦涩在舌间荡漾开来,充溢齿喉。之后,深吸-口气,余香满唇,在肺腑间蔓延开来,涤尽了-切地疲惫冷漠。人仿佛也醉了,朦胧中,久久不愿醒来。 是夜,茶香满室,杯中茶由淡变浓,浮浮沉沉,聚聚散散,苦涩清香中慢慢感悟:人生亦如茶。]]></content>
<categories>
<category>随笔</category>
</categories>
</entry>
</search>
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
HTML
1
https://gitee.com/xiweile/xiweile.github.io.git
git@gitee.com:xiweile/xiweile.github.io.git
xiweile
xiweile.github.io
xiweile.github.io
master

搜索帮助