Java反射系列(3):从spring反射工具ReflectionUtils说起

2024-04-03 03:44

本文主要是介绍Java反射系列(3):从spring反射工具ReflectionUtils说起,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

传送门

兼容性引发的"血案"

ReflectionUtils的原理

目的有三

ReflectionUtils的API使用

Method

getAllDeclaredMethods

findMethod

invokeMethod

Field

getDeclaredFields

findField

getField

makeAccessible

Constructor

accessibleConstructor


传送门

在比较早的时候,就讨论过java反射的一些用法及概念:

Java反射系列(1):入门基础

以及反射的基石Class对象! 

Java反射系列(2):从Class获取父类方法说起

今天就从工作中实际的例子来看看反射的应用。 

兼容性引发的"血案"

对于Java程序员来说,调用别人写的类库、使用开源的Jar包似乎无可厚非,甚至是"天经地义"!正是开源的这种无私精神带来了这门语言的繁荣,打造了其遍地开花的生态。而github更是诸多极客们一展风采的宝库舞台,但是这也带来了问题:里面的各种库质量良莠不齐,既有spring这种Java体系"基石"类的产品,也有很多"小而美"的工具包,比如hutool包!

但凡事有利就有弊,最近系统就出现了因为升级hutool包导致功能不可用的场景:

系统里面有一个对db数据自动加解密Mybatis插件,存储时将表里面的数据自动加密,读取时自动解密。其实现原理就是通过Mybatis的拦截器,拦截请求之后,得到原始里面数据,加解密之后用反射机制替换原始数据以此实现动态加解密功能。功能并不复杂,抛开效率性能等因素之外,也是一个比较合理的数据"脱敏"实现机制。但是里面的的反射机制用到了hutool的包,升级时版本不兼容导致功能不可能用,最终用sprig包的ReflectionUtils工具进行了紧急替换,评估之后最终移除了该hutool的引用,后续不再使用!

由此可见,一个好的开源产品,除了功能强大易用之外,升级的版本兼容性也是必不可少的条件之一。这里引用一下网上对此的观点:

ReflectionUtils的原理

对于上面提到的加解密插件,后续可以提供一个实现到码云供参考,这里仅仅表明它是反射机制使用的一个具体场景,不是这里的重点,就不做过多讨论。

目的有三

而这里重点目标有三个:

  • 使用开源包时要慎重,尤其是在生产级应用,越是大公司对此要求越严格
  • ReflectionUtils的使用,熟悉一下里面的API
  • 最终了解反射的用途,深入反射机制

ReflectionUtils的API使用

ReflectionUtils是spring的核心包提供工具类,路径为:org.springframework.util.ReflectionUtils。

在Java反射系列(2):从Class获取父类方法说起里面讨论了Class对象,所以这里重点看看另外几个比较重要的对象:Method,Field。

还是以前面例子的一个简单请求类为例,父类BaseAuthReq :

public class BaseAuthReq
{/** 应用ID */private String clientId;/** 应用身份密钥 */private String clientSecret;public String getClientId(){return clientId;}public void setClientId(String clientId){this.clientId = clientId;}public String getClientSecret(){return clientSecret;}public void setClientSecret(String clientSecret){this.clientSecret = clientSecret;}
}

GetTokenReq继承BaseAuthReq:

public class GetTokenReq extends BaseAuthReq
{/** 授权码类型 */private String grantType;/** 授权码 */private String code;public String getGrantType(){return grantType;}public void setGrantType(String grantType){this.grantType = grantType;}public String getCode(){return code;}public void setCode(String code){this.code = code;}
}

Method

GetTokenReq类里面有2个属性,grantType、code及对应的get、set方法,那通过ReflectionUtils工具类如何获取这些方法呢?

getAllDeclaredMethods

观察下它的API,里面有一个方法,看起来可以满足需求:

public static Method[] getAllDeclaredMethods(Class<?> leafClass)

现在写一个测试方法来验证一下:

@Testpublic void testMethod() {// 获取当前类的Class对象Class clazz = GetTokenReq.class;System.out.println("clazz:" + clazz.getName());Method[] allDeclaredMethods = ReflectionUtils.getAllDeclaredMethods(clazz);for (Method method : allDeclaredMethods) {System.out.println(method.getName());}}

运行一下,输出打印结果:

clazz:com.tw.tsm.auth.dto.request.GetTokenReq
// 以下4个为目标方法
setGrantType
getCode
getGrantType
setCodegetClientId
setClientId
setClientSecret
getClientSecret
finalize
wait
wait
wait
equals
toString
hashCode
getClass
clone
notify
notifyAll
registerNatives

从输出可以看出,GetTokenReq类里面2个属性grantType、code对应的get、set方法都打印出来了,但是除此以外,还多了很多方法,它们又是从哪里来的呢?

把上面的输出打印改一下:

@Testpublic void testMethod(){// 获取当前类的Class对象Class clazz = GetTokenReq.class;System.out.println("clazz:" + clazz.getName());Method[] allDeclaredMethods = ReflectionUtils.getAllDeclaredMethods(clazz);for (Method method : allDeclaredMethods){// 输出方法所在类,及方法名称System.out.println("class:" + method.getDeclaringClass().getName() + ", method:" + method.getName());}}

运行一下,输出打印结果:

clazz:com.tw.tsm.auth.dto.request.GetTokenReq
class:com.tw.tsm.auth.dto.request.GetTokenReq, method:getGrantType
class:com.tw.tsm.auth.dto.request.GetTokenReq, method:setCode
class:com.tw.tsm.auth.dto.request.GetTokenReq, method:getCode
class:com.tw.tsm.auth.dto.request.GetTokenReq, method:setGrantType
class:com.tw.tsm.auth.dto.BaseAuthReq, method:setClientId
class:com.tw.tsm.auth.dto.BaseAuthReq, method:setClientSecret
class:com.tw.tsm.auth.dto.BaseAuthReq, method:getClientSecret
class:com.tw.tsm.auth.dto.BaseAuthReq, method:getClientId
class:java.lang.Object, method:finalize
class:java.lang.Object, method:wait
class:java.lang.Object, method:wait
class:java.lang.Object, method:wait
class:java.lang.Object, method:equals
class:java.lang.Object, method:toString
class:java.lang.Object, method:hashCode
class:java.lang.Object, method:getClass
class:java.lang.Object, method:clone
class:java.lang.Object, method:notify
class:java.lang.Object, method:notifyAll
class:java.lang.Object, method:registerNatives

从上面的输出可以清晰的看到,getAllDeclaredMethods可以打印类自身及所有父类的方法:

直接看下源码,看下到底是怎么实现的:

/*** Get all declared methods on the leaf class and all superclasses.* Leaf class methods are included first.* @param leafClass the class to introspect* @throws IllegalStateException if introspection fails*/public static Method[] getAllDeclaredMethods(Class<?> leafClass) {// 声明一个Method集合,现在是空的final List<Method> methods = new ArrayList<>(20);// 如何获取Class的Method集合,就在这里面实现的doWithMethods(leafClass, methods::add);return methods.toArray(EMPTY_METHOD_ARRAY);}

所以跟进doWithMethods,注意这个方法要求传一个lambda函数MethodCallback

	/*** Action to take on each method.*/@FunctionalInterfacepublic interface MethodCallback {/*** Perform an operation using the given method.* @param method the method to operate on*/void doWith(Method method) throws IllegalArgumentException, IllegalAccessException;}

这里的实现就是将Method添加到刚才的methods集合里面(要了解lambda函数的见JAVA8-lambda表达式1:什么是lambda表达式)

public static void doWithMethods(Class<?> clazz, MethodCallback mc, @Nullable MethodFilter mf) {// 获得Method方法列表,包括所有继承Method[] methods = getDeclaredMethods(clazz, false);for (Method method : methods) {// 条件过滤,这里为null,先不管它,都是一些辅助功能if (mf != null && !mf.matches(method)) {continue;}try {// 执行方法回调,这里就是将前面的methods::add:将当前方法添加到集合中mc.doWith(method);}catch (IllegalAccessException ex) {throw new IllegalStateException("Not allowed to access method '" + method.getName() + "': " + ex);}}// 以下递归调用,获取父类的Method方法列表if (clazz.getSuperclass() != null && (mf != USER_DECLARED_METHODS || clazz.getSuperclass() != Object.class)) {doWithMethods(clazz.getSuperclass(), mc, mf);}else if (clazz.isInterface()) {for (Class<?> superIfc : clazz.getInterfaces()) {doWithMethods(superIfc, mc, mf);}}}

跟进getDeclaredMethods(clazz, false),马上就看到庐山真面了:

private static Method[] getDeclaredMethods(Class<?> clazz, boolean defensive) {Assert.notNull(clazz, "Class must not be null");// 本地缓存,出于性能设计,先不管它Method[] result = declaredMethodsCache.get(clazz);if (result == null) {try { // 这几个方法里面最核心的代码,通过Class获取声明的方法列表,jdk自带,其它的都是辅助功能Method[] declaredMethods = clazz.getDeclaredMethods();List<Method> defaultMethods = findConcreteMethodsOnInterfaces(clazz);if (defaultMethods != null) {result = new Method[declaredMethods.length + defaultMethods.size()];System.arraycopy(declaredMethods, 0, result, 0, declaredMethods.length);int index = declaredMethods.length;for (Method defaultMethod : defaultMethods) {result[index] = defaultMethod;index++;}}else {result = declaredMethods;}// 本地缓存,出于性能设计,先不管它declaredMethodsCache.put(clazz, (result.length == 0 ? EMPTY_METHOD_ARRAY : result));}catch (Throwable ex) {throw new IllegalStateException("Failed to introspect Class [" + clazz.getName() +"] from ClassLoader [" + clazz.getClassLoader() + "]", ex);}}return (result.length == 0 || !defensive) ? result : result.clone();

可通过debug看到具体代码执行验证:

有了getAllDeclaredMethods方法,要获得某一指定方法名称的就很容易了,直接从getAllDeclaredMethods列表里面过滤,这是很自然的事情。ReflectionUtils也提供了类似的method查找方法

findMethod
/*** Attempt to find a {@link Method} on the supplied class with the supplied name* and no parameters. Searches all superclasses up to {@code Object}.* <p>Returns {@code null} if no {@link Method} can be found.* @param clazz the class to introspect* @param name the name of the method* @return the Method object, or {@code null} if none found*/@Nullablepublic static Method findMethod(Class<?> clazz, String name) {return findMethod(clazz, name, EMPTY_CLASS_ARRAY);}

方法有2个参数,一个是目标Class,一个是目标方法名称,返回值即是Method。写个例子来看看,获取getCode()方法:

@Testpublic void testFindMethod() {// 获取当前类的Class对象Class clazz = GetTokenReq.class;System.out.println("clazz:" + clazz.getName());// 获得getCode方法Method method = ReflectionUtils.findMethod(clazz, "getCode");System.out.println(method.getName());}

 直接看下源码,看下到底是怎么实现的:

// 声明一个空对象数组
private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0];public static Method findMethod(Class<?> clazz, String name) {return findMethod(clazz, name, EMPTY_CLASS_ARRAY);}public static Method findMethod(Class<?> clazz, String name, @Nullable Class<?>... paramTypes) {Assert.notNull(clazz, "Class must not be null");Assert.notNull(name, "Method name must not be null");Class<?> searchType = clazz;// 通过while循环,从当前类递归向上查找while (searchType != null) {// 果不其然,通过前面介绍的getDeclaredMethods获得方法列表,再进行方法名称过滤Method[] methods = (searchType.isInterface() ? searchType.getMethods() :getDeclaredMethods(searchType, false));for (Method method : methods) {if (name.equals(method.getName()) && (paramTypes == null || hasSameParams(method, paramTypes))) {return method;}}// 向上查找父类searchType = searchType.getSuperclass();}return null;}

现在再改一下上面的例子,获取setCode()方法:

@Testpublic void testFindMethod() {// 获取当前类的Class对象Class clazz = GetTokenReq.class;System.out.println("clazz:" + clazz.getName());Method getCodeMethod = ReflectionUtils.findMethod(clazz, "getCode");System.out.println(getCodeMethod.getName());Method setCodeMethod = ReflectionUtils.findMethod(clazz, "setCode");System.out.println(setCodeMethod.getName());}

运行一下,输出打印结果,会发现出错了:

这是怎么回事呢?观察一下getCode()与setCode(String code方法) :

    public String getCode(){return code;}public void setCode(String code){this.code = code;}

 细心的你发现没有,区别在于,setCode是有一个String参数的,而getCode是无参数的。所以对于有参数的方法,获得Method需要如下写法:

// 指明参数的类型,用一个数组表示
Method setCodeMethod = ReflectionUtils.findMethod(clazz, "setCode", new Class[]{String.class});
invokeMethod

对于反射机制来说,非常重要的一个功能就是通过反射执行目标方法,否则大多数场景下的反射都没意义。看看ReflectionUtils提供的方法:

// 默认的空参数列表
private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];public static Object invokeMethod(Method method, @Nullable Object target) {return invokeMethod(method, target, EMPTY_OBJECT_ARRAY);}

有2个参数:一个是要执行的方法method,另一个是被执行方法的目标对象。这个要怎么理解呢,还是写一个例子来看:

@Testpublic void testInvokeMethod(){// 创建一个对象,所有参数都是nullGetTokenReq getTokenReq = new GetTokenReq();Class clazz = getTokenReq.getClass();System.out.println("clazz:" + clazz.getName());// 执行setCode方法,参数为"ss";由于setCode返回void,此时得到的执行结果为nullMethod setCodeMethod = ReflectionUtils.findMethod(clazz, "setCode", new Class[] {String.class});Object setResult = ReflectionUtils.invokeMethod(setCodeMethod, getTokenReq, new Object[] {"ss"});System.out.println("setCode方法执行返回结果:" + setResult);// 执行getCode方法,由于getCode返回String,此时得到的执行结果为为"ss"Method getCodeMethod = ReflectionUtils.findMethod(clazz, "getCode");Object getResult = ReflectionUtils.invokeMethod(getCodeMethod, getTokenReq);System.out.println("getCode方法执行返回结果:" + getResult);}

 运行一下,输出打印结果:

clazz:com.tw.tsm.auth.dto.request.GetTokenReq
setCode方法执行返回结果:null
getCode方法执行返回结果:ss

可以看出,invokeMethod方法同findMethod一样,当有参数时,需要传递参数类型的。这一点从源码就能发现:

public static Object invokeMethod(Method method, @Nullable Object target, @Nullable Object... args) {try {
// 最后一个参数args为动态参数,指的就是方法要接收的参数列表return method.invoke(target, args);}catch (Exception ex) {handleReflectionException(ex);}throw new IllegalStateException("Should never get here");}

Field

GetTokenReq类里面有2个属性,grantType、code及对应的get、set方法;前面通过ReflectionUtils工具类获取了对应的方法,依赖的是反射机制提供的Method对象。那通过ReflectionUtils工具类如何获取这些属性呢?这就要依赖反射机制提供的Field对象了。

getDeclaredFields

观察下它的API,里面有一个方法,看起来可以满足需求:

private static Field[] getDeclaredFields(Class<?> clazz)

但是很遗憾,这个方法是私有的,ReflectionUtils类不能直接调用。不过它提供了另外的一个方法:

public static void doWithFields(Class<?> clazz, FieldCallback fc) { doWithFields(clazz, fc, null); }

其中FieldCallback也是一个lambda函数,有点类似MethodCallback。

@FunctionalInterfacepublic interface FieldCallback {/*** Perform an operation using the given field.* @param field the field to operate on*/void doWith(Field field) throws IllegalArgumentException, IllegalAccessException;}

所以可以仿照getAllDeclaredMethods来达到getDeclaredFields目的:

@Testpublic void testGetDeclaredFields(){// 获取当前类的Class对象Class clazz = GetTokenReq.class;System.out.println("clazz:" + clazz.getName());List<Field> fieldList = Lists.newArrayList();ReflectionUtils.doWithFields(clazz, fieldList::add);for (Field field : fieldList){// 输出属性所在类,及属性名称System.out.println("class:" + field.getDeclaringClass().getName() + ", field:" + field.getName());}}

运行一下,输出打印结果:

clazz:com.tw.tsm.auth.dto.request.GetTokenReq
class:com.tw.tsm.auth.dto.request.GetTokenReq, field:grantType
class:com.tw.tsm.auth.dto.request.GetTokenReq, field:code
class:com.tw.tsm.auth.dto.BaseAuthReq, field:clientId
class:com.tw.tsm.auth.dto.BaseAuthReq, field:clientSecret

 从输出可以看出,GetTokenReq类里面2个属性grantType、code及父类clientId、clientSecret都打印出来了,但是Object类的相关属性却没有,这是为什么呢?直接看下源码,看下到底是怎么实现的:

public static void doWithFields(Class<?> clazz, FieldCallback fc, @Nullable FieldFilter ff) {// Keep backing up the inheritance hierarchy.Class<?> targetClass = clazz;do {// 获取当前类所有声明的属性列表Field[] fields = getDeclaredFields(targetClass);for (Field field : fields) {if (ff != null && !ff.matches(field)) {continue;}try {// 执行回调函数,这里就是测试方法的里面的list.addfc.doWith(field);}catch (IllegalAccessException ex) {throw new IllegalStateException("Not allowed to access field '" + field.getName() + "': " + ex);}}targetClass = targetClass.getSuperclass();}// 递归调用,向上查找,排除Object类,所以前面输出没有Object的相关属性while (targetClass != null && targetClass != Object.class);

通过此方法可以发现,最终的关键回到上面的私有方法getDeclaredFields:

private static Field[] getDeclaredFields(Class<?> clazz) {Assert.notNull(clazz, "Class must not be null");// 本地缓存,出于性能设计,先不管它Field[] result = declaredFieldsCache.get(clazz);if (result == null) {try {// 最关键的代码:通过反射机制,获取所有声明的属性列表result = clazz.getDeclaredFields();// 本地缓存,出于性能设计,先不管它declaredFieldsCache.put(clazz, (result.length == 0 ? EMPTY_FIELD_ARRAY : result));}catch (Throwable ex) {throw new IllegalStateException("Failed to introspect Class [" + clazz.getName() +"] from ClassLoader [" + clazz.getClassLoader() + "]", ex);}}return result;}
findField
/*** Attempt to find a {@link Field field} on the supplied {@link Class} with the* supplied {@code name}. Searches all superclasses up to {@link Object}.* @param clazz the class to introspect* @param name the name of the field* @return the corresponding Field object, or {@code null} if not found*/@Nullablepublic static Field findField(Class<?> clazz, String name) {return findField(clazz, name, null);}

 方法有2个参数,一个是目标Class,一个是目标字段名称,返回值即是Field。写个例子来看看,获取code属性:

    @Testpublic void testFindField() {// 获取当前类的Class对象Class clazz = GetTokenReq.class;System.out.println("clazz:" + clazz.getName());Field field = ReflectionUtils.findField(clazz, "code");System.out.println("class:" + field.getDeclaringClass().getName() + ", field:" + field.getName());}

 运行一下,输出打印结果:

clazz:com.tw.tsm.auth.dto.request.GetTokenReq
class:com.tw.tsm.auth.dto.request.GetTokenReq, field:code
getField

上面通过findField方法可以获取对象的属性,那怎么获取这个属性值呢?写个例子来看看,获取grantType属性:

@Testpublic void testGetField(){// 创建一个对象,所有参数都是nullGetTokenReq getTokenReq = new GetTokenReq();// 设置grantType为密码模式getTokenReq.setGrantType("password");Class clazz = getTokenReq.getClass();System.out.println("clazz:" + clazz.getName());// 获取grantType字段属性Field field = ReflectionUtils.findField(clazz, "grantType");System.out.println("class:" + field.getDeclaringClass().getName() + ", field:" + field.getName());// 获得grantType字段属性值:理论上应该是passwordObject grantType = ReflectionUtils.getField(field, getTokenReq);System.out.println("通过反射获取属性:" + field.getName() + ", 值为:" + grantType);}

运行一下,输出打印结果,会发现出错了:

翻译一下这个错误信息:

无法访问方法或字段:Class org.springframework.util。ReflectionUtils无法访问com.tw.tsm.auth.dto.request类的成员。带有修饰符“private”的GetTokenReq 

说直白点就是:不能直接访问私有字段属性,因为grantType在GetTokenReq类中是private:

public class GetTokenReq extends BaseAuthReq
{/** 授权码类型 */private String grantType;
}

直接看源码:

public static Object getField(Field field, @Nullable Object target) {try {// 最关键的代码:通过反射机制,获取属性值return field.get(target);}catch (IllegalAccessException ex) {handleReflectionException(ex);}throw new IllegalStateException("Should never get here");}public static void handleReflectionException(Exception ex) {if (ex instanceof NoSuchMethodException) {throw new IllegalStateException("Method not found: " + ex.getMessage());}if (ex instanceof IllegalAccessException) {// 就是这里出现的错误throw new IllegalStateException("Could not access method or field: " + ex.getMessage());}if (ex instanceof InvocationTargetException) {handleInvocationTargetException((InvocationTargetException) ex);}if (ex instanceof RuntimeException) {throw (RuntimeException) ex;}throw new UndeclaredThrowableException(ex);}

 要解决这个问题,就需要用到下面这个方法。

makeAccessible

继续修改上面的例子,增加一行代码:

@Testpublic void testGetField(){// 创建一个对象,所有参数都是nullGetTokenReq getTokenReq = new GetTokenReq();// 设置grantType为密码模式getTokenReq.setGrantType("password");Class clazz = getTokenReq.getClass();System.out.println("clazz:" + clazz.getName());// 获取grantType字段属性Field field = ReflectionUtils.findField(clazz, "grantType");System.out.println("class:" + field.getDeclaringClass().getName() + ", field:" + field.getName());// 设置属性可以访问ReflectionUtils.makeAccessible(field);// 获得grantType字段属性值:理论上应该是passwordObject grantType = ReflectionUtils.getField(field, getTokenReq);System.out.println("通过反射获取属性:" + field.getName() + ", 值为:" + grantType);}

 运行一下,输出打印结果,发现正确输出了:

clazz:com.tw.tsm.auth.dto.request.GetTokenReq
class:com.tw.tsm.auth.dto.request.GetTokenReq, field:grantType
通过反射获取属性:grantType, 值为:password

直接看下源码,看下到底是怎么实现的:

public static void makeAccessible(Field field) {if ((!Modifier.isPublic(field.getModifiers()) ||!Modifier.isPublic(field.getDeclaringClass().getModifiers()) ||Modifier.isFinal(field.getModifiers())) && !field.isAccessible()) {
// 最关键的代码:通过反射机制,设置属性可以访问			
field.setAccessible(true);}}

Constructor

accessibleConstructor

写个例子来看看,如何创建一个对象:

try {Class clazz = getTokenReq.getClass();System.out.println("clazz:" + clazz.getName());Constructor<GetTokenReq> constructor = ReflectionUtils.accessibleConstructor(clazz, String.class, String.class);GetTokenReq req = constructor.newInstance("a", "b");System.out.println(req);} catch (Exception e) {e.printStackTrace();}

运行一下,输出打印结果:

GetTokenReq{grantType='a', code='b'}

这篇关于Java反射系列(3):从spring反射工具ReflectionUtils说起的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/871827

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

Java进阶13讲__第12讲_1/2

多线程、线程池 1.  线程概念 1.1  什么是线程 1.2  线程的好处 2.   创建线程的三种方式 注意事项 2.1  继承Thread类 2.1.1 认识  2.1.2  编码实现  package cn.hdc.oop10.Thread;import org.slf4j.Logger;import org.slf4j.LoggerFactory

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

高效录音转文字:2024年四大工具精选!

在快节奏的工作生活中,能够快速将录音转换成文字是一项非常实用的能力。特别是在需要记录会议纪要、讲座内容或者是采访素材的时候,一款优秀的在线录音转文字工具能派上大用场。以下推荐几个好用的录音转文字工具! 365在线转文字 直达链接:https://www.pdf365.cn/ 365在线转文字是一款提供在线录音转文字服务的工具,它以其高效、便捷的特点受到用户的青睐。用户无需下载安装任何软件,只