仿牛客项目地址https://gitee.com/Sher-Locked/niuke
技术架构
功能模块
用户:
论坛:
交友:
断点调试和日志功能。
日志:
//properties
logging.level.com.example.niuke = debug
logging.file.name=d:/log/niuke/xxx.log //存放日志路径
//java
private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
logger.debug("debug");
logger.info("info");
logger.warn("warn");
logger.error("error");
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-mailartifactId>
dependency>
spring.mail.host=smtp.qq.com
spring.mail.port=465
spring.mail.username=[email protected]
spring.mail.password=xx.xx.xx
spring.mail.protocol=smtp
spring.mail.properties.mail.smtp.ssl.enable = true
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper messageHelper = new MimeMessageHelper(message);
messageHelper.setFrom(from);
messageHelper.setTo(to);
messageHelper.setSubject(subject);
messageHelper.setText(content, true);
mailSender.send(messageHelper.getMimeMessage());
Context content = new Context();//Thymeleaf的
content.setVariable("username", "愿你被世界温柔以待!");
String process = templateEngine.process("/email/demo", content);
mailClient.sendMail(to, subject, process);
Cookie
可以通过@CookieValue("code") String code
获取对应的cookie值
//创建cookie
public void setCookie(HttpServletResponse res) {
Cookie cookie = new Cookie("code", CommunityUtil.generateUUID());
cookie.setPath("/test");//cookie作用范围
cookie.setMaxAge(60*10);//cookie存活时间
res.addCookie(cookie);//添加cookie
return "set Cookie";
}
Session
通过session.getAttribute(" ");取值
public String test(HttpSession session){
session.setAttribute("id", 1);
session.setAttribute("naem","dongfang");
return "set session";
}
分布式不用Session的原因:
多服务器时可能在服务端有不同的session,产生session共享问题
解决方案:Session一致性(我的另一篇博文)
<dependency>
<groupId>com.github.pengglegroupId>
<artifactId>kaptchaartifactId>
<version>2.3.2version>
dependency>
@Bean
public Producer kaptchaProducer() {
Properties properties = new Properties();
properties.setProperty("kaptcha.image.width", "100");
properties.setProperty("kaptcha.iamge.height", "40");
properties.setProperty("kaptcha.textproducer.font.size", "32");
properties.setProperty("kaptcha.textproducer.font.color", "0,0,0");
properties.setProperty("kaptcha.textproducer.char.string", "0123456789ABCDEFGHIJKLMNOPQ");
properties.setProperty("kaptcha.textproducer.char.length", "4");
properties.setProperty("kaptcha.noise.impl", "com.google.code.kaptcha.impl.NoNoise");
DefaultKaptcha kaptcha = new DefaultKaptcha();
Config config = new Config(properties);
kaptcha.setConfig(config);
return kaptcha;
}
String text = kaptchaProducer.createText();
BufferedImage image = kaptchaProducer.createImage(text);
//验证码存入session
session.setAttribute("kaptcha",text);
//给浏览器声明返回的类型
response.setContentType("image/png");
try {
OutputStream os = response.getOutputStream();
ImageIO.write(image, "png", os);
} catch (IOException e) {
e.printStackTrace();
}
拦截器的应用:
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginTicketInterceptor)
.excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg");
}
}
@Component
public class HostHolder {
private ThreadLocal<User> users = new ThreadLocal<>();
public void setUser(User user) {
users.set(user);
}
public User getUser() {
return users.get();
}
public void clear() {
users.remove();
}
}
前缀树:
敏感词过滤器:
οnclick="send();"
function send() {
$.post(
"/xxx/xxx",
{"name":"xxx","age":"xxx"},
function(data) {
console.log(data);
}
)
}
discussPost.setTitle(HtmlUtils.htmlEscape(discussPost.getTitle()));//防止注入
discussPost.setContent(HtmlUtils.htmlEscape(discussPost.getContent()));//防止注入
ACID
Spring事务管理
声明式事务
@Transaction(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)
编程式事务
@Autowired
private TransactionTemplate transactionTemplate;
transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITED);
transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
return transactionTemplate.execute(new TransactionCallback<Object>(){ 重写方法 });
@ControllerAdvice 修饰类
@ExceptionHandler 修饰方法
@ModelAttribute 修饰方法
@DataBinder 修饰方法
AOP的实现:
SpringAOP:
Redis编程式事务
@Test
public void testTransactional() {
Object obj = redisTemplate.execute(new SessionCallback() {
@Override
public Object execute(RedisOperations redisOperations) throws DataAccessException {
String redisKey = "test";
//启用事务
redisOperations.multi();
redisOperations.opsForSet().add(redisKey, "dongfang");
redisOperations.opsForSet().add(redisKey, "wangquan");
redisOperations.opsForSet().add(redisKey, "tushan");
return redisOperations.exec();
}
});
}
Kafka 是一个分布式流媒体平台。
应用:消息系统、日志收集、用户行为追踪、流式处理。
特点:高吞吐量、消息持久化、高可靠性、高扩展性。
zookeeper-server-start.bat config\zookeeper.properties
开启zookeeper
kafka-server-start.bat config\server.properties
开启kafka
kafka-topics.bat --create --bootstrap-server localhost:9092 --replication-factor 1 --partitions 1 --topic test
新建类别
kafka-console-producer.bat --broker-list localhost:9092 --topic test
开启生产者
kafka-console-consumer.bat --bootstrap-server localhost:9092 --topic test --from-beginning
开启消费者
阻塞队列
BlockingQueue
解决线程通信的问题
阻塞方法:put、take
生产者消费者模式
生成者:产生数据的线程
消费者:使用数据的线程
实现类
ArrayBlockingQueue
LinkedBlockingQueue
PriorityBlockingQueue
<dependency>
<groupId>org.springframework.kafkagroupId>
<artifactId>spring-kafkaartifactId>
dependency>
zookeeper-server-start.bat config\zookeeper.properties
开启zookeeperkafka-server-start.bat config\server.properties
开启kafka#kafka
spring.kafka.bootstrap-servers=localhost:9092
spring.kafka.consumer.group-id=niuke-consumer-group
spring.kafka.consumer.enable-auto-commit=true
#ms
spring.kafka.consumer.auto-commit-interval=3000
@SpringBootTest
public class KafkaTest {
@Resource
private KafkaProducer kafkaProducer;
@Test
public void testKafka() {
kafkaProducer.sendMessage("test", "你好");
kafkaProducer.sendMessage("test", "在吗");
try {
Thread.sleep(1000 * 10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@Component
class KafkaProducer{
@Resource
private KafkaTemplate kafkaTemplate;
public void sendMessage(String topic, String content) {
kafkaTemplate.send(topic, content);
}
}
@Component
class KafkaConsumer {
@KafkaListener(topics = {"test"})
public void handleMessage(ConsumerRecord record) {
System.out.println(record.value());
}
}
分布式、RestFul 风格的搜索引擎,各种类型的数据的检索,实时搜索服务,PB级数据
术语:
索引(数据库)、类型(表)、文档(行)(Json)、字段(列)。
集群、节点、分片(shards)、副本(replicas)(备份)。
配置:
/plugins/ik
下curl -X GET "localhost:9200/_cat/health?v"
查看健康状况
curl -X GET "localhost:9200/_cat/nodes?v"
查看连接状况
curl -X GET "localhost:9200/_cat/indices?v"
查看索引
curl -X PUT "localhost:9200/test"
新建索引
curl -X DELETE "localhost:9200/test"
删除索引
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-elasticsearchartifactId>
dependency>
# ElasticSearch
spring.elasticsearch.rest.username=niuke
# 9200http 9300tcp
spring.elasticsearch.rest.uris=http://localhost:9200
@Document(indexName = "discusspost", indexStoreType = "_doc", shards = 6, replicas = 3)
public class DiscussPost {
@Id
private int id;
@Field(type = FieldType.Integer)
private int userId;
@Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_smart")
private String title;
@Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_smart")
private String content;
@Field(type = FieldType.Integer)
private int type;//0-普通,1-置顶
@Field(type = FieldType.Integer)
private int status;//0-正常,1-精华,2-拉黑
@Field(type = FieldType.Date)
private Date createTime;
@Field(type = FieldType.Integer)
private int commentCount;
@Field(type = FieldType.Double)
private double score;
@Repository
public interface DiscussPostRepository extends ElasticsearchRepository<DiscussPost, Integer> {
}
NativeSearchQuery build = new NativeSearchQueryBuilder()
.withQuery(QueryBuilders.multiMatchQuery(keyword, "title", "content"))
.withSort(SortBuilders.fieldSort("type").order(SortOrder.DESC))
.withSort(SortBuilders.fieldSort("score").order(SortOrder.DESC))
.withSort(SortBuilders.fieldSort("createTime").order(SortOrder.DESC))
.withPageable(PageRequest.of(0, 10))
.withHighlightFields(
new HighlightBuilder.Field("title").preTags("").postTags(""),
new HighlightBuilder.Field("content").preTags("").postTags("")
).build();
<meta name="_csrf" th:content="${_csrf.token}">
<meta name="_csrf_header" th:content="${_csrf.headerName}"/>
var token = $("meta[name='_csrf']").attr("content");
var header = $("meta[name='_csrf_header']").attr("content");
$(document).ajaxSend(function (e, xhr, options){
xhr.setRequestHeader(header, token)
});
<dependency>
<groupId>org.thymeleaf.extrasgroupId>
<artifactId>thymeleaf-extras-springsecurity5artifactId>
dependency>
<html xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
对字段 sec:authorize="hasAnyAuthority('moderator')
一些JS代码
$("#wonderfulButton").attr("disabled","disabled"); 设置其不可用
location.href = "/index"; 跳转页面
HyperLogLog(合并和去重)
Bitmap(对位的运算)
用户:
论坛:
交友:
当把一些用户删除的时候,他们的推文还在,导致展示推文的时候会查不到这个用户以及推文。
报错信息:
There was an unexpected error (type=Internal Server Error, status=500). An error happened during template parsing (template: "class path resource [templates//index.html]") org.thymeleaf.exceptions.TemplateInputException: An error happened during template parsing (template: "class path resource [templates//index.html]")
同样:我将149号用户删除了呜呜呜呜,找不到用户的名字,一直报错
@Autowired和@Resource的区别
Hikari和Druid的区别
Spring
Impl实现同一个接口时,实现IOC时对于容器难以判断 1.加@Primary
注解2.@Repository
时加name,注入时加@Qualifier
HTTP:request.getMethod .getServletPath .getHeadernames 对应发送HTTP的头文本
HTTP:response.setContentType(“text/html;charset=utf-8”) 对应返回HTTP的头文本
@RequestParam
的一些参数 name=" " 对应前端name,required=" " ,defaultValue=" " 默认值