依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
    <version>2.1.5.RELEASE</version>
</dependency>

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.0</version>
</dependency>

自定义注解

package com.licai.web.annotations;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @ClassName:  IsBuyGood   
 * @Description:TODO(判断当前用户是否购买指定商品)
 * @param url  未购买,跳转页面的相对路径
 * @param request中需有goodsId   
 * @author: wd
 * @date:  2020年3月20日  上午11:28:45

    * 自定义注解
 * 注解的声明:public @interface 注解名称
 *
 * 元注解:标记注解的注解
 * @Documented:表示该注解会被javadoc命令写入api文档中
 * @Target:注解的标记位置
 *  ElementType.ANNOTATION_TYPE:该注解可以标记别的注解
 *  ElementType.CONSTRUCTOR:标注到构造方法
 *  ElementType.FIELD:标注到成员属性
 *  ElementType.LOCAL_VARIABLE:标注到局部变量
 *  ElementType.METHOD:标注到方法上
 *  ElementType.PACKAGE:标注到包上
 *  ElementType.PARAMETER:标注到方法形参上
 *  ElementType.TYPE:标注到类、接口、枚举类上
 * @Retention:注解的作用范围
 *  RetentionPolicy.SOURCE:注解的有效范围只在源码中,编译后就被丢弃
 *  RetentionPolicy.CLASS:注解有效范围在编译文件中,运行时丢弃
 *  RetentionPolicy.RUNTIME:注解在运行时仍然有效,这个范围的注解可以通过反射获取
 *
 * 注解内的方法声明:
 * 类型 方法名() [defualt 默认值];
 *
 * 注意:
 * 如果一个属性没有设置default默认值,在标记这个注解时,必须给定该属性值
 * 如果一个属性的名字为value,则在赋值时可以省略属性名。当如果需要赋值两个以上的属性,则value不能省略
 * 如果一个属性的类型是数组类型,则应该用{}赋值,如果只要给一个值,{}可以省略

 */
@Target(ElementType.METHOD)//作用范围,方法上
@Retention(RetentionPolicy.RUNTIME)//运行时
@Documented
public @interface IsBuyGood {

    String url();

}

切面类

package com.licai.web.annotations.aspect;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.alibaba.dubbo.config.annotation.Reference;
import com.app.dubboservice.order.service.DubboOrderService;
import com.app.order.model.Order;
import com.app.user.model.UserInfo;
import com.licai.web.annotations.IsBuyGood;

@Aspect
@Component
public class IsBuyGoodAspect {
	
	@Reference(check = false)
	private DubboOrderService dubboOrderService;
	@Value("${spring.profiles.active}")
    private String profile;
	
    /**
     * 环绕增强
     * 判断controller的方法上有没有@IsLogin注解
     * 如果有就对其进行增强
     * 增强效果
     * 1/给方法的形参列表User user 注入值
     * 如果登录了就注入user
     * 如果没登录就注入null
     * 2.如果IsLogin的value=false表示不强制跳转到登录页面
     * 3.如果IsLogin的value=true表示强制跳转到登录页面,一旦发现cookie中没有值直接跳转不允许执行目标方法
     */
    @Around("@annotation(isBuyGood)")
    //表达式代表所有路径下面的xxxController类中的方法带@IsLogin注解的
    public Object isBuyGood(ProceedingJoinPoint proceedingJoinPoint,IsBuyGood isBuyGood) throws Throwable {
        //获得request请求
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        HttpSession seesion = request.getSession();
		Integer userId=null;
        if(seesion!=null){
			UserInfo userInfo = (UserInfo) seesion.getAttribute("loginUser");
			if(userInfo!=null){
				userId=userInfo.getUserId();
			}
		}

        //表示已经登录或者不需要登录就可以继续操作
        //获得形参列表
        Object[] args = proceedingJoinPoint.getArgs();
        boolean isBuy = false;
        //因为登录后需要带returnUrl跳转到之前的页面,所以需要通过request对象获得url
        String requestURL = request.getRequestURL().toString();
        //带参数
        //requestURL.append(request.getQueryString());
        requestURL=requestURL.replace(request.getRequestURI(),"" );
        String url=requestURL+request.getContextPath()+isBuyGood.url();
        //解决中文乱码问题
//            String url = URLEncoder.encode(requestURL.toString(), "utf-8");
        //同样解决&带参数当做一个参数的问题
//            url = url.replace("&","%26");
        if(userId != null){
            
            String goodId = request.getParameter("goodsId");// 商品id
	        if(userId!=null && !goodId.isEmpty()) {
	            Order order = dubboOrderService.queryUserGoodsOrder(Integer.parseInt(goodId),userId);
	            if(order!=null) {
	                isBuy = true;
	            }else {
	            	isBuy = false;
	            }
	            if (profile != null && "dev".equals(profile)) {
	    			// 测试环境
	    			if("10898".equals(goodId)) {
	    				isBuy = true;
	    			}
	    		}else {
	    			if("11033".equals(goodId) || "11149".equals(goodId) || "11183".equals(goodId)) {
	    				isBuy = true;
	    			}
	    		}
	        }
            if(!isBuy) {
            	return "redirect:"+url;
            }
            //false继续执行目标方法
        }

        //执行目标方法  --- 带指定的形参列表
        Object result = proceedingJoinPoint.proceed(args);
        //执行目标方法后的方法返回值就是目标方法的返回值
        return result;
    }
}

使用

@RequestMapping("lianXi")
@IsBuyGood(url="/home")
public String lianXi() {
    
}