描述
上一篇(一个六年经验的python后端是怎么学习用java写API的(3)Java 开发环境搭建)开发环境准备完成后,就可以开始看java语法和框架了。
- 廖雪峰java语法教程, 半天时间过一遍语法,看到集合那里即可
- dropwizard文档 半天时间过一遍 Getting Started 部分
代码
parrot tag: first_api
dropwizard 与 django 的简单对比
dropwizard
java
└── com
└── reworkplan
├── ParrotApplication.java
├── bundles
│ ├── CorsBundle.java
│ └── MysqlBundle.java
├── common
│ ├── Constants.java
│ └── response
│ ├── MetaListResponse.java
│ └── MetaMapperResponse.java
├── config
│ └── ParrotConfiguration.java
├── mappers
│ ├── ArticleMapper.java
│ └── handlers
│ └── DateTimeTypeHandler.java
├── models
│ └── Article.java
├── modules
│ ├── ApplicationModule.java
│ └── InjectorFactory.java
└── resources
├── ArticlesResource.java
└── BasePath.java
django
├── common
│ ├── __init__.py
│ ├── constants.py
│ ├── data_structure.py
│ ├── db.py
│ ├── exceptions.py
│ ├── oauth_utils.py
│ ├── permissions.py
│ ├── rest.py
│ ├── sms.py
│ ├── tools.py
│ ├── upload.py
│ └── weixin.py
├── config
│ ├── __init__.py
│ ├── settings
│ │ ├── __init__.py
│ │ ├── common.py
│ │ ├── development.py
│ │ ├── private.example.py
│ │ └── production.py
│ ├── urls.py
│ └── wsgi.py
├── extracter
│ ├── __init__.py
│ ├── admin.py
│ ├── apps.py
│ ├── migrations
│ │ ├── 0001_initial.py
│ │ └── __init__.py
│ ├── models.py
│ ├── tests.py
│ ├── urls.py
│ └── views.py
├── manage.py
web 框架的设计都是大同小异的,概念映射如下:
- 主程序入口: django wsgi.py;dropwizard ParrotApplication.java
- 环境变量等常量配置:django settings目录;dropwizard Config 目录 ParrotConfiguration.java,另外还有个配置文件config/environment.yml
- 普通常量配置:django没有这个概念,我一般放在common.constants下;dropwizard我创建了个common Constants.java
- 路由定义:django 的 urls.py;dropwizard 他是放在Resource类上通过Path注解实现,然后在ParrotApplication.java中注册Path类
- 具体API实现:django 的 views.py 中继承自 APIView 的类,配置在urls.py中;dropwizard 的 Resource目录下的类,需要在然后在ParrotApplication.java中注册
- 数据库的ORM:django 中 的 models.py;dropwizard 使用 mybatis,对应 mappers目录下的类,每个mappers还要在还需resources的xml目录下添加mybatis格式的xml去写具体的sql,并且需要写一个jdbc的sqlsession,需要在ParrotApplication中注册mapper和sqlsession,要自己做很多的基础工作(返回db的datetime需要自己写时间格式的handler等等)
- 处理逻辑:django是直接在models上实现一些逻辑,这对应于dropwizard的service目录(代码写到这暂时没有逻辑需要处理所以没创建service),具体api接口需要柔和多个逻辑,django直接调用各个model上的逻辑+api内在写一部分;dropwizard则在resource.java里面调用各个service的Impl类组合逻辑
- middleware:django 实现 middleware.py,在settings中配置,可以放在任何模块下;java放在一个叫做bundles的目录下,在ParrotApplication中注册
大概就是这样,另外java还有叫做注入的东西,所以我创建了modules目录 希望通过InjectorFactory做一些事情,暂时没调通
实现第一个 API Get /articles/{id}
实现 resource
- 通过Path注解定义api的路由
- Produces注解说返回一个json
- GET注解说明的restful的get方法
- 方法内的 PathParam注解是接受路由参数id
- 方法内则需要一个mapper去执行数据库的操作
- 定义一个自定义的reponse对象(或者不定义直接返回model对象),我习惯返回这两种格式: 列表
{"meta":{}, "data": []}
, 单个元素或者普通数据{"meta":{}, "data": {}}
@Path(BasePath.ARTICLE_API)
@Produces(APPLICATION_JSON)
public class ArticlesResource {
private final SqlSessionFactory sessionFactory;
public ArticlesResource(SqlSessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
@Path("/{id}")
@GET
@Timed
public MetaMapperResponse get(@NotNull @PathParam("id") Integer articleId) {
MetaMapperResponse response = new MetaMapperResponse();
try (SqlSession session = sessionFactory.openSession()) {
ArticleMapper articleMapper = session.getMapper(ArticleMapper.class);
Boolean isActive = true;
Article article = articleMapper.select(articleId, isActive);
response.setData(article);
}
return response;
}
}
实现mapper
- 实现一个interface,定义mapper的方法
- 然后需要在 resources的xml目录下实现这个mapper定义方法的sql,例如 java中定义了个
Article select(@Param("id") Integer id, @Param("isActive") Boolean isActive)
, 那么xml里面就需要有这个东西的定义<select id="select" parameterType="hashmap" resultMap="ArticleResultMap">
- xml中定义resultMap是指将sql返回的内容格式化到这个resultMap里面,类似django的serializer,但是并不智能,例如datetime他就不知道怎么处理,然后会报错,需要实现一个mappers.handlers.DateTimeTypeHandler, 然后在 bundles.MysqlBundle 里面注册这个handler,才能正确处理datetime格式的字段
- xml中的parameterType="hashmap",对应于interface里面的Param注解,如果没有会报找不到参数
- 回调resource里面发现,
ArticleMapper articleMapper = session.getMapper(ArticleMapper.class);
mapper需要通过session.getMapper的形式,而session是在ParrotApplication中注册并且添加了ArticleMapper这个类,SqlSessionFactory sessionFactory = mysqlBundle.getSqlSessionFactory(); sessionFactory.getConfiguration().addMapper(ArticleMapper.class);
。
public interface ArticleMapper {
Article select(@Param("id") Integer id, @Param("isActive") Boolean isActive);
List<Article> selectAll(@Param("isActive") Boolean isActive,
@Param("offset") Integer offset,
@Param("limit") Integer limit);
Integer countAll (@Param("isActive") Boolean isActive);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.reworkplan.mappers.ArticleMapper">
<resultMap id="ArticleResultMap" type="com.reworkplan.models.Article">
<id property="id" column="id"/>
<result property="title" column="title"/>
<result property="cover" column="cover"/>
<result property="description" column="description"/>
<result property="is_active" column="is_active"/>
<result property="created" column="created" />
</resultMap>
<select id="select" parameterType="hashmap" resultMap="ArticleResultMap">
SELECT id, title, cover, description, is_active, created
FROM extracter_wxarticle WHERE id = #{id}
and is_active=#{isActive} and is_delete=0
</select>
<select id="countAll" parameterType="hashmap" resultType="int">
SELECT count(*)
FROM extracter_wxarticle
WHERE
is_active=#{isActive} and is_delete=0
</select>
</mapper>
ParrotApplication
- 主入口在main,而run时候执行的命令是 server config/environment.yml,推测Enviroments读的是config/environment.yml
- initialize和run的先后顺序目前我不清楚,可以看到initiallize里面注册的是middleware,run里面注册的是mapper和resource
public class ParrotApplication extends Application<ParrotConfiguration> {
private final InjectorFactory injectorFactory;
private final MysqlBundle mysqlBundle;
public ParrotApplication() {
this.injectorFactory = new InjectorFactory();
this.mysqlBundle = new MysqlBundle();
}
public ParrotApplication(InjectorFactory injectorFactory) {
this.injectorFactory = injectorFactory;
this.mysqlBundle = new MysqlBundle();
}
@Override
public void initialize(Bootstrap<ParrotConfiguration> bootstrap) {
bootstrap.addBundle(new TemplateConfigBundle());
bootstrap.addBundle(new Java8Bundle());
bootstrap.addBundle(new MultiPartBundle());
bootstrap.addBundle(new CorsBundle());
bootstrap.addBundle(mysqlBundle);
super.initialize(bootstrap);
}
@Override
public void run(ParrotConfiguration configuration, Environment environment) throws Exception {
SqlSessionFactory sessionFactory = mysqlBundle.getSqlSessionFactory();
sessionFactory.getConfiguration().addMapper(ArticleMapper.class);
environment.jersey().register(JodaTimeParamConverterProvider.class);
environment.jersey().register(new ArticlesResource(sessionFactory));
}
public static void main(String[] args) throws Exception {
new ParrotApplication(new InjectorFactory()).run(args);
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。