本章讲述如何进行业务开发,比如我们需要开发一个联系人的业务,显示联系人列表,增加联系人,修改联系人信息,删除联系人。此处所有的开发都不需要重启服务器,完全动态加载。我已经把代码提交到了src/test/resources/groovy目录下,可以直接进行测试。
1. 定义entity
@Entity(name = "TestContact") class TestContact extends LongKeyEntity { String name String phone }
定义了一个TestContact对象,对象包括联系人姓名和电话两个字段,并继承于LongKeyEntity,LongKeyEntity定义了id是long值,并自动增加
2. 定义Dao
@Repository class TestContactDao extends BaseDao<TestContact> { @Autowired public TestContactDao(SessionFactory sessionFactory) { super(sessionFactory) } def list() { createCriteria().list() } @PostConstruct def createTableIfNecessary() { def session = sessionFactory().openStatelessSession() try { session.createSQLQuery(""" CREATE TABLE IF NOT EXISTS `${TestContact.class.simpleName}` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `name` varchar(100) NOT NULL, `phone` varchar(100) NOT NULL, `createdTime` datetime NOT NULL, `updatedTime` datetime NOT NULL, `createdBy` varchar(100) NOT NULL, `updatedBy` varchar(100) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; """).executeUpdate() } finally { session.close() } } }
Dao继承于BaseDao,提供了一些最基本的操作,比如save一个对象,通过id获取一个对象,所以这里只定义了一个list方法,用来显示所有联系人。由于是测试使用,所以在创建该dao的同时也创建测试数据表。
3. 定义service
@Service @Transactional @Import([groovy.com.geo.aop.TestAspect.class, groovy.com.geo.config.TestResourceConfig.class]) class TestContactService extends BaseService<TestContact> { @Autowired public TestContactService(TestContactDao dao) { super(dao) } }
定义一个service,继承于BaseService,BaseService是对BaseDao的方法进行事务定义。
注意,这里的service并没有定义一个类似下面的方法
def list() { dao().list() }
主要是因为我使用了groovy的动态调用方法,定义在了BaseService里,会自动去调用dao的相应的方法,所以不需要再重复定义了
还有一点注意,这里引入了Import的注解,目的是让classloader可以获取到TestAspect和TestResourceConfig的配置信息,并注册到当前的applicationContext中,TestAspect定义了一个AOP切面,TestResourceConfig则是定义了在这个service下需要使用的配置,比如hibernate的配置,事务管理器的配置,及其他资源的定义
@Configuration @EnableAspectJAutoProxy @Aspect class TestAspect { def logger = LoggerFactory.getLogger(getClass()) @Before('execution (* groovy.com.geo.service..*(..))') def before() { logger.info "i am invoked........." } }
这里的AOP定义了拦截所有groovy.com.geo.service包下的servcie类的方法,在调用这些方法前先打印一条被调用的信息
@Configuration class TestResourceConfig extends BasicResourceConfig { @Override def Class<?>[] defineHibernateAnnotatedClasses() { return [TestContact.class] } }
这里继承了BasicResourceConfig,它定义了一个sessionFactory和一个事务管理器。这里只需要告诉BasicResoureConfig去查找哪个entity类让hibernate知道即可。
4. 定义controller
@RestController @RequestMapping("/testContact") class TestContactController { @Autowired TestContactService testService @RequestMapping(value = "/list", method = [RequestMethod.GET]) def list() { testService.list() } @RequestMapping(value = "/save", method = [RequestMethod.POST]) def save(@RequestBody TestContact updatedContact) { def currentUser = UserSession.CONTEXT().get() def oldContact = updatedContact if(updatedContact.id) { oldContact = testService.get(updatedContact.id) BeanUtils.copyProperties(updatedContact, oldContact, "createdBy", "createdTime", "updatedBy", "updatedTime") } else { oldContact.setCreatedBy(currentUser.username) } oldContact.setUpdatedBy(currentUser.username) testService.save(oldContact).toString() } @RequestMapping(value = "/delete/{id}", method = [RequestMethod.DELETE]) def delete(@PathVariable("id") Long id) { def oldContact = testService.load(id) testService.delete(oldContact) } }
这里定义了一个contact的controller,提供了CRUD操作,这里需要注意,需要为每个controller在类上定义一个RequestMapping,这里定义的是testContact,然后每个操作都需要定义对应的RequestMapping,这样框架才能正确找到相应的入口
5. 定义入口映射
找到如图位置的groovy文件,点击进入定义controller的mapping入口
class DynamicEntryMapping { public static final def map = [ "testContact": "groovy.com.geo.web.TestContactController", ] }
定义testContact对应的controller类名,目的是让框架可以定位到这个controller文件
6. 配置安全
框架使用spring security,这里需要你稍微了解一下spring security的web expression,对url进行安全配置
如图找到DynamicSecurityMapping,然后定义安全
class DynamicSecurityMapping { public static final def map = [ "/**": "hasRole('ROLE_SUPER_ADMIN')", ] }
上面的意思是所有的请求都需要有超级管理员的权限
至此,所有的业务开发完成,只需要访问业务相关的页面,这里访问的是http://localhost:8080/test/contact.html
整个过程不需要你重启服务器,每个类都可以进行动态的增删改