SpringBoot的AOP实现!!(手把手教会!超详细的攻略)

面向切面编程(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>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值