面向切面编程(AOP)概述
面向切面编程(Aspect-Oriented Programming, AOP)是一种编程范式,它允许开发者将横切关注点(cross-cutting concerns)从业务逻辑中分离出来,以提高代码的模块化和重用性。横切关注点是那些影响多个类和模块的关注点,例如日志记录、事务管理、安全性等。AOP的核心概念包括切面(Aspect)、连接点(Join Point)、通知(Advice)和切入点(Pointcut)。
Spring AOP与AspectJ
Spring AOP是Spring框架提供的AOP实现,它基于代理模式,支持运行时增强。Spring AOP适用于大多数应用场景,尤其是当切面的数量不是很多时。AspectJ是一个独立的AOP框架,它提供了比Spring AOP更全面的功能,包括编译时和加载时的增强。Spring框架集成了AspectJ,使得开发者可以在Spring应用中使用AspectJ的高级功能。
AOP的优势
AOP的主要优势包括减少代码重复、提高开发效率、简化维护和提高代码的可读性。通过使用AOP,开发者可以集中精力编写业务逻辑,而将非核心的公共服务(如日志记录和事务管理)封装在切面中,从而降低了系统各部分之间的耦合度。
AOP的应用场景
AOP在多种场景中都非常有用,例如:
- 日志记录:可以在不修改业务代码的情况下,统一添加日志记录功能。
- 性能监控:通过AOP可以在方法执行前后添加计时代码,以监控和分析方法的性能。
- 事务管理:Spring的声明式事务管理就是基于AOP实现的,可以轻松地为业务方法添加事务边界。
- 安全性:可以使用AOP在方法级别实现访问控制和安全检查。
- 异常处理:可以通过AOP在特定的异常发生时执行特定的处理逻辑。
代码编写
anno层
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface MyLog { }
aspect层
@Component @Aspect @Slf4j public class MyLogAspect { @Autowired private HttpServletRequest request; @Autowired private JWTUtils jwtUtils; @Autowired private OperateLogMapper operateLogMapper; @Around("@annotation(cn.wolfcode.anno.MyLog)") public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable { //操作人 id --当前登录员工的id //获取请求头中的JWT令牌,解析令牌--就能获取 登录员工 String jwt = request.getHeader("token"); Claims claims = jwtUtils.parseJWT(jwt); Integer operateUser = (Integer) claims.get("id"); //当前操作时间 Date operateTime = new Date(); //操作类名 String className = joinPoint.getTarget().getClass().getName(); //操作方法 String methodName = joinPoint.getSignature().getName(); //操作方法参数 Object[] args = joinPoint.getArgs(); String methodParams = Arrays.toString(args); long begin = System.currentTimeMillis(); //调用原始目标方法运行 Object result = joinPoint.proceed(); long end = System.currentTimeMillis(); //方法返回值 String returnValue = JSONObject.toJSONString(result); //操作耗时时间 Long costTime = end - begin; //记录日志 OperateLog operateLog = new OperateLog(null,operateUser,operateTime,className,methodName,methodParams,returnValue,costTime); operateLogMapper.insert(operateLog); log.info("记录AOP操作日志,{}", operateLog); return result; }
实体类
@Data @NoArgsConstructor @AllArgsConstructor public class OperateLog { private Integer id; //ID private Integer operateUser; //操作人ID private Date operateTime; //操作时间 private String className; //操作类名 private String methodName; //操作方法名 private String methodParams; //操作方法参数 private String returnValue; //操作方法返回值 private Long costTime; //操作耗时 }
mapper层
public interface OperateLogMapper { //插入日志数据 @Insert("insert into operate_log (operate_user, operate_time, class_name, method_name, method_params, return_value, cost_time) " + "values (#{operateUser}, #{operateTime}, #{className}, #{methodName}, #{methodParams}, #{returnValue}, #{costTime});") void insert(OperateLog operateLog); }
@Slf4j @Component public class LoginCheckInterceptor implements HandlerInterceptor { @Autowired private JwtUtils jwtUtils; public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler) throws Exception { try { jwtUtils.parseJWT(req.getHeader("token")); return true; } catch (Exception e) { resp.setContentType("application/json;charset=utf-8"); Result not_login = Result.error("NOT_LOGIN"); String s = JSONObject.toJSONString(not_login); resp.getWriter().println(s); e.printStackTrace(); return false; } }
@Configuration public class Webconfig implements WebMvcConfigurer { //拦截器对象 @Autowired private LoginCheckInterceptor loginCheckInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { //注册自定义拦截器对象 registry.addInterceptor(loginCheckInterceptor) .addPathPatterns("/**")//拦截所有请求 .excludePathPatterns("/login");//不拦截登录请求 } }
拓展
动态SQL的概念和用途
动态SQL是一种在程序运行时根据不同的条件构建和执行SQL语句的技术。它允许开发者编写灵活的查询,这些查询可以根据用户输入、应用状态或其他动态变化的因素来改变。动态SQL在需要根据不同条件组合或排除SQL子句时特别有用,例如在构建复杂的WHERE子句或执行动态更新和插入操作时。
MyBatis中的动态SQL
MyBatis是一个流行的持久层框架,它支持动态SQL。MyBatis的动态SQL允许在XML映射文件或注解中使用特定的标签(如<if>
, <choose>
, <trim>
, <foreach>
等)来根据条件动态地组装SQL语句。这些标签可以在运行时根据传递给MyBatis的参数来决定是否包含某个部分的SQL代码,从而避免了手动拼接字符串和处理潜在的SQL注入风险。
使用动态SQL的优势
- 减少代码重复:通过动态SQL,可以在一个地方编写通用的SQL模板,并根据不同的条件动态地添加或省略部分SQL代码,从而减少了代码的重复。
- 提高代码可读性:动态SQL通过使用标签来组织条件逻辑,使得SQL代码更加清晰易懂。
- 易于维护:当业务规则变化时,只需修改动态SQL中的条件逻辑,而无需重构整个SQL语句。
- 提高安全性:MyBatis的动态SQL通过预编译的方式来执行SQL,这有助于防止SQL注入攻击。
动态SQL的实际应用场景
- 构建复杂的WHERE子句:根据用户的多个筛选条件动态地添加WHERE条件。
- 执行动态的CRUD操作:根据不同的业务需求动态地插入、更新或删除数据库记录。
- 条件性的JOIN:根据某些条件决定是否加入特定的表。
- 动态排序:根据用户的选择动态地改变查询结果的排序方式。
结论
动态SQL是数据库编程中的一个重要工具,它提供了编写灵活和可维护SQL代码的能力。MyBatis等框架通过提供动态SQL的支持,使得这一技术更加容易被广泛采用。在设计数据库交互时,考虑使用动态SQL可以提高应用的灵活性和响应能力。
<!--更新--> <update id="update"> update emp <set> <if test="username!=null and username !=''"> username = #{username}, </if> <if test="name!=null and name!=''"> name = #{name}, </if> <if test="image!=null and image!=''"> image = #{image}, </if> <if test="gender!=null"> gender=#{gender}, </if> <if test="job != null"> job = #{job}, </if> <if test="hiredate != null"> hiredate = #{hiredate}, </if> <if test="deptId != null"> dept_id = #{deptId}, </if> <if test="updateTime != null"> update_time = #{updateTime} </if> </set> where id = #{id} </update> <delete id="delete"> delete from emp where id in <foreach collection="ids" open="(" separator="," close=")" item="id"> #{id} </foreach> </delete> <!--条件查询--> <select id="list" resultType="cn.wolfcode.domain.Emp"> select * from emp <where> <if test="name != null and name != ''"> name like concat('%',#{name},'%') </if> <if test="gender != null"> and gender=#{gender} </if> <if test="begin != null and end != null"> and hiredate between #{begin} and #{end} </if> </where> order by update_time desc </select> </mapper>