# community **Repository Path**: lsua/community ## Basic Information - **Project Name**: community - **Description**: Springboot论坛项目 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2020-08-31 - **Last Updated**: 2023-02-06 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ### 自定义注解检查登录状态 1.编写一个注解类 ```java package com.ls.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface LoginRequired { } ``` 2.编写拦截器,拦截使用该注解的方法 ```java package com.ls.controller.interceptor; import com.ls.annotation.LoginRequired; import com.ls.util.CommunityUtil; import com.ls.util.UserContextHolder; import com.ls.util.UserContextHolder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.lang.reflect.Method; /** * 拦截加了自定义注解 @LoginRequired 的请求 */ @Component public class LoginRequiredInterceptor implements HandlerInterceptor { @Autowired UserContextHolder hostHolder; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 判断拦截到的是否是方法 if (handler instanceof HandlerMethod) { HandlerMethod handlerMethod = (HandlerMethod) handler; Method method = handlerMethod.getMethod(); // 获取 @LoginRequired 注解 LoginRequired loginRequired = method.getAnnotation(LoginRequired.class); String xRequestedWith = request.getHeader("x-requested-with"); // 需要登录,用户没有登录 if (loginRequired != null && hostHolder.getUser() == null) { if ("XMLHttpRequest".equals(xRequestedWith)) { // 如果是异步请求 response.setContentType("application/plain;charset=utf-8"); response.getWriter().write(CommunityUtil.getJSONString(302, "您还未登录,请登录!")); } else { response.sendRedirect(request.getContextPath() + "/toLogin"); } return false; } } return true; } } ``` 3.到mvc配置类注册拦截器 ```java package com.ls.config; import com.ls.controller.interceptor.AlphaInterceptor; import com.ls.controller.interceptor.LoginRequiredInterceptor; import com.ls.controller.interceptor.LoginTicketInterceptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class WebMvcConfig implements WebMvcConfigurer { @Autowired private AlphaInterceptor alphaInterceptor; @Autowired private LoginTicketInterceptor loginTicketInterceptor; @Autowired private LoginRequiredInterceptor loginRequiredInterceptor; // 拦截器 @Override public void addInterceptors(InterceptorRegistry registry) { // registry.addInterceptor(alphaInterceptor) // .excludePathPatterns("/**/*.css","/**/*.js","/**/*.jpg","/**/*.jpeg","/**/*.png") // .addPathPatterns("/"); // 注册拦截器 拦截除静态资源的所有请求 registry.addInterceptor(loginTicketInterceptor) .excludePathPatterns("/**/*.css","/**/*.js","/**/*.jpg","/**/*.jpeg","/**/*.png"); registry.addInterceptor(loginRequiredInterceptor) .excludePathPatterns("/**/*.css","/**/*.js","/**/*.jpg","/**/*.jpeg","/**/*.png"); } } ``` Mybatis允许使用`foreach`标签对集合进行遍历,遍历ids,结果为以"("开始,每一项为id,间隔符为",",以")"结尾. ```sql update community.message set status = #{status} where id in #{id} ``` ### 统一处理异常 表现层处理异常 把错误页面放到templates目录下的error目录里面,用对应的错误命名页面,Springboot就会自动跳转到发生对应错误状态的页面。 * @ControllerAdvice 用于修饰类,表示该类是Controller的全局配置类。 在此类中,可以对Controller进行如下三种全局配置:异常处理方案、绑定数据方案、绑定参数方案。 * @ExceptionHandler 用于修饰方法,该方法会在Controller出现异常后被调用,用于处理捕获到的异常。 * @ModelAttribute 用于修饰方法,该方法会在Controller方法执行前被调用,用于为Model对象绑定参数。 * @DataBinder 用于修饰方法,该方法会在Controller方法执行前被调用,用于绑定参数的转换器。 ```java @ControllerAdvice(annotations = Controller.class) public class ExceptionAdvice { private static final Logger logger = LoggerFactory.getLogger(ExceptionAdvice.class); // 用于修饰方法,该方法会在Controller出现异常后被调用,用于处理捕获到的异常。 @ExceptionHandler({Exception.class}) public void handleException(Exception e, HttpServletRequest request, HttpServletResponse response) throws IOException { logger.error("服务器发生异常: " + e.getMessage()); for (StackTraceElement element : e.getStackTrace()) { logger.error(element.toString()); } String xRequestedWith = request.getHeader("x-requested-with"); if ("XMLHttpRequest".equals(xRequestedWith)) { // 异步请求 response.setContentType("application/plain;charset=utf-8"); PrintWriter writer = response.getWriter(); writer.write(CommunityUtil.getJSONString(500, "服务器异常")); } else { // 重定向到错误页面 response.sendRedirect(request.getContextPath() + "/error"); } } } ``` ### 统一处理日志 AOP概念 * Aspect Oriented Programing,即面向方面(切面)编程。 * AOP是一种编程思想,是对OOP的补充,可以进一步提高编程的效率。 ![img](readme/img.png) AOP的实现 * Aspectj * Spring AOP Spring AOP * JDK动态代理 java提供的动态代理技术,可以在运行时创建接口的代理实例。 Spring AOP默认采用此方式,在接口代理实例中织入代码。 * GGLib动态代理 采用底层的字节码技术,在运行时创建子类代理实例。 当目标对象不存在接口时,Spring AOP会采用此种方式,在子类实例中织入代码。 ```java @Component @Aspect public class ServiceLogAspect { private static final Logger logger = LoggerFactory.getLogger(ServiceLogAspect.class); // 切入点 @Pointcut("execution(* com.ls.service.*.*(..))") public void pointcut() { } @Before("pointcut()") public void before(JoinPoint joinPoint) { // 用户(ip)[1.2.3.4],在(时间)[xxx],访问了[com.nowcoder.community.service.xxx()]. ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); String requestURI = request.getRequestURI(); String ip = request.getRemoteHost(); LocalDate date = LocalDate.now(); LocalTime time = LocalTime.now().withNano(0); String now = date + " " + time; String target = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName(); logger.info("访问了" + requestURI); // 打印url logger.info(String.format("用户[%s],在[%s],访问了[%s].", ip, now, target)); } } ``` ## 4 ### 4.1 Redis入门 Springboot整合redis 导入包 ```xml org.springframework.boot spring-boot-starter-data-redis ``` 配置redis ```properties # RedisProperties spring.redis.database=11 spring.redis.host=localhost spring.redis.port=6379 ``` 写redis配置类 ```java @Configuration public class RedisConfig { @Bean public RedisTemplate redisTemplate(RedisConnectionFactory factory) { RedisTemplate template = new RedisTemplate<>(); template.setConnectionFactory(factory); // 设置key的序列化方式 template.setKeySerializer(RedisSerializer.string()); // 设置value的序列化方式 template.setValueSerializer(RedisSerializer.json()); // 设置hash的key的序列化方式 template.setHashKeySerializer(RedisSerializer.string()); // 设置hash的value的序列化方式 template.setHashValueSerializer(RedisSerializer.json()); template.afterPropertiesSet(); return template; } } ```