一直没有写博客的习惯,但是这几年走过来,却发现了总没留下什么,所以是时候对过去做些记录了,一是为了缅怀过去,二是对技术重述,三是留作借鉴。
按照现在很多企业的开发都是基于springboot、或者一些微服务都用到了springcloud、dubbo,为什么会想着回归到以前的技术呢。我觉得,对于现在接触软件行业的人来说,对于工具的使用越来越容易了,但是对于技术的理解确越来越困难。由于太多的技术不断发展,而且发展的很快,不断的改进、升华、包装,这些都是经验与思想的结晶,对我们来说,只是看到了结果,确很难去看穿这些过程,那么如何然我们去理解这些由于时代变迁与技术革新留下的成果物,是不是只能从头再来,这个真不得而知,每个人接触新事物、学习能力、思维模式等等方面都有很大差异,没有什么方式能够弥补时代欠下我们的经验,那么只有一步一个脚印,按自己的方式走下去了....
记得最开始进入这个行业,也是4年前了,那个时候用到的就是spring、springmvc、hibernate orm、jsp来完成企业项目的开发,那么今天,我也将试着按照这样的脚步走一遍。之后也会涉及到springboot、springcloud、mybatis、redis、zookeeper、vue等这些前后端目前都比较流行的技术进行下去,档案更多的是完成效果,那么具体为什么、如何理解等比较深层次的东西,只有在以后的日子慢慢理解,从源码的角度与各种渠道进行解析,这是一个比较漫长的过程,包括现在,都是学习与记录的过程。
现在同样先构建一个maven的空项目,首先通过spring-bom配置spring环境依赖的各种jar的最佳版本,
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-framework-bom</artifactId>
<version>3.2.16.RELEASE</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
然后无非就是spring的一些包,比如bean、core、context、web、mvc,档案现在和hibernate配合,需要 shring的orm和hibernate-croe,当然还有一些数据库驱动、数据库连接池、json处理、测试、日志以为web开发相关的包,如servlet-api等。
一般mvc项目都会遵循这样的结构:
其中core中主要会有一些扩展,这个可能只能在之后来慢慢补充了,由于这是一个简单的应用,各个所有业务会划分成多模块存在与一个项目中,直接回存在一些依赖于调用。
可以看下实体是怎么处理的:
@Data
@Entity
@Table(name = "USER")
@JsonIgnoreProperties({"password"})
public class User implements Domain {
@Id
@Column(name = "USER_ID",length = 36)
@GeneratedValue(generator = "system-id")
@GenericGenerator(name = "system-id", strategy = "uuid")
private String userId;
@JoinColumn(name = "AGENCY_ID")
@ManyToOne(fetch = FetchType.LAZY)
private Agency agency;
@Column(name = "LOGIN_NAME",length = 56)
private String loginName;
@Column(name = "USER_CAPTION",length = 16)
private String userCaption;
@Column(name = "PASSWORD",length = 24)
private String password;
@Column(name = "BIRTHDAY",length = 24)
private String birthday;
@Column(name = "TELEPHONE",length = 16)
private String telephone;
@Column(name = "EMAIL",length = 36)
private String email;
@Column(name = "QQ",length = 12)
private String qq;
@Column(name = "WEIXIN",length = 56)
private String weixin;
@Column(name = "DESCRIPTION",columnDefinition="varchar(256)")
private String description;
}
因为使用的是hibernate orm,所以不会有hibernate的配置,这里只用jpa的相关注解即可。同时由java 实体生成数据库表,所以对每个属性都加上了注解。而doa与service层则相对简单
public interface UserDao extends Dao<User,String> {
}
@Repository
public class UserDaoImpl extends HibernateDao<User,String> implements UserDao {
@Override
protected Class<User> getModelClazz() {
return User.class;
}
}
public interface UserService extends BaseService {
List<User> getUsers();
User getUser(String id);
User saveUser(User user);
void removeUser(String id);
}
@Transactional
@Service
public class UserServiceImpl extends BaseServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Override
public List<User> getUsers() {
return userDao.getAll();
}
@Override
public User getUser(String id) {
return userDao.get(id);
}
@Override
public User saveUser(User user) {
return userDao.save(user);
}
@Override
public void removeUser(String id) {
userDao.remove(id);
}
}
其实可以看到,这些接口都有一定的普遍性,如果有多个功能模块,基本都是一些重复复制的工作,所以完全可以在完成一定阶段后,通过模板来生成这些代码,网上有非常多的反向工程工具,同时通过一些模板框架如freemaker、volacity来实现,那么只用保证代码的统一结构后,就大大减少了工作量。
关于web层其实就是一些congtroller了,没什么特别的:
@Controller @RequestMapping("/user") public class UserController { @Autowired private UserService userService; @ResponseBody @RequestMapping("/getUsers") public List<User> getUsers(){ return userService.getUsers(); } @ResponseBody @RequestMapping("/getUser") public User getUser(@RequestParam String id){ return userService.getUser(id); } @ResponseBody @RequestMapping("/saveUser") public User saveUser(User user){ return userService.saveUser(user); } @ResponseBody @RequestMapping("removeUser") public void removeUser(@RequestParam String id){ userService.removeUser(id); } }
web资源都在webapp中,如下:
将css、js以及 静态资源文件放在resources中,configs主要放各种扩展配置,pages就是页面了。
首先可以看下web.xml的配置:
<?xml version="1.0" encoding="UTF-8"?>
<!--
metadata-complete = true,则servlet相关的注解
-->
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1" metadata-complete="true">
<description>
springmvc hibernate app
</description>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>WEB-INF/configs/spring/applicationContext.xml</param-value>
</context-param>
<!-- 字符集过滤 -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!--Open Session in View 解决lazy时加载问题-->
<filter>
<filter-name>openSessionInViewFilter</filter-name>
<filter-class>org.springframework.orm.hibernate4.support.OpenSessionInViewFilter</filter-class>
<init-param>
<param-name>singleSession</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>openSessionInViewFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>WEB-INF/configs/spring/applicationContext-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
这里主要的配置就是spring的上下文已经web上下文。其次就是编码过滤以及hibernate Open Session in View问题,关键监听ContextLoaderListener,这个是springweb环境的重要处理环节。
最后要说的就是一些配置了,比如spring相关的,主要有两个,一个是spring上下文applicationContext.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 扫描指定包下的spring注解,完成bean的注册 -->
<context:component-scan base-package="com.sucl.shms">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- 加载属性文件 -->
<context:property-placeholder location="classpath*:configs/config-*.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<!-- 初始化连接大小 -->
<property name="initialSize" value="${jdbc.initialSize}"></property>
<!-- 连接池最大数量 -->
<property name="maxActive" value="${jdbc.maxActive}"></property>
<!-- 连接池最大空闲 -->
<property name="maxIdle" value="${jdbc.maxIdle}"></property>
<!-- 连接池最小空闲 -->
<property name="minIdle" value="${jdbc.minIdle}"></property>
<!-- 获取连接最大等待时间 -->
<property name="maxWait" value="${jdbc.maxWait}"></property>
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="${jdbc.timeBetweenEvictionRunsMillis}" />
<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="${jdbc.minEvictableIdleTimeMillis}" />
<property name="validationQuery" value="${jdbc.validationQuery}" />
<property name="testWhileIdle" value="${jdbc.testWhileIdle}" />
<property name="testOnBorrow" value="${jdbc.testOnBorrow}" />
<property name="testOnReturn" value="${jdbc.testOnReturn}" />
<property name="maxOpenPreparedStatements" value="${jdbc.maxOpenPreparedStatements}" />
<!-- 打开removeAbandoned功能 -->
<property name="removeAbandoned" value="${jdbc.removeAbandoned}" />
<!-- 1800秒,也就是30分钟 -->
<property name="removeAbandonedTimeout" value="${jdbc.removeAbandonedTimeout}" />
<!-- 关闭abanded连接时输出错误日志 -->
<property name="logAbandoned" value="${jdbc.logAbandoned}" />
</bean>
<!--配置session工厂-->
<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="com.sucl.shms.*.entity" />
<property name="hibernateProperties">
<props>
<!--<prop key="hibernate.current_session_context_class">org.springframework.orm.hibernate4.SpringSessionContext</prop> <!– SessionFactory.getCurrentSession() 需要指定jta | thread | managed | custom –>-->
<prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop> <!--hibernate根据实体自动生成数据库表-->
<prop key="hibernate.dialect">${hibernate.dialect}</prop> <!--指定数据库方言-->
<prop key="hibernate.show_sql">${hibernate.show_sql}</prop> <!--在控制台显示执行的数据库操作语句-->
<prop key="hibernate.format_sql">${hibernate.format_sql}</prop> <!--在控制台显示执行的数据哭操作语句(格式)-->
</props>
</property>
</bean>
<!-- 事物管理器配置 -->
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
</beans>
这里主要是添加包的扫描配置、属性文件加载、数据源、orm依赖已经事务。
而web环境由于内容较少,配置也相对简单:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.sucl.shms.*.web"/>
<mvc:annotation-driven/>
<mvc:default-servlet-handler/>
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="WEB-INF/pages/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
其余的无非就是一些页面资源了,这个与我们所选择的前端框架有关系,目前没有做前后分离,但是从Controller可以看出,用了@ResponseBody,因此我们返回的已经是数据流,而非页面了。
其实到这里大的方面也差不多了,那么还有一些点:
- 数据库操作的抽象
- 代码模板生成器的设计
- 页面与数据的控制
- 异常处理
- 前端界面的设计
- 具体细节的说明
- .....
还有很多都会在下一周全部完成。最后会形成一个比较完善可以直接使用的管理系统,当然只会包含系统模块了。
代码:https://github.com/suspring/springmvc-hibernate-ms 将会继续更新。
处理代码生成器没有生成,其他功能都有个轮廓,前端采用layui实现,没有做过多的包装。
代码生成器可以参考mybatis-plus的,代码已经引入,需要从源码分析如何从数据库中取出相关的表、字段、类型等信息。
其余功能目前不再完善,下一步将看看spring4.x通过注解方式构建web环境,。