首页 文章 精选 留言 我的

精选列表

搜索[Solon框架],共10000篇文章
优秀的个人博客,低调大师

SpringMVC验证框架Validation用法

1. 分组 有的时候,我们对一个实体类需要有多中验证方式,在不同的情况下使用不同验证方式,比如说对于一个实体类来的id来说,保存的时候是不需要的,对于更新时是必须的,可以如下配置: [java]view plaincopy publicclassUserModel{ @NotNull(message="{id.empty}",groups={First.class}) privateintid; @NotNull(message="{username.empty}",groups={First.class,Second.class}) privateStringusername; @NotNull(message="{content.empty}",groups={First.class,Second.class}) privateStringcontent; publicintgetId(){ returnid; } publicvoidsetId(intid){ this.id=id; } publicStringgetUsername(){ returnusername; } publicvoidsetUsername(Stringusername){ this.username=username; } publicStringgetContent(){ returncontent; } publicvoidsetContent(Stringcontent){ this.content=content; } } publicinterfaceFirst{ } publicinterfaceSecond{ } 通过 groups 对验证进行分组 在controler中的代码如下: [java]view plaincopy @RequestMapping(value="/save.action",method=RequestMethod.POST) publicStringsave(@Validated({Second.class})UserModeluserModel,BindingResultresult){ if(result.hasErrors()){ return"validate/error"; } return"redirect:/success"; } @RequestMapping(value="/update.action",method=RequestMethod.POST) publicStringupdate(@Validated({First.class,Second.class})UserModeluser,BindingResultresult){ if(result.hasErrors()){ return"validate/error"; } return"redirect:/success"; } 2. 组序列 默认情况下,不同组别的约束验证是无序的,然而在某些情况下,约束验证的顺序却很重要,如下面两个例子:(1)第二个组中的约束验证依赖于一个稳定状态来运行,而这个稳定状态是由第一个组来进行验证的。(2)某个组的验证比较耗时,CPU 和内存的使用率相对比较大,最优的选择是将其放在最后进行验证。因此,在进行组验证的时候尚需提供一种有序的验证方式,这就提出了组序列的概念。 一个组可以定义为其他组的序列,使用它进行验证的时候必须符合该序列规定的顺序。在使用组序列验证的时候,如果序列前边的组验证失败,则后面的组将不再给予验证。 下例中声明了组 GroupA.class,GroupB.class 和 Group.class,其中 default,GroupA,GroupB 均为 Group 的序列。 [java]view plaincopy publicinterfaceGroupA{ } publicinterfaceGroupB{ } @GroupSequence({Default.class,GroupA.class,GroupB.class}) publicinterfaceGroup{ } publicclassUser{ @NotEmpty(message="firstnamemaybeempty") privateStringfirstname; @NotEmpty(message="middlenamemaybeempty",groups=Default.class) privateStringmiddlename; @NotEmpty(message="lastnamemaybeempty",groups=GroupA.class) privateStringlastname; @NotEmpty(message="countrymaybeempty",groups=GroupB.class) privateStringcountry; } [java]view plaincopy @RequestMapping(value="/update.action",method=RequestMethod.POST) publicStringregister(@Validated(Group.class)Useruser,BindingResultresult){ if(result.hasErrors()){ return"validate/error"; } return"redirect:/success"; } 3. 验证多个对象 当我们在一个功能处理方法上需要验证多个模型对象时,需要通过如下形式来获取验证结果: [java]view plaincopy @RequestMapping("/validate/multi") publicStringmulti(@Valid@ModelAttribute("a")Aa,BindingResultaErrors,@Valid@ModelAttribute("b")Bb,BindingResultbErrors){ if(aErrors.hasErrors()){//如果a模型对象验证失败 return"validate/error"; } if(bErrors.hasErrors()){//如果a模型对象验证失败 return"validate/error"; } return"redirect:/success"; } 每一个模型对象后边都需要跟一个Errors或BindingResult对象来保存验证结果,其方法体内部可以使用这两个验证结果对象来选择出错时跳转的页面或处理的逻辑。 4. Junit测试 当自定义拓展Validation时,可以使用如下方法进行测试: [java]view plaincopy @Test publicvoidtestValidate(){ AnnotationDescriptor<EqualsAny>descriptor=newAnnotationDescriptor<EqualsAny>(EqualsAny.class); EqualsAnyequalsAny=AnnotationFactory.create(descriptor); EqualsAnyValidatorequalsAnyValidator=newEqualsAnyValidator(); equalsAnyValidator.initialize(equalsAny); Assert.assertTrue(equalsAnyValidator.isValid("123",null)); } 另外再讲一点spring对自定义JSR-303限制类型支持的新特性,那就是Spring支持往ConstraintValidator里面注入bean对象。例如在EqualsAnyValidator中利用@Resource注解注入其他Bean对象。

优秀的个人博客,低调大师

MVC框架,webx 流程解读

写在前面的话 写这篇文章的目的,主要还是站在一个新人的角度做一些沉淀,同样也为了方便后面的人快速熟悉集团web开发常用技术。这篇文章,将分析一个请求发到服务端,经tomcat容器、webx映射直到最后调用controller入口的过程,对java web基础知识结合webx做一个整体回顾。同时,也将简单分析集团web的基本分层方式,以及VO、DTO、DO等数据实例在各层所起的作用。 Web容器 理解web容器,也是理解我们程序的运行平台,同时也是了解servlet处理流程的基础所在。tomcat的主要结构如下图: Connector Connector是tomcat的连接器。Tomcat在监听80端口的时候,一个HTTP请求访问过来,实际上是通过在80端口用socket来输入HTTP报文。Connector通过socket读取报文文本并进行解析,然后将报文内容封装为request实体,并将响应结果利用response进行封装,新起一个线程,并交给container容器进行处理。 Container container是所有子容器的父接口。Engine主要负责对请求进行分发,而Host就是tomcat虚拟host功能的实体,Context就是我们一个应用服务的完整环境,每次一个请求对应的一个新的servlet,都是封装在Context中的。 Tomcat在处理Connector传来的request和response时,就是通过责任链模式,一层一层的去调用Engine、Host、Context并封装出一个servletWrapper来进行doService操作。 Servlet处理流程及WebX的调用 上面简单介绍了一下Tomcat的容器结构,接下来具体分析处理一个请求的过程。 tomcat容器执行过程 请求到达后,会新起一个线程并由Container进行处理。 Container中的Engine、Host、Context、Wrapper都继承了ValveBase抽象类并实现了其invoke方法。这也类似于webx的pipeline,对请求进行流水线处理。 首先是StandardEngineValve的invoke。engine通过对请求host域名的解析,映射到合适的host容器中去,然后调用host的invoke方法。 StandardEngine engine = (StandardEngine)this.getContainer(); Host host = (Host)engine.map(request, true); if(host == null) { ((HttpServletResponse)response.getResponse()).sendError(400, sm.getString("standardEngine.noHost", request.getRequest().getServerName())); } else { host.invoke(request, response); } 在host的invoke中,实际是调用了流水线的invoke方法: public void invoke(Request request, Response response) throws IOException, ServletException { this.pipeline.invoke(request, response); } 最终又调用到standardPipeline中的invokeNext: public void invokeNext(Request request, Response response) throws IOException, ServletException { Integer current = (Integer)this.state.get(); int subscript = current.intValue(); this.state.set(new Integer(subscript + 1)); if(subscript < this.valves.length) { this.valves[subscript].invoke(request, response, this); } else { if(subscript != this.valves.length || this.basic == null) { throw new ServletException(sm.getString("standardPipeline.noValve")); } this.basic.invoke(request, response, this); } } 可以看到流水线的设计模式是将engine、host、context等容器的实例放进一个数组中,并在运行时依次执行。 然后就到了StandardHostValve中,同样实行invoke方法: StandardHost host = (StandardHost)this.getContainer(); Context context = (Context)host.map(request, true); 逻辑也类似,就是找到对应的context,也就是我们的应用并调用invoke方法。 context的invoke主要做了两件事: 1、获取session并绑定 2、找到该url对应的wrapper,并调用wrapper的invoke方法。 这里先略过session的获取绑定过程,直接来到wrapper的调用中去。在servletWrapper中,我们可以看到几个熟悉的对象: StandardWrapper wrapper = (StandardWrapper)this.getContainer(); ServletRequest sreq = request.getRequest(); ServletResponse sres = response.getResponse(); Servlet servlet = null; HttpServletRequest hreq = null; servlet会通过wrapper进行分配: servlet = wrapper.allocate(); 从allocate方法中,我们可以看到servlet实际是通过类加载器记载的,并且当servlet加载完毕后,会调用初始化方法init。 classClass = classLoader.loadClass(actualClass); ... servlet = (Servlet)classClass.newInstance(); ... servlet.init(this.facade); 有了servlet以后,就开始出现另一个重要角色:ApplicationFilterChain。FilterChain会传入request和response并对servlet进行过滤: filterChain.doFilter(sreq, sres); 在doFilter中,会调用到internalDoFilter 方法。该方法对filters进行遍历调用: this.iterator = this.filters.iterator(); ... filter.doFilter(request, response, this); 可以看到,FilterChain 采用链式模式,通过多次调用chain.doFilter(request, response)方法会多次调用internalDoFilter方法,从而通过Iterator调用所有注册的Filter。 Webx执行流程 以一个简单的接口为例子,来看一看webX最终是如何调用到我们的execute方法的。 public class DemoScreen extends BmsBaseModule { public void execute(@Param('param') String param) { ... } } 首先,继续上一部分的Filter开始看。Filter是servlet的标准过滤器,而上部分最后提到的filters则封装了web.xml文件中关于Filter的配置: <filter> <filter-name>webx</filter-name> <filter-class>com.alibaba.citrus.webx.servlet.WebxFrameworkFilter</filter-class> <init-param> <param-name>excludes</param-name> <param-value>*/checkpreload.htm<!-- 需要被“排除”的URL路径,以逗号分隔,如/static, *.jpg。适合于映射静态页面、图片。 --></param-value> </init-param> <init-param> <param-name>passthru</param-name> <param-value><!-- 需要被“略过”的URL路径,以逗号分隔,如/myservlet, *.jsp。适用于映射servlet、filter。 对于passthru请求,webx的request-contexts服务、错误处理、开发模式等服务仍然可用。 --></param-value> </init-param> </filter> 上面是一个典型的webx配置,可以看到,设置的过滤器WebXFrameworkFilter会被ApplicationFilterChain执行。来看其中的doFilter方法: if (isExcluded(path)) { log.debug("Excluded request: {}", path); chain.doFilter(request, response); return; } 校验我们设置的excluded参数,如果需要排除,那么通过doFilter的internalDoFilter来链式调用下一个过滤器。 关键语句在此: getWebxComponents().getWebxRootController().service(request, response, chain); 调用webx RootController的service方法。service中首先对HttpServletRequest 和 HttpServletResponse 实例封装进RequestContext中。后续会调用 WebxRootControllerImpl 的 handleRequest 方法: 然后,会根据路径找到WebxComponent: WebxComponent component = getComponents().findMatchedComponent(path); 在findMatchedComponent中会依据url来进行匹配: for (WebxComponent component : this) { if (component == defaultComponent) { continue; } String componentPath = component.getComponentPath(); if (!path.startsWith(componentPath)) { continue; } // path刚好等于componentPath,或者path以componentPath/为前缀 if (path.length() == componentPath.length() || path.charAt(componentPath.length()) == '/') { matched = component; break; } } webx在Spring启动的时候,通过对文件目录的扫描,装配进符合规则的组件Component,比如xxx.module.screen下的类,来和url做匹配。找到对应的component后: served = component.getWebxController().service(requestContext); 然后就到了webx的pipeline处理流程: public boolean service(RequestContext requestContext) throws Exception { PipelineInvocationHandle handle = pipeline.newInvocation(); handle.invoke(); // 假如pipeline被中断,则视作请求未被处理。filter将转入chain中继续处理请求。 return !handle.isBroken(); } pipeline 同样是链式模式,会依次调用注册的valve。常用的valve可能包括url检查、登录检查、csrfToken校验等,同样pipeline中的一些条件语句比如Choose、Loop等同样也是一个valve,调用其invoke方法其实也是生成了一个新的pipeline并执行invoke。比如这样一个when-otherwise语句中就会有如下代码: if (!satisfied && otherwiseBlock != null) { otherwiseBlock.newInvocation(pipelineContext).invoke(); } 此外,比较重要的valve就是负责处理表单的PerformActionVavle、负责执行screen的PerformScreenValve。 因为screen在应用中经常是一个接口的入口,并最终调用到业务逻辑部分的代码。所以这里我们分析一下PerformScreenValve这个阀门是如何调用到我们的业务逻辑代码的。screenValve的invoke最终调用了performScreenModule函数。显示依据规则找到了我们的module,然后执行了execute方法: Module module = finder.getScreenModule(); ... module.execute(); 然后调用了DataBindingAdapter的execute方法: private final MethodInvoker executeMethod; ... executeMethod.invoke(moduleObject, log); 在MethodInvoker这个类里面,我们可以看到装配execute方法参数的过程。 首先,在扫描类中注解的时候,webX会为每一个execute方法中有@Param注解的参数生成一个DataResolver。MethodInvoker中会有如下代码获取参数,最终会调用FastMethod的invoke方法。 Object[] args = new Object[resolvers.length]; for (int i = 0; i < args.length; i++) { Object value; value = resolvers[i].resolve(); } ... fastMethod.invoke(moduleObject, args); 最后就是反射调用的过程了,我们的接口object即为moduleObject,而args即为带有@Param注解的参数。最终的整体调用流程如下图: 项目分层 关于web层、service层、manager层与DAO层 执行到我们发布的接口后,再来简单的说一下web项目常见分层手段。 在我们的项目中,常见的是依次调用web层,service层,manager,dao层。数据实体在VO、DTO、DO之间流转。 因为在执行某个业务的时候,可能需要查询多个表的数据并进行拼接。比如现在需要这样一条数据D,由A,B,C组成。如果仅仅提供一个方法查询A、B、C并拼接为D返回,那么下次如果又有一个需求为E(A+B)的数据,无疑又要重新写查询A和B的代码并拼接。 这样,我们把代码分层,使各部分职责独立。其中service负责拼接,manager负责查询,这样不同的service可以对manager进行复用。同时,由于DTO和DO不是完全对应,查询的时候往往要把DTO转换成DO查询条件去调用DAO,而DAO返回的结果DO又和web层所需要的数据格式不一致,所以常常要转换成DTO。 manager的作用,就是处理DTO到DO,然后DAO生成DO再到DTO的过程。如下就是一个manager: BmsPurchaseOrderQuery purchaseOrderQuery = new BmsPurchaseOrderQuery(); purchaseOrderQuery.setPurchaseCode(purchaseCode); List<BmsPurchaseOrderDO> orderDOList = bmsPurchaseOrderDAO.select(purchaseOrderQuery); Assert.notEmpty(orderDOList); return orderDOList.get(0); 假设查询了订单的数据。然后再来一个manger查询订单明细的数据: @Override public List<BmsPurchaseOrderTransOutInfoDO> getTranOutInfoListByCode(String subscribeCode) { //查询子单明细 BmsPurchaseOrderTransOutInfoDO transOutInfoQuery = new BmsPurchaseOrderTransOutInfoDO(); transOutInfoQuery.setSubscribeCode(subscribeCode); List<BmsPurchaseOrderTransOutInfoDO> transOutInfoDOList = bmsPurchaseOrderTransOutInfoDAO.select(transOutInfoQuery); Assert.notNull(transOutInfoDOList); return transOutInfoDOList; } 然后,在service对订单和明细进行拼接。 总而言之,分层如下: web层(也就是webX中的screen):处理表单,调用服务,返回结果 service层:业务逻辑,数据拼接 manager层:粒度比较细的查询 dao层:sql语句映射 分层可能会导致代码量增大,但是可以提高程序的可复用性,方便程序维护。 总结 至此为止,关于webx服务的请求处理的来龙去脉简单的分析了一遍。了解大致处理流程还是比较有必要的,这样当我们的程序出现异常,可以及时的定位问题。当打断点时,面对层层的调用栈也不会显得不知所措。当然,在这里也是对自己经常接触到的技术做一个简单的沉淀。

优秀的个人博客,低调大师

volley框架使用小Demo

MainActivity public class MainActivity extends AppCompatActivity { private static final String URL = "https://www.baidu.com/"; private RequestQueue mQueue; // volley的请求队列 @BindView(R.id.volley_get) Button btn; TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mQueue = Volley.newRequestQueue(getApplicationContext()); ButterKnife.bind(this); } @OnClick({R.id.volley_get}) public void onClick(View v) { get(); } /** * 创建一个请求,这里我们做一个最简单的通过GET方式请求网页源码的操作。请求成功后打印结果。 */ private void get() { StringRequest request = new StringRequest(Request.Method.GET, URL, new Response.Listener<String>() { @Override public void onResponse(String arg0) { //Toast.makeText(getApplicationContext(), arg0, Toast.LENGTH_LONG).show(); textView = (TextView) findViewById(R.id.text); textView.setText(arg0.toString()); //Log.d("onResponse", arg0); } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError arg0) { //Toast.makeText(getApplicationContext(), arg0.toString(), Toast.LENGTH_LONG).show(); textView = (TextView) findViewById(R.id.text); textView.setText(arg0.toString()); } }); mQueue.add(request); } } activity_main.xml <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.an.volleytest.MainActivity"> <Button android:id="@+id/volley_get" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="get请求" android:layout_alignParentTop="true" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" android:id="@+id/text" android:layout_marginTop="25dp" android:layout_below="@+id/volley_get" android:layout_centerHorizontal="true" /> </RelativeLayout> 捕获.PNG 点击按钮 hello world 替换为请求网址的html文件

优秀的个人博客,低调大师

区块链开发知识框架

区块链学习更多的是去理解这种去中心化的思想和去中心化的价值所在,语言倒是其次的东西。 不过由于以太坊和Hyperledger Fabric 对go语言支持最好,所以go语言肯定是要学习的。 下面把我另外一个问题的答案copy过来,希望对你有所帮助。 背景介绍:本人从2015年开始研究区块链技术,也算是对区块链有一定的了解吧。 申明:预估看到本问题的知乎er应该都是想要入门区块链,那么本回答也主要是进行一个入门级别的介绍。只是入门级别介绍,不适合区块链从业人员提升学习。 ----------------------------------- 回答的目录如下: 一、区块链是什么? 二、从业务角度学习区块链技术? 三、从技术角度学习区块链技术? 四、学习资料推荐与共享? ------------------------------------- 一、区块链是什

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册