一. 注解介绍
注解Annotation是一种引用数据类型,编译之后也是生成.class类型的java文件
语法:修饰符 @interface 注解类型名
- 注解可以出现在类上、方法上、属性上、甚至注解上等…
- JDK中内置的注解@Override(复写)、@SuppressWarnings(忽略编译器的警告)等
元注解
元注解就是用来修饰注解的,是注解上的注解;常见的元注解有@Target、@Retention、@Documented、@Inherited.
@Target:表示当前注解使用在什么位置
1 2 3 4
| 例如1:@Target(ElementType.METHOD) Target内部的值使用枚举ElementType表示,表示的主要位置有:注解、构造方法、属性、局部变量、函数、包、参数和类(默认值)。 例如2:@Target({ElementType.METHOD,ElementType.TYPE}) 多个位置使用数组的写法
|
@Retention:定义被它所标记的注解能保留多久
1 2 3 4 5 6 7
| Retention注解有一个属性value,是RetentionPolicy类型的,Enum RetentionPolicy是一个枚举类型 这个枚举决定了Retention注解应该如何去保持,也可理解为Rentention搭配 RententionPolicy使用 RetentionPolicy有3个值:CLASS,RUNTIME,SOURCE
@Retention(RetentionPolicy.SOURCE):注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃 @Retention(RetentionPolicy.CLASS):注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期 @Retention(RetentionPolicy.RUNTIME):注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在
|
@Documented:加了这个注解的注解,在生成文档的时候,可以在文档中显示出来
1 2 3
| @Documented public @interface A{ }
|
@Inherited:加了这个注解的注解,能被继承
1 2 3 4 5 6 7 8 9 10
| @Inherited public @interface A{ }
@A class B(){ }
class C extends B{ }
|
二. 自定义权限注解,项目启动时扫描注解加权限
1. 定义注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| package io.coderyeah.basic.annotation;
import java.lang.annotation.*;
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface PreAuthorize { String sn(); String name(); }
|
以后在方法或类上加了这个@PreAuthorize
注解,都会去执行一段业务代码【例如:添加权限到t_promission
】,但是要先扫描这个注解,然后解析这个注解,再去执行相应的业务代码
2. 例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| @Autowired private DepartmentService departmentService;
@PreAuthorize(name = "部门列表", sn = "department:list") @LogAnnotation(module = "部门模块", operate = "分页查询部门列表") @ApiOperation("查询部门列表") @PostMapping("/list") public Result list(@RequestBody(required = false) DeptDTO deptDTO) { return departmentService.list(deptDTO); }
|
3. 扫描注解加权限
注解定义之后,需要扫描。就像业务代码中@Service注解,服务启动的时候就会去扫描,生成业务对象。并注入到Controller使用。如果启动的时候业务代码中没有添加@Service注解,启动会报错的。
4. 自定义的注解怎么扫描呢?而且要在服务器启动的时候自动扫描?
- 可以通过Web三大组件:Servlet、过滤器Filter、监听器Listenter
5 .权限注解扫描监听器
监听器:监听四大作用域的变化和属性的变更
application的类型:ServletContext
- 这个对象会在服务器启动的时候自动生成,而且是唯一一个。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| package io.coderyeah.system.listener;
import io.coderyeah.system.service.PermissionScanService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.annotation.WebListener;
@WebListener @Slf4j public class PermissionScanInitListener implements ServletContextListener {
@Autowired private IPermissionScanService permissionScanService; @Override public void contextInitialized(ServletContextEvent servletContextEvent) { new Thread(new Runnable() { @Override public void run() { log.info("权限初始化开始******************************************"); System.out.println("权限初始化开始******************************************"); permissionScanService.scanPermission(); System.out.println("权限初始化结束******************************************"); } }).start(); } @Override public void contextDestroyed(ServletContextEvent servletContextEvent) { } }
|
6. 启动类
1 2 3 4 5 6 7 8 9 10
| @SpringBootApplication @MapperScan("io.coderyeah.*.mapper")
@ServletComponentScan(value = {"io.coderyeah.system.listener"}) public class PetHomeApplication { public static void main(String[] args) { SpringApplication.run(PetHomeApplication.class,args); } }
|
7. 业务接口
此业务接口专门用来解析注解@PreAuthorize(name = "部门列表",sn= "department:list")
和注解上的参数,并获取出来添加到权限表t_permission。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
| package io.coderyeah.system.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import io.coderyeah.basic.annotation.PreAuthorize; import io.coderyeah.basic.util.ClassUtils; import io.coderyeah.system.domain.Permission; import io.coderyeah.system.mapper.PermissionMapper; import io.coderyeah.system.service.PermissionService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.web.bind.annotation.*;
import java.io.File; import java.io.FileFilter; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.HashSet; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern;
@Service public class PermissionServiceImpl extends ServiceImpl<PermissionMapper, Permission> implements PermissionService { private static final String PKG_PREFIX = "io.coderyeah."; private static final String PKG_SUFFIX = ".controller"; @Autowired private PermissionMapper permissionMapper;
@Override public void scanPermission() { String path = this.getClass().getResource("/").getPath() + "/io/coderyeah/"; File file = new File(path); File[] files = file.listFiles(new FileFilter() { @Override public boolean accept(File file) { return file.isDirectory(); } });
Set<Class> clazzes = new HashSet<>(); assert files != null; for (File fileTmp : files) { System.out.println("===============权限注解解析:获取所有的包=============="); System.out.println(fileTmp.getName()); clazzes.addAll(ClassUtils.getClasses(PKG_PREFIX + fileTmp.getName() + PKG_SUFFIX)); }
for (Class clazz : clazzes) { Method[] methods = clazz.getMethods(); if (methods == null || methods.length < 1) { return; } for (Method method : methods) { String uri = getUri(clazz, method); try { PreAuthorize preAuthorizeAnno = method.getAnnotation(PreAuthorize.class); if (preAuthorizeAnno == null) { continue; } String name = preAuthorizeAnno.name(); String permissionSn = preAuthorizeAnno.sn(); Permission permissionTmp = permissionMapper.selectOne(new LambdaQueryWrapper<Permission>().eq(Permission::getSn, permissionSn)); if (permissionTmp == null) { Permission permission = new Permission(); permission.setName(name); permission.setSn(permissionSn); permission.setUrl(uri); permissionMapper.insert(permission); } else { permissionTmp.setName(name); permissionTmp.setSn(permissionSn); permissionTmp.setUrl(uri); permissionMapper.updateById(permissionTmp); } } catch (Exception e) { e.printStackTrace(); } } } }
private String getUri(Class clazz, Method method) { String classPath = ""; Annotation annotation = clazz.getAnnotation(RequestMapping.class); if (annotation != null) { RequestMapping requestMapping = (RequestMapping) annotation; String[] values = requestMapping.value(); if (values != null && values.length > 0) { classPath = values[0]; if (!"".equals(classPath) && !classPath.startsWith("/")) classPath = "/" + classPath; } } GetMapping getMapping = method.getAnnotation(GetMapping.class); String methodPath = ""; if (getMapping != null) { String[] values = getMapping.value(); if (values != null && values.length > 0) { methodPath = values[0]; if (!"".equals(methodPath) && !methodPath.startsWith("/")) methodPath = "/" + methodPath; } }
PostMapping postMapping = method.getAnnotation(PostMapping.class); if (postMapping != null) { String[] values = postMapping.value(); if (values != null && values.length > 0) { methodPath = values[0]; if (!"".equals(methodPath) && !methodPath.startsWith("/")) methodPath = "/" + methodPath; } }
DeleteMapping deleteMapping = method.getAnnotation(DeleteMapping.class); if (deleteMapping != null) { String[] values = deleteMapping.value(); if (values != null && values.length > 0) { methodPath = values[0]; if (!"".equals(methodPath) && !methodPath.startsWith("/")) methodPath = "/" + methodPath; } }
PutMapping putMapping = method.getAnnotation(PutMapping.class); if (putMapping != null) { String[] values = putMapping.value(); if (values != null && values.length > 0) { methodPath = values[0]; if (!"".equals(methodPath) && !methodPath.startsWith("/")) methodPath = "/" + methodPath; }
}
PatchMapping patchMapping = method.getAnnotation(PatchMapping.class); if (patchMapping != null) { String[] values = patchMapping.value(); if (values != null && values.length > 0) { methodPath = values[0]; if (!"".equals(methodPath) && !methodPath.startsWith("/")) methodPath = "/" + methodPath; } }
RequestMapping requestMapping = method.getAnnotation(RequestMapping.class); if (requestMapping != null) { String[] values = requestMapping.value(); if (values != null && values.length > 0) { methodPath = values[0]; if (!"".equals(methodPath) && !methodPath.startsWith("/")) methodPath = "/" + methodPath; } } return classPath + methodPath; }
private String getPermissionSn(String value) { String regex = "\\[(.*?)]"; Pattern p = Pattern.compile("(?<=\\()[^\\)]+"); Matcher m = p.matcher(value); String permissionSn = null; if (m.find()) { permissionSn = m.group(0).substring(1, m.group().length() - 1); } return permissionSn; } }
|
8. 工具类:ClassUtils.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
| package io.coderyeah.basic.util;
import java.io.File; import java.io.FileFilter; import java.io.IOException; import java.net.URL; import java.net.URLDecoder; import java.util.Enumeration; import java.util.LinkedHashSet; import java.util.Set;
public class ClassUtils {
public static Set<Class<?>> getClasses(String pack) {
Set<Class<?>> classes = new LinkedHashSet<Class<?>>(); boolean recursive = true; String packageName = pack; String packageDirName = packageName.replace('.', '/'); Enumeration<URL> dirs; try { dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName); while (dirs.hasMoreElements()) { URL url = dirs.nextElement(); String protocol = url.getProtocol(); if ("file".equals(protocol)) { String filePath = URLDecoder.decode(url.getFile(), "UTF-8"); findAndAddClassesInPackageByFile(packageName, filePath, recursive, classes); } } } catch (IOException e) { e.printStackTrace(); }
return classes; }
public static void findAndAddClassesInPackageByFile(String packageName, String packagePath, final boolean recursive, Set<Class<?>> classes) { File dir = new File(packagePath); if (!dir.exists() || !dir.isDirectory()) { return; } File[] dirfiles = dir.listFiles(new FileFilter() { public boolean accept(File file) { return (recursive && file.isDirectory()) || (file.getName().endsWith(".class")); } }); for (File file : dirfiles) { if (file.isDirectory()) { findAndAddClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive, classes); } else { String className = file.getName().substring(0, file.getName().length() - 6); try { classes.add( Thread.currentThread().getContextClassLoader().loadClass(packageName + '.' + className)); } catch (ClassNotFoundException e) { e.printStackTrace(); } } } }
}
|