')
$table. append ( $tr)
$tr. append ( ` ${ user. name} ` )
$tr. append ( ` ${ user. age} ` )
}
} )
} )
} )
script>
Layui
官网:http://layuimini.99php.cn/
文档:http://layuimini.99php.cn/docs/
演示:http://layuimini.99php.cn/onepage/v2/index.html
Github:https://github.com/zhongshaofa/layuimini/tree/v2-onepage
下载:https://codeload.github.com/zhongshaofa/layuimini/zip/refs/heads/v2-onepage
注意 :Layui中的相对路径都是相对于index.html来说的。
注意 :layuimini的表格要求服务器返回的JSON数据格式如下:
{
"code" : 0 ,
"data" : [
{
"id" : 1 ,
"name" : "职业" ,
"value" : "job" ,
"intro" : "一份工作"
} ,
{
"id" : 2 ,
"name" : "性格" ,
"value" : "character" ,
"intro" : "人的性格"
}
] ,
"count" : 87
}
MySQL建议
最大IP地址:255.255 .255 .255
CREATE TABLE user (
age TINYINT UNSIGNED ,
ip VARCHAR ( 15 )
)
CREATE TABLE user (
age TINYINT UNSIGNED ,
ip INT UNSIGNED
)
INSERT INTO user VALUES ( 10 , INET_ATON( '255.255.255.255' ) )
SELECT INET_NTOA( ip) FROM user
注意:
数据字典
使用PowerDesigner:
drop table if exists dict_item;
drop table if exists dict_type;
create table dict_item
(
id bigint not null ,
name varchar ( 20 ) not null ,
value varchar ( 20 ) not null ,
no int not null default 0 comment '用来排序,数字越小,优先级越高,越先展示' ,
type_id bigint comment '该条目所属的数据字典类型' ,
status int not null default 1 comment '是否启用该条目,0:不启用,1:启用' ,
primary key ( id) ,
unique key AK_UK_1 ( name, type_id) ,
unique key AK_UK_2 ( value , type_id)
) ;
alter table dict_item comment '数据字典每一项具体的内容' ;
create table dict_type
(
id bigint not null auto_increment ,
name varchar ( 20 ) not null comment '名称是展示在客户端的,是有可能会发生改变的' ,
value varchar ( 20 ) not null comment '值不会发生改变,编写SQL操作数据时,一般使用value而不是name' ,
intro varchar ( 100 ) comment '防止程序员忘记该数据字典类型的作用、功能(根据项目需求可有可无)' ,
primary key ( id) ,
unique key AK_UK_1 ( name) ,
unique key AK_UK_2 ( value )
) ;
alter table dict_type comment '数据字典类型' ;
alter table dict_item add constraint FK_Reference_1 foreign key ( type_id)
references dict_type ( id) on delete restrict on update restrict ;
使用IDEA自带的数据库工具+不使用外键+数据库使用最优字段类型:
create table jk. dict_type
(
id smallint unsigned auto_increment comment '主键'
primary key ,
name varchar ( 20 ) default '' not null comment '名称' ,
value varchar ( 20 ) default '' not null comment '值' ,
intro varchar ( 100 ) default '' not null comment '简介' ,
constraint dict_type_name_uindex
unique ( name) ,
constraint dict_type_value_uindex
unique ( value )
)
comment '数据字典类型' ;
create table jk. dict_item
(
id smallint unsigned auto_increment comment '主键'
primary key ,
name varchar ( 20 ) default '' not null comment '名称' ,
value varchar ( 20 ) default '' not null comment '值' ,
type_id smallint unsigned not null comment '类型id' ,
sn smallint unsigned default 0 not null comment '排序序号:默认为0,值越大,越优先排列展示' ,
enabled tinyint unsigned default 1 not null comment '是否启用:0,禁用;1,启用;默认为1' ,
constraint dict_item_name_type_id_uindex
unique ( name, type_id) ,
constraint dict_item_value_type_id_uindex
unique ( value , type_id)
)
comment '数据字典条目' ;
客户端:
服务端:
POJO—Query:
@Data
public class PageQuery {
private static final int MIN_SIZE = 1 ;
private static final int DEFAULT_SIZE = 10 ;
private long size;
private long page;
private List < ? > data;
private long total;
private long pages;
public long getSize ( ) {
return size < MIN_SIZE ? DEFAULT_SIZE : size;
}
public long getPage ( ) {
return page < MIN_SIZE ? MIN_SIZE : page;
}
}
@EqualsAndHashCode ( callSuper = true )
@Data
public class KeywordQuery extends PageQuery {
private String keyword;
}
@EqualsAndHashCode ( callSuper = true )
@Data
public class DictTypeQuery extends KeywordQuery {
}
ServiceImpl:
@Service
@Transactional
public class DictTypeServiceImpl extends ServiceImpl < DictTypeMapper , DictType > implements DictTypeService {
@Autowired
private DictTypeMapper mapper;
@Override
@Transactional ( readOnly = true )
public void list ( DictTypeQuery query) {
final String keyword = query. getKeyword ( ) ;
LambdaQueryWrapper < DictType > wrapper = new LambdaQueryWrapper < > ( ) ;
if ( ! StringUtils . isEmpty ( keyword) ) {
wrapper. like ( DictType :: getName , keyword) . or ( )
. like ( DictType :: getIntro , keyword) . or ( )
. like ( DictType :: getValue , keyword) ;
}
wrapper. orderByDesc ( DictType :: getId ) ;
Page < DictType > page = new Page < > ( query. getNo ( ) , query. getSize ( ) ) ;
mapper. selectPage ( page, wrapper) ;
query. setData ( page. getRecords ( ) ) ;
query. setPages ( page. getPages ( ) ) ;
query. setTotal ( page. getTotal ( ) ) ;
query. setSize ( getSize ( ) ) ;
query. setPage ( getCurrent ( ) ) ;
}
}
Controller:
@RestController
@RequestMapping ( "/dictTypes" )
public class DictTypeController {
@Autowired
private DictTypeService service;
@GetMapping
public Map < String , Object > list ( DictTypeQuery query) {
service. list ( query) ;
final Map < String , Object > map = new HashMap < > ( ) ;
map. put ( "msg" , "" ) ;
map. put ( "data" , query. getData ( ) ) ;
map. put ( "count" , query. getTotal ( ) ) ;
map. put ( "code" , 0 ) ;
return map;
}
@PostMapping ( "/remove" )
public Map < String , Object > remove ( String id) {
final String [ ] ids = id. split ( "," ) ;
final Map < String , Object > map = new HashMap < > ( ) ;
if ( service. removeByIds ( Arrays . asList ( ids) ) ) {
map. put ( "msg" , "删除成功" ) ;
map. put ( "code" , 0 ) ;
} else {
map. put ( "msg" , "删除失败" ) ;
map. put ( "code" , 1 ) ;
}
return map;
}
@PostMapping ( "/save" )
public Map < String , Object > save ( DictType dictType) {
final Map < String , Object > map = new HashMap < > ( ) ;
if ( service. saveOrUpdate ( dictType) ) {
map. put ( "msg" , "保存成功" ) ;
map. put ( "code" , 0 ) ;
} else {
map. put ( "msg" , "保存失败" ) ;
map. put ( "code" , 1 ) ;
}
return map;
}
}
封装MyBatis-Plus方便查询
以查询DictType(数据字典类型)为例
enhance—MPPage、MPQueryWrapper:
public class MPPage < T > extends Page < T > {
private final PageQuery query;
public MPPage ( PageQuery query) {
super ( query. getPage ( ) , query. getSize ( ) ) ;
this . query = query;
}
public void updateQuery ( ) {
query. setData ( getRecords ( ) ) ;
query. setPages ( getPages ( ) ) ;
query. setTotal ( getTotal ( ) ) ;
query. setSize ( getSize ( ) ) ;
query. setPage ( getCurrent ( ) ) ;
}
}
public class MPQueryWrapper < T > extends LambdaQueryWrapper < T > {
@SafeVarargs
public final MPQueryWrapper < T > like ( Object val, SFunction < T , ? > . . . funcs) {
if ( val == null || funcs == null || funcs. length == 0 ) return this ;
final String str = val. toString ( ) ;
if ( str. length ( ) == 0 ) return this ;
return ( MPQueryWrapper < T > ) nested ( ( wrapper) -> {
for ( SFunction < T , ? > func : funcs) {
wrapper. like ( func, str) . or ( ) ;
}
} ) ;
}
}
动态代理更新Query对象:
@Configuration
@EnableAspectJAutoProxy
public class SpringConfig {
}
@Aspect
@Component
public class PageMapperInterceptor {
@Around ( "execution(public com.baomidou.mybatisplus.core.metadata.IPage com.baomidou.mybatisplus.core.mapper.BaseMapper.selectPage(com.baomidou.mybatisplus.core.metadata.IPage, com.baomidou.mybatisplus.core.conditions.Wrapper))" )
public Object updateQuery ( ProceedingJoinPoint point) throws Throwable {
Object result = point. proceed ( ) ;
final Object [ ] args = point. getArgs ( ) ;
if ( args != null && args. length > 0 ) {
Object arg = args[ 0 ] ;
if ( arg instanceof MPPage ) {
( ( MPPage < ? > ) arg) . updateQuery ( ) ;
}
}
return result;
}
}
使用—ServiceImpl:
@Transactional
@Service
public class DictTypeServiceImpl extends ServiceImpl < DictTypeMapper , DictType > implements DictTypeService {
@Override
@Transactional ( readOnly = true )
public void list ( DictTypeQuery query) {
MPQueryWrapper < DictType > wrapper = new MPQueryWrapper < > ( ) ;
wrapper. like ( query. getKeyword ( ) , DictType :: getName , DictType :: getValue , DictType :: getIntro ) ;
wrapper. orderByDesc ( DictType :: getId ) ;
baseMapper. selectPage ( new MPPage < > ( query) , wrapper) ;
}
}
Controller:
@RestController
@RequestMapping ( "/dictTypes" )
public class DictTypeController {
@Autowired
private DictTypeService service;
@GetMapping
public Map < String , Object > list ( DictTypeQuery query) {
service. list ( query) ;
final Map < String , Object > map = new HashMap < > ( ) ;
map. put ( "msg" , "" ) ;
map. put ( "data" , query. getData ( ) ) ;
map. put ( "count" , query. getTotal ( ) ) ;
map. put ( "code" , 0 ) ;
return map;
}
}
封装给客户端的返回值
public class R extends HashMap < String , Object > {
public static final int CODE_SUCCESS = 0 ;
private static final String K_CODE = "code" ;
private static final String K_MSG = "msg" ;
private static final String K_DATA = "data" ;
public R setCode ( int code) {
return add ( K_CODE, code) ;
}
public R setMsg ( String msg) {
return add ( K_MSG, msg) ;
}
public R setData ( Object data) {
return add ( K_DATA, data) ;
}
public R add ( String key, Object data) {
put ( key, data) ;
return this ;
}
}
public final class Rs {
private Rs ( ) {
}
public static final int CODE_SUCCESS = 0 ;
public static final int CODE_ERROR_DEFAULT = 1 ;
private static R success ( ) {
return new R ( ) . setCode ( CODE_SUCCESS) ;
}
public static R success ( PageQuery query) {
return success ( ) . setData ( query. getData ( ) ) ;
}
public static R success ( String msg) {
return success ( ) . setMsg ( msg) ;
}
public static R success ( PageQuery query, String msg) {
return success ( ) . setData ( query. getData ( ) ) . setMsg ( msg) ;
}
public static R error ( ) {
return new R ( ) . setCode ( CODE_ERROR_DEFAULT) ;
}
public static R error ( String msg) {
return error ( ) . setMsg ( msg) ;
}
public static R error ( int code, String msg) {
return new R ( ) . setCode ( code) . setMsg ( msg) ;
}
public static R r ( boolean success) {
return new R ( ) . setCode ( success ? CODE_SUCCESS : CODE_ERROR_DEFAULT) ;
}
public static R r ( boolean success, String msg) {
return r ( success) . setMsg ( msg) ;
}
public static R r ( boolean success, Object data) {
return r ( success) . setData ( data) ;
}
}
Controller使用:
@RestController
@RequestMapping ( "/dictTypes" )
public class DictTypeController {
@Autowired
private DictTypeService service;
@GetMapping
public R list ( DictTypeQuery query) {
service. list ( query) ;
return Rs . success ( query) . add ( "count" , query. getTotal ( ) ) ;
}
@PostMapping ( "/remove" )
public R remove ( String id) {
final String [ ] ids = id. split ( "," ) ;
final boolean success = service. removeByIds ( Arrays . asList ( ids) ) ;
final String msg = success ? "删除成功" : "删除失败" ;
return Rs . r ( success, msg) ;
}
@PostMapping ( "/save" )
public R save ( DictType dictType) {
if ( ! service. saveOrUpdate ( dictType) ) {
throw new RuntimeException ( "保存失败" ) ;
}
return Rs . success ( "保存成功" ) ;
}
}
统一异常处理+HTTP响应状态码
如果服务器端操作失败的话,比如删除失败、保存失败,那么给客户端返回的StatusCode就不应该是200,应该是400/500,原因如下:
客户端(前端)极大可能是根据HTTP请求的响应状态码来判断某个请求是否成功的,而不是通过服务器返回的JSON数据的某个属性值来判断
比如AJAX的回调方法默认就是通过HTTP的响应状态码来判断是否请求成功的。
因此如果服务器处理数据失败,应该修改响应状态码200(OK)为其它StatusCode,比如400、500。
public interface JSONable {
default String jsonString ( ) throws Exception {
return JSONs . getMAPPER ( ) . writeValueAsString ( this ) ;
}
}
public class R extends HashMap < String , Object > implements JSONable {
}
@ControllerAdvice
public class ExceptionInterceptor {
@ExceptionHandler ( Throwable . class )
public void exceptionHandlerOther ( Throwable throwable, HttpServletResponse response) throws Exception {
response. setCharacterEncoding ( "UTF-8" ) ;
response. setStatus ( 400 ) ;
response. getWriter ( ) . write ( Rs . error ( throwable. getMessage ( ) ) . jsonString ( ) ) ;
}
private Throwable getRealCause ( Throwable throwable) {
Throwable cause = throwable. getCause ( ) ;
while ( cause != null ) {
throwable = cause;
cause = cause. getCause ( ) ;
}
return throwable;
}
}
@RestController
@RequestMapping ( "/dictTypes" )
public class DictTypeController {
@Autowired
private DictTypeService service;
@PostMapping ( "/remove" )
public R remove ( String id) {
final String [ ] ids = id. split ( "," ) ;
if ( ! service. removeByIds ( Arrays . asList ( ids) ) ) {
throw new RuntimeException ( "删除失败" ) ;
}
return Rs . success ( "删除成功" ) ;
}
}
还可以继续封装:
public enum CodeMsg {
BAD_REQUEST ( 400 , "请求出错" ) ,
UNAUTHORIZED ( 401 , "未授权" ) ,
FORBIDDEN ( 403 , "禁止访问" ) ,
NOT_FOUND ( 404 , "资源不存在" ) ,
INTERNAL_SERVER_ERROR ( 500 , "服务器内部错误" ) ,
OPERATE_OK ( R . CODE_SUCCESS, "操作成功" ) ,
SAVE_OK ( R . CODE_SUCCESS, "保存成功" ) ,
REMOVE_OK ( R . CODE_SUCCESS, "删除成功" ) ,
OPERATE_ERROR ( 40001 , "操作失败" ) ,
SAVE_ERROR ( 40002 , "保存失败" ) ,
REMOVE_ERROR ( 40003 , "删除失败" ) ,
UPLOAD_IMG_ERROR ( 40004 , "图片上传失败" ) ,
WRONG_USERNAME ( 50001 , "用户名不存在" ) ,
WRONG_PASSWORD ( 50002 , "密码错误" ) ,
USER_LOCKED ( 50003 , "用户被锁定,无法正常登录" ) ,
WRONG_CAPTCHA ( 50004 , "验证码错误" ) ,
NO_TOKEN ( 60001 , "没有Token,请登录" ) ,
TOKEN_EXPIRED ( 60002 , "Token过期,请重新登录" ) ,
NO_PERMISSION ( 60003 , "没有相关的操作权限" ) ;
private final int code;
private final String msg;
CodeMsg ( int code, String msg) {
this . code = code;
this . msg = msg;
}
public int getCode ( ) {
return code;
}
public String getMsg ( ) {
return msg;
}
}
@EqualsAndHashCode ( callSuper = true )
@Data
public class CommonException extends RuntimeException {
private int code;
public CommonException ( ) {
this ( CodeMsg . BAD_REQUEST. getCode ( ) , null ) ;
}
public CommonException ( String msg) {
this ( msg, null ) ;
}
public CommonException ( int code, String msg) {
this ( code, msg, null ) ;
}
public CommonException ( String msg, Throwable cause) {
this ( CodeMsg . BAD_REQUEST. getCode ( ) , msg, cause) ;
}
public CommonException ( int code, String msg, Throwable cause) {
super ( msg, cause) ;
this . code = code;
}
public CommonException ( CodeMsg codeMsg) {
this ( codeMsg, null ) ;
}
public CommonException ( CodeMsg codeMsg, Throwable cause) {
this ( codeMsg. getCode ( ) , codeMsg. getMsg ( ) , cause) ;
}
public int getCode ( ) {
return code;
}
}
public final class Rs {
private Rs ( ) {
}
public static final String K_COUNT = "count" ;
private static final int CODE_SUCCESS = 0 ;
private static final int CODE_ERROR_DEFAULT = CodeMsg . BAD_REQUEST. getCode ( ) ;
private static R success ( ) {
return new R ( ) . setCode ( CODE_SUCCESS) ;
}
public static R success ( PageQuery query) {
return success ( ) . setData ( query. getData ( ) ) ;
}
public static R success ( String msg) {
return success ( ) . setMsg ( msg) ;
}
public static R success ( Object data) {
return success ( ) . setData ( data) ;
}
public static R success ( PageQuery query, String msg) {
return success ( ) . setData ( query. getData ( ) ) . setMsg ( msg) ;
}
public static R success ( CodeMsg codeMsg) {
return success ( ) . setMsg ( codeMsg. getMsg ( ) ) ;
}
public static R error ( ) {
return error ( CODE_ERROR_DEFAULT) ;
}
public static R error ( int code) {
return new R ( ) . setCode ( code) ;
}
public static R error ( String msg) {
return error ( ) . setMsg ( msg) ;
}
public static R error ( int code, String msg) {
return error ( code) . setMsg ( msg) ;
}
public static R error ( Throwable e) {
R r = error ( ) ;
if ( e instanceof CommonException ) {
r. setCode ( ( ( CommonException ) e) . getCode ( ) ) ;
}
return r;
}
public static R r ( boolean success) {
return new R ( ) . setCode ( success ? CODE_SUCCESS : CODE_ERROR_DEFAULT) ;
}
public static R r ( boolean success, String msg) {
return r ( success) . setMsg ( msg) ;
}
public static R r ( boolean success, Object data) {
return r ( success) . setData ( data) ;
}
public static R exception ( String msg) {
throw new CommonException ( msg) ;
}
public static R exception ( CodeMsg codeMsg) {
throw new CommonException ( codeMsg) ;
}
}
@ControllerAdvice
public class ExceptionInterceptor {
@ExceptionHandler ( Throwable . class )
public void handle ( Throwable throwable,
HttpServletResponse response) throws Exception {
response. setContentType ( MediaType . APPLICATION_JSON_VALUE) ;
response. setCharacterEncoding ( StandardCharsets . UTF_8. displayName ( ) ) ;
response. setStatus ( 400 ) ;
response. getWriter ( ) . write ( Rs . error ( throwable) . json ( ) ) ;
}
private Throwable getRealCause ( Throwable throwable) {
Throwable cause = throwable. getCause ( ) ;
while ( cause != null ) {
throwable = cause;
cause = cause. getCause ( ) ;
}
return throwable;
}
}
其实异常处理器还有更简便的写法:
@RestControllerAdvice
@Slf4j
public class CommonExceptionHandler {
@ExceptionHandler ( Throwable . class )
@ResponseStatus ( code = HttpStatus . BAD_REQUEST)
public JSONResult handle ( Throwable throwable) {
log. error ( "error" , throwable) ;
return JSONResults . exception ( throwable) ;
}
}
Controller使用:
@RestController
@RequestMapping ( "/dictTypes" )
public class DictTypeController {
@Autowired
private DictTypeService service;
@GetMapping
public R list ( DictTypeQuery query) {
service. list ( query) ;
return Rs . success ( query) . add ( Rs . K_COUNT, query. getTotal ( ) ) ;
}
@PostMapping ( "/remove" )
public R remove ( String id) {
final String [ ] ids = id. split ( "," ) ;
if ( ! service. removeByIds ( Arrays . asList ( ids) ) ) {
throw new CommonException ( CodeMsg . REMOVE_ERROR) ;
}
return Rs . success ( CodeMsg . REMOVE_OK) ;
}
@PostMapping ( "/save" )
public R save ( DictType dictType) {
if ( ! service. saveOrUpdate ( dictType) ) {
throw new CommonException ( CodeMsg . SAVE_ERROR) ;
}
return Rs . success ( CodeMsg . SAVE_OK. getMsg ( ) ) ;
}
}
还可以将Controller中的公共代码抽取出来:
public abstract class BaseController < T > {
protected abstract IService < T > service ( ) ;
@GetMapping ( "/list" )
public R list ( ) {
return Rs . success ( service ( ) . list ( ) ) ;
}
@PostMapping ( "/remove" )
public R remove ( String id) {
final String [ ] ids = id. split ( "," ) ;
if ( ! service ( ) . removeByIds ( Arrays . asList ( ids) ) ) {
Rs . exception ( CodeMsg . REMOVE_ERROR) ;
}
return Rs . success ( CodeMsg . REMOVE_OK) ;
}
@PostMapping ( "/save" )
public R save ( T entity) {
if ( ! service ( ) . saveOrUpdate ( entity) ) {
Rs . exception ( CodeMsg . SAVE_ERROR) ;
}
return Rs . success ( CodeMsg . SAVE_OK) ;
}
}
@RestController
@RequestMapping ( "/dictTypes" )
public class DictTypeController extends BaseController < DictType > {
@Autowired
private DictTypeService service;
@GetMapping
public R list ( DictTypeQuery query) {
service. list ( query) ;
return Rs . success ( query) . add ( Rs . K_COUNT, query. getTotal ( ) ) ;
}
@Override
protected IService < DictType > service ( ) {
return service;
}
}
统一异常处理—配合Shiro
@RestControllerAdvice
只能拦截到Controller抛出的异常
public class ErrorFilter implements Filter {
public static final String ERROR_URI = "/handleError" ;
@Override
public void doFilter ( ServletRequest request, ServletResponse response, FilterChain chain) throws IOException , ServletException {
try {
chain. doFilter ( request, response) ;
} catch ( Exception e) {
request. setAttribute ( ERROR_URI, e) ;
request. getRequestDispatcher ( ERROR_URI) . forward ( request, response) ;
}
}
}
@Configuration
public class SpringMVCConfig implements WebMvcConfigurer {
@Bean
public FilterRegistrationBean < Filter > filterRegistrationBean ( ) {
FilterRegistrationBean < Filter > bean = new FilterRegistrationBean < > ( ) ;
bean. setFilter ( new ErrorFilter ( ) ) ;
bean. addUrlPatterns ( "/*" ) ;
bean. setOrder ( Ordered . HIGHEST_PRECEDENCE) ;
return bean;
}
}
@RestController
public class ErrorController {
@RequestMapping ( ErrorFilter . ERROR_URI)
public void handle ( HttpServletRequest request) throws Exception {
throw ( Exception ) request. getAttribute ( ErrorFilter . ERROR_URI) ;
}
}
@RestControllerAdvice
@Slf4j
public class CommonExceptionHandler {
@ExceptionHandler ( Throwable . class )
@ResponseStatus ( code = HttpStatus . BAD_REQUEST)
public JSONResult handle ( Throwable t) {
log. error ( "handle" , t) ;
if ( t instanceof CommonException ) {
return handle ( ( CommonException ) t) ;
} else if ( t instanceof BindException ) {
return handle ( ( BindException ) t) ;
} else if ( t instanceof ConstraintViolationException ) {
return handle ( ( ConstraintViolationException ) t) ;
} else if ( t instanceof AuthorizationException ) {
return JSONResults . error ( CodeMsg . NO_PERMISSION) ;
}
Throwable cause = t. getCause ( ) ;
if ( cause != null ) {
return handle ( cause) ;
}
return JSONResults . error ( ) ;
}
private JSONResult handle ( CommonException ce) {
return JSONResults . error ( ce. getCode ( ) , ce. getMessage ( ) ) ;
}
private JSONResult handle ( BindException be) {
List < ObjectError > errors = be. getBindingResult ( ) . getAllErrors ( ) ;
List < String > defaultMsgs = Streams . map ( errors, ObjectError :: getDefaultMessage ) ;
String msg = StringUtils . collectionToDelimitedString ( defaultMsgs, ", " ) ;
return JSONResults . error ( msg) ;
}
private JSONResult handle ( ConstraintViolationException cve) {
List < String > msgs = Streams . map ( cve. getConstraintViolations ( ) , ConstraintViolation :: getMessage ) ;
String msg = StringUtils . collectionToDelimitedString ( msgs, ", " ) ;
return JSONResults . error ( msg) ;
}
}
@Configuration
public class ShiroConfig {
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean ( Realm realm) {
ShiroFilterFactoryBean filterBean = new ShiroFilterFactoryBean ( ) ;
filterBean. setSecurityManager ( new DefaultWebSecurityManager ( realm) ) ;
Map < String , Filter > filters = new HashMap < > ( ) ;
filters. put ( "token" , new TokenFilter ( ) ) ;
filterBean. setFilters ( filters) ;
Map < String , String > urlMap = new LinkedHashMap < > ( ) ;
urlMap. put ( ErrorFilter . ERROR_URI, "anon" ) ;
urlMap. put ( "/**" , "token" ) ;
filterBean. setFilterChainDefinitionMap ( urlMap) ;
return filterBean;
}
}
数据的一致性
外键有优点,同样,也有它的缺点
如果在项目中使用了外键,那么表与表之间的数据一致性其实是不用我们操心的,因为有外键自动帮我们约束。
因此对于那些小型的、对于数据一致性要求很高的项目,需要使用外键。
但是大型的、分布式的互联网项目出于对数据库的性能、备份、迁移、维护等原因的考虑,一般而言在设计数据库时是不使用外键的,那么这时表与表之间的联系,也就是数据一致性问题怎么解决呢?
答案就是需要我们自己在应用层做好相应的处理,但做好这个处理并不简单。
假设现在要对某张表的进行数据一致性的处理,有许多非常麻烦的点
虽然数据库没有使用外键,但对业务来讲,表与表之间应该是有联系的
那么对这张表进行删除、更新、添加等操作都需要考虑数据一致性,具体到代码,就是remove、save、update这种方法有很多,需要都考虑到
有可能会有很多表在数据上都关联这张表,因此我们需要清楚每一张表与每一张表之间的关联关系,知道了表与表之间的关联关系后,才能去逐个处理
解决方案:自己写一个保证数据一致性的小框架,这个小框架的特点:
MJ老师编写框架经验:
如果你自己想写一个比较好用的框架
首先应该从应用的角度出发,先从使用者(自己、其他开发者)的角度出发
考虑别人应该怎么用这个框架、这个框架能够怎样简化开发、这个框架怎么样能够使开发变得更爽、更高效、更敏捷
然后再考虑减少BUG
然后再考虑安全问题
然后再考虑性能问题
然后再考虑解耦、抽取、可扩展…
拼音库—tinypinyin的使用
< dependency>
< groupId> com.github.promeg groupId>
< artifactId> tinypinyin artifactId>
< version> 2.0.3 version>
dependency>
@Override
public boolean updateById ( PlateRegion entity) {
processPinyin ( entity) ;
return super . updateById ( entity) ;
}
@Override
public boolean save ( PlateRegion entity) {
processPinyin ( entity) ;
return super . save ( entity) ;
}
private void processPinyin ( PlateRegion entity) {
final String name = entity. getName ( ) ;
if ( StringUtils . isEmpty ( name) ) return ;
entity. setPinyin ( Pinyin . toPinyin ( name, "_" ) ) ;
}
MapStruct
作用:对象转换
< dependency>
< groupId> org.mapstruct groupId>
< artifactId> mapstruct artifactId>
< version> ${map.struct.version} version>
< scope> provided scope>
dependency>
< dependency>
< groupId> org.mapstruct groupId>
< artifactId> mapstruct-processor artifactId>
< version> ${map.struct.version} version>
< scope> provided scope>
dependency>
@Mapper
public interface MapStruct {
MapStruct INSTANCE = Mappers . getMapper ( MapStruct . class ) ;
DictItem vo2po ( ReqSaveDictItem reqSaveVo) ;
DictType vo2po ( ReqSaveDictType reqSaveVo) ;
ExamPlace vo2po ( ReqSaveExamPlace reqSaveVo) ;
ExamPlaceCourse vo2po ( ReqSaveExamPlaceCourse reqSaveVo) ;
PlateRegion vo2po ( ReqSavePlateRegion reqSaveVo) ;
RespDictItem po2vo ( DictItem po) ;
RespDictType po2vo ( DictType po) ;
RespExamPlace po2vo ( ExamPlace po) ;
RespExamPlaceCourse po2vo ( ExamPlaceCourse po) ;
RespPlateRegion po2vo ( PlateRegion po) ;
}
基本使用:
final DictItem dictItem = MapStruct . INSTANCE. vo2po ( new ReqSaveDictItem ( ) ) ;
final RespDictItem respDictItem = MapStruct . INSTANCE. po2vo ( new DictItem ( ) ) ;
项目中使用:
public abstract class BaseController < T , ReqSave > {
protected abstract Function < ReqSave , T > function ( ) ;
@PostMapping ( "/save" )
public JSONResult save ( @Valid ReqSave entity) {
service. saveOrUpdate ( function ( ) . apply ( entity) ) ;
}
}
public class DictItemController extends BaseController < DictItem , ReqSaveDictItem > {
@Override
protected Function < ReqSaveDictItem , DictItem > function ( ) {
return MapStruct . INSTANCE:: vo2po ;
}
}
public class PlateRegionServiceImpl extends ServiceImpl < PlateRegionMapper , PlateRegion > implements PlateRegionService {
public JSONDataResult < List < RespPlateRegion > > listProvinces ( ) {
final List < RespPlateRegion > data = baseMapper. selectList ( wrapper)
. stream ( ) . map ( MapStruct . INSTANCE:: po2vo )
. collect ( Collectors . toList ( ) ) ;
return JSONResults . success ( data) ;
}
}
自定义转换规则 :
public class MapStructFormatter {
@Qualifier
@Target ( ElementType . METHOD)
@Retention ( RetentionPolicy . CLASS)
public @interface Date2Millis { }
@Date2Millis
public static Long date2millis ( Date date) {
if ( date == null ) return null ;
return date. getTime ( ) ;
}
}
@Data
public class SysUser {
private Date loginTime;
}
@Data
@ApiModel ( "系统用户" )
public class RespSysUser {
@ApiModelProperty ( "最后一次登录的时间" )
private Long loginTime;
}
@Mapper ( uses = {
MapStructFormatter . class
} )
public interface MapStructs {
@Mapping ( source = "loginTime" ,
target = "loginTime" ,
qualifiedBy = MapStructFormatter. Date2Millis . class )
RespSysUser po2vo ( SysUser po) ;
}
登录—简单登录
< dependency>
< groupId> com.github.whvcse groupId>
< artifactId> easy-captcha artifactId>
< version> 1.6.2 version>
dependency>
@RestController
@RequestMapping ( "/sysUsers" )
@Api ( tags = "系统用户" , description = "SysUser" )
public class SysUserController extends BaseController < SysUser , ReqSaveSysUser > {
@Autowired
private SysUserService service;
@GetMapping ( "/captcha" )
@ApiOperation ( "生成验证码" )
public void captcha ( HttpServletRequest request,
HttpServletResponse response) throws Exception {
CaptchaUtil . out ( request, response) ;
}
@PostMapping ( "/login" )
@ApiOperation ( "登录" )
public JSONDataResult < RespLogin > login ( ReqLogin reqVo, HttpServletRequest request) {
if ( CaptchaUtil . ver ( reqVo. getCaptcha ( ) , request) ) {
return JSONResults . success ( service. login ( reqVo) ) ;
}
JSONResults . exception ( CodeMsg . WRONG_CAPTCHA) ;
return null ;
}
}
@Override
public RespLogin login ( ReqLogin reqVo) {
MPLambdaQueryWrapper < SysUser > wrapper = new MPLambdaQueryWrapper < > ( ) ;
wrapper. eq ( SysUser :: getUsername , reqVo. getUsername ( ) ) ;
SysUser po = baseMapper. selectOne ( wrapper) ;
if ( po == null ) {
return JsonVos . raise ( CodeMsg . WRONG_USERNAME) ;
}
if ( ! po. getPassword ( ) . equals ( reqVo. getPassword ( ) ) ) {
return JsonVos . raise ( CodeMsg . WRONG_PASSWORD) ;
}
if ( po. getStatus ( ) == Constants. SysUserStatus . LOCKED) {
return JsonVos . raise ( CodeMsg . USER_LOCKED) ;
}
po. setLoginTime ( new Date ( ) ) ;
baseMapper. updateById ( po) ;
RespLogin vo = MapStruct . INSTANCE. po2loginVo ( po) ;
return vo;
}
前端Ajax登录:
Ajaxs. loadPost ( {
uri : 'sysUsers/login' ,
data : data. field,
success : ( response ) => {
location. href = '../index.html'
} ,
xhrFields : {
withCredentials : true
}
}
登录—Token
后端
登录
@Data
@ApiModel ( "登录成功的结果" )
public class RespLogin {
@ApiModelProperty ( "登录令牌" )
private String token;
}
@PostMapping ( "/login" )
@ApiOperation ( "登录" )
public JSONDataResult < RespLogin > login ( ReqLogin reqVo, HttpServletRequest request) {
if ( CaptchaUtil . ver ( reqVo. getCaptcha ( ) , request) ) {
return JSONResults . success ( service. login ( reqVo) ) ;
}
JSONResults . exception ( CodeMsg . WRONG_CAPTCHA) ;
return null ;
}
@Override
public RespLogin login ( ReqLogin reqVo) {
MPLambdaQueryWrapper < SysUser > wrapper = new MPLambdaQueryWrapper < > ( ) ;
wrapper. eq ( SysUser :: getUsername , reqVo. getUsername ( ) ) ;
SysUser po = baseMapper. selectOne ( wrapper) ;
if ( po == null ) {
return JsonVos . raise ( CodeMsg . WRONG_USERNAME) ;
}
if ( ! po. getPassword ( ) . equals ( reqVo. getPassword ( ) ) ) {
return JsonVos . raise ( CodeMsg . WRONG_PASSWORD) ;
}
if ( po. getStatus ( ) == Constants. SysUserStatus . LOCKED) {
return JsonVos . raise ( CodeMsg . USER_LOCKED) ;
}
po. setLoginTime ( new Date ( ) ) ;
baseMapper. updateById ( po) ;
String token = UUID. randomUUID ( ) . toString ( ) ;
Caches . putToken ( token, po) ;
RespLogin vo = MapStruct . INSTANCE. po2loginVo ( po) ;
vo. setToken ( token) ;
return vo;
}
退出登录
@PostMapping ( "/logout" )
@ApiOperation ( "退出登录" )
public JSONResult logout ( @RequestHeader ( "Token" ) String token) {
Caches . removeToken ( token) ;
return JSONResults . success ( CodeMsg . LOGOUT_OK) ;
}
前端
登录:
class DataKey {
static USER = 'user'
static TOKEN = 'token'
static TOKEN_HEADER = 'Token'
}
Ajaxs. loadPost ( {
uri : 'sysUsers/login' ,
data : data. field,
success : ( response ) => {
Datas. save ( DataKey. USER , response. data)
location. href = '../index.html'
} ,
xhrFields : {
withCredentials : true
}
} )
需要确保登录后的每次请求都带上Token信息
static get ( ) {
let ret = layui. data ( this . TABLE )
for ( let i = 0 ; i < arguments. length; i++ ) {
if ( ! ret) return null
ret = ret[ arguments[ i] ]
}
return ret
}
static _addTokenHeader ( cfg ) {
const token = Datas. get ( DataKey. USER , DataKey. TOKEN )
if ( token) {
if ( ! cfg. headers) {
cfg. headers = { }
}
cfg. headers[ DataKey. TOKEN_HEADER ] = token
}
}
static ajax ( cfg ) {
cfg. url = Commons. url ( cfg. uri)
Commons. _addTokenHeader ( cfg)
$. ajax ( cfg)
}
_init ( ) {
const cfg = this . _commonCfg ( )
cfg. url = Commons. url ( this . _cfg. uri)
$. extend ( cfg, this . _cfg)
cfg. elem = cfg. selector
Commons. _addTokenHeader ( cfg)
this . _innerTable = this . _layuiTable ( ) . render ( cfg)
this . _cfg = cfg
}
登出:
$ ( '.login-out' ) . click ( ( ) => {
Ajaxs. loadPost ( {
uri : 'sysUsers/logout' ,
success : ( ) => {
Datas. remove ( DataKey. USER )
Layers. msgSuccess ( '退出登录成功' , ( ) => {
location. href = 'page/login.html'
} )
}
} )
} )
权限管理—RBAC
可以登录后台管理系统的员工/系统管理员:比如:sys_user
(表名以sys_
开头)
使用产品的用户/客户(APP、小程序、网页):比如:user
表结构设计 :
可以登录后台管理系统的员工/系统管理员:比如:sys_user(表名以sys_开头)
使用产品的用户/客户(APP、小程序、网页):比如:user
create table if not exists jk. sys_resource
(
id tinyint unsigned auto_increment comment '主键'
primary key ,
name varchar ( 15 ) default '' not null comment '名称' ,
uri varchar ( 100 ) default '' not null comment '链接地址' ,
permission varchar ( 100 ) default '' not null comment '权限标识' ,
type tinyint unsigned default 0 not null comment '资源类型(0是目录,1是菜单,2是按钮)PS:按钮就是增删改查之类的能点击的' ,
icon varchar ( 100 ) default '' not null comment '图标' ,
sn tinyint unsigned default 0 not null comment '序号' ,
parent_id tinyint unsigned default 0 not null comment '父资源id' ,
constraint sys_resource_parent_id_name_uindex
unique ( parent_id, name)
)
comment '资源' ;
create table if not exists jk. sys_role
(
id tinyint unsigned auto_increment comment '主键'
primary key ,
name varchar ( 15 ) default '' not null comment '角色名称' ,
constraint sys_role_name_uindex
unique ( name)
)
comment '角色' ;
create table if not exists jk. sys_role_resource
(
role_id tinyint unsigned default 0 not null comment '角色id' ,
resource_id tinyint unsigned default 0 not null comment '资源id' ,
primary key ( resource_id, role_id)
)
comment '角色-资源' ;
create table if not exists jk. sys_user
(
id smallint unsigned auto_increment comment '主键'
primary key ,
nickname varchar ( 15 ) default '' not null comment '昵称' ,
username varchar ( 15 ) default '' not null comment '登录用的用户名' ,
password char ( 32 ) default '' not null comment '登录用的密码,密码经过MD5加密之后就是32位的字符串' ,
create_time datetime default CURRENT_TIMESTAMP not null comment '创建的时间' ,
login_time datetime null comment '最后一次登录的时间' ,
status tinyint unsigned default 0 not null comment '账号的状态,0是正常,1是锁定' ,
constraint sys_user_username_uindex
unique ( username)
)
comment '用户(可以登录后台系统的)' ;
create table if not exists jk. sys_user_role
(
role_id tinyint unsigned default 0 not null comment '角色id' ,
user_id smallint unsigned default 0 not null comment '用户id' ,
primary key ( user_id, role_id)
)
comment '用户-角色' ;
逻辑删除
逻辑删除—MyBatisPlus
create table user
(
id int unsigned auto_increment
primary key ,
name varchar ( 15 ) default '' not null ,
deleted tinyint unsigned default 0 not null comment '1是被删除,0是未删除' ,
constraint user_name_uindex
unique ( name)
) ;
企业级文件上传
@RequestBody修饰的请求参数
前端/客户端将Content-Type改为:application/json
请求体传递符合要求的JSON字符串【JSON优势:灵活、前端处理方便、第三方库也多】
@PostMapping ( "/save1" )
@ApiOperation ( "添加或更新" )
public JsonVo save1 ( User user) {
JsonVo jsonVo = new JsonVo ( ) ;
jsonVo. setMsg ( service. saveOrUpdate ( user) ? "保存成功" : "保存失败" ) ;
return jsonVo;
}
@PostMapping ( "/save2" )
@ApiOperation ( "添加或更新" )
public JsonVo save2 ( @RequestBody User user) {
JsonVo jsonVo = new JsonVo ( ) ;
jsonVo. setMsg ( service. saveOrUpdate ( user) ? "保存成功" : "保存失败" ) ;
return jsonVo;
}
单元测试
Spring单元测试
SpringBoot单元测试
< dependency>
< groupId> org.springframework.boot groupId>
< artifactId> spring-boot-starter-test artifactId>
< scope> test scope>
dependency>
@RunWith ( SpringRunner . class )
@SpringBootTest
public class SpringAMQPTest {
@Autowired
private RabbitTemplate rabbitTemplate;
@Test
public void testSendSimpleQueue ( ) {
String queueName = "simple.queue" ;
Object message = "Hello, SpringAMQP! I am LP!" ;
rabbitTemplate. convertAndSend ( queueName, message) ;
}
}
打包部署
打包部署—jar
打包部署—war
注意
配置JackSON将Model转为JSON时,不包含值为null的属性:
application.yml:
spring :
jackson :
default-property-inclusion : non_null
Java代码:
public final class JSONs {
private JSONs ( ) {
}
private static final ObjectMapper MAPPER = new ObjectMapper ( ) ;
static {
MAPPER. setDefaultPropertyInclusion ( JsonInclude. Include . NON_NULL) ;
}
public static ObjectMapper getMAPPER ( ) {
return MAPPER;
}
}
MySQL配置:
#url=jdbc:mysql://127.0.0.1:3306/lp_resume
#url=jdbc:mysql://localhost:3306/lp_resume
#url=jdbc:mysql:///lp_resume
#UTC:世界同一时间
#url=jdbc:mysql:///lp_resume?serverTimezone=UTC&useSSL=false
#中国时间:serverTimezone=Asia/Shanghai == serverTimezone=GMT+8
#url=jdbc:mysql:///lp_resume?serverTimezone=GMT+8&useSSL=false
url=jdbc:mysql:///lp_resume?serverTimezone=Asia/Shanghai&useSSL=false
############使用IDEA连接数据库############
#使用IDEA连接MySQL数据库时,由于“+”是一个特殊字符,因此需要编码处理为:“%2B”
#例如:jdbc:mysql:///?serverTimezone=GMT%2B8&useSSL=false
#或者:jdbc:mysql:///?serverTimezone=Asia/Shanghai&useSSL=false
HTML的button标签,默认类型是
,因此button如果是其它类型的话,最好显示声明button的type,比如:
客户端向服务器发送请求参数时
如果http://localhost:8080/jk/dictTypes/list?page=1&size=20
,那么服务器获取到的keyword
就是null
如果http://localhost:8080/jk/dictTypes/list?page=1&size=20&keyword=
,那么服务器获取到的keyword
就是""(空字符串)
数据库中,表名和字段名建议使用``、字符串建议使用’'(单引号)
MySQL数据库,行(记录)从0开始,列(字段)从1开始
标准JSON格式:key使用""(双引号):
[
{
"age" : 10 ,
"name" : "lp"
} ,
{
"age" : 20 ,
"name" : "ruoyu"
}
]
{
"string" : "value" ,
"integer" : 10 ,
"bool" : true ,
"null" : null ,
"array" : [ ] ,
"obj" : { }
}
补充:ChromeJSON插件
https://chrome.google.com/webstore/detail/json-handle/iahnhfdhidomcpggpaimmmahffihkfnj
参考
小码哥-李明杰: Java从0到架构师③进阶互联网架构师.
本文完,感谢您的关注支持!
你可能感兴趣的:(JAVA,java,spring,boot,MyBatisPlus,Shiro)
移动端城市区县二级联动选择功能实现包
good2know
本文还有配套的精品资源,点击获取简介:本项目是一套为移动端设计的jQuery实现方案,用于简化用户在选择城市和区县时的流程。它包括所有必需文件:HTML、JavaScript、CSS及图片资源。通过动态更新下拉菜单选项,实现城市到区县的联动效果,支持数据异步加载。开发者可以轻松集成此功能到移动网站或应用,并可基于需求进行扩展和优化。1.jQuery移动端解决方案概述jQuery技术简介jQuery
SpringMVC执行流程(原理),通俗易懂
国服冰
SpringMVC spring mvc
SpringMVC执行流程(原理),通俗易懂一、图解SpringMVC流程二、进一步理解Springmvc的执行流程1、导入依赖2、建立展示的视图3、web.xml4、spring配置文件springmvc-servlet5、Controller6、tomcat配置7、访问的url8、视图页面一、图解SpringMVC流程图为SpringMVC的一个较完整的流程图,实线表示SpringMVC框架提
深入解析JVM工作原理:从字节码到机器指令的全过程
一、JVM概述Java虚拟机(JVM)是Java平台的核心组件,它实现了Java"一次编写,到处运行"的理念。JVM是一个抽象的计算机器,它有自己的指令集和运行时内存管理机制。JVM的主要职责:加载:读取.class文件并验证其正确性存储:管理内存分配和垃圾回收执行:解释或编译字节码为机器指令安全:提供沙箱环境限制恶意代码二、JVM架构详解JVM由三个主要子系统组成:1.类加载子系统类加载过程分为
Spring进阶 - SpringMVC实现原理之DispatcherServlet处理请求的过程
倾听铃的声
后端 spring java mvc 开发语言 分布式
前文我们有了IOC的源码基础以及SpringMVC的基础,我们便可以进一步深入理解SpringMVC主要实现原理,包含DispatcherServlet的初始化过程和DispatcherServlet处理请求的过程的源码解析。本文是第二篇:DispatcherServlet处理请求的过程的源码解析。@pdaiSpring进阶-SpringMVC实现原理之DispatcherServlet处理请求的
JVM 内存模型深度解析:原子性、可见性与有序性的实现
练习时长两年半的程序员小胡
JVM 深度剖析:从面试考点到生产实践 jvm java 内存模型
在了解了JVM的基础架构和类加载机制后,我们需要进一步探索Java程序在多线程环境下的内存交互规则。JVM内存模型(JavaMemoryModel,JMM)定义了线程和主内存之间的抽象关系,它通过规范共享变量的访问方式,解决了多线程并发时的数据一致性问题。本文将从内存模型的核心目标出发,详解原子性、可见性、有序性的实现机制,以及volatile、synchronized等关键字在其中的作用。一、J
Flowable 高级扩展:自定义元素与性能优化实战
练习时长两年半的程序员小胡
Flowable 流程引擎实战指南 流程图 flowable BPMN 流程引擎 java
在前五篇文章中,我们从基础概念、流程设计、API实战、SpringBoot集成,到外部系统协同,逐步构建了Flowable的应用体系。但企业级复杂场景中,原生功能往往难以满足定制化需求——比如需要特殊的审批规则网关、与决策引擎联动实现动态路由,或是在高并发场景下优化流程引擎性能。本文将聚焦Flowable的高级扩展能力,详解如何自定义流程元素、集成规则引擎,并掌握大型系统中的性能调优策略。一、自定
Java | 多线程经典问题 - 售票
Ada54
一、售票需求1)同一个票池2)多个窗口卖票,不能出售同一张票二、售票问题代码实现(线程与进程小总结,请戳:Java|线程和进程,创建线程)step1:定义SaleWindow类实现Runnable接口,覆盖run方法step2:实例化SaleWindow对象,创建Thread对象,将SaleWindow作为参数传给Thread类的构造函数,然后通过Thread.start()方法启动线程step3
SpringMVC的执行流程
1、什么是MVCMVC是一种设计模式。MVC的原理图如下所示M-Model模型(完成业务逻辑:有javaBean构成,service+dao+entity)V-View视图(做界面的展示jsp,html……)C-Controller控制器(接收请求—>调用模型—>根据结果派发页面2、SpringMVC是什么SpringMVC是一个MVC的开源框架,SpringMVC=Struts2+Spring,
2018-09-27 aop相关
蒋超_58dc
1.静态织入,需要使用aspectj专用的compilermaven工程可以采用:https://www.mojohaus.org/aspectj-maven-plugin/2.动态织入,配合spring,创建代理来执行3.
JAVA接口机结构解析
秃狼
SpringBoot 八股文 Java java 学习
什么是接口机在Java项目中,接口机通常指用于与外部系统进行数据交互的中间层,负责处理请求和响应的转换、协议适配、数据格式转换等任务。接口机的结构我们的接口机的结构分为两个大部分,外部接口机和内部接口机,在业务的调度上也是通过mq来实现的,只要的目的就是为了解耦合和做差异化。在接口机中主要的方法就是定时任务,消息的发送和消费,其他平台调用接口机只能提供外部接口机的方法进行调用,外部接口机可以提供消
最新阿里四面面试真题46道:面试技巧+核心问题+面试心得
风平浪静如码
前言做技术的有一种资历,叫做通过了阿里的面试。这些阿里Java相关问题,都是之前通过不断优秀人才的铺垫总结的,先自己弄懂了再去阿里面试,不然就是去丢脸,被虐。希望对大家帮助,祝面试成功,有个更好的职业规划。一,阿里常见技术面1、微信红包怎么实现。2、海量数据分析。3、测试职位问的线程安全和非线程安全。4、HTTP2.0、thrift。5、面试电话沟通可能先让自我介绍。6、分布式事务一致性。7、ni
LVS+Keepalived实现高可用和负载均衡
2401_84412895
程序员 lvs 负载均衡 运维
2、开启网卡子接口配置VIP[root@a~]#cd/etc/sysconfig/network-scripts/[root@anetwork-scripts]#cp-aifcfg-ens32ifcfg-ens32:0[root@anetwork-scripts]#catifcfg-ens32:0BOOTPROTO=staticDEVICE=ens32:0ONBOOT=yesIPADDR=10.1
图论算法经典题目解析:DFS、BFS与拓扑排序实战
周童學
数据结构与算法 深度优先 算法 图论
图论算法经典题目解析:DFS、BFS与拓扑排序实战图论问题是算法面试中的高频考点,本博客将通过四道LeetCode经典题目(均来自"Top100Liked"题库),深入讲解图论的核心算法思想和实现技巧。涵盖DFS、BFS、拓扑排序和前缀树等知识点,每道题配有Java实现和易错点分析。1.岛屿数量(DFS遍历)问题描述给定一个由'1'(陆地)和'0'(水)组成的二维网格,计算岛屿的数量。岛屿由水平或
【异常】使用 LiteFlow 框架时,提示错误ChainDuplicateException: [chain name duplicate] chainName=categoryChallenge
本本本添哥
002 - 进阶开发能力 java
一、报错内容Causedby:com.yomahub.liteflow.exception.ChainDuplicateException:[chainnameduplicate]chainName=categoryChallengeatcom.yomahub.liteflow.parser.helper.ParserHelper.lambda$null$0(ParserHelper.java:1
Java并发核心:线程池使用技巧与最佳实践! | 多线程篇(五)
bug菌¹
Java实战(进阶版) java Java零基础入门 Java并发 线程池 多线程篇
本文收录于「Java进阶实战」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!环境说明:Windows10+IntelliJIDEA2021.3.2+Jdk1.8本文目录前言摘要正文何为线程池?为什么需要线程池?线程池的好处线程池使用场景如何创建线程池?线程池的常见配置源码解析案例分享案例代码演示案例运行
Java 队列
tryxr
java 开发语言 队列
队列一般用什么哪种结构实现队列的特性数据入队列时一定是从尾部插入吗数据出队列时一定是从头部删除吗队列的基本运算有什么队列支持随机访问吗队列的英文表示什么是队列队列从哪进、从哪出队列的进出顺序队列是用哪种结构实现的Queue和Deque有什么区别Queue接口的方法Queue中的add与offer的区别offer、poll、peek的模拟实现如何利用链表实现队列如何利用顺序表实现队列什么叫做双端队列
JVM 内存分配与回收策略:从对象创建到内存释放的全流程
在JVM的运行机制中,内存分配与回收策略是连接对象生命周期与垃圾收集器的桥梁。它决定了对象在堆内存中的创建位置、存活过程中的区域迁移,以及最终被回收的时机。合理的内存分配策略能减少GC频率、降低停顿时间,是优化Java应用性能的核心环节。本文将系统解析JVM的内存分配规则、对象晋升机制,以及实战中的内存优化技巧。一、对象优先在Eden区分配:新生代的“临时缓冲区”大多数情况下,Java对象在新生代
代码随想录算法训练营第三十五天
01背包问题二维题目链接01背包问题二维题解importjava.util.Scanner;publicclassMain{publicstaticvoidmain(String[]args){Scannersc=newScanner(System.in);intM=sc.nextInt();intN=sc.nextInt();int[]space=newint[M];int[]value=new
微信公众号回调java_处理微信公众号消息回调
weixin_39607620
微信公众号回调java
1、背景在上一节中,咱们知道如何接入微信公众号,可是以后公众号会与咱们进行交互,那么微信公众号如何通知到咱们本身的服务器呢?咱们知道咱们接入的时候提供的url是GET/mp/entry,那么公众号以后产生的事件将会以POST/mp/entry发送到咱们本身的服务器上。html2、代码实现,此处仍是使用weixin-java-mp这个框架实现一、引入weixin-java-mpcom.github.
[spring6: Mvc-网关]-源码解析
推荐阅读:[spring6:Mvc-函数式编程]-源码解析GatewayServerMvcAutoConfiguration@AutoConfiguration(after={HttpClientAutoConfiguration.class,RestTemplateAutoConfiguration.class,RestClientAutoConfiguration.class,FilterAu
学C++的五大惊人好处
为什么要学c++学c++有什么用学习c++的好处有1.中考可以加分2.高考可能直接录取3.就业广且工资高4.在未来30--50年c++一定是一个很受欢迎的职业5.c++成功的例子deepsick等AI智能C++语言兼备编程效率和编译运行效率的语言C++语言是C语言功能增强版,在c语言的基础上添加了面向对象编程和泛型编程的支持既继承了C语言高效,简洁,快速和可移植的传统,又具备类似Java、Go等其
Java8 Stream流的sorted()的排序【正序、倒序、多字段排序】
Tony666688888
java windows 开发语言
针对集合排序,java8可以用Stream流的sorted()进行排序。示例Bean以下我们会使用这个Bean来做示例。publicclassOrder{privateStringweight;privateDoubleprice;privateStringdateStr;//忽略getter、setter、构造方法、toString}字段排序首先是比较器Comparator,形式如下:Compa
用代码生成艺术字:设计个性化海报的秘密
本文围绕“用代码生成艺术字:设计个性化海报的秘密”展开,先概述代码生成艺术字在海报设计中的独特价值,接着介绍常用的代码工具(如HTML、CSS、JavaScript等),详细阐述从构思到实现的完整流程,包括字体样式设计、动态效果添加等,还分享了提升艺术字质感的技巧及实际案例。最后总结代码生成艺术字的优势,为设计师提供打造个性化海报的实用指南,助力提升海报设计的独特性与吸引力,符合搜索引擎SEO标准
java实习生40多天有感
别拿爱情当饭吃
从5月15日开始,我开始第一步步入社会,我今年大三,在一家上市互联网公司做一名实习生,主要做java后端开发。开始的时候,觉得公司的环境挺不错的,不过因为公司在CBD,所以隔壁的午饭和晚饭都要20+RMB,而且还吃不饱,这让我感觉挺郁闷的。一到下午,我就会犯困(因为饿)。因此,我又不得不买一些干粮在公司屯着。关于技术,有一个比较大的项目在需求调研当中,我们做实习生,就是辅助项目经理,测试功能,并且
大数据技术笔记—spring入门
卿卿老祖
篇一spring介绍spring.io官网快速开始Aop面向切面编程,可以任何位置,并且可以细致到方法上连接框架与框架Spring就是IOCAOP思想有效的组织中间层对象一般都是切入service层spring组成前后端分离已学方式,前后台未分离:Spring的远程通信:明日更新创建第一个spring项目来源:科多大数据
大学生入门:初识方法及其易踩坑的点
在java学习过程中,我们不难发现有很多重复使用的功能代码块,每次使用如果都要重新写一遍,岂不是很麻烦,就算是“cv”大法,感觉也不是很方便,那么,有什么办法可以解决这个问题呢?方法!java中,一段可重用的,用于执行特定功能的代码块叫做方法,它可以接收参数、返回结果,并且可以被多次使用。一、方法的基本结构[修饰符]返回值类型方法名([参数列表])[throws异常类型]{//方法体}[throw
[Ljava.lang.Object; cannot be cast to [Ljava.lang.String;
这些不会的
解释:这个错误是很常见的错误,错误的提示已经很清楚了就是java的Object数组不能转换成为String[]数组,这就说明你要转换的数组它本身是Object类型的数组,但是你却非要把它转换为String类的数组,这当然是错误的。示例:[java]viewplaincopypackagecom.dada;importjava.util.ArrayList;importjava.util.List;
HikariCP调试日志深度解析:生产环境故障排查完全指南
HikariCP调试日志深度解析:生产环境故障排查完全指南更新时间:2025年7月4日|作者:资深架构师|适用版本:HikariCP5.x+|难度等级:中高级前言在生产环境中,数据库连接池往往是系统性能的关键瓶颈。HikariCP作为当前最流行的Java连接池,其调试日志包含了丰富的运行时信息,能够帮助我们快速定位和解决各种连接池相关问题。本文将深入解析HikariCP的日志体系,提供一套完整的故
大学社团管理系统(11831)
codercode2022
java spring boot spring echarts spring cloud sentinel java-rocketmq
有需要的同学,源代码和配套文档领取,加文章最下方的名片哦一、项目演示项目演示视频二、资料介绍完整源代码(前后端源代码+SQL脚本)配套文档(LW+PPT+开题报告)远程调试控屏包运行三、技术介绍Java语言SSM框架SpringBoot框架Vue框架JSP页面Mysql数据库IDEA/Eclipse开发四、项目截图有需要的同学,源代码和配套文档领取,加文章最下方的名片哦!
今年校招竞争真激烈
12_05
程序员满大街,都要找不到工作了。即使人工智能满大街,我也后悔当初没学机器学习,后悔当初没学Java。C++真难找工作。难道毕了业就失业吗?好担心!
解线性方程组
qiuwanchi
package gaodai.matrix;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
Scanner scanner = new Sc
在mysql内部存储代码
annan211
性能 mysql 存储过程 触发器
在mysql内部存储代码
在mysql内部存储代码,既有优点也有缺点,而且有人倡导有人反对。
先看优点:
1 她在服务器内部执行,离数据最近,另外在服务器上执行还可以节省带宽和网络延迟。
2 这是一种代码重用。可以方便的统一业务规则,保证某些行为的一致性,所以也可以提供一定的安全性。
3 可以简化代码的维护和版本更新。
4 可以帮助提升安全,比如提供更细
Android使用Asynchronous Http Client完成登录保存cookie的问题
hotsunshine
android
Asynchronous Http Client是android中非常好的异步请求工具
除了异步之外还有很多封装比如json的处理,cookie的处理
引用
Persistent Cookie Storage with PersistentCookieStore
This library also includes a PersistentCookieStore whi
java面试题
Array_06
java 面试
java面试题
第一,谈谈final, finally, finalize的区别。
final-修饰符(关键字)如果一个类被声明为final,意味着它不能再派生出新的子类,不能作为父类被继承。因此一个类不能既被声明为 abstract的,又被声明为final的。将变量或方法声明为final,可以保证它们在使用中不被改变。被声明为final的变量必须在声明时给定初值,而在以后的引用中只能
网站加速
oloz
网站加速
前序:本人菜鸟,此文研究总结来源于互联网上的资料,大牛请勿喷!本人虚心学习,多指教.
1、减小网页体积的大小,尽量采用div+css模式,尽量避免复杂的页面结构,能简约就简约。
2、采用Gzip对网页进行压缩;
GZIP最早由Jean-loup Gailly和Mark Adler创建,用于UNⅨ系统的文件压缩。我们在Linux中经常会用到后缀为.gz
正确书写单例模式
随意而生
java 设计模式 单例
单例模式算是设计模式中最容易理解,也是最容易手写代码的模式了吧。但是其中的坑却不少,所以也常作为面试题来考。本文主要对几种单例写法的整理,并分析其优缺点。很多都是一些老生常谈的问题,但如果你不知道如何创建一个线程安全的单例,不知道什么是双检锁,那这篇文章可能会帮助到你。
懒汉式,线程不安全
当被问到要实现一个单例模式时,很多人的第一反应是写出如下的代码,包括教科书上也是这样
单例模式
香水浓
java
懒汉 调用getInstance方法时实例化
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if(null == ins
安装Apache问题:系统找不到指定的文件 No installed service named "Apache2"
AdyZhang
apache http server
安装Apache问题:系统找不到指定的文件 No installed service named "Apache2"
每次到这一步都很小心防它的端口冲突问题,结果,特意留出来的80端口就是不能用,烦。
解决方法确保几处:
1、停止IIS启动
2、把端口80改成其它 (譬如90,800,,,什么数字都好)
3、防火墙(关掉试试)
在运行处输入 cmd 回车,转到apa
如何在android 文件选择器中选择多个图片或者视频?
aijuans
android
我的android app有这样的需求,在进行照片和视频上传的时候,需要一次性的从照片/视频库选择多条进行上传
但是android原生态的sdk中,只能一个一个的进行选择和上传。
我想知道是否有其他的android上传库可以解决这个问题,提供一个多选的功能,可以使checkbox之类的,一次选择多个 处理方法
官方的图片选择器(但是不支持所有版本的androi,只支持API Level
mysql中查询生日提醒的日期相关的sql
baalwolf
mysql
SELECT sysid,user_name,birthday,listid,userhead_50,CONCAT(YEAR(CURDATE()),DATE_FORMAT(birthday,'-%m-%d')),CURDATE(), dayofyear( CONCAT(YEAR(CURDATE()),DATE_FORMAT(birthday,'-%m-%d')))-dayofyear(
MongoDB索引文件破坏后导致查询错误的问题
BigBird2012
mongodb
问题描述:
MongoDB在非正常情况下关闭时,可能会导致索引文件破坏,造成数据在更新时没有反映到索引上。
解决方案:
使用脚本,重建MongoDB所有表的索引。
var names = db.getCollectionNames();
for( var i in names ){
var name = names[i];
print(name);
Javascript Promise
bijian1013
JavaScript Promise
Parse JavaScript SDK现在提供了支持大多数异步方法的兼容jquery的Promises模式,那么这意味着什么呢,读完下文你就了解了。
一.认识Promises
“Promises”代表着在javascript程序里下一个伟大的范式,但是理解他们为什么如此伟大不是件简
[Zookeeper学习笔记九]Zookeeper源代码分析之Zookeeper构造过程
bit1129
zookeeper
Zookeeper重载了几个构造函数,其中构造者可以提供参数最多,可定制性最多的构造函数是
public ZooKeeper(String connectString, int sessionTimeout, Watcher watcher, long sessionId, byte[] sessionPasswd, boolea
【Java命令三】jstack
bit1129
jstack
jstack是用于获得当前运行的Java程序所有的线程的运行情况(thread dump),不同于jmap用于获得memory dump
[hadoop@hadoop sbin]$ jstack
Usage:
jstack [-l] <pid>
(to connect to running process)
jstack -F
jboss 5.1启停脚本 动静分离部署
ronin47
以前启动jboss,往各种xml配置文件,现只要运行一句脚本即可。start nohup sh /**/run.sh -c servicename -b ip -g clustername -u broatcast jboss.messaging.ServerPeerID=int -Djboss.service.binding.set=p
UI之如何打磨设计能力?
brotherlamp
UI ui教程 ui自学 ui资料 ui视频
在越来越拥挤的初创企业世界里,视觉设计的重要性往往可以与杀手级用户体验比肩。在许多情况下,尤其对于 Web 初创企业而言,这两者都是不可或缺的。前不久我们在《右脑革命:别学编程了,学艺术吧》中也曾发出过重视设计的呼吁。如何才能提高初创企业的设计能力呢?以下是 9 位创始人的体会。
1.找到自己的方式
如果你是设计师,要想提高技能可以去设计博客和展示好设计的网站如D-lists或
三色旗算法
bylijinnan
java 算法
import java.util.Arrays;
/**
问题:
假设有一条绳子,上面有红、白、蓝三种颜色的旗子,起初绳子上的旗子颜色并没有顺序,
您希望将之分类,并排列为蓝、白、红的顺序,要如何移动次数才会最少,注意您只能在绳
子上进行这个动作,而且一次只能调换两个旗子。
网上的解法大多类似:
在一条绳子上移动,在程式中也就意味只能使用一个阵列,而不使用其它的阵列来
警告:No configuration found for the specified action: \'s
chiangfai
configuration
1.index.jsp页面form标签未指定namespace属性。
<!--index.jsp代码-->
<%@taglib prefix="s" uri="/struts-tags"%>
...
<s:form action="submit" method="post"&g
redis -- hash_max_zipmap_entries设置过大有问题
chenchao051
redis hash
使用redis时为了使用hash追求更高的内存使用率,我们一般都用hash结构,并且有时候会把hash_max_zipmap_entries这个值设置的很大,很多资料也推荐设置到1000,默认设置为了512,但是这里有个坑
#define ZIPMAP_BIGLEN 254
#define ZIPMAP_END 255
/* Return th
select into outfile access deny问题
daizj
mysql txt 导出数据到文件
本文转自:http://hatemysql.com/2010/06/29/select-into-outfile-access-deny%E9%97%AE%E9%A2%98/
为应用建立了rnd的帐号,专门为他们查询线上数据库用的,当然,只有他们上了生产网络以后才能连上数据库,安全方面我们还是很注意的,呵呵。
授权的语句如下:
grant select on armory.* to rn
phpexcel导出excel表简单入门示例
dcj3sjt126com
PHP Excel phpexcel
<?php
error_reporting(E_ALL);
ini_set('display_errors', TRUE);
ini_set('display_startup_errors', TRUE);
if (PHP_SAPI == 'cli')
die('This example should only be run from a Web Brows
美国电影超短200句
dcj3sjt126com
电影
1. I see. 我明白了。2. I quit! 我不干了!3. Let go! 放手!4. Me too. 我也是。5. My god! 天哪!6. No way! 不行!7. Come on. 来吧(赶快)8. Hold on. 等一等。9. I agree。 我同意。10. Not bad. 还不错。11. Not yet. 还没。12. See you. 再见。13. Shut up!
Java访问远程服务
dyy_gusi
httpclient webservice get post
随着webService的崛起,我们开始中会越来越多的使用到访问远程webService服务。当然对于不同的webService框架一般都有自己的client包供使用,但是如果使用webService框架自己的client包,那么必然需要在自己的代码中引入它的包,如果同时调运了多个不同框架的webService,那么就需要同时引入多个不同的clien
Maven的settings.xml配置
geeksun
settings.xml
settings.xml是Maven的配置文件,下面解释一下其中的配置含义:
settings.xml存在于两个地方:
1.安装的地方:$M2_HOME/conf/settings.xml
2.用户的目录:${user.home}/.m2/settings.xml
前者又被叫做全局配置,后者被称为用户配置。如果两者都存在,它们的内容将被合并,并且用户范围的settings.xml优先。
ubuntu的init与系统服务设置
hongtoushizi
ubuntu
转载自:
http://iysm.net/?p=178 init
Init是位于/sbin/init的一个程序,它是在linux下,在系统启动过程中,初始化所有的设备驱动程序和数据结构等之后,由内核启动的一个用户级程序,并由此init程序进而完成系统的启动过程。
ubuntu与传统的linux略有不同,使用upstart完成系统的启动,但表面上仍维持init程序的形式。
运行
跟我学Nginx+Lua开发目录贴
jinnianshilongnian
nginx lua
使用Nginx+Lua开发近一年的时间,学习和实践了一些Nginx+Lua开发的架构,为了让更多人使用Nginx+Lua架构开发,利用春节期间总结了一份基本的学习教程,希望对大家有用。也欢迎谈探讨学习一些经验。
目录
第一章 安装Nginx+Lua开发环境
第二章 Nginx+Lua开发入门
第三章 Redis/SSDB+Twemproxy安装与使用
第四章 L
php位运算符注意事项
home198979
位运算 PHP &
$a = $b = $c = 0;
$a & $b = 1;
$b | $c = 1
问a,b,c最终为多少?
当看到这题时,我犯了一个低级错误,误 以为位运算符会改变变量的值。所以得出结果是1 1 0
但是位运算符是不会改变变量的值的,例如:
$a=1;$b=2;
$a&$b;
这样a,b的值不会有任何改变
Linux shell数组建立和使用技巧
pda158
linux
1.数组定义 [chengmo@centos5 ~]$ a=(1 2 3 4 5) [chengmo@centos5 ~]$ echo $a 1 一对括号表示是数组,数组元素用“空格”符号分割开。
2.数组读取与赋值 得到长度: [chengmo@centos5 ~]$ echo ${#a[@]} 5 用${#数组名[@或
hotspot源码(JDK7)
ol_beta
java HotSpot jvm
源码结构图,方便理解:
├─agent Serviceab
Oracle基本事务和ForAll执行批量DML练习
vipbooks
oracle sql
基本事务的使用:
从账户一的余额中转100到账户二的余额中去,如果账户二不存在或账户一中的余额不足100则整笔交易回滚
select * from account;
-- 创建一张账户表
create table account(
-- 账户ID
id number(3) not null,
-- 账户名称
nam