您当前的位置: 首页 > 

java持续实践

暂无认证

  • 5浏览

    0关注

    746博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

设计模式 外观模式

java持续实践 发布时间:2022-03-26 18:29:45 ,浏览量:5

文章目录
      • 外观模式 情景
      • 外观模式任务实战
        • 不使用外观模式的写法
        • 使用外观模式的写法 (行动)

外观模式 情景

外观模式用于降低调用方的使用接口的复杂逻辑组合。对服务中的通用性复杂逻辑进行中间件层包装,让使用方可以只关心业务开发。

外观模式任务实战

此案例是对接口的访问权限进行控制, 只对添加了白名单的人, 进行接口访问.

不使用外观模式的写法

创建tutorials-13.0-1项目 白名单的创建方式如下. 直接在接口中进行白名单控制, 这样代码耦合度太高, 且每个接口都这样改的话, 工作量也大.

public class HelloWorldController {
    public UserInfo queryUserInfo(@RequestParam String userId) {

        // 做白名单拦截
        List userList = new ArrayList();
        userList.add("1001");
        userList.add("aaaa");
        userList.add("ccc");
        if (!userList.contains(userId)) {
            return new UserInfo("1111", "非白名单可访问用户拦截!");
        }
        return new UserInfo("虫虫:" + userId, 19, "天津市南开区旮旯胡同100号");
    }
}
使用外观模式的写法 (行动)

创建tutorials-13.0-0模块, 引入pom如下

4.0.0

    tutorials-13.0-0
    org.example
    1.0-SNAPSHOT

    
        org.springframework.boot
        spring-boot-starter-parent
        2.1.2.RELEASE
         
    

    
        UTF-8
        UTF-8
        1.8
    

    
        
            org.springframework.boot
            spring-boot-starter-web
        
        
            org.springframework.boot
            spring-boot-starter-thymeleaf
        
        
        
            org.springframework.boot
            spring-boot-starter-test
            test
        
        
        
            javax.servlet
            javax.servlet-api
            provided
            4.0.0
        
        
        
            javax.servlet
            jstl
            1.2
        
        
        
            com.alibaba
            fastjson
            1.2.58
        
        
            dom4j
            dom4j
            1.6.1
        
        
        
            org.apache.commons
            commons-lang3
            3.8
        
        
        
            com.thoughtworks.xstream
            xstream
            1.4.10
        
        
            org.projectlombok
            lombok
            1.16.18
            provided
        
    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
            
                org.apache.maven.plugins
                maven-compiler-plugin
                3.2
                
                    1.8
                    1.8
                    UTF-8
                
            
            
                org.apache.maven.plugins
                maven-jar-plugin
                3.2.0
                
                    
                        false
                        true
                        
                            true
                            true
                        
                        
                            ${maven.build.timestamp}
                        
                    
                
            
        
    

创建一个接口如下

@RestController
public class HelloWorldController {
    @RequestMapping(path = "/api/queryUserInfo", method = RequestMethod.GET)
    public UserInfo queryUserInfo(@RequestParam String userId) {
        return new UserInfo("张三: " + userId, 18, "广州市白云区");
    }
}

创建启动类

@SpringBootApplication
public class HelloWordApplication {

    public static void main(String[] args) {
        SpringApplication.run(HelloWordApplication.class, args);
    }
}

创建tutorials-13.0-2模块 该模块是进行外观模式的改造, uml图如下 在这里插入图片描述 创建自定义注解DoDoor

package com.thc.design.door.annotation;

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

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DoDoor {
    // 需要从入参取值的属性字段,如果是对象则从对象中取值,如果是单个值则直接使用
    String key() default "";

    // 预设拦截时返回值,是返回对象的Json
    String returnJson() default "";
}

创建获取配置的类

public class StarterService {
    private String userStr;

    public StarterService(String userStr) {
        this.userStr = userStr;
    }

    // 根据自定义的符号切割获取配置数组
    public String[] split(String separatorChar) {
        return this.userStr.split(separatorChar);
    }
}

@ConfigurationProperties("itstack.door")
public class StarterServiceProperties {

    private String userStr;

    public String getUserStr() {
        return userStr;
    }

    public void setUserStr(String userStr) {
        this.userStr = userStr;
    }
}

@Configuration
@ConditionalOnClass(StarterService.class)
@EnableConfigurationProperties(StarterServiceProperties.class)
public class StarterAutoConfigure {

    @Autowired
    private StarterServiceProperties properties;

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnProperty(prefix = "itstack.door", value = "enabled", havingValue = "true")
    StarterService starterService() {
        return new StarterService(properties.getUserStr());
    }
}

创建DoJoinPoint切面处理类

import com.alibaba.fastjson.JSON;
import com.thc.design.door.annotation.DoDoor;
import com.thc.design.door.config.StarterService;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * 类名称:DoJoinPoint
 * 类描述:白名单切面逻辑处理
 * 创建时间:2022/3/26 14:36
 */
@Aspect
@Component
public class DoJoinPoint {

    private Logger logger = LoggerFactory.getLogger(DoJoinPoint.class);

    @Autowired
    private StarterService starterService;

    // 定义切面,这里采用的是注解路径,也就是所有的加入这个注解的方法都会被切面进行管理。
    @Pointcut("@annotation(com.thc.design.door.annotation.DoDoor)")
    public void aopPoint() {

    }

    @Around("aopPoint()")
    public Object doRouter(ProceedingJoinPoint jp) throws Throwable {
        // 获取内容
        Method method = getMethod(jp);
        DoDoor door = method.getAnnotation(DoDoor.class);

        // 获取字段值
        String keyValue = getFiledValue(door.key(), jp.getArgs());
        logger.info("itstack door handler method: {} , value: {}", method.getName(), keyValue);

        if (null == keyValue || "".equals(keyValue)) return jp.proceed();

        //获取所有的白名单, 并转成数组
        String[] split = starterService.split(",");
        for (String str : split) {
            if (keyValue.equals(str)) {
                // 白名单 过滤放行
                return jp.proceed();
            }
        }
        // 拦截
        return returnObject(door, method);
    }

    // 返回对象
    private Object returnObject(DoDoor doGate, Method method) throws IllegalAccessException, InstantiationException {
        Class returnType = method.getReturnType();
        // 获取错误返回值
        String returnJson = doGate.returnJson();
        if ("".equals(returnJson)) {
            return returnType.newInstance();
        }
        return JSON.parseObject(returnJson, returnType);
    }

    // 获取属性值
    private String getFiledValue(String filed, Object[] args) {
        String filedValue = null;
        for (Object arg : args) {
            try {
                if (StringUtils.isBlank(filedValue)) {
                    filedValue = BeanUtils.getProperty(arg, filed);
                } else {
                    break;
                }
            } catch (Exception e) {
                // e.printStackTrace();
                if (args.length == 1) {
                    return args[0].toString();
                }
            }
        }
        return filedValue;
    }

    private Method getMethod(ProceedingJoinPoint jp) throws NoSuchMethodException {
        Signature sig = jp.getSignature();
        MethodSignature methodSignature = (MethodSignature) sig;
        return getClass(jp).getMethod(methodSignature.getName(), methodSignature.getParameterTypes());
    }

    private Class getClass(JoinPoint jp) {
        return jp.getTarget().getClass();
    }
}

创建spring.factories文件, 对配置类进行加载.

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.thc.design.door.config.StarterAutoConfigure

在tutorials-13.0-0模块中, 引入tutorials-13.0-2模块的jar包

       
            org.springframework.boot
            tutorials-13.0-2
            1.0-SNAPSHOT
        

在HelloWorldController加上权限注解

@RestController
public class HelloWorldController {

    @DoDoor(key = "userId", returnJson = "{\"code\":\"1111\",\"info\":\"非白名单可访问用户拦截!\"}")
    @RequestMapping(path = "/api/queryUserInfo", method = RequestMethod.GET)
    public UserInfo queryUserInfo(@RequestParam String userId) {
        return new UserInfo("张三: " + userId, 18, "广州市白云区");
    }
}

在application.yml中配置如下

server:
  port: 8088

spring:
  application:
    name: door-demo

itstack:
  door:
    enabled: true
    userStr: 1001,aaaa,ccc

定义了开启权限, 并且只指定了三个有权限的用户id 启动tutorials-13.0-0 模块. 访问http://localhost:8088/api/queryUserInfo?userId=1006 响应如下 在这里插入图片描述 访问 http://localhost:8088/api/queryUserInfo?userId=ccc 在这里插入图片描述 结果: 可以看到成功实现了权限访问 . 并且把权限控制写在接口外.

此节的疑问 DoJoinPoint 类中, getFiledValue方法似乎有问题, 一直进入catch中报异常. arg只是参数, 不是对象, 不知为何要用BeanUtils.getProperty 方法. 应该是直接使用 return args[0].toString() 即可. 在这里插入图片描述

关注
打赏
1658054974
查看更多评论
立即登录/注册

微信扫码登录

0.0434s