SSM:
Spring-SpringMVC-MyBatis
项目结构:
Spring
SpringConfig
MyBatis
MybatisConfig
JdbcConfig
jdbc.properties
SpringMVC
SpringMvcConfig
ServletConfig
文件内容:
Spring
SpringConfig
@Configuration
@ComponentScan({"com.xxz.service"})
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class,MyBatisConfig.class})
@EnableTransactionManagement
public class SpringConfig {
}
MyBatis
JdbcConfig:
public class JdbcConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;@Bean
public DataSource dataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource){
DataSourceTransactionManager ds = new DataSourceTransactionManager();
ds.setDataSource(dataSource);
return ds;
}
}
MybatisConfig:
public class MyBatisConfig {
@Bean
public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource);
factoryBean.setTypeAliasesPackage("com.xxz.domain");
return factoryBean;
}@Bean
public MapperScannerConfigurer mapperScannerConfigurer(){
MapperScannerConfigurer msc = new MapperScannerConfigurer();
msc.setBasePackage("com.xxz.dao");
return msc;
}}
jdbc.properties:
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm_db
jdbc.username=root
jdbc.password=root
SpringMVC
SpringMvcConfig
@Configuration
@ComponentScan("com.xxz.controller")
@EnableWebMvc
public class SpringMvcConfig {
}
ServletConfig:web容器配置类
public class ServletConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
//两个配置类方法加载对应的bean,会造出两个容器对象,SpringMvcConfig的容器可以访问SpringConfig的容器,但SpringConfig的容器不能访问SpringMvcConfig的容器。不想太多配置文件的话,把SpringConfig整合到SpringMvcConfig,然后只定义getServletConfigClasses方法的SpringMvcConfig就行,getRootConfigClasses就空着。protected Class<?>[] getRootConfigClasses() {
return new Class[]{SpringConfig.class};
}protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}protected String[] getServletMappings() {
return new String[]{"/"};
}
}
然后就是写domain、dao、service和controller
service根据自己实际情况决定是否开启事务:
@Transactional public interface BookService { }
DAO层我自己记录一下写法:
public interface xxzDao {
// @Insert("insert into tbl_book values(null,#{type},#{name},#{description})")
@Insert("insert into tbl_book (type,name,description) values(#{type},#{name},#{description})")
public void save(Book book);@Update("update tbl_book set type = #{type}, name = #{name}, description = #{description} where id = #{id}")
public void update(Book book);@Delete("delete from tbl_book where id = #{id}")
public void delete(Integer id);@Select("select * from tbl_book where id = #{id}")
public Book getById(Integer id);@Select("select * from tbl_book")
public List<Book> getAll();
}
表现层数据封装
原来没有考虑格式的时候,格式样式多,前端不好统一处理,所以要统一格式。
设置统一数据返回结果类(可以自定义,也可以用其他框架的)
@Data
public class Result {
private Object data;
private Integer code;
private String msg;
}
封装数据到data属性中:
{ "data":true }
封装操作结果到code属性中:
{ "code": 20041, "data":null }
code编码的样式:
public class Code {
public static final Integer SAVE_OK = 20011;
public static final Integer DELETE_OK = 20021;
public static final Integer UPDATE_OK = 20031;
public static final Integer GET_OK = 20041;
public static final Integer SAVE_ERR = 20010;
}
封装特殊消息到message(msg)属性中:
{ "code": 20041, "data":null ,"msg": "数据查询失败,请重试!"}
代码案例:
@RequestMapping("/books")
public class BookController {
@Autowired
private BookService bookService;
@GetMapping("/{id}")
public Result getById(@PathVariable Integer id) {
Book book = bookService.getById(id);
Integer code = book != null ? Code.GET_OK : Code.GET_ERR;
String msg = book != null ? "" : "数据查询失败,请重试!";
return new Result(code,book,msg);
}
}
异常
项目异常分类和处理方案:
业务异常(BusinessException)
规范的用户行为产生的异常(发送对应消息传递给用户,提醒规范操作)
不规范的用户行为操作产生的异常
系统异常(SystemException)
项目运行过程中可预计且无法避免的异常(发送固定消息传递给用户,提醒运维,日志入库)
其他异常(Exception)
编程人员未预期到的异常(发送固定消息传递给用户,提醒开发人员,日志入库)
异常原因:
框架内部抛出的异常:因使用不合规导致
数据层抛出的异常:因外部服务器故障导致(例如:服务器访问超时)
业务层抛出的异常:因业务逻辑书写错误导致(例如:遍历业务书写操作,导致索引异常等)
表现层抛出的异常:因数据收集、校验等规则导致(例如:不匹配的数据类型间导致异常)
工具类抛出的异常:因工具类书写不严谨不够健壮导致(例如:必要释放的连接长期未释放等)
自定义异常
public class SystemException extends RuntimeException{
private Integer code;
public SystemException(Integer code,String message) {
super(message);
this.code = code;
}
public SystemException( Integer code,String message, Throwable cause) { super(message, cause);
this.code = code;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
}
给异常编码
public class Code {
public static final Integer SYSTEM_UNKNOW_ERROR = 50001;
public static final Integer SYSTEM_TIMEOUT_ERROR = 50002;
public static final Integer PROJECT_VALIDATE_ERROR = 60001;
public static final Integer PROJECT_BUSINESS_ERROR = 60002;
}
拦截异常
所有的异常均抛出到表现层进行处理。但全在表现层处理异常,每个方法中还要单独书写,代码书写量巨大且意义不强,所以这里引入异常处理器(AOP思想)
@RestControllerAdvice
//这个注解自带@ResponseBody注解与@Component注解
public class ProjectExceptionAdvice {
@ExceptionHandler(Exception.class)
public Result doException(Exception ex){
return new Result(666,null);
}
@ExceptionHandler(SystemException.class)
public Result doSystemException(SystemException ex){
// 记录日志(错误堆栈)
// 发送邮件给开发人员
// 发送短信给运维人员
return new Result(ex.getCode(),null,ex.getMessage());
}
}