在执行

Factory<org.apache.shiro.mgt.SecurityManager> factory =
new IniSecurityManagerFactory("classpath:shiro.ini");

这条语句后,解析了ini配置文件,将需要的信息保存到了Ini的sections这个Map容器中,以节点名=》节点对象的方式保存

这个节点对象也是个Map,它保存了节点内容的key value值

解析已经完整,那么接下来的就是创建配置文件中配置的一些类了,比如自定义的realm,resolver,matcher,permission,sessionDao啥的

从下面这条语句开始

org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance();

下面是实现Factory类的类图

一、创建SecurityManager

 private SecurityManager createSecurityManager(Ini ini, Ini.Section mainSection) {

         Map<String, ?> defaults = createDefaults(ini, mainSection);//创建了一个叫做defaults的Map容器,这个容器用来装默认需要创建的类的实例,比如SecuirtyManager,IniRealm
Map<String, ?> objects = buildInstances(mainSection, defaults);// SecurityManager securityManager = getSecurityManagerBean(); boolean autoApplyRealms = isAutoApplyRealms(securityManager); if (autoApplyRealms) {
//realms and realm factory might have been created - pull them out first so we can
//initialize the securityManager:
Collection<Realm> realms = getRealms(objects);
//set them on the SecurityManager
if (!CollectionUtils.isEmpty(realms)) {
applyRealmsToSecurityManager(realms, securityManager);
}
} return securityManager;
}

第3行的创建了一个叫做defaults的Map容器,第4行创建了一个叫做objects的Map容器,先来看看defaults是怎么创建的

  protected Map<String, ?> createDefaults(Ini ini, Ini.Section mainSection) {
Map<String, Object> defaults = new LinkedHashMap<String, Object>(); SecurityManager securityManager = createDefaultInstance();//创建一个默认的安全管理器
defaults.put(SECURITY_MANAGER_NAME, securityManager);//将默认管理器存到defaults这个Map容器中,并且它的key为securityManager,这个名字是我们在ini配置文件中可以使用的 if (shouldImplicitlyCreateRealm(ini)) {//这里是创建IniRealm这个类的对象,只有在ini配置文件中有其他节点的存在并且有roles或者users其中一个或者两个节点时才会创建
Realm realm = createRealm(ini);
if (realm != null) {
defaults.put(INI_REALM_NAME, realm);//将IniRealm实例保存到默认的Map中
}
} return defaults;
}

这个defaults容器里面存储了一些默认需要创建的实例,比如SecurityManager,IniRealm

接下来再看看objects这个容器是怎么创建的

private Map<String, ?> buildInstances(Ini.Section section, Map<String, ?> defaults) {
this.builder = new ReflectionBuilder(defaults);
return this.builder.buildObjects(section);
}

进到代码里面后发现它首先先创建了一个builder,从字面上是一个反射建造者,它的构造体如下

public ReflectionBuilder(Map<String, ?> defaults) {
this.objects = CollectionUtils.isEmpty(defaults) ? new LinkedHashMap<String, Object>() : defaults;
}

上面这段代码,只是为了给这个建造者的成员容器objects添加引用,给的是前面创建的defaults容器的引用,这个容器中已经创建了secruityManager,还有IniRealm

这个反射建造者定义了许多的静态常量。

private static final String OBJECT_REFERENCE_BEGIN_TOKEN = "$";//这个很熟悉吧,ini配置文件中引用某个变量时使用的美元符号
private static final String ESCAPED_OBJECT_REFERENCE_BEGIN_TOKEN = "\\$";//转义的对象引用
private static final String GLOBAL_PROPERTY_PREFIX = "shiro";//全局属性前缀
private static final char MAP_KEY_VALUE_DELIMITER = ':';//这个也很熟悉吧,权限的
private static final String HEX_BEGIN_TOKEN = "0x";
private static final String NULL_VALUE_TOKEN = "null";
private static final String EMPTY_STRING_VALUE_TOKEN = "\"\"";//空字符串,也许会有人有疑问,为啥不是直接""呢,因为在ini配置文件里面,表示空字符串就得写上""
private static final char STRING_VALUE_DELIMETER = '"';//权限中会用到,main节点中map类型的会用到比如"user:delete,query","resource:delete,update"
private static final char MAP_PROPERTY_BEGIN_TOKEN = '[';//过滤器中传递参数
private static final char MAP_PROPERTY_END_TOKEN = ']';//过滤器中传递参数

创建了放射建造者之后,代码继续调用了this.builder.buildObjects(section);这条语句

从字面上讲用节点来构造对象

 public Map<String, ?> buildObjects(Map<String, String> kvPairs) {//记住这个kvPairs是一个Section,它继承了Map容器,里面存了节点中的内容
if (kvPairs != null && !kvPairs.isEmpty()) { // Separate key value pairs into object declarations and property assignment
// so that all objects can be created up front //https://issues.apache.org/jira/browse/SHIRO-85 - need to use LinkedHashMaps here:
Map<String, String> instanceMap = new LinkedHashMap<String, String>();//用于保存实例的Map
Map<String, String> propertyMap = new LinkedHashMap<String, String>();//用于保存属性的Map for (Map.Entry<String, String> entry : kvPairs.entrySet()) {
if (entry.getKey().indexOf('.') < || entry.getKey().endsWith(".class")) {//判断这个key是否没有包含.或者是否是以.class结束的字符串
instanceMap.put(entry.getKey(), entry.getValue());
} else {
propertyMap.put(entry.getKey(), entry.getValue());//如果这个key有.并且不是以.class结尾的,那么就表示这个key值对应的是一个属性注入的值
}
} // Create all instances
for (Map.Entry<String, String> entry : instanceMap.entrySet()) {
createNewInstance((Map<String, Object>) objects, entry.getKey(), entry.getValue());//循环遍历这个实例Map创建对应的类的实例
} // Set all properties
for (Map.Entry<String, String> entry : propertyMap.entrySet()) {
applyProperty(entry.getKey(), entry.getValue(), objects);
}
} //SHIRO-413: init method must be called for constructed objects that are Initializable
LifecycleUtils.init(objects.values()); return objects;
}

这里有三个重点,第一个是第21行的创建实例

第二个是注入属性值,

第三个是初始化创建的对象。

首先先分析一下createNewInstance这个方法

 protected void createNewInstance(Map<String, Object> objects, String name, String value) {

         Object currentInstance = objects.get(name);//从objects容器中取得当前name指定的对象
if (currentInstance != null) {//如果已经存在,将打印日志,提示这个对象已经存在了,没有必要多次定义
log.info("An instance with name '{}' already exists. " +
"Redefining this object as a new instance of type {}", name, value);
} Object instance;//name with no property, assume right hand side of equals sign is the class name:
try {
instance = ClassUtils.newInstance(value);//创建实例
if (instance instanceof Nameable) {//如果这个实例实现了Nameable接口,那么就可以进行命名,比如realm的实现类就是一种可以命名的类
((Nameable) instance).setName(name);
}
} catch (Exception e) {
String msg = "Unable to instantiate class [" + value + "] for object named '" + name + "'. " +
"Please ensure you've specified the fully qualified class name correctly.";
throw new ConfigurationException(msg, e);
}
objects.put(name, instance);//以name=》instance保存
}

重点第11行,ClassUtil是shiro实现的一个类助手

public static Object newInstance(String fqcn) {
return newInstance(forName(fqcn));
}

看一下forName方法

 public static Class forName(String fqcn) throws UnknownClassException {

        Class clazz = THREAD_CL_ACCESSOR.loadClass(fqcn);

        if (clazz == null) {
if (log.isTraceEnabled()) {
log.trace("Unable to load class named [" + fqcn +
"] from the thread context ClassLoader. Trying the current ClassLoader...");
}
clazz = CLASS_CL_ACCESSOR.loadClass(fqcn);
} if (clazz == null) {
if (log.isTraceEnabled()) {
log.trace("Unable to load class named [" + fqcn + "] from the current ClassLoader. " +
"Trying the system/application ClassLoader...");
}
clazz = SYSTEM_CL_ACCESSOR.loadClass(fqcn);
} if (clazz == null) {
String msg = "Unable to load class named [" + fqcn + "] from the thread context, current, or " +
"system/application ClassLoaders. All heuristics have been exhausted. Class could not be found.";
throw new UnknownClassException(msg);
} return clazz;
}

上面的是通过类名来加载类,标红的都是类加载器,如果线程上下文加载器没有加载到类,那么就叫类加载器加载,还加载不到就叫系统上下文加载器加载

下面是这些类加载器的获取源码

 private static final ClassLoaderAccessor THREAD_CL_ACCESSOR = new ExceptionIgnoringAccessor() {
@Override
protected ClassLoader doGetClassLoader() throws Throwable {
return Thread.currentThread().getContextClassLoader();
}
}; /**
* @since 1.0
*/
private static final ClassLoaderAccessor CLASS_CL_ACCESSOR = new ExceptionIgnoringAccessor() {
@Override
protected ClassLoader doGetClassLoader() throws Throwable {
return ClassUtils.class.getClassLoader();
}
}; /**
* @since 1.0
*/
private static final ClassLoaderAccessor SYSTEM_CL_ACCESSOR = new ExceptionIgnoringAccessor() {
@Override
protected ClassLoader doGetClassLoader() throws Throwable {
return ClassLoader.getSystemClassLoader();
}
};

很好,既然已经将类加载进来了,那么接下来就应该进行实例化了

  public static Object newInstance(Class clazz) {
if (clazz == null) {
String msg = "Class method parameter cannot be null.";
throw new IllegalArgumentException(msg);
}
try {
return clazz.newInstance();
} catch (Exception e) {
throw new InstantiationException("Unable to instantiate class [" + clazz.getName() + "]", e);
}
}

第7行,多么眼熟的构造实例的方式啊

好了,对象全都创建好了,那肯定就存起来啊,统统都存到反射建造者的objects容器中了

对象的建造告一段落了,那么接下就应该进行属性注入了,从下面这段开始

for (Map.Entry<String, String> entry : propertyMap.entrySet()) {
applyProperty(entry.getKey(), entry.getValue(), objects);
}

进入applyProperty方法

  protected void applyProperty(String key, String value, Map objects) {

         int index = key.indexOf('.');

         if (index >= ) {
String name = key.substring(, index);//截取.前面的字符串。比如securityManager.authenticator.authenticationStrategy,会获取到security这个字符串
String property = key.substring(index + , key.length());//获取属性,比如上面这个字符串会被获取成authenticator.authenticationStrategy if (GLOBAL_PROPERTY_PREFIX.equalsIgnoreCase(name)) {//标红的常量值是shiro
applyGlobalProperty(objects, property, value);
} else {
applySingleProperty(objects, name, property, value);
} } else {
throw new IllegalArgumentException("All property keys must contain a '.' character. " +
"(e.g. myBean.property = value) These should already be separated out by buildObjects().");
}
}

重点先看到第12行,第10行稍后再看,看懂了第12行,那么第10行也是懂了

这个方法传入了创建了securityManager和IniRealm以及其他的实例,比如sessionManager,啊我们在[main]节点配置的类的容器,还有变量名name,属性名,还有配置文件中的属性值,比如$sessionDao

applySingleProperty方法

  protected void applySingleProperty(Map objects, String name, String property, String value) {
Object instance = objects.get(name);//从objects中获取到name对应的对象
if (property.equals("class")) {
throw new IllegalArgumentException("Property keys should not contain 'class' properties since these " +
"should already be separated out by buildObjects()."); } else if (instance == null) {
String msg = "Configuration error. Specified object [" + name + "] with property [" +
property + "] without first defining that object's class. Please first " +
"specify the class property first, e.g. myObject = fully_qualified_class_name " +
"and then define additional properties.";
throw new IllegalArgumentException(msg); } else {
applyProperty(instance, property, value);
}
}

继续进入applyProperty方法

 protected void applyProperty(Object object, String propertyName, String stringValue) {

         Object value;

         if (NULL_VALUE_TOKEN.equals(stringValue)) {//null
value = null;
} else if (EMPTY_STRING_VALUE_TOKEN.equals(stringValue)) {//""
value = StringUtils.EMPTY_STRING;
} else if (isIndexedPropertyAssignment(propertyName)) {//内部判断这个属性是不是以]结尾的
String checked = checkForNullOrEmptyLiteral(stringValue);//检查stringValue是否为null或者empty,否则直接返回原值
value = resolveValue(checked); //解析这个这个值,获得真正的对象
} else if (isTypedProperty(object, propertyName, Set.class)) {
value = toSet(stringValue);
} else if (isTypedProperty(object, propertyName, Map.class)) {
value = toMap(stringValue);
} else if (isTypedProperty(object, propertyName, List.class)) {
value = toList(stringValue);
} else if (isTypedProperty(object, propertyName, Collection.class)) {
value = toCollection(stringValue);
} else if (isTypedProperty(object, propertyName, byte[].class)) {
value = toBytes(stringValue);
} else if (isTypedProperty(object, propertyName, ByteSource.class)) {
byte[] bytes = toBytes(stringValue);
value = ByteSource.Util.bytes(bytes);
} else {
String checked = checkForNullOrEmptyLiteral(stringValue);
value = resolveValue(checked);
} applyProperty(object, propertyName, value);
} }

我们发现第12行,14,16等,都调用了一个叫isTypedProperty的方法,我们先来看看这个方法,

重点要说的resolveValue方法先押后

  protected boolean isTypedProperty(Object object, String propertyName, Class clazz) {
if (clazz == null) {
throw new NullPointerException("type (class) argument cannot be null.");
}
try {
PropertyDescriptor descriptor = PropertyUtils.getPropertyDescriptor(object, propertyName);//获得属性描述器
if (descriptor == null) {
String msg = "Property '" + propertyName + "' does not exist for object of " +
"type " + object.getClass().getName() + ".";
throw new ConfigurationException(msg);
}
Class propertyClazz = descriptor.getPropertyType();
return clazz.isAssignableFrom(propertyClazz);
} catch (ConfigurationException ce) {
//let it propagate:
throw ce;
} catch (Exception e) {
String msg = "Unable to determine if property [" + propertyName + "] represents a " + clazz.getName();
throw new ConfigurationException(msg, e);
}
}

第6行的属性描述器干了好多的事情

  public PropertyDescriptor getPropertyDescriptor(Object bean,
String name)
throws IllegalAccessException, InvocationTargetException,
NoSuchMethodException { if (bean == null) {
throw new IllegalArgumentException("No bean specified");
}
if (name == null) {
throw new IllegalArgumentException("No name specified for bean class '" +
bean.getClass() + "'");
} // Resolve nested references
while (resolver.hasNested(name)) {//判断是否存在内嵌的属性引用,如name为a.b
String next = resolver.next(name);//解析,如a.b,返回后变成a
Object nestedBean = getProperty(bean, next);
if (nestedBean == null) {
throw new NestedNullException
("Null property value for '" + next +
"' on bean class '" + bean.getClass() + "'");
}
bean = nestedBean;
name = resolver.remove(name);
} // Remove any subscript from the final name value
name = resolver.getProperty(name); // Look up and return this property from our cache
// creating and adding it to the cache if not found.
if (name == null) {
return (null);
} PropertyDescriptor[] descriptors = getPropertyDescriptors(bean);
if (descriptors != null) { for (int i = ; i < descriptors.length; i++) {
if (name.equals(descriptors[i].getName())) {
return (descriptors[i]);
}
}
} PropertyDescriptor result = null;
FastHashMap mappedDescriptors =
getMappedPropertyDescriptors(bean);
if (mappedDescriptors == null) {
mappedDescriptors = new FastHashMap();
mappedDescriptors.setFast(true);
mappedDescriptorsCache.put(bean.getClass(), mappedDescriptors);
}
result = (PropertyDescriptor) mappedDescriptors.get(name);
if (result == null) {
// not found, try to create it
try {
result = new MappedPropertyDescriptor(name, bean.getClass());
} catch (IntrospectionException ie) {
/* Swallow IntrospectionException
* TODO: Why?
*/
}
if (result != null) {
mappedDescriptors.put(name, result);
}
} return result; }

第15行的hasNested

public boolean hasNested(String expression) {
if (expression == null || expression.length() == ) {
return false;
} else {
return (remove(expression) != null);//如果返回的不是空,那表明有嵌套属性,否则没有
}
}

remove方法的代码如下

 public String remove(String expression) {
if (expression == null || expression.length() == ) {
return null;
}
String property = next(expression);//获取截取过后的字符串,如a.b返回a
if (expression.length() == property.length()) {//如果经过处理后的字符串和源字符串的长度一样说明没有嵌套的属性
return null;
}
int start = property.length();
if (expression.charAt(start) == NESTED) {//截取.后面的字符串。
start++;
}
return expression.substring(start);//截取剩下的字符串
}
}

下面是next方法的代码,判断这个属性是否包含嵌入的属性,比如authenticator.authenticationStrategy像这样的属性是需要进行判断的,具体代码如下

 if (expression == null || expression.length() == ) {
return null;
}
boolean indexed = false;
boolean mapped = false;
for (int i = ; i < expression.length(); i++) {
char c = expression.charAt(i);
if (indexed) {
if (c == INDEXED_END) {//[
return expression.substring(, i + );
}
} else if (mapped) {
if (c == MAPPED_END) {//)
return expression.substring(, i + );
}
} else {
if (c == NESTED) {//.
return expression.substring(, i);
} else if (c == MAPPED_START) {//(
mapped = true;
} else if (c == INDEXED_START) {//]
indexed = true;
}
}
}
return expression;

以上代码分这些个情况,a.b,a[],a()

String next = resolver.next(name);
Object nestedBean = getProperty(bean, next);

它调用完了next后,继续调用了getProperty方法,而这个方法又立即调用了getNestedProperty方法,如果原始属性值为a.b,那么以下name参数应该为a

 public Object getNestedProperty(Object bean, String name)
throws IllegalAccessException, InvocationTargetException,
NoSuchMethodException { if (bean == null) {
throw new IllegalArgumentException("No bean specified");
}
if (name == null) {
throw new IllegalArgumentException("No name specified for bean class '" +
bean.getClass() + "'");
} // Resolve nested references
while (resolver.hasNested(name)) {//又使用了解析器对这个name进行了一次嵌套引用的判断
String next = resolver.next(name);
Object nestedBean = null;
if (bean instanceof Map) {//判断当前的bean是否为Map
nestedBean = getPropertyOfMapBean((Map) bean, next);//比如next为a(b)
} else if (resolver.isMapped(next)) {//判断这个next是否被映射的,也是a(b)这种形式的
nestedBean = getMappedProperty(bean, next);//获取到被映射的属性
} else if (resolver.isIndexed(next)) {//判断这个next有没有[]
nestedBean = getIndexedProperty(bean, next);//比如a[1],[1]
} else {
nestedBean = getSimpleProperty(bean, next);
}
if (nestedBean == null) {
throw new NestedNullException
("Null property value for '" + name +
"' on bean class '" + bean.getClass() + "'");
}
bean = nestedBean;
name = resolver.remove(name);
} if (bean instanceof Map) {
bean = getPropertyOfMapBean((Map) bean, name);
} else if (resolver.isMapped(name)) {
bean = getMappedProperty(bean, name);
} else if (resolver.isIndexed(name)) {
bean = getIndexedProperty(bean, name);
} else {
bean = getSimpleProperty(bean, name);
}
return bean; }

第18这个用法我好像没有用过,不过从源码中可以看出它可以创个a(b)这个字符串,最后它把a和b拆开,如果a不为空字符串或者null,那么就直接bean.get(a),如果a为空,那么就bean.get(b)

第20行判断当前的bean是否是DynaBean的子类的实例,并且从一个实现了DynaClass的实例中的一个Map取数据,也就是说我们可以在ini配置文件中配置配置DynaBean,DynaClass连个实例,并且可以给DynaClass实例的Map写入初始化的值。。。。。。。。。。。算了,我不知道,我编不下去了,我在Google上没搜到,shiro文档中没找到。

第22行的如果只有[i],那么取到index就是i,然后判断这个bean是否为数组还是List,然后直接返回相应下标的值

直接看到第42行,获得简单属性

  public Object getSimpleProperty(Object bean, String name)
throws IllegalAccessException, InvocationTargetException,
NoSuchMethodException {
//省略部分代码
// Retrieve the property getter method for the specified property
PropertyDescriptor descriptor =
getPropertyDescriptor(bean, name);//获得这个name对应的属性描述
if (descriptor == null) {
throw new NoSuchMethodException("Unknown property '" +
name + "' on class '" + bean.getClass() + "'" );
}
Method readMethod = getReadMethod(bean.getClass(), descriptor);
if (readMethod == null) {
throw new NoSuchMethodException("Property '" + name +
"' has no getter method in class '" + bean.getClass() + "'");
} // Call the property getter and return the value
Object value = invokeMethod(readMethod, bean, EMPTY_OBJECT_ARRAY);
return (value); }

第42行通过name获得对应这个bean的属性描述器,内部调用了

beanInfo = Introspector.getBeanInfo(beanClass);

Introspector类是JDK提供的一个工具,它在java.beans包下,getBeanInfo是获取其所有属性、公开的方法和事件。

通过返回的beanInfo信息,beanInfo.getPropertyDescriptors()得到一个PropertyDescriptor[],然后

for (int i = ; i < descriptors.length; i++) {
if (name.equals(descriptors[i].getName())) {
return (descriptors[i]);
}
}

找到对应的属性描述返回即可

返回属性描述器之后,我们又回到了isTypeProperty方法

  protected boolean isTypedProperty(Object object, String propertyName, Class clazz) {
if (clazz == null) {
throw new NullPointerException("type (class) argument cannot be null.");
}
try {
PropertyDescriptor descriptor = PropertyUtils.getPropertyDescriptor(object, propertyName);//此时已经获得了属性描述器
if (descriptor == null) {
String msg = "Property '" + propertyName + "' does not exist for object of " +
"type " + object.getClass().getName() + ".";
throw new ConfigurationException(msg);
}
Class propertyClazz = descriptor.getPropertyType();//判断这个属性是什么类型的
return clazz.isAssignableFrom(propertyClazz);//判断这个属性所属的类型是否是clazz的子类,或者是它本身
} catch (ConfigurationException ce) {
//let it propagate:
throw ce;
} catch (Exception e) {
String msg = "Unable to determine if property [" + propertyName + "] represents a " + clazz.getName();
throw new ConfigurationException(msg, e);
}
}

这个方法返回到这个applyProperty方法

  protected void applyProperty(Object object, String propertyName, String stringValue) {

         Object value;

         if (NULL_VALUE_TOKEN.equals(stringValue)) {
value = null;
} else if (EMPTY_STRING_VALUE_TOKEN.equals(stringValue)) {
value = StringUtils.EMPTY_STRING;
} else if (isIndexedPropertyAssignment(propertyName)) {
String checked = checkForNullOrEmptyLiteral(stringValue);
value = resolveValue(checked);
} else if (isTypedProperty(object, propertyName, Set.class)) {
value = toSet(stringValue);
} else if (isTypedProperty(object, propertyName, Map.class)) {
value = toMap(stringValue);
} else if (isTypedProperty(object, propertyName, List.class)) {
value = toList(stringValue);
} else if (isTypedProperty(object, propertyName, Collection.class)) {
value = toCollection(stringValue);
} else if (isTypedProperty(object, propertyName, byte[].class)) {
value = toBytes(stringValue);
} else if (isTypedProperty(object, propertyName, ByteSource.class)) {
byte[] bytes = toBytes(stringValue);
value = ByteSource.Util.bytes(bytes);
} else {
String checked = checkForNullOrEmptyLiteral(stringValue);
value = resolveValue(checked);
} applyProperty(object, propertyName, value);
}

看到了吧,刚才的clazz就是这里面的的集合,Set.class,Map.class等等

就假设当前属性是Map的,看看这个就好了,其他的也就很简单了

  protected Map<?, ?> toMap(String sValue) {
String[] tokens = StringUtils.split(sValue, StringUtils.DEFAULT_DELIMITER_CHAR,
StringUtils.DEFAULT_QUOTE_CHAR, StringUtils.DEFAULT_QUOTE_CHAR, true, true);//, "
if (tokens == null || tokens.length <= ) {
return null;
} //SHIRO-423: check to see if the value is a referenced Map already, and if so, return it immediately:
if (tokens.length == && isReference(tokens[])) {
Object reference = resolveReference(tokens[]);
if (reference instanceof Map) {
return (Map)reference;
}
} Map<String, String> mapTokens = new LinkedHashMap<String, String>(tokens.length);
for (String token : tokens) {
String[] kvPair = StringUtils.split(token, MAP_KEY_VALUE_DELIMITER);
if (kvPair == null || kvPair.length != ) {
String msg = "Map property value [" + sValue + "] contained key-value pair token [" +
token + "] that does not properly split to a single key and pair. This must be the " +
"case for all map entries.";
throw new ConfigurationException(msg);
}
mapTokens.put(kvPair[], kvPair[]);
} //now convert into correct values and/or references:
Map<Object, Object> map = new LinkedHashMap<Object, Object>(mapTokens.size());
for (Map.Entry<String, String> entry : mapTokens.entrySet()) {
Object key = resolveValue(entry.getKey());
Object value = resolveValue(entry.getValue());
map.put(key, value);
}
return map;
} // @since 1.2.2
// TODO: make protected in 1.3+
private Collection<?> toCollection(String sValue) { String[] tokens = StringUtils.split(sValue);
if (tokens == null || tokens.length <= ) {
return null;
} //SHIRO-423: check to see if the value is a referenced Collection already, and if so, return it immediately:
if (tokens.length == && isReference(tokens[])) {
Object reference = resolveReference(tokens[]);
if (reference instanceof Collection) {
return (Collection)reference;
}
} //now convert into correct values and/or references:
List<Object> values = new ArrayList<Object>(tokens.length);
for (String token : tokens) {
Object value = resolveValue(token);
values.add(value);
}
return values;
}

我们看看第2行是怎么对字符串值进行分割的,Map的字符串值是这种形式的key1:$object1, key2:$object2

看下split的方法


//aLine是字符串值,比如key1:$object1, key2:$object2,delimiter=>, beginQuoteChar=>" endQuoteChar=>" retainQuotes=>true trimTokens=>true
 1 public static String[] split(String aLine, char delimiter, char beginQuoteChar, char endQuoteChar,
boolean retainQuotes, boolean trimTokens) {
String line = clean(aLine);
if (line == null) {
return null;
} List<String> tokens = new ArrayList<String>();
StringBuilder sb = new StringBuilder();
boolean inQuotes = false; for (int i = ; i < line.length(); i++) { char c = line.charAt(i);
if (c == beginQuoteChar) {//在引号内的逗号不会产生分割的作用。
// this gets complex... the quote may end a quoted block, or escape another quote.
// do a 1-char lookahead:
if (inQuotes // we are in quotes, therefore there can be escaped quotes in here.//连续的引号会去掉一个,比如""=>","""=>"",""""=>""
&& line.length() > (i + ) // there is indeed another character to check.
&& line.charAt(i + ) == beginQuoteChar) { // ..and that char. is a quote also.
// we have two quote chars in a row == one quote char, so consume them both and
// put one on the token. we do *not* exit the quoted text.
sb.append(line.charAt(i + ));
i++;
} else {
inQuotes = !inQuotes;
if (retainQuotes) {
sb.append(c);
}
}
} else if (c == endQuoteChar) {
inQuotes = !inQuotes;
if (retainQuotes) {
sb.append(c);
}
} else if (c == delimiter && !inQuotes) {
String s = sb.toString();
if (trimTokens) {
s = s.trim();
}
tokens.add(s);
sb = new StringBuilder(); // start work on next token
} else {
sb.append(c);
}
}
String s = sb.toString();
if (trimTokens) {
s = s.trim();
}
tokens.add(s);
return tokens.toArray(new String[tokens.size()]);
}

上面这段程序呢,完全可以拷贝到自己的代码中进行测试,debug,我觉得这样更好理解,这段代码的表示在引号内的逗号是不会产生分割作用的

并且如果有多个连续的引号,那么会去掉一部分引号。那这里使用引号用来干什么了,有了引号你就可以在里面写一些shiro的关键字符,

比如"key1:hello":122, key2:"helo,object2",用引号包裹起来的字符串会被视为一个整体,不会做解析,不过$符号还是会被解析的

为什么呢,因为shiro无法确认你会不会走非主流,比如你来这样的

,\=bean=com.test.User

,feizhuliu=com.test.Test                -------------->里面有一个Map的属性

,feizhuliu.map=user:"$,\=bean"     -------------->所以啊,你这个$还是得解析的,要不然,直接被解析成字符串,用户是不会答应的,用户是上帝嘛(虽然这个用户不是个好人,我也承认我不是个好人)

用户可能又要说了,我要的就是包含$的字符串。

很简单,直接加个反斜杠"\$,\=bean"

当字符串被解析字符串数组之后,shiro对这些字符串数组进行了引用判断,判断是否带有$符号

  if (tokens.length ==  && isReference(tokens[])) {
Object reference = resolveReference(tokens[]);
if (reference instanceof Map) {
return (Map)reference;
}
}

上面这段代码表示当tokens数组的值只有一个的时候,就检查这个值的是否是$开头,如果是,说明应用了一个Map实例,这个Map实例肯定是在配置文件中定义的,并且像其他的

实例一样被存在了反射建造者的objects容器中,直接去掉$符号,去这个容器中查找就是了,找到就返回,找不到就直接报错,因为你引用了一个不存在的实例。

如果能够拿到值,那么还要判断这个值是不是Map类型,如果是,直接返回

isReference

protected boolean isReference(String value) {
return value != null && value.startsWith(OBJECT_REFERENCE_BEGIN_TOKEN);//$
}

resolveReference

 protected Object resolveReference(String reference) {
String id = getId(reference);//内部代码referenceToken.substring(OBJECT_REFERENCE_BEGIN_TOKEN.length());去掉$符号
log.debug("Encountered object reference '{}'. Looking up object with id '{}'", reference, id);
final Object referencedObject = getReferencedObject(id);//Object o = objects != null && !objects.isEmpty() ? objects.get(id) : null;
if (referencedObject instanceof Factory) {//如果这个对象是一个工厂对象,那么就直接调用他的工厂方法返回实例
return ((Factory) referencedObject).getInstance();
}
return referencedObject;
}

如果返回的实例不是Map类型怎么办?

 Map<String, String> mapTokens = new LinkedHashMap<String, String>(tokens.length);
for (String token : tokens) {
String[] kvPair = StringUtils.split(token, MAP_KEY_VALUE_DELIMITER);//其实这个方法层层调用,又调用到了上面我们说的那个分割字符串的方法,只不过,它把其中的retainQuotes变成了false,分隔符改成了:
if (kvPair == null || kvPair.length != ) {
String msg = "Map property value [" + sValue + "] contained key-value pair token [" +
token + "] that does not properly split to a single key and pair. This must be the " +
"case for all map entries.";
throw new ConfigurationException(msg);
}
mapTokens.put(kvPair[], kvPair[]);
}

第3行分割字符串最终调用的方法和上面讲的一样,只不过它将其中的一个retainQuotes参数修改成了false,如果修改成了false,那么"就会被去掉

除非出现了两个引号连在一起的情况下会被保留一个,比如key3:"$,"\=user"=》key3:$,\=user  key3:""$,"\=user"=>key3:"$,\=user

它们以分隔符:进行分割

接着上面讲的,如果是以$符号开头的,得到的对象却不是Map类型的,这个字符串可能存在这么几个可能性,比如$key:value或者$key

$key:value可以被分割

$key不能被分割,那么这种情况会直接报错

字符串分割完成后就开始拿到对应的属性值了

Map<Object, Object> map = new LinkedHashMap<Object, Object>(mapTokens.size());
for (Map.Entry<String, String> entry : mapTokens.entrySet()) {
Object key = resolveValue(entry.getKey());
Object value = resolveValue(entry.getValue());
map.put(key, value);
}

看到resolveValue方法

 protected Object resolveValue(String stringValue) {
Object value;
if (isReference(stringValue)) {//讲过了,就是判断一下是不是以$开头
value = resolveReference(stringValue);//上面说过了,就是从objects这个容器中获取到对应的值
} else {
value = unescapeIfNecessary(stringValue);//解析普通的字符串,如果有用\$做前缀的,那么就去掉\
}
return value;
}

解析成值后,返回,开始注入依赖了

  protected void applyProperty(Object object, String propertyPath, Object value) {

         int mapBegin = propertyPath.indexOf(MAP_PROPERTY_BEGIN_TOKEN);//判断这个属性是否存在[
int mapEnd = -;
String mapPropertyPath = null;
String keyString = null; String remaining = null; if (mapBegin >= ) {
//a map is being referenced in the overall property path. Find just the map's path:
mapPropertyPath = propertyPath.substring(, mapBegin);
//find the end of the map reference:
mapEnd = propertyPath.indexOf(MAP_PROPERTY_END_TOKEN, mapBegin);
//find the token in between the [ and the ] (the map/array key or index):
keyString = propertyPath.substring(mapBegin+, mapEnd);//得到【】内的内容 //find out if there is more path reference to follow. If not, we're at a terminal of the OGNL expression
if (propertyPath.length() > (mapEnd+)) {
remaining = propertyPath.substring(mapEnd+);
if (remaining.startsWith(".")) {
remaining = StringUtils.clean(remaining.substring());
}
}
} if (remaining == null) {
//we've terminated the OGNL expression. Check to see if we're assigning a property or a map entry:
if (keyString == null) {
//not a map or array value assignment - assign the property directly:
setProperty(object, propertyPath, value);
} else {
//we're assigning a map or array entry. Check to see which we should call:
if (isTypedProperty(object, mapPropertyPath, Map.class)) {
Map map = (Map)getProperty(object, mapPropertyPath);
Object mapKey = resolveValue(keyString);
//noinspection unchecked
map.put(mapKey, value);
} else {
//must be an array property. Convert the key string to an index:
int index = Integer.valueOf(keyString);
setIndexedProperty(object, mapPropertyPath, index, value);
}
}
} else {
//property is being referenced as part of a nested path. Find the referenced map/array entry and
//recursively call this method with the remaining property path
Object referencedValue = null;
if (isTypedProperty(object, mapPropertyPath, Map.class)) {
Map map = (Map)getProperty(object, mapPropertyPath);
Object mapKey = resolveValue(keyString);
referencedValue = map.get(mapKey);
} else {
//must be an array property:
int index = Integer.valueOf(keyString);
referencedValue = getIndexedProperty(object, mapPropertyPath, index);
} if (referencedValue == null) {
throw new ConfigurationException("Referenced map/array value '" + mapPropertyPath + "[" +
keyString + "]' does not exist.");
} applyProperty(referencedValue, remaining, value);
}
}

上面这段代码又属性名称进行了一系列的判断,判断它是否包含中括号,如果有就拿到中括号中的下标值,并且检查这个中括号后面还有没有.之类,如果有那么又继续isTyped....啥的

这些判断的方法都说过了,没必要再说,这些判断都是为了得到最后那个需要进行注入的bean和属性。

跳过一堆需要进行判断的语句,直接看看它是怎么注入的

翻过几座山,跳过几条河,看到了这么一句代码

BeanUtilsBean.getInstance().setProperty(bean, name, value);

下面是setProperty的方法(删除了部分日志信息)

 public void setProperty(Object bean, String name, Object value)
throws IllegalAccessException, InvocationTargetException { // Resolve any nested expression to get the actual target bean
Object target = bean;
Resolver resolver = getPropertyUtils().getResolver();//又拿到了这个解析器
while (resolver.hasNested(name)) {//又进行了内嵌判断
try {
target = getPropertyUtils().getProperty(target, resolver.next(name));
name = resolver.remove(name);
} catch (NoSuchMethodException e) {
return; // Skip this property setter
}
} // Declare local variables we will require
String propName = resolver.getProperty(name); // Simple name of target property
Class type = null; // Java type of target property
int index = resolver.getIndex(name); // Indexed subscript value (if any)获得下表值
String key = resolver.getKey(name); // Mapped key value (if any)获得key值,这些在前面已经说过了 // Calculate the property type
if (target instanceof DynaBean) {
DynaClass dynaClass = ((DynaBean) target).getDynaClass();
DynaProperty dynaProperty = dynaClass.getDynaProperty(propName);
if (dynaProperty == null) {
return; // Skip this property setter
}
type = dynaProperty.getType();
} else if (target instanceof Map) {
type = Object.class;
} else if (target != null && target.getClass().isArray() && index >= ) {
type = Array.get(target, index).getClass();
} else {
PropertyDescriptor descriptor = null;
try {
descriptor =
getPropertyUtils().getPropertyDescriptor(target, name);//获得属性描述器,前面说过了,它是通过jdk的java.beans包下的Introspector来获取bean信息的
if (descriptor == null) {
return; // Skip this property setter
}
} catch (NoSuchMethodException e) {
return; // Skip this property setter
}
if (descriptor instanceof MappedPropertyDescriptor) {//判断当前的属性描述是否为被映射属性的属性描述
if (((MappedPropertyDescriptor) descriptor).getMappedWriteMethod() == null) {
if (log.isDebugEnabled()) {
log.debug("Skipping read-only property");
}
return; // Read-only, skip this property setter
}
type = ((MappedPropertyDescriptor) descriptor).
getMappedPropertyType();
} else if (index >= && descriptor instanceof IndexedPropertyDescriptor) {//判断是否为索引属性描述
if (((IndexedPropertyDescriptor) descriptor).getIndexedWriteMethod() == null) {
if (log.isDebugEnabled()) {
log.debug("Skipping read-only property");
}
return; // Read-only, skip this property setter
}
type = ((IndexedPropertyDescriptor) descriptor).
getIndexedPropertyType();
} else if (key != null) {
if (descriptor.getReadMethod() == null) {//获得可以读取属性的方法,如getter方法
if (log.isDebugEnabled()) {
log.debug("Skipping read-only property");
}
return; // Read-only, skip this property setter
}
type = (value == null) ? Object.class : value.getClass();
} else {
if (descriptor.getWriteMethod() == null) {//获得可以写属性的方法,如setter方法
if (log.isDebugEnabled()) {
log.debug("Skipping read-only property");
}
return; // Read-only, skip this property setter
}
type = descriptor.getPropertyType();//获得这个属性的类型
}
}
//下面一堆的判断,判断这个属性是不是数组,是不是String,如果是就采用相应的convert取处理
// Convert the specified value to the required type
Object newValue = null;
if (type.isArray() && (index < )) { // Scalar value into array
if (value == null) {
String[] values = new String[];
values[] = null;
newValue = getConvertUtils().convert(values, type);
} else if (value instanceof String) {
newValue = getConvertUtils().convert(value, type);
} else if (value instanceof String[]) {
newValue = getConvertUtils().convert((String[]) value, type);
} else {
newValue = convert(value, type);
}
} else if (type.isArray()) { // Indexed value into array
if (value instanceof String || value == null) {
newValue = getConvertUtils().convert((String) value,
type.getComponentType());
} else if (value instanceof String[]) {
newValue = getConvertUtils().convert(((String[]) value)[],
type.getComponentType());
} else {
newValue = convert(value, type.getComponentType());
}
} else { // Value into scalar
if (value instanceof String) {
newValue = getConvertUtils().convert((String) value, type);
} else if (value instanceof String[]) {
newValue = getConvertUtils().convert(((String[]) value)[],
type);
} else {
newValue = convert(value, type);
}
} // Invoke the setter method
try {
getPropertyUtils().setProperty(target, name, newValue);
} catch (NoSuchMethodException e) {
throw new InvocationTargetException
(e, "Cannot set " + propName);
} }

上面有对value值进行类型的判断,它可能是个数组,也可能是一个字符串,比如

[main]

zhang=123,253,45,1246,5               ------------------->这个是一个数组

haha=hehe                                     ------------------->这是个普通字符串

我们来看下convert这个方法的代码

  protected Object convert(Object value, Class type) {
Converter converter = getConvertUtils().lookup(type);
if (converter != null) {
log.trace(" USING CONVERTER " + converter);
return converter.convert(type, value);
} else {
return value;
}
}

第2行代码是到一个WeakFastHashMap类型的Map中找相应类型的转换器,这个Map中注册了这些转换器

 register(Boolean.TYPE,   throwException ? new BooleanConverter()    : new BooleanConverter(Boolean.FALSE));
register(Byte.TYPE, throwException ? new ByteConverter() : new ByteConverter(ZERO));
register(Character.TYPE, throwException ? new CharacterConverter() : new CharacterConverter(SPACE));
register(Double.TYPE, throwException ? new DoubleConverter() : new DoubleConverter(ZERO));
register(Float.TYPE, throwException ? new FloatConverter() : new FloatConverter(ZERO));
register(Integer.TYPE, throwException ? new IntegerConverter() : new IntegerConverter(ZERO));
register(Long.TYPE, throwException ? new LongConverter() : new LongConverter(ZERO));
register(Short.TYPE, throwException ? new ShortConverter() : new ShortConverter(ZERO));

拿其中的一个叫BooleanConverter的转换器看看

protected Object convertToType(Class type, Object value) throws Throwable {

        // All the values in the trueStrings and falseStrings arrays are
// guaranteed to be lower-case. By converting the input value
// to lowercase too, we can use the efficient String.equals method
// instead of the less-efficient String.equalsIgnoreCase method.
String stringValue = value.toString().toLowerCase();
     //private String[] trueStrings = {"true", "yes", "y", "on", "1"};等于这里面某个值就为true
for(int i=; i<trueStrings.length; ++i) {
if (trueStrings[i].equals(stringValue)) {
return Boolean.TRUE;
}
}
//private String[] falseStrings = {"false", "no", "n", "off", "0"};等于这里面某个值就为false
for(int i=; i<falseStrings.length; ++i) {
if (falseStrings[i].equals(stringValue)) {
return Boolean.FALSE;
}
} throw new ConversionException("Can't convert value '" + value + "' to a Boolean");
}

以上这些就是转换器,如果存在相应的转换器就会将原先的value值进行转换成其他的value值返回,转换完成后,接下来就是获得setter方法,进行反射调用了

// Invoke the setter method
try {
getPropertyUtils().setProperty(target, name, newValue);
} catch (NoSuchMethodException e) {
throw new InvocationTargetException
(e, "Cannot set " + propName);
}

进入setProperty一路跟踪,期间又回到那些嵌套属性判断的方法了,最后调用

 PropertyDescriptor descriptor =
getPropertyDescriptor(bean, name);

获得这个属性对应的属性描述器

继续调用了

Method writeMethod = getWriteMethod(bean.getClass(), descriptor);
Method getWriteMethod(Class clazz, PropertyDescriptor descriptor) {
return (MethodUtils.getAccessibleMethod(clazz, descriptor.getWriteMethod()));
}

descriptor.getWriteMethod()这个代码是获得一个bean的写方法,就是setter方法,这个是JDK中提供的方法

MethodUtils.getAccessibleMethod方法的代码如下

  public static Method getAccessibleMethod(Class clazz, Method method) {

         // Make sure we have a method to check
if (method == null) {
return (null);
} // If the requested method is not public we cannot call it
if (!Modifier.isPublic(method.getModifiers())) {//判断这个方法是不是public的
return (null);
} boolean sameClass = true;
if (clazz == null) {
clazz = method.getDeclaringClass();
} else {
sameClass = clazz.equals(method.getDeclaringClass());//比较当前传进来的这个bean和这个写方法所在的类是否是相同或者是它的子类
if (!method.getDeclaringClass().isAssignableFrom(clazz)) {
throw new IllegalArgumentException(clazz.getName() +
" is not assignable from " + method.getDeclaringClass().getName());
}
} // If the class is public, we are done
if (Modifier.isPublic(clazz.getModifiers())) {//判断这个类是不是public的
if (!sameClass && !Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
setMethodAccessible(method); // Default access superclass workaround//如果这个类是public的,方法不是public的,方法对应的类不是public的,那么就设置accessible为true
}
return (method);
}

上面这个方法没什么好说的就是对这个方法和所传的类类型是否相同,或者存在父子关系,还有就是对访问权限的设置

好了,得到setter方法了,那么接下来就是进行调用了

  invokeMethod(writeMethod, bean, values);
return method.invoke(bean, values);

总结

难就难在各种递归解析,考虑各种需要解析的情况,逻辑上比较复杂,真服了写这些代码的大神,ok!!!结束。

shiro创建配置对象的更多相关文章

  1. activiti学习3:流程引擎对象和流程引擎配置对象

    目录 activiti学习3:流程引擎对象和流程引擎配置对象 一.activiti的简单使用流程 二.流程引擎配置对象ProcessEngineConfiguration的介绍 三.activiti配 ...

  2. Shiro中Subject对象的创建与绑定流程分析

    我们在平常使用Shrio进行身份认证时,经常通过获取Subject 对象中保存的Session.Principal等信息,来获取认证用户的信息,也就是说Shiro会把认证后的用户信息保存在Subjec ...

  3. springboot启动配置原理之一(创建SpringApplication对象)

    几个重要的事件回调机制 配置在META-INF/spring.factories ApplicationContextInitializer SpringApplicationRunListener ...

  4. [课本10.1.4]JDBC数据库连接池- C3P0数据源--通过构造方法创建数据源对象--通过配置文件创建数据源对象[推荐]

    JDBC- C3P0数据源 /*重点提醒*/ 连接数据库的较低的jar包版本会与较高版本的mysql版本有冲突; 通过把mysql 8.0的版本降到5.5, jar包仍使用较高的 mysql-conn ...

  5. 7_1.springboot2.x启动配置原理_1.创建SpringApplication对象

    环境准备 springboot2.1.9.idea2019. pom.xml 解析 几个重要的事件回调机制 配置在META-INF/spring.factories ApplicationContex ...

  6. 跟开涛老师学shiro -- INI配置

    之前章节我们已经接触过一些INI配置规则了,如果大家使用过如spring之类的IoC/DI容器的话,Shiro提供的INI配置也是非常类似的,即可以理解为是一个IoC/DI容器,但是区别在于它从一个根 ...

  7. Shiro ini配置

    Shiro.ini配置: ini配置文件类似Java中的properties(key = value),不过提供了key/value分类的特性,每个部分的key不重复即可 在eclipse中设置打开方 ...

  8. WCF初探-13:WCF客户端为双工服务创建回调对象

    前言: 在WCF初探-5:WCF消息交换模式之双工通讯(Duplex)博文中,我讲解了双工通信服务的一个应用场景,即订阅和发布模式,这一篇,我将通过一个消息发送的例子讲解一下WCF客户端如何为双工服务 ...

  9. Spring学习笔记之 Spring IOC容器(一)之 实例化容器,创建JavaBean对象,控制Bean实例化,setter方式注入,依赖属性的注入,自动装配功能实现自动属性注入

    本节主要内容:       1.实例化Spring容器示例    2.利用Spring容器创建JavaBean对象    3.如何控制Bean实例化    4.利用Spring实现bean属性sett ...

随机推荐

  1. SpringBoot中资源初始化加载的几种方式(看这一片就够了)

    一.问题 在平时的业务模块开发过程中,难免会需要做一些全局的任务.缓存.线程等等的初始化工作,那么如何解决这个问题呢?方法有多种,但具体又要怎么选择呢? 二.资源初始化 1.既然要做资源的初始化,那么 ...

  2. 惊:FastThreadLocal吞吐量居然是ThreadLocal的3倍!!!

    说明 接着上次手撕面试题ThreadLocal!!!面试官一听,哎呦不错哦!本文将继续上文的话题,来聊聊FastThreadLocal,目前关于FastThreadLocal的很多文章都有点老有点过时 ...

  3. 【JDK基础】java基础的一些资料

    工具:https://blog.csdn.net/javazejian/article/details/72828483 类加载器:https://blog.csdn.net/X5fnncxzq4/a ...

  4. C# 死锁 TaskCompletionSource

    在异步转同步时,使用不当容易造成死锁(程序卡死) 看如下案例: 有一个异步方法 private static async Task TestAsync() { Debug.WriteLine(&quo ...

  5. springboot使用RabbitMQ实现延时任务

    延时队列顾名思义,即放置在该队列里面的消息是不需要立即消费的,而是等待一段时间之后取出消费.那么,为什么需要延迟消费呢?我们来看以下的场景 订单业务: 在电商/点餐中,都有下单后 30 分钟内没有付款 ...

  6. 关于sql 中delete, truncate,drop的理解与总结

    关于文章,本文是在网上查找资料整理而来,方便以后记忆 delete 命令删除的数据可以恢复, truncate 命令删除的数据是不可以恢复的,全删的那种 drop 命令删除的数据不可恢复,连表结构都删 ...

  7. Windows和linux环境下按文件名和字符串搜索命令

    Windows 1.遍历C盘下所有txt 命令:for /r c:\ %i in (*.txt) do @echo %i 注释:for 循环的意思 /r 按照路径搜索 c:\ 路径 %i in   ( ...

  8. Gym 101257B:2Trees(DFS+思维)

    http://codeforces.com/gym/101257/problem/B 题意:给出两棵叶子数一样的树,在将叶子合并之后,对这个图进行染色,相邻的结点颜色不能相同,问最少需要染的颜色数,并 ...

  9. Don’t Repeat Yourself

    The Don’t Repeat Yourself (DRY) principle states that duplication in logic should be eliminated via ...

  10. Spring 核心技术(2)

    接上篇:Spring 核心技术(1) version 5.1.8.RELEASE 1.3 Bean概述 Spring IoC 容器管理一个或多个bean,他们都是根据提供的配置元数据(例如 XML 中 ...