用Java反射来获取所有接口说明

本文介绍如何在SpringBoot项目中使用Shiro进行权限控制,并通过Java反射获取所有接口的注解信息,包括权限、请求方式及路径等,实现动态权限管理。

我项目中pom配置文件添加

	<!-- https://mvnrepository.com/artifact/org.reflections/reflections -->
		<dependency>
			<groupId>org.reflections</groupId>
			<artifactId>reflections</artifactId>
			<version>0.9.8</version>
		</dependency>
AuthModel.java
package com.springboot_springdatajpa.springdatajpa.model;

import lombok.Data;

import java.io.Serializable;
import java.util.Date;

/**
 * @Author zhaomengxia
 * @create 2019/9/3 14:38
 */
@Data
public class AuthModel implements Serializable{

    private String authName;

    private String authUrl;

    private String authUniqueMark;

    private Date createTime;

    private String methodType;
}

LoginController.java

package com.springboot_springdatajpa.springdatajpa.controller;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.*;

/**
 * 用户接口,用于测试的接口
 * 这里使用了标准的restful接口的风格,swagger自动的API接口,shiro接口权限注解
 * @RequiresPermissions组合成的一个controller。当然也可以使用其他技术,只要能够获取到接口信息就行
 * @Author zhaomengxia
 * @create 2019/9/3 11:26
 */
@RestController
@Api(tags = "登录")
@RequestMapping("/test")
public class LoginController {


    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @RequestMapping(value = "/login", method = RequestMethod.POST)
    @ApiOperation("登录接口")
    @RequiresPermissions("user:list")
    public String login(
            @RequestParam(value = "username", required = true) String userName,
            @RequestParam(value = "password", required = true) String password,
            @RequestParam(value = "rememberMe", required = true, defaultValue = "false") boolean rememberMe
    ) {
        logger.info("==========" + userName + password + rememberMe);
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(userName, password);
        token.setRememberMe(rememberMe);

        try {
            subject.login(token);
        } catch (AuthenticationException e) {
            e.printStackTrace();
//            rediect.addFlashAttribute("errorText", "您的账号或密码输入错误!");
            return "{\"Msg\":\"您的账号或密码输入错误\",\"state\":\"failed\"}";
        }
        return "{\"Msg\":\"登陆成功\",\"state\":\"success\"}";
    }

    @RequestMapping("/")
    @ResponseBody
    @RequiresPermissions("user:get")
    @ApiOperation(value = "测试")
    public String index() {
        return "no permission";
    }
}

AnnoTest.java
package com.springboot_springdatajpa.springdatajpa;

import com.alibaba.fastjson.JSON;
import com.springboot_springdatajpa.springdatajpa.model.AuthModel;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.reflections.Reflections;
import org.reflections.scanners.MethodAnnotationsScanner;
import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder;
import org.springframework.web.bind.annotation.*;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;

/**
 * Java反射注解妙用(获取所有接口说明)
 * 这里用反射来获取所有接口的信息
 * @Author zhaomengxia
 * @create 2019/9/3 14:29
 */
public class AnnoTest {

    public static void main(String[] args) {

        getRequestMappingMethod("com.springboot_springdatajpa.springdatajpa.controller");
    }

    private static void getRequestMappingMethod(String scanPackage){
        //设置扫描路径
        Reflections reflections=new Reflections(new ConfigurationBuilder()
                .setUrls(ClasspathHelper.forPackage(scanPackage)).addScanners(new MethodAnnotationsScanner()));

        //扫描包内带有@RequiresPermissions注解的所有方法集合
        Set<Method> methods=reflections.getMethodsAnnotatedWith(RequiresPermissions.class);

        List<AuthModel> authModels=new ArrayList<>();
        Date now=new Date();

        //循环获取方法
        methods.forEach(method -> {
            String methodType="";
                //获取类上的RequestMapping注解的值,作为请求的基础路径
                String authUrl = method.getDeclaringClass().getAnnotation(RequestMapping.class).value()[0];

                if (method.getAnnotation(PutMapping.class) != null) {
                    methodType = "put";
                    if (method.getAnnotation(PutMapping.class).value().length > 0) {
                        authUrl = method.getAnnotation(PutMapping.class).value()[0];
                    }
                } else if (method.getAnnotation(GetMapping.class) != null) {
                    methodType = "get";
                    if (method.getAnnotation(GetMapping.class).value().length > 0) {
                        authUrl = method.getAnnotation(GetMapping.class).value()[0];
                    }
                } else if (method.getAnnotation(PostMapping.class) != null) {
                    methodType = "post";
                    if (method.getAnnotation(PostMapping.class).value().length > 0) {
                        authUrl = method.getAnnotation(PostMapping.class).value()[0];
                    }
                } else if (method.getAnnotation(DeleteMapping.class) != null) {
                    methodType = "delete";
                    if (method.getAnnotation(DeleteMapping.class).value().length > 0) {
                        authUrl = method.getAnnotation(DeleteMapping.class).value()[0];
                    }
                }

                AuthModel authModel = new AuthModel();
                authModel.setMethodType(methodType);
                authModel.setAuthUniqueMark(method.getAnnotation(RequiresPermissions.class).value()[0]);
                authModel.setAuthUrl(authUrl);
                    authModel.setAuthName(method.getDeclaringClass().getAnnotation(Api.class).value() +
                            "-" + method.getAnnotation(ApiOperation.class).value());
                authModel.setCreateTime(now);
                authModels.add(authModel);
        });

        System.out.println(JSON.toJSONString(authModels));

    }
}

控制台输出结果

所设计代码在项目中的位置

源代码

https://github.com/zhaomengxia/springboot_mybatis.git

### Java反射机制获取所有实现了特定接口的类 Java反射机制允许程序在运行时自省或“检查”其结构,并可以在运行期间修改内部属性。对于获取实现了特定接口的所有类,可以利用`ServiceLoader`这一工具来加载服务提供者[^1]。 当使用`ServiceLoader`时,需确保目标实现类已注册至相应的配置文件中,通常位于`META-INF/services/`目录下,文件名为接口全限定名,内容则为各实现类的全限定名。以下是基于`ServiceLoader`的一个简单例子: ```java import java.util.ServiceLoader; public class InterfaceImplFinder { public static void main(String[] args) { ServiceLoader<YourInterface> loader = ServiceLoader.load(YourInterface.class); for (YourInterface impl : loader) { System.out.println(impl.getClass().getName()); } } } ``` 上述代码展示了如何遍历并打印出给定接口`YourInterface`的所有实现类名称。然而需要注意的是,此方式依赖于SPI(Service Provider Interfaces)机制,即需要开发者手动维护配置文件中的条目。 另一种不完全可靠但更通用的做法是扫描整个classpath下的所有类,尝试判断它们是否实现了指定接口。这涉及到复杂的字节码操作以及性能开销,在实际项目里应谨慎采用。一种简化版思路如下所示: ```java import java.io.File; import java.net.URL; import java.util.ArrayList; import java.lang.reflect.Modifier; public final class ClassScanner { private static String modifyPath(String path){ if(path.startsWith("/")) return path.substring(1); return path; } @SuppressWarnings("unchecked") public static ArrayList<Class<?>> getInterfaceImpls(Class<?> target,ClassLoader cl)throws Exception{ URL url=cl.getResource(""); File dir=new File(modifyPath(url.getPath())); ArrayList<Class<?>> classes=getClasses(dir,"",cl); ArrayList<Class<?>> result=new ArrayList<>(); for(Class<?> clazz:classes){ if(!clazz.isInterface()&&!Modifier.isAbstract(clazz.getModifiers())&&target.isAssignableFrom(clazz)){ result.add(clazz); } } return result; } // ... other methods to support the above function ... } ``` 这段代码片段定义了一个静态方法`getInterfaceImpls`用于返回实现了传入接口的所有具体类列表[^2]。请注意这种方法可能无法覆盖所有场景特别是面对模块化应用或者复杂部署环境的时候。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值