SpringBoot常用依赖和配置文件
项目打包
xml
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<defaultGoal>compile</defaultGoal>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-compiler- plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven- plugin</artifactId>
<configuration>
<mainClass>com.example.project.module.MainClass</mainClass>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</pluginManagement>
</build>
ORM
| 组件 |
|---|
| mybatis |
| mybatis-plus |
| hibernate |
mybtis
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
<!--阿里连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
yaml
properties
mybatis.type-aliases-package=com.lujiahong.pojo
mybatis.mapper-locations=classpath:mapper/*.xml
java-dao
@Mapper
@Repository
public interface UserMapper {
User queryUserByName(String name);
}
dao.xml
<?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.lujiahong.mapper.UserMapper">
<select id="queryUserByName" resultType="com.lujiahong.pojo.User" parameterType="string">
select * from user where name = #{name}
</select>
</mapper>
java-service
public interface UserService {
User queryUserByName(String name);
}
java-serviceImpl
@Service
public class UserServiceImpl implements UserService {
@Autowired
UserMapper userMapper;
@Override
public User queryUserByName(String name) {
return userMapper.queryUserByName(name);
}
}
mybtis-plus
xml
<!--mybatis-plus的依赖-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
<!--逆向工程所需的依赖-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.4.1</version>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.2</version>
</dependency>
<!--需要打印sql语句在控制台再引用下面依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j</artifactId>
<version>1.3.8.RELEASE</version>
</dependency>
yaml
mybatis-plus:
typeAliasesPackage: com.公司名.项目名.实体包
configuration:
#SpringBoot+MyBatis-plus如何配置log4j日志输出(sql),需引入log4j和logging依赖
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
java-entity
@Data
@TableName("user")
@AllArgsConstructor
@NoArgsConstructor
public class UserDO {
@TableId(value = "u_id", type = IdType.AUTO)
private String userId;
@TableField("u_phone")
private String phone;
@TableField("u_pwd")
private String pwd;
}
dao.xml
<?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.project.dao.IRoleDao">
<sql id="roleIdMap">
pk_id,
r_name,
r_describe
</sql>
<resultMap id="roleMap" type="RoleBean">
<id property="roleId" column="pk_id"></id>
<result property="roleName" column="r_name"></result>
<result property="roleDescribe" column="r_describe"></result>
</resultMap>
<insert id="addRole">
insert into t_role values(null,#{roleName},#{roleDescribe})
</insert>
<delete id="delRole">
delete from t_role where pk_id = #{roleId}
</delete>
<update id="updateRole">
update t_role set r_name = #{roleName},r_describe = #{roleDescribe} where pk_id = #{roleId}
</update>
<select id="findByRoleId" resultMap="roleMap">
select <include refid="roleIdMap"></include> from t_role where pk_id = #{roleId}
</select>
<select id="findByItem" resultMap="roleMap">
select <include refid="roleIdMap"></include> from t_role where 1=1
<if test="choose == '角色名'">
and r_name like concat(concat('%',#{info}),'%')
</if>
<if test="choose == '角色描述'">
and r_describe like concat(concat('%',#{info}),'%')
</if>
</select>
<select id="checkRoleName" resultMap="roleMap">
select <include refid="roleIdMap"></include> from t_role where r_name = #{roleName}
</select>
</mapper>
java-dao
public interface UserDao extends BaseMapper<UserDO> {}
java-service
public interface UserService extends IService<UserDO> {}
java-serviceIpml
@Service
public class UserServiceImpl extends ServiceImpl<UserDao, UserDO> implements UserService {
@Resource
private UserDao userDao;
}
java-逆向工程
public class CodeGenerator {
/**
* 读取控制台内容
*/
public static String scanner(String tip) {
Scanner scanner = new Scanner(System.in);
StringBuilder help = new StringBuilder();
help.append("请输入" + tip + ":");
System.out.println(help.toString());
if (scanner.hasNext()) {
String ipt = scanner.next();
if (StringUtils.isNotBlank(ipt)) {
return ipt;
}
}
throw new MybatisPlusException("请输入正确的" + tip + "!");
}
public static void main(String[] args) {
// 代码生成器
AutoGenerator mpg = new AutoGenerator();
// 全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
System.out.println(projectPath);
gc.setServiceName("%sService");
gc.setMapperName("%sDAO");
gc.setEntityName("%sDO");
// ---------------------------------------------------------------------------
gc.setOutputDir(projectPath + "/image-upload-back/src/main/java");
gc.setAuthor("bdk");
gc.setOpen(false);
// gc.setSwagger2(true); 实体属性 Swagger2 注解
mpg.setGlobalConfig(gc);
// 数据源配置
DataSourceConfig dsc = new DataSourceConfig();
// ---------------------------------------------------------------------------
dsc.setUrl("jdbc:mysql://localhost:6789/shiro?useUnicode=true&useSSL=false&characterEncoding=utf8");
// dsc.setSchemaName("public");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("lovo");
mpg.setDataSource(dsc);
// 包配置
PackageConfig pc = new PackageConfig();
// pc.setModuleName(scanner("模块名"));
// ---------------------------------------------------------------------------
pc.setParent("com.lujiahong.imageupload");
pc.setMapper("dao");
pc.setEntity("model.ObjDO");
mpg.setPackageInfo(pc);
// 自定义配置
InjectionConfig cfg = new InjectionConfig() {
@Override
public void initMap() {
// to do nothing
}
};
// 如果模板引擎是 freemarker
// String templatePath = "/templates/mapper.xml.ftl";
// 如果模板引擎是 velocity
String templatePath = "/templates/mapper.xml.vm";
// 自定义输出配置
List<FileOutConfig> focList = new ArrayList<>();
// 自定义配置会被优先输出
focList.add(new FileOutConfig(templatePath) {
@Override
public String outputFile(TableInfo tableInfo) {
// 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!
// ---------------------------------------------------------------------------
return projectPath + "/image-upload-back/src/main/resources/mapper/"
+ tableInfo.getEntityName() + StringPool.DOT_XML;
}
});
/*
cfg.setFileCreate(new IFileCreate() {
@Override
public boolean isCreate(ConfigBuilder configBuilder, FileType fileType, String filePath) {
// 判断自定义文件夹是否需要创建
checkDir("调用默认方法创建的目录,自定义目录用");
if (fileType == FileType.MAPPER) {
// 已经生成 mapper 文件判断存在,不想重新生成返回 false
return !new File(filePath).exists();
}
// 允许生成模板文件
return true;
}
});
*/
cfg.setFileOutConfigList(focList);
mpg.setCfg(cfg);
// 配置模板
TemplateConfig templateConfig = new TemplateConfig();
// 配置自定义输出模板
//指定自定义模板路径,注意不要带上.ftl/.vm, 会根据使用的模板引擎自动识别
// templateConfig.setEntity("templates/entity2.java");
// templateConfig.setService();
// templateConfig.setController();
templateConfig.setXml(null);
mpg.setTemplate(templateConfig);
// 策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel);
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
// strategy.setSuperEntityClass("你自己的父类实体,没有就不用设置!");
strategy.setEntityLombokModel(true);
strategy.setRestControllerStyle(true);
// 公共父类
// strategy.setSuperControllerClass("你自己的父类控制器,没有就不用设置!");
// 写于父类中的公共字段
strategy.setSuperEntityColumns("id");
strategy.setInclude(scanner("表名,多个英文逗号分割").split(","));
strategy.setControllerMappingHyphenStyle(true);
// strategy.setTablePrefix(pc.getModuleName() + "_");
mpg.setStrategy(strategy);
// mpg.setTemplateEngine(new FreemarkerTemplateEngine());
mpg.execute();
}
}
jpa-hibernate
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>2.3.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>5.4.22.Final</version>
</dependency>
yaml
spring:
jpa:
show-sql: true
database: mysql
hibernate:
ddl-auto: update
naming:
physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
properties:
hibernate:
dialect: org.hibernate.dialect.MySQL5Dialect
java-entity
@Getter
@Setter
@Entity
@Table(name = "mv_order")
@ApiModel(value = "订单实体")
@ToString
public class OrderEntity{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@ApiModelProperty(value = "ID",hidden = true)
private long orderId;
@ApiModelProperty(value = "订单编号")
@Column(length = 48)
private String orderNum;
@ApiModelProperty(value = "电影名")
@Column(length = 48)
private String movieName;
@ApiModelProperty(value = "购买数量")
private int movieNum;
@ApiModelProperty(value = "价格",example = "50")
private float price;
@ApiModelProperty(value = "购买时间")
private String playDate;
@ApiModelProperty(value = "0-已支付未出票,1-已出票(投递到MQ)",example = "0")
private int tag;
}
java-dao
public interface IOrderAddDao extends CrudRepository<OrderEntity,Long> {}
java-service
public interface IOrderAddService {}
java-serviceIpml
@Service
public class OrderServiceImpl implements IOrderAddService {
@Resource
private IOrderAddDao orderAddDao;
}
鉴权
| 组件 |
|---|
| shiro |
| spring security |
Jwt
xml
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.10.3</version>
</dependency>
java-util
/**
* 创建token,并验证token;作用原理类似与session中sessionId验证权限访问的作用;
* 不同点是:token针对的是分布式开发,不同服务器间访问数据的唯一标识
* 通常是利用拦截器,在用户登录的时候进行验证,验证成功,分发一个token,之后不同服务器间crud数据,通过token即可
*/
public class CreateJWT {
private static final long EXPIRE_TIME=1000*2;
private static final String TOKEN_SECRET="J177";
public static String getToken(UserDto userDto){
//过期时间
Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
//私钥及加密算法
Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
//设置头信息
HashMap<String, Object> header = new HashMap<>(2);
header.put("typ", "JWT");
header.put("alg", "HS256");
//附带username和userID生成签名
String token= JWT.create().withHeader(header)
.withClaim("userName",userDto.getUserName())
.withClaim("password",userDto.getPassWord())
.withExpiresAt(date).sign(algorithm);
return token;
}
public static String getToken(String userName,String power){
//过期时间
Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
//私钥及加密算法
Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
//设置头信息
HashMap<String, Object> header = new HashMap<>(2);
header.put("typ", "JWT");
header.put("alg", "HS256");
//附带username和userID生成签名
String token= JWT.create().withHeader(header)
.withClaim("userName",userName)
.withClaim("power",power)
.withExpiresAt(date).sign(algorithm);
return token;
}
public static boolean verifyToken(String token){
boolean bl=true;
//私钥及加密算法
Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
//构建认证对象
JWTVerifier verifier = JWT.require(algorithm).build();
try {
//认证 ,认证失败抛出异常
DecodedJWT jwt = verifier.verify(token);
//拿到token中的内容
// System.out.println(jwt.getClaim("userName").asString());
// System.out.println(jwt.getClaim("password").asString());
}catch (Exception e){
bl=false;
}
return bl;
}
}
Shiro
xml
<!--subject:用户
SecuirtyManager:管理所有用户
Realm:连接数据-->
<!--Shiro整合Spring的包-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.1</version>
</dependency>
<!--Shiro整合Thymeleaf -->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
java-ShiroConfig
@Configuration
public class ShiroConfig {
//ShiroFilterFactoryBean Shiro过滤的Bean
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
//设置安全管理器
bean.setSecurityManager(defaultWebSecurityManager);
/**
* 添加shiro的内置过滤器(表示通过一个map设置过滤器链)
* anon:无需认证就可以访问
* authc:必须认证了才能访问
* user:必须用有了 rememberMe 功能才能访问
* perms:拥有某个资源的权限才能访问
* role:拥有某个角色权限才能访问
*/
Map<String, String> filterMap = new LinkedHashMap<>();
//下面两个put整合在一起使用通配符*:filterMap.put("/user/*","authc");
filterMap.put("/user/add","authc");
filterMap.put("/user/update","authc");
//授权,未授权跳转到未授权页面
filterMap.put("/user/add","perms[user:add]");
filterMap.put("/user/update","perms[user:update]");
bean.setFilterChainDefinitionMap(filterMap);
//设置未授权跳转页面
bean.setUnauthorizedUrl("/noauth");
//设置登录请求跳转页面
bean.setLoginUrl("/toLogin");
return bean;
}
//DefaultWebSecurityManager 安全对象
@Bean(name = "securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//关联UserRealm
securityManager.setRealm(userRealm);
return securityManager;
}
//创建realm对象,需要自定义类(UserRealm)
@Bean(name = "userRealm")
public UserRealm userRealm(){
return new UserRealm();
}
//整合ShiroDialect:用来整合shiro thymeleaf
@Bean
public ShiroDialect getShiroDialect(){
return new ShiroDialect();
}
}
java-realm
/**
* 自定义的UserRealm需要继承AuthorizingRealm重写里面的方法
(认证和授权)
*/
public class UserRealm extends AuthorizingRealm {
@Autowired
UserService userService;
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行了==>>授权doGetAuthorizationInfo");
//找到AuthorizationInfo实现类SimpleAuthorizationInfo
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//获取登录用户对象,前提在认证所传递的三个参数中的第一个prinicpal即为认证成功的用户对象
Subject subject = SecurityUtils.getSubject();
User user = (User) subject.getPrincipal();
//按用户持有的权限给用户授权
info.addStringPermission(user.getPerms());
return info;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行了==>>认证doGetAuthorizationInfo");
//authenticationToken即为登录时保存的用户信息
UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
//用户名不存在,抛出异常UnknownAccountException
User user = userService.queryUserByName(token.getUsername());
if(null==user){
System.out.println("用户名不存在,抛出异常UnknownAccountException");
return null;
}
/**
* 注:密码的验证是交给shrio来做的,为了安全
* shiro提供密码加密(1、md5加密_把密码直接加密;2、md5盐值加密_除了密码还额加一些属性)
* SimpleAuthenticationInfo是AuthenticationInfo的实现类
* prinicpal登录成功的用户对象,供授权时使用
* credentials密码
* realmName认证名
*/
return new SimpleAuthenticationInfo(user,user.getPwd(),"");
}
Spring Scurity
xml
DB
| 组件 |
|---|
| MySQL/MariaDB |
| Oracle |
| SQL Server |
| PostgrcSQL |
| DB2 |
mysql
xml
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
yaml
spring:
datasource:
username: root
password: lovo
url: jdbc:mysql://localhost:6789/shiro?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
driver-class-name: com.mysql.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
引擎模板
| 组件 | 功能 |
|---|---|
| thymeleaf | 主要渲染xml,HTML,HTML5而且与springboot整合。 |
| Velocity | 不仅可以用于界面展示(HTML.xml等)还可以生成输入java代码,SQL语句等文本格式。 |
| FreeMarker | 功能与Velocity差不多,但是语法更加强大,使用方便。 |
| jsp | 是一种动态网页开发技术。它使用JSP标签在HTML网页中插入Java代码。 |
thymeleaf
xml
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring5</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-java8time</artifactId>
</dependency>
yaml
spring:
thymeleaf:
enabled: true #开启thymeleaf视图解析
encoding: utf-8 #编码
prefix: classpath:/templates/ #前缀
cache: false #是否使用缓存
mode: HTML #严格的HTML语法模式
suffix: .html #后缀名
html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
<head>
<meta charset="UTF-8">
<title>首页</title>
</head>
<body>
<h1>首页</h1>
<p th:text="${msg}"></p>
<hr>
<div th:if="${session.loginUser==null}">
<a th:href="@{/toLogin}">登录</a>
</div>
<!--shiro:hasPermission="user:add" 表示有user:add权限就显示-->
<div shiro:hasPermission="user:add">
<a th:href="@{/user/add}">add</a>
</div >
<div shiro:hasPermission="user:update">
<a th:href="@{/user/update}">update</a>
</div>
</body>
</html>
日志
| 组件 |
|---|
| Log4j |
| Log4j2 |
| Logback |
| jboss logging |
log4j
xml
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
yaml
logging:
level:
root: info
java-test
public void TestLog4j2Application{
private static Logger logger = LogManager.getLogger(TestLog4j2Application.class);
public static void main(String[] args) {
logger.error("log4j2----------------\n");
SpringApplication.run(TestLog4j2Application.class, args);
}
}
工具
| 组件 | 作用 |
|---|---|
| swagger | 接口调试 |
| junit | 单元测试 |
| lombok | 简化类书写 |
| configuration-processor | 配置提示 |
| devtools | 热部署 |
| validation | 注册验证 |
| fileuplod | 文件上传下载 |
| aspectjweaver | AOP |
swagger
xml
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
java-config
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket createRestApi(){
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any()).build();
}
private ApiInfo apiInfo(){
return new ApiInfoBuilder()
.title("商城前端管理系统API文档")
.description("这是一份关于商城前端管理系统的接口文档")
.version("1.0")
.build();
}
}
http-swagger
http://localhost:8080/swagger-ui.html
junit
xml
<!--JUnit的好处在于。 可以对代码进行单元测试。 并且可以根据单元测试生成单元测试报告-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
java-test
/**
* 注解的介绍:
* @BeforeClass: 所有的方法运行前运行一次,必须是静态方法
* @Before 要在所有的测试方法运行前运行,每个方法都运行一次
* @Test 用在方法上,表示这是一个测试的方法
* @After 要在所有的测试方法运行后运行,每个方法都运行一次
* @AfterClass:所有的方法运行后运行一次,必须是静态方法
* 否则会出现以下异常:
* java.lang.Exception: Method init() should be static
* java.lang.Exception: Method destroy() should be static
*/
public class Demo1 {
//声明全局变量
Calc calc = new Calc();
//有一个方法要在所有的测试方法运行前运行,每个方法都运行一次
@Before
public void begin() {
System.out.println("我在你前面运行了");
}
@BeforeClass
public static void init() {
System.out.println("所有的方法运行前运行一次");
}
@AfterClass
public static void destroy() {
System.out.println("所有的方法运行完以后运行一次");
}
@After
public void end() {
System.out.println("我在测试方法后面运行");
}
/*
* a. 必须是public
* b. 没有返回值
* c. 方法没有参数
*/
@Test
public void testAdd() {
System.out.println(calc.add(4, 8));
}
}
lombok
xml
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
lombok常用的几个注解:
| @Setter 注解在类或字段,注解在类时为所有字段生成setter方法,注解在字段上时只为该字段生成setter方法。 |
|---|
| @Getter 使用方法同上,区别在于生成的是getter方法。 |
| @ToString 注解在类,添加toString方法。 |
| @EqualsAndHashCode 注解在类,生成hashCode和equals方法。 |
| @NoArgsConstructor 注解在类,生成无参的构造方法。 |
| @RequiredArgsConstructor 注解在类,为类中需要特殊处理的字段生成构造方法,比如final和被@NonNull注解的字段。 |
| @AllArgsConstructor 注解在类,生成包含类中所有字段的构造方法。 |
| @Data 注解在类,生成setter/getter、equals、canEqual、hashCode、toString方法,如为final属性,则不会为该属性生成setter方法。 |
| @Slf4j 注解在类,生成log变量,严格意义来说是常量。private static final Logger log = LoggerFactory.getLogger(UserController.class); |
configuration-processor
xml
<!--写配置文件的时候,提供提示-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
devtools
<dependencies>
<!-- devtools热部署依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<!-- 防止将依赖传递到其他模块中 -->
<optional>true</optional>
<!-- 只在运行时起作用,打包时不打进去(防止线上执行打包后的程序,启动文件监听线程File Watcher,耗费大量的内存资源) -->
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<!-- devtools依赖此配置(否则,devtools不生效)。 -->
<fork>true</fork>
</configuration>
</plugin>
</plugins>
</build>
yaml
spring:
devtools:
restart:
# 开启热部署(更改文件后,自动重启)
enabled: true
## 设置哪些资源变动后不触发热部署,会覆盖默认的exclude内容(资源不会触发重启,但会触发实时重新加载)
# exclude: WEB-INF/**,static/**
## 监控额外的路径(优先于exclude)
# additional-paths: src/main/java
validation
xml
<!--注册验证用户信息-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
java-DTO
@Data
@ApiModel
public class StudentDTO {
private Integer id;
@ApiModelProperty(value = "学生姓名",required = true,example = "小王")
@NotBlank(message = "姓名不能为空")
@Pattern(regexp = "^[\u4e00-\u9fa5]{2,6}$",message = "姓名字符2-6个")
private String name;
@ApiModelProperty(value = "学生性别",required = true,example = "男")
@NotBlank(message = "性别不能为空")
private String sex;
@ApiModelProperty(value = "学生年龄",required = true,example = "22")
@NotNull(message = "年龄不能为空")
@Max(value = 120,message = "最大限度年龄120")
@Min(value = 18,message = "最小限度年龄120")
private Integer age;
@ApiModelProperty(value = "学生号码",required = true,example = "13956455645")
@NotBlank(message = "号码不能为空")
@Pattern(regexp = "^((13[0-9])|(14[5|7])|(15([0-3]|[5-9]))|(18[0,5-9]))\\d{8}$",message = "手机号必须为11位数字,\n" +
"* 移动号码段:139、138、137、136、135、134、150、151、152、157、158、159、182、183、187、188、147\n" +
"* 联通号码段:130、131、132、136、185、186、145\n" +
"* 电信号码段:133、153、180、189")
private String phone;
@ApiModelProperty(value = "学生密码",required = true,example = "888")
@NotBlank(message = "密码不能为空")
@Pattern(regexp = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[$@$!%*?&.])[A-Za-z\\d$@$!%*?&.]{6,20}$",message = "– 最少6个字符\n" +
"– 至少有1个大写字符\n" +
"– 至少1个小写字符\n" +
"– 至少1个特殊字符")
private String pwd;
@ApiModelProperty(value = "学生头像",required = true,example = "pic")
@NotBlank(message = "图片路径不能为空")
private String img;
@ApiModelProperty(value = "班级名",required = true,example = "高一五班")
@NotBlank(message = "班级名不能为空")
private String className;
}
java-Controller
@ApiOperation("添加学生")
// @ApiImplicitParam(name = "studentDTO",value = "学生传输对象")
@PostMapping(value = "/add")
public Object addStu(@RequestBody @Valid StudentDTO studentDTO){
studentService.addStu(studentDTO);
return null;
}
fileupload
<!--文件上传和下载-->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
aspectjweaver
xml
<!--aspectjweaver包含aspectjrt,所以我们只需要引入aspectjweaver依赖包就可以了-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
Json
| 组件 |
|---|
| fastjson |
| gson |
| jacakson |
fastjson
<!--fastjson是阿里巴巴的开源JSON解析库,它可以解析JSON格式的字符串,支持将Java Bean序列化为JSON字符串,也可以从JSON字符串反序列化到JavaBean。-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.73</version>
</dependency>
java-test
String text = JSON.toJSONString(obj); //序列化
VO vo = JSON.parseObject("{...}", VO.class); //反序列化
gson
xml
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.2</version>
</dependency>
java
response.getWriter().print(new Gson().toJson(resultVO));
jackson
xml
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.11.3</version>
</dependency>
java
response.getWriter().print(new ObjectMapper().writeValueAsString(resultVO));
java-util
public class WebUtil {
public static void writeJson(Object object, ServletResponse response) {
PrintWriter out = null;
try {
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
out = response.getWriter();
ObjectMapper objectMapper = new ObjectMapper();
out.write(objectMapper.writeValueAsString(object));
} catch (IOException e) {
e.printStackTrace();
} finally {
if (out != null) {
out.close();
}
}
}
}
分页
| 组件 |
|---|
| pagehelper |
| mybatis-plus |
pagehelper
xml
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.0</version>
</dependency>
java-servlet
@WebServlet(name = "FindItemJurServlet",value = "/findItemJur")
public class FindItemJurServlet extends HttpServlet {
private IJurisdictionService ser = new JurisdictionServiceImpl();
private ResultVO resultVO = new ResultVO();
private static int num = 5;
private static String getChoose,getInfo;
private List<JurisdictionBean> list;
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String choose = request.getParameter("pageParam[choose]");
String info = request.getParameter("pageParam[info]");
String page = request.getParameter("pageParam[page]");
String size = request.getParameter("pageParam[size]");
if(size!=null){
num=Integer.parseInt(size);
}
PageHelper.startPage(page==null?1:Integer.parseInt(page),size==null?num:Integer.parseInt(size));
if(choose!=null && info!=null){
getChoose =choose;
getInfo = info;
list= ser.findByItem(choose,info);
}else{
list= ser.findByItem(getChoose,getInfo);
}
PageInfo pageInfo = PageInfo.of(list);
if(list!=null){
resultVO.setStatus(1);
resultVO.setMessage("成功");
resultVO.setData(pageInfo);
response.getWriter().print(new ObjectMapper().writeValueAsString(resultVO));
}else{
resultVO.setStatus(0);
resultVO.setMessage("失败");
resultVO.setData(null);
response.getWriter().print(new ObjectMapper().writeValueAsString(resultVO));
}
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request,response);
}
}
mybatis-plus
xml
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
java-config
@Configuration
public class MyBatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
java-PageVO
@Data
@AllArgsConstructor
@NoArgsConstructor
public class PageVO<t> {
private Long pageNum;
private Long pageSize;
private Long total;
private List<t> list;
}
java-serviceImpl
@Override
public PageVO<TAddressDO> findAllPageAndSize(TConsumerDO consumerDO, FindAddressDTO findAddressDTO) {
System.out.println(findAddressDTO);
QueryWrapper<TAddressDO> queryWrapper = new QueryWrapper();
if(findAddressDTO.getProvince() != null) {
queryWrapper.like("province", findAddressDTO.getProvince());
queryWrapper.like("city", findAddressDTO.getCity());
if ("收货人".equals(findAddressDTO.getSelect())) {
queryWrapper.like("consignee", findAddressDTO.getInfo());
} else if ("手机号".equals(findAddressDTO.getSelect())) {
queryWrapper.like("phone", findAddressDTO.getInfo());
} else if ("详细地址".equals(findAddressDTO.getSelect())) {
queryWrapper.like("address", findAddressDTO.getInfo());
}
}
queryWrapper.eq("consumer_code",consumerDO.getConsumerCode());
queryWrapper.eq("delete_tab",DeleteStatus.NO_DELETE.getStatus());
Page<TAddressDO> p = new Page<>(findAddressDTO.getPage(),findAddressDTO.getSize());
IPage<TAddressDO> iPage = addressDAO.selectPage(p, queryWrapper);
return new PageVO<TAddressDO>( iPage.getCurrent(),iPage.getSize(),iPage.getTotal(),iPage.getRecords());
}
wrapper构造图
springboot + mybatis plus中的条件构造器queryWrapper、updateWrapper
NoSql
| 组件 |
|---|
| redis |
| MongoDB |
| Elasticsearch |
Redis
<!--Redis依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<!-- 排除lettuce包,使用jedis代替-->
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--Redis客户端:jedis依赖-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.3.0</version>
</dependency>
<!--org.redisson应用于分布式锁的创建-->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.5.0</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.25.Final</version>
</dependency>
<!--guava实现布隆过滤器-->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>29.0-jre</version>
</dependency>
yaml
spring:
redis:
host: localhost
password: 666
port: 8002
java-config
@Configuration
public class RedisConfig {
/**
* 利用依赖jedis框架连接redis;jedis自带了redis五种类型的存取方法;
* 项目中直接采用了直接封装好的框架(RedisCacheUtil),而不用原生的方法了
* 此bean也可以不需要,直接通过依赖
* @return
*/
@Bean
public Jedis jedis(){
Jedis jedis = new Jedis("127.0.0.1",8002);
jedis.auth("666");
return jedis;
}
/**
* 实现Redis的分布式锁,除了自己基于redis client原生api来实现之外,还可以使用开源框架:Redission
* 参考链接:https://blog.csdn.net/xiaoxiaole0313/article/details/107011095/
* @return
*/
@Bean
public RedissonClient redisson() {
Config config = new Config();
SingleServerConfig singleServerConfig = config.useSingleServer();
singleServerConfig.setAddress("redis://127.0.0.1:" + 8002);
singleServerConfig.setTimeout(3000);
// singleServerConfig.setDatabase(database);
singleServerConfig.setPassword("666");
return Redisson.create(config);
}
/**
* 布隆过滤器(Bloom Filter)
* 默认误差率3%。肯定不存在以及可能存在
* 可通过构造函数去设置误差率
* create(Funnel<? super T> funnel, int expectedInsertions, double fpp)
* 参考链接:https://www.cnblogs.com/yulibostu/articles/11716443.html
*/
@Bean
public BloomFilter bloomFilter(){
BloomFilter bloomFilter=BloomFilter.create(Funnels.stringFunnel(Charsets.UTF_8),10000,0.001);
bloomFilter.put("魔警察");
bloomFilter.put("66.0");
return bloomFilter;
}
/**
* 好像可以代替实体类去实现序列化这个接口
* 对于 redis的String、hash、list、set、zSet这几种类型,可以把对象转化为json格式(利用了jackson),而不转化为二进制了
* @param factory
* @return
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
// 使用Jackson2JsonRedisSerialize 替换默认的jdkSerializeable序列化
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
// hash的key也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// value序列化方式采用jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
// hash的value序列化方式采用jackson
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
java-util
@Service(value = "redisCacheUtil")
public class RedisCacheUtil {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
* 指定缓存失效时间
* @param key 键
* @param time 时间(秒)
* @return
*/
public boolean expire(String key, long time) {
try {
if (time > 0) {
redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据key 获取过期时间
* @param key 键 不能为null
* @return 时间(秒) 返回0代表为永久有效
*/
public long getExpire(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
/**
* 判断key是否存在
* @param key 键
* @return true 存在 false不存在
*/
public boolean hasKey(String key) {
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除缓存
* @param key 可以传一个值 或多个
*/
public void del(String... key) {
if (key != null && key.length > 0) {
if (key.length == 1) {
redisTemplate.delete(key[0]);
} else {
redisTemplate.delete(CollectionUtils.arrayToList(key));
}
}
}
/**
* 普通缓存获取
* @param key 键
* @return 值
*/
public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}
/**
* 普通缓存放入
*
* @param key 键
* @param value 值
* @return true成功 false失败
*/
public boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 普通缓存放入并设置时间
* @param key 键
* @param value 值
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
* @return true成功 false 失败
*/
public boolean set(String key, Object value, long time) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
/**
* 递增
* @param key 键
* @param delta 要增加几(大于0)
* @return
*/
public Long incr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递增因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, delta);
}
/**
* 递减
* @param key 键
* @param delta 要减少几(小于0)
* @return
*/
public long decr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递减因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, -delta);
}
/**
* HashGet
* @param key 键 不能为null
* @param item 项 不能为null
* @return 值
*/
public Object hget(String key, String item) {
return redisTemplate.opsForHash().get(key, item);
}
/**
* 获取hashKey对应的所有键值
* @param key 键
* @return 对应的多个键值
*/
public Map<Object, Object> hmget(String key) {
return redisTemplate.opsForHash().entries(key);
}
/**
* HashSet
* @param key 键
* @param map 对应多个键值
* @return true 成功 false 失败
*/
public boolean hmset(String key, Map<String, Object> map) {
try {
redisTemplate.opsForHash().putAll(key, map);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* HashSet 并设置时间
* @param key 键
* @param map 对应多个键值
* @param time 时间(秒)
* @return true成功 false失败
*/
public boolean hmset(String key, Map<String, Object> map, long time) {
try {
redisTemplate.opsForHash().putAll(key, map);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
* @param key 键
* @param item 项
* @param value 值
* @return true 成功 false失败
*/
public boolean hset(String key, String item, Object value) {
try {
redisTemplate.opsForHash().put(key, item, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
* @param key 键
* @param item 项
* @param value 值
* @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
* @return true 成功 false失败
*/
public boolean hset(String key, String item, Object value, long time) {
try {
redisTemplate.opsForHash().put(key, item, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除hash表中的值
* @param key 键 不能为null
* @param item 项 可以使多个 不能为null
*/
public void hdel(String key, Object... item) {
redisTemplate.opsForHash().delete(key, item);
}
/**
* 判断hash表中是否有该项的值
* @param key 键 不能为null
* @param item 项 不能为null
* @return true 存在 false不存在
*/
public boolean hHasKey(String key, String item) {
return redisTemplate.opsForHash().hasKey(key, item);
}
/**
* hash递增 如果不存在,就会创建一个 并把新增后的值返回
* @param key 键
* @param item 项
* @param by 要增加几(大于0)
* @return
*/
public double hincr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, by);
}
/**
* hash递减
* @param key 键
* @param item 项
* @param by 要减少记(小于0)
* @return
*/
public double hdecr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, -by);
}
/**
* 根据key获取Set中的所有值
* @param key 键
* @return
*/
public Set<Object> sGet(String key) {
try {
return redisTemplate.opsForSet().members(key);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 根据value从一个set中查询,是否存在
* @param key 键
* @param value 值
* @return true 存在 false不存在
*/
public boolean sHasKey(String key, Object value) {
try {
return redisTemplate.opsForSet().isMember(key, value);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将数据放入set缓存
* @param key 键
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSet(String key, Object... values) {
try {
return redisTemplate.opsForSet().add(key, values);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 将set数据放入缓存
* @param key 键
* @param time 时间(秒)
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSetAndTime(String key, long time, Object... values) {
try {
Long count = redisTemplate.opsForSet().add(key, values);
if (time > 0)
expire(key, time);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 获取set缓存的长度
* @param key 键
* @return
*/
public long sGetSetSize(String key) {
try {
return redisTemplate.opsForSet().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 移除值为value的
* @param key 键
* @param values 值 可以是多个
* @return 移除的个数
*/
public long setRemove(String key, Object... values) {
try {
Long count = redisTemplate.opsForSet().remove(key, values);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 获取list缓存的内容
* @param key 键
* @param start 开始
* @param end 结束 0 到 -1代表所有值
* @return
*/
public List<Object> lGet(String key, long start, long end) {
try {
return redisTemplate.opsForList().range(key, start, end);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 获取list缓存的长度
* @param key 键
* @return
*/
public long lGetListSize(String key) {
try {
return redisTemplate.opsForList().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 通过索引 获取list中的值
* @param key 键
* @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
* @return
*/
public Object lGetIndex(String key, long index) {
try {
return redisTemplate.opsForList().index(key, index);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 将list放入缓存
* @param key 键
* @param value 值
* @return
*/
public boolean lSet(String key, Object value) {
try {
redisTemplate.opsForList().rightPush(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public boolean lSet(String key, Object value, long time) {
try {
redisTemplate.opsForList().rightPush(key, value);
if (time > 0)
expire(key, time);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
* @param key 键
* @param value 值
* @return
*/
public boolean lSet(String key, List<Object> value) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public boolean lSet(String key, List<Object> value, long time) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
if (time > 0)
expire(key, time);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据索引修改list中的某条数据
* @param key 键
* @param index 索引
* @param value 值
* @return
*/
public boolean lUpdateIndex(String key, long index, Object value) {
try {
redisTemplate.opsForList().set(key, index, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 移除N个值为value
* @param key 键
* @param count 移除多少个
* @param value 值
* @return 移除的个数
*/
public long lRemove(String key, long count, Object value) {
try {
Long remove = redisTemplate.opsForList().remove(key, count, value);
return remove;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
}
java-StringUtile
/**
* 对于一些验证、异常、返回结果等采用,此种归类的工具类,直接调用,方便更改
* 工具类采用静态:在jvm中划分存储空间。不会消失。公有,供其它类调用;缺点:比较占用空间
* 当在其他类调用该静态方法时是随着类的定义而被分配和装载入内存中。而非静态方法属于对象的具体实例,只有在类的对象创建时在对象的内存中才有这个方法的代码段。
* 参考链接:https://blog.csdn.net/myysophia/article/details/88593596
*/
public class StringUtile {
public static final String UPDATE_OK="修改成功";
public static final String UPDATE_ERROR="修改失败";
public static final String BUY_OK="购买成功";
public static final String BUY_NO="购买失败";
public static final int REDIS_TIME=60*10;
public static final int PENETRATE_TIME=3;
}
java-SerializeUtil
/**
* serialize(Object obj) 序列化
* unserizlize(byte[] byt)反序列化
*/
public class SerializeUtil {
public static byte[] serialize(Object obj) {
ObjectOutputStream obi = null;
ByteArrayOutputStream bai = null;
try {
bai = new ByteArrayOutputStream();
obi = new ObjectOutputStream(bai);
obi.writeObject(obj);
byte[] byt = bai.toByteArray();
return byt;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public static Object unserizlize(byte[] byt) {
ObjectInputStream oii = null;
ByteArrayInputStream bis = null;
bis = new ByteArrayInputStream(byt);
try {
oii = new ObjectInputStream(bis);
Object obj = oii.readObject();
return obj;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
java-service
public interface IMovieService {
/**
* 按姓名价格持续电影集合(缓存雪崩——采用随机数设置redis的key值过期时间,避免集体key值失效)
* @param movieName
* @param price
* @return
*/
List<MovieEntity> findAllByMovieNameAndPrice1(String movieName, float price);
/**
* 按姓名价格持续电影集合(缓存穿透——1、对查询为null的key值,直接把value赋值为null;2、布隆过滤器)
* @param movieName
* @param price
* @return
*/
List<MovieEntity> findAllByMovieNameAndPrice2(String movieName, float price);
/**
* 按姓名价格持续电影集合(缓存击穿——对于高并发访问同一个key值,即将失效(过期)时候,
* 由于是分布式不同服务期间去访问同一个数据,因此采用分布式锁机制,redission和zookeeper,本次采用redission)
* @param movieName
* @param price
* @return
*/
List<MovieEntity> findAllByMovieNameAndPrice3(String movieName, float price) throws InterruptedException;
}
java-serviceImpl
@Service
public class MovieServieImpl implements IMovieService {
@Resource
private IMovieEntityDao movieEntityDao;
//原生jedis
@Resource
private Jedis jedis;
//封装redis的工具类,实际采用
@Resource
private RedisCacheUtil redisCacheUtil;
//布隆过滤器——用于穿透,过滤掉不明不白的key
@Resource
private BloomFilter bloomFilter;
//分布式锁——用于击穿,允许一个线程访问数据库,把数据保于预缓存
@Resource
private RedissonClient redisson;
/**
* 避免缓存雪崩——随机设置key过期时间
* @param movieName
* @param price
* @return
*/
@Override
public List<MovieEntity> findAllByMovieNameAndPrice1(String movieName, float price) {
long beforeTime = System.currentTimeMillis();
List<MovieEntity> movieEntityList = null;
//自定义redis缓存中的key值
String key = "movie="+movieName+"and"+price;
movieEntityList = (List<MovieEntity>) redisCacheUtil.get(key);
if(movieEntityList!=null){
return movieEntityList;
}else {
movieEntityList = movieEntityDao.findAllByMovieNameAndPrice(movieName, price);
//StringUtile.REDIS_TIME为600,加1后保证取陌后的区间在1——600
Long time = System.currentTimeMillis() % StringUtile.REDIS_TIME + 1;
redisCacheUtil.set(key, movieEntityList, time);
}
System.out.println("--------------------------------------------------------");
long afterTime = System.currentTimeMillis();
long time = afterTime-beforeTime;
System.out.println(time);
return movieEntityList;
}
/**
* 避免缓存穿透——1、不存在的key,value赋值为null;2、布隆过滤器
* @param movieName
* @param price
* @return
*/
@Override
public List<MovieEntity> findAllByMovieNameAndPrice2(String movieName, float price) {
long beforeTime = System.currentTimeMillis();
List<MovieEntity> movieEntityList = null;
if(bloomFilter.mightContain(movieName) && bloomFilter.mightContain(String.valueOf(price))) {
//自定义redis缓存中的key值
String key = "movie=" + movieName + "and" + price;
movieEntityList = (List<MovieEntity>) redisCacheUtil.get(key);
if (movieEntityList != null) {
return movieEntityList;
} else {
movieEntityList = movieEntityDao.findAllByMovieNameAndPrice(movieName, price);
if (!movieEntityList.isEmpty()) {
//StringUtile.REDIS_TIME为600,加1后保证取陌后的区间在1——600
Long time = System.currentTimeMillis() % StringUtile.REDIS_TIME + 1;
redisCacheUtil.set(key, movieEntityList, time);
} else {
//StringUtile.PENETRATE_TIME为3,不存在的key,设置较短的过期时间
redisCacheUtil.set(key, movieEntityList, StringUtile.PENETRATE_TIME);
}
}
}
System.out.println("--------------------------------------------------------");
long afterTime = System.currentTimeMillis();
long time = afterTime-beforeTime;
System.out.println(time);
return movieEntityList;
}
/**
* 避免缓存击穿——分布式锁redission
* @param movieName
* @param price
* @return
*/
@Override
public List<MovieEntity> findAllByMovieNameAndPrice3(String movieName, float price) throws InterruptedException {
long beforeTime = System.currentTimeMillis();
List<MovieEntity> movieEntityList = null;
if(bloomFilter.mightContain(movieName) && bloomFilter.mightContain(String.valueOf(price))) {
//自定义redis缓存中的key值
String key = "movie=" + movieName + "and" + price;
movieEntityList = (List<MovieEntity>) redisCacheUtil.get(key);
if (movieEntityList != null) {
System.out.println("--------------------------------------------------------");
long afterTime = System.currentTimeMillis();
long time = afterTime-beforeTime;
System.out.println(time);
return movieEntityList;
} else {
//很明显,击穿后获取数据在数据库,所以在此设置线程获取锁
RLock lock = redisson.getLock("movie");
//tryLock(等待获取锁时间,释放锁时间,释放锁时间格式)
if(lock.tryLock(5,5, TimeUnit.SECONDS)) {
movieEntityList = movieEntityDao.findAllByMovieNameAndPrice(movieName, price);
if (!movieEntityList.isEmpty()) {
//StringUtile.REDIS_TIME为600,加1后保证取陌后的区间在1——600
Long time = System.currentTimeMillis() % StringUtile.REDIS_TIME + 1;
redisCacheUtil.set(key, movieEntityList, time);
} else {
//StringUtile.PENETRATE_TIME为3,不存在的key,设置较短的过期时间
redisCacheUtil.set(key, movieEntityList, StringUtile.PENETRATE_TIME);
}
//得到数据,释放锁(快于等待等待释放锁时间,释放锁时间只是为了预防其它问题,避免一个线程一直占用锁,造成死锁现象)
lock.unlock();
}else{
//说明有线程占用锁,要求下一个线程等待
Thread.sleep(600);
return this.findAllByMovieNameAndPrice3(movieName,price);
}
}
}
System.out.println("--------------------------------------------------------");
long afterTime = System.currentTimeMillis();
long time = afterTime-beforeTime;
System.out.println(time);
return movieEntityList;
}
}
java-jedis(五中数据类型的使用)
public class testRedis {
private static Jedis jedis = null;
@Before
public void before(){
jedis = RedisDao.getRedis();
}
@Test
public void testString(){
jedis.set("username","LuJiaHong");
System.out.println(jedis.get("username"));
}
/**
* redis 的 String 类型支持 二进制 存取对象,必先满足对象的序列化 和 反序列化
*/
@Test
public void testObj(){
UserDto userDto = new UserDto("赵云","123456","超级管理员");
//序列化-调用工具类SerializeUtil
jedis.set("user".getBytes(), SerializeUtil.serialize(userDto));
//反序列化
System.out.println((UserDto)SerializeUtil.unserizlize(jedis.get("user".getBytes())));
}
@Test
public void testHash(){
List<UserDto> list = UserDto.userDtoList;
Map<String,String> map = new HashMap<>();
map.put("user1",list.get(0).toString());
map.put("user2",list.get(1).toString());
map.put("user3",list.get(2).toString());
jedis.hmset("users",map);
System.out.println(jedis.hmget("users","user1","user2","user3"));
}
@Test
public void testList(){
jedis.lpush("man","june","tom","jack");
System.out.println(jedis.lpop("man"));
System.out.println(jedis.rpop("man"));
}
@Test
public void testSet(){
jedis.sadd("students1","jim","kok","opo");
jedis.sadd("students2","jim","kok","opo","yui");
System.out.println(jedis.sinter("students1","students2"));
System.out.println(jedis.sdiff("students1","students2"));
System.out.println(jedis.sdiff("students2","students1"));
System.out.println(jedis.sunion("students1","students2"));
}
@Test
public void testZSet(){
Map<String, Double> map1 = new HashMap<>();
for(int i = 0 ; i< 5 ; i++){
map1.put("a"+i,i+0.0);
}
jedis.zadd("num1",map1);
System.out.println(jedis.zrange("num1",0,-1));
jedis.zincrby("num1",6,"a1");
System.out.println(jedis.zrevrange("num1",0,-1));
}
@After
public void after(){
jedis.close();
}
}
MQ
| 组件 | 作用 |
|---|---|
| RabbitMQ | Erlang语言开发,基于AMQP协议来实现。用在企业系统内,对数据一致性、稳定性和可靠性要求很高的场景,对性能和吞吐量的要求还在其次。 |
| ActiveMQ | 类似于ZeroMQ,它能够以代理人和点对点的技术实现队列。同时类似于RabbitMQ,它少量代码就可以高效地实现高级应用场景。 |
| RocketMQ | 在阿里集团被广泛应用在订单,交易,充值,流计算,消息推送,日志流式处理,binglog分发等场景。 |
| Kafka | 日志收集和传输,适合产生大量数据的互联网服务的数据收集业务。 |
| ZeroMQ | 号称最快的消息队列系统,尤其针对大吞吐量的需求场景。 |
RabbitMQ
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
java-MQConfig
/**
* rabbitMQ消息队列用来削峰,是异步处理(即发出请求后不需要立马收到回应,即可执行其它的请求)
*/
@Configuration
public class MQConfig {
//注入工厂(已经预加载)
@Autowired
CachingConnectionFactory connectionFactory;
//rabbitmq模板
@Bean(value = "rabbitTemplate")
public RabbitTemplate rabbitTemplate(){
return new RabbitTemplate(connectionFactory);
}
//创建普通队列
// @Bean
// public Queue createPointQueue(){
// return new Queue("j177_point");
// }
//死信队列
@Bean
public Queue deadQueue(){
return new Queue("deadQueue");
}
/**
*DLX(Dead-Letter-Exchange)也是一个正常的Exchange,和一般的Exchange没有任何的区别,他能在任何的队列上被指定,实际上就是设置某个队列的属性。
* 当这个队列出现死信的时候,RabbitMQ就会自动将这条消息重新发布到Exchange上去,进而被路由到另一个队列。
* 可以监听这个队列中的消息作相应的处理,这个特性可以弥补rabbitMQ以前支持的immediate参数的功能;-------
* 从此可以看出一个消息队列绑定了两个交换机(一个是DLX,一个是自己rounteKey指定的交换机)
* @return生成一个死信交换机对象
*/
@Bean
public DirectExchange deadExchange(){
return new DirectExchange("deadExchange");
}
//死信交换机队列绑定
@Bean
public Binding deadQueueToDeadExchange(Queue deadQueue,DirectExchange deadExchange){
return BindingBuilder.bind(deadQueue).to(deadExchange).with("dead_routing_key");
}
/**
* 死信(dead message,就是没有任何消费者消费)
* @return 与DLX(死信交换机)绑定的一个队列:j177_point;因此上面单独创建的bean队列对象j177_point就没用了,用这个跟DLX绑定的队列对象就行
* 普通队列与DLX绑定的时候,需要指明:1、绑定的死信交换机;2、死信路由key;3、声明队列的TTL(当时间一过期就会通过死信交换机 按死信路由key 指定到 死信队列);---------
* 什么是TTL?即:time to live 生存时间;
* 1、支持消息的过期时间,在消息发送时可以指定。
* 2、支持队列过期时间,在消息入队列开始计算时间,只要超过了队列的超时时间配置,那么消息就会自动的清除
*/
@Bean
public Queue createPointQueue(){
//关联死信交换机
Map<String, Object> args = new HashMap<>(3);
// x-dead-letter-exchange 这里声明当前队列绑定的死信交换机
args.put("x-dead-letter-exchange", "deadExchange");
// x-dead-letter-routing-key 这里声明当前队列的死信路由key
args.put("x-dead-letter-routing-key","dead_routing_key");
// x-message-ttl 声明队列的TTL,有效时间30s,时间一过,要是没有消费者消费,就指定到死信队列;避免暂用队列的空间,也避免队列里面会出现越来越多的死信
args.put("x-message-ttl",30000);
return QueueBuilder.durable("j177_point").withArguments(args).build();
}
@Bean
public Queue createTopicQueue(){
return new Queue("j177_topic");
}
@Bean
public Queue createFanoutQueue(){
return new Queue("j177_fanout");
}
//创建交换机
@Bean
public DirectExchange directExchange(){
return new DirectExchange("directExchange");//点到点,按路由键指定交换机发往的消息队列
}
@Bean
public TopicExchange topicExchange(){
return new TopicExchange("topicExchange");//模糊匹配,通过键可能发往到一个或多个消息队列
}
@Bean
public FanoutExchange fanoutExchange(){ return new FanoutExchange("fanoutExchange");//扇形发往消息队列,每个消息队列都能够收到扇形交换机发往的消息
}
//把队列绑定到交换机
@Bean
public Binding createPointQueueToDirectExchange(Queue createPointQueue, DirectExchange directExchange){
return BindingBuilder.bind(createPointQueue).to(directExchange).with("j177.order");
}
@Bean
public Binding createTopicQueueToTopicExchange(Queue createTopicQueue, TopicExchange topicExchange){
return BindingBuilder.bind(createTopicQueue).to(topicExchange).with("j177.#");
}
@Bean
public Binding createFanoutQueueToFanoutExchange(Queue createFanoutQueue,FanoutExchange fanoutExchange){
return BindingBuilder.bind(createFanoutQueue).to(fanoutExchange);
}
}
java-service
public interface IBuyMovie {
/**
* 创建购买订单(点对点)
* @param order
* @return
*/
boolean createOrderByDirect(OrderEntity order);
/**
* 创建购买订单(模糊)
* @param order
* @return
*/
boolean createOrderByTopic(OrderEntity order);
/**
* 创建购买订单(扇形)
* @param order
* @return
*/
boolean createOrderByFanout(OrderEntity order);
/**
* 通过callback回调函数,判断布尔b值真假,确定消息是否投递到MQ,若b==true,表明消息投递到MQ,则执行updateOrder方法,修改tag为1,表示下单成功,已出票(
* 这里把tag改为1,只是专门针对于消息成功投递到了MQ,并不代表该消息被消费者(movie服务器)接收,若消费者没有接收,才会真正的发送消息给客户,购票失败;
* )
* @param orderNum 订单编号
* @param tag 订单状态
* @return
*/
boolean updateOrder(String orderNum,int tag);
}
java-serviceImpl-生产者-发送消息到队列
@Service
public class OrderService implements IBuyMovie{
@Autowired
RabbitTemplate rabbitTemplate;
@Autowired
private IOrderDao orderDao;
/**
* 构建生产者发送消息到MQ的回调函数,来确定消息投递到MQ是否成功,若成功好执行生产者内部的业务方法(
* 列如下面就是,判断b==true,就表明投递成功,订单系统就按照传递到MQ的唯一标识属性(orderNum)来设置tag=1,表示待出票)
*/
RabbitTemplate.ConfirmCallback callback=(CorrelationData correlationData, boolean b, String s)->{
//获得MQ返回的唯一标志orderNum
String orderNum= correlationData.getId();
if(b){
this.updateOrder(orderNum,1);
}
};
@Override
public boolean createOrderByDirect(OrderEntity order) {
boolean bl=false;
//创建订单
String orderNUm= String.valueOf(System.currentTimeMillis());
//创建时间
LocalDate today = LocalDate.now();
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd");
String dateStr = today.format(fmt);
order.setOrderNum(orderNUm);
order.setPlayDate(dateStr);
order.setTag(0);
//保存订单
Object object= orderDao.save(order);
if(null!=object) {
//创建DTO
OrderDto dto = new OrderDto();
dto.setOrderNum(orderNUm);
dto.setMovieName(order.getMovieName());
dto.setMovieNum(order.getMovieNum());
dto.setPlayDate(dateStr);
//开启手动确认
rabbitTemplate.setMandatory(true);//开启消息确认
//绑定回调函数
rabbitTemplate.setConfirmCallback(callback);
//组装关联数据
CorrelationData correlationData=new CorrelationData(orderNUm);
//发送到MQ
rabbitTemplate.convertAndSend("directExchange","j177.order",dto,correlationData);
bl=true;
}
return bl;
}
@Override
public boolean createOrderByTopic(OrderEntity order) {
boolean bl=false;
//创建订单
String orderNUm= String.valueOf(System.currentTimeMillis());
//创建时间
LocalDate today = LocalDate.now();
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd");
String dateStr = today.format(fmt);
order.setOrderNum(orderNUm);
order.setPlayDate(dateStr);
order.setTag(0);
//保存订单
Object object= orderDao.save(order);
if(null!=object) {
//创建DTO
OrderDto dto = new OrderDto();
dto.setOrderNum(orderNUm);
dto.setMovieName(order.getMovieName());
dto.setMovieNum(order.getMovieNum());
dto.setPlayDate(dateStr);
//发送到MQ
rabbitTemplate.convertAndSend("topicExchange","j177.order",dto);
bl=true;
}
return bl;
}
@Override
public boolean createOrderByFanout(OrderEntity order) {
boolean bl=false;
//创建订单
String orderNUm= String.valueOf(System.currentTimeMillis());
//创建时间
LocalDate today = LocalDate.now();
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd");
String dateStr = today.format(fmt);
order.setOrderNum(orderNUm);
order.setPlayDate(dateStr);
order.setTag(0);
//保存订单
Object object= orderDao.save(order);
if(null!=object) {
//创建DTO
OrderDto dto = new OrderDto();
dto.setOrderNum(orderNUm);
dto.setMovieName(order.getMovieName());
dto.setMovieNum(order.getMovieNum());
dto.setPlayDate(dateStr);
//发送到MQ
rabbitTemplate.convertAndSend("fanoutExchange",dto);
bl=true;
}
return bl;
}
@Override
@Transactional
public boolean updateOrder(String orderNum,int tag) {
boolean bl=false;
int count= orderDao.updateOrder(orderNum,tag);
if(count>0){
bl=true;
}
return bl;
}
}
java-serviceIpml-消费者-从消息队列中拿取监听的消息消费
@Service
public class OrderMQ {
@Autowired
private IMovieDao movieDao;
@Resource
private IMovieService movieService ;
@RabbitListener(queues = {"j177_point","deadQueue"})
public void getOrderByDirect(OrderDto orderDto, Channel channel,@Header(AmqpHeaders.DELIVERY_TAG) long tag){
MovieEntity movieEntity = movieDao.findMovieEntityByMovieName(orderDto.getMovieName());
int movieNums = movieEntity.getMovieNums();
if(movieNums!=0){
int movieNum = orderDto.getMovieNum();
if(movieNums>=movieNum){
movieEntity.setMovieNums(movieNums-movieNum);
movieDao.saveAndFlush(movieEntity);
movieService.updateTag(orderDto.getOrderNum(),3);
OrderMovieDto orderMovieDto = new OrderMovieDto();
orderMovieDto.setOrderNum(orderDto.getOrderNum());
orderMovieDto.setMovieName(orderDto.getMovieName());
orderMovieDto.setMovieNum(orderDto.getMovieNum());
orderMovieDto.setPrice(movieEntity.getPrice());
orderMovieDto.setPriceTotal(movieEntity.getPrice()*orderDto.getMovieNum());
orderMovieDto.setPlayDate(orderDto.getPlayDate());
orderMovieDto.setMovieStatus(3);
System.out.println("购票信息:"+orderMovieDto);
}
}else{
movieService.updateTag(orderDto.getOrderNum(),4);
movieEntity.setMovieTag(1);
movieDao.saveAndFlush(movieEntity);
System.out.println("4-->票已售空");
}
try {
//tag-队列中数据的标记,flase-非批量确认
channel.basicAck(tag,false);
//不确认,false不批量处理,true,不消费,继续保留在队列中
// channel.basicNack(tag,false,true);
} catch (IOException e) {
e.printStackTrace();
}
}
@RabbitListener(queues = "j177_topic")
public void getOrderByTopic(OrderDto orderDto){
System.out.println(orderDto.getMovieName());
}
@RabbitListener(queues = "j177_fanout")
public void getOrderByFanout(OrderDto orderDto){
System.out.println(orderDto.getMovieName());
}
}
SpringCloud
| 组件 | 作用 |
|---|---|
| Nginx | 反向代理负载均衡 |
| zuul | 服务路由和过滤 |
| Eureka | 服务治理(服务注册与发现) |
| Feign | 服务器之间的远程调用 |
| Hystrix | 限流、降级、熔断 |
| Ribbon | 服务内部实现负载均衡 |