springboot注解和Aspect切面

场景分析
    public User gerUser(String id) {
        //1.查询缓存
        boolean cache = CacheUtil.isCache(id);
        //2.缓存命中,返回数据
        if (cache) {
            System.out.println("缓存命中了-----------------");
            return CacheUtil.getCache(id);
        }
        //3.缓存没有命中,查询数据库
        User user = UserMapper.getUserById(id);
        //4.写入缓存
        CacheUtil.cache(id, user);
        //5.写入日志
        LogUtils.log(user);
        return user;
    }

存在问题:代码臃肿。违背了业务接口的单一职责原理。代码复用性很差。

使用注解和切面改进
1.pom依赖
 <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.5.RELEASE</version>
        <relativePath/>
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.68</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
1.application.properties
server.port=8900
2.App.java
@SpringBootApplication
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }
}
3.User.java
@Data
public class User {
    private String id;
    private String name;
    private Integer age;
}
4.UserMapper.java
public class UserMapper {
	//模拟数据库,实际可以通过mybatis整合查询数据库
    public static User getUserById(String id){
        System.out.println("查询数据库");
        User user = new User();
        user.setId(id);
        user.setName("tom-"+ id);
        user.setAge(22);
        return user;
    }
}
5.UserService.java
@Service
public class UserService {
    //原始代码
    public User gerUser(String id) {
        //1.查询缓存
        boolean cache = CacheUtil.isCache(id);
        //2.缓存命中,返回数据
        if (cache) {
            System.out.println("缓存命中了-----------------");
            return CacheUtil.getCache(id);
        }
        //3.缓存没有命中,查询数据库
        User user = UserMapper.getUserById(id);
        //4.写入缓存
        CacheUtil.cache(id, user);
        //5.写入日志
        LogUtils.log(user);
        return user;
    }
    //该进后的代码  添加了该注解,查询缓存写日志的工作在切面中实现了
    //注意这里使用了el表达式,通过 #id获取传入的参数。
    @LogCache(key = "#id")
    public User gerUserAnno(String id) {
        User user = UserMapper.getUserById(id);
        return user;
    }
}
6.CacheUtil.java
public class CacheUtil {
	//模拟redis。实际可以整合redis
    public static Map<String, User> map = new HashMap<>();
    static {
        User user = new User();
        user.setId("1");
        user.setName("tom");
        user.setAge(22);
        map.put("1", user);
    }
    public static boolean isCache(String key) {
        return map.containsKey(key);
    }
    public static User getCache(String key) {
        return map.get(key);
    }
    public static void cache(String key, User user) {
        System.out.println("写入缓存--------");
        map.put(key, user);
    }
}
7.核心:LogCacheAspect.java
@Aspect
@Component
public class LogCacheAspect {

    // 代表ai.test.service包下所有的类(第2个.),所有方法(第3个.),参数不限(括号内两个..),返回值不限(第1个.)
    @Pointcut("execution( * ai.test.service.*.*(..))")
    public void pointCut() {
    }

    // 匹配所有带有 @LogCache的注解
    @Pointcut("@annotation(ai.test.annotation.LogCache)")
    public void pointCut2() {
    }

    //环绕通知 执行方法之前做什么,执行方法之后做什么
    @Around(value = "pointCut2()")
    public Object doAroundAdvice(ProceedingJoinPoint proceedingJoinPoint) {
        //拦截的是一个方法,这个proceedingJoinPoint就代表了整个方法。
        Object proceed = null;
        try {
            System.out.println("环绕通知----------前--------------");
            //获取注解上的参数
            MethodSignature methodSignature = (MethodSignature) (proceedingJoinPoint.getSignature()); //这是一个方法的标识
            //获取到拦截的方法
            Method method = methodSignature.getMethod();
            //从方法上获取到注解(这个注解也可以直接通过参数传递进来)
            LogCache annotation = method.getAnnotation(LogCache.class);
            String id = annotation.key();
            System.out.println("注解上的参数是:" + id); //直接获取到的值是: #id (没有被el表达式解析过)
            System.out.println("需要经过el表达式解析id,具体如下");
            //获取方法的形参
            Parameter[] parameters = method.getParameters();
            String[] parameterNames = new String[parameters.length];
            for (int i = 0; i < parameters.length; i++) {
                parameterNames[i] = parameters[i].getName();
            }
            //获取方法的实参
            Object[] args = proceedingJoinPoint.getArgs();
            id = SpelParser.getKey(id, parameterNames, args);
            System.out.println("el表达式处理后的id:" + id);
            boolean cache = CacheUtil.isCache(id);
            //2.缓存命中,返回数据
            if (cache) {
                System.out.println("缓存命中了-----------------");
                return CacheUtil.getCache(id);
            }
            proceed = proceedingJoinPoint.proceed(); //执行主方法
            System.out.println("环绕通知-----------后-------------");
            User user = (User) proceed;
            //4.写入缓存
            CacheUtil.cache(id, user);
            //5.写入日志
            LogUtils.log(user);

        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return proceed;
    }

}
8.LogCache.java
@Target({ElementType.METHOD}) //作用在方法上
@Retention(RetentionPolicy.RUNTIME) //运行时
public @interface LogCache {
    String key();
}
9.SpelParser.java
//EL表达式中 占位符的解析
public class SpelParser {
    private static ExpressionParser parser = new SpelExpressionParser();
    /**
     *
     * @param key  el表达式字符串,站位符以#开头
     * @param parameterNames 形参名称,方法中传入的名称,占位符名称
     * @param args    实参
     * @return
     */
    public static String getKey(String key,String[] parameterNames,Object[] args){
        //1.把字符串转变为el表达式
        Expression exp = parser.parseExpression(key);
        //2.将形参和形参值以配对的方式配置到赋值上下文中。就是一个map
        EvaluationContext context = new StandardEvaluationContext();
        if(args.length <=0){
            return null;
        }
        for (int i = 0; i < args.length; i++) {
            context.setVariable(parameterNames[i],args[i]);
        }
        //3.根据赋值上下文运算el表达式
        return exp.getValue(context,String.class);
    }

    public static void main(String[] args) {
        String key = "#name+' '+#age"; //el表达式  一定要写成这样
        //形参名称
        String name = "name";
        String age = "age";
        String[] parameterNames = new String[]{name,age};
        //实参 args赋值
        Object[] argsarr = new Object[2];
        argsarr[0] = "tom";
        argsarr[1] = 22;
        System.out.println(SpelParser.getKey(key,parameterNames,argsarr));
    }
}
10.AppTest.java(test/java目录下)
@SpringBootTest(classes = App.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class AppTest {
    @Test
    public void testAnno() {
        // 1.获取一个类
        Class<UserService> aClass = UserService.class;
        Method[] methods = aClass.getMethods();
        for (Method method : methods) {
            LogCache annotation = method.getAnnotation(LogCache.class);
            if (annotation != null) {
                //有这个注解
                String key = annotation.key();
                System.out.println(key);
            }
        }
    }

    @Autowired
    private UserService userService;
	//参数值为2 回去查询数据库。参数值为1,会直接命中缓存。
    @Test
    public void testuser(){
        userService.gerUserAnno("2");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值