- 浏览: 13466 次
- 性别:
- 来自: 成都
最新评论
概述:本文按三个部分依次循序渐进对mybatis源码-原理进行分析
1.MyBatis 启动流程
2.MyBatis 执行流程
3.MyBatis 核心组件
第一部分 启动流程
1. Resources 工具类
Resources 通过对ClassLoaderWrapper进一步封装,使调用方无需关心到底调用的是那个类加载器来加载资源文件,为此提供了多个重载的获取资源的方法,分为如下5 类:
1)getResourceURL 从classpath读取文件,返回为URL
2)getResourceAsStream 从classpath读取返回为输入流
3)getResourceAsProperties 同上,返回为properties
4)getResourceAsReader 返回为Reader
5)getResourceAsFile 返回为File
每个方法都有相应的重载方法可提供外部的ClassLoader来加载资源,再看下ClassLoaderWrapper:
后面这三个类加载器在默认情况下都是同一个AppClassLoader的同一个实例,如图:
这样做的好处就是拓展性,即便我们提供的mybatis.xml文件在外部,假设自定义了一个URL的ClassLoader,对后面加载其他的mapper.xml也不会有任何影响。
2.SessionFactoryBuilder
SessionFactoryBuilder中全是各式各样的Build方法
最主要的还是最后两个构造方法,直接进入XMLConfigBuilder
3.XMLConfigBuilder构造方法
3.1 初始化
3.2 XMLMapperEntityResolver初始化
3. XPathParser 初始化
这个过程中会调用 commonConstructor进行 xpath和entityResolver的初始化,然后调用CreateDocument对document进行初始化,后面使用Document结合Xpath对xml进行解析。xpath是xml文档查询语言,EntityResolver 实现了EntityResolver 接口,此接口只有一个方法
解析器在进行解析时会调用此接口进行xml的实现类进行dtd验证XMLMapperEntityResolver
将dtd文件指向jar包中的mapper.xml 和mybatis.config对应的dtd文件。
到此XmlPaser就创建完毕了,这里就是mybatis解析xml的方式DOM+Xpath。
4. 回到XMLConfigBuilder的构造方法
5.Configuration
Configuration 是Mybatis中的核心配置类,包含了mybatis所有的配置信息
继续进行初始化
Configuration中包含一个
默认注册了所有的基本类型、包装类型、集合类型等简写到class的映射。
在构造函数初始化之前还会对默认变量进行赋值,下面对Configuration中几个重要的变量进行初始化
反射工具包ReflectorFactory
工厂设计模式:DefaultReflectorFactory实现了ReflectorFactory接口,按类缓存Reflector,Reflector中包含当前类的所有元数据信息:getter、setter、可读、可写字段 ,以及getter,Setter对应的Invoker,Invoker是对反射的轻量级封装:
Invoker接口有三个实现类:GetFieldInvoker、MethodInvoker、SetFieldInvoker,其中MethodInvoker:
这样做的好处就是:定义接口将设置值与反射隔离,只关注Invoker,而不用管是到底是怎样设置值。
TODO 后面补充TypeParameterResolver
初始化 DefaultObjectFactory
实现类
DefaultObjectFactory具有创建普通对象和Collection对象的功能
创建 DefaultObjectWrapperFactory
TODO 默认是空的 现在还不清楚这个有什么用
创建ProxyFactory
//TODO
创建 MapperRegistry
//TODO
创建InterceptorChain
//创建 TypeHandlerRegistry
等等
6.初始化父类初始化BaseBuilder
BaseBuilder 两个成员变量typeAliasRegistry和typeHandlerRegistry,可从别名找到类型,再从类型找到类型对应的处理器。
7.继续执行XMLConfigBuilder
初始化私有变量localReflectorFactory,在configuration中也有这样一个reflectorFactory的实例。
8.初始化ErrorContext并设置值
ErrorContext使用ThreadLocal 存储每个线程对应的错误信息
每个错误信息都包含:当前错误文件,当前activity,当前object ,错误消息,sql等,通过reset方法可重置当前线程错误信息上下文,最后通过toString格式化输出。
9. 设置Configuration变量 可覆盖原有配置信息
10.到此XMLConfigBuilder构造方法执行完毕,总结一下这个过程中大致做了哪些工作:
1)XPathParser 解析器的初始化,赋给XMLConfigBuilder的parser
2)初始化Configuration
1.MyBatis 启动流程
2.MyBatis 执行流程
3.MyBatis 核心组件
第一部分 启动流程
String resource = "mybatis.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession openSession = sqlSessionFactory.openSession(); Dept dept = openSession.selectOne("dao.DeptDAO.select",1); System.out.println(dept.toString());
1. Resources 工具类
public class Resources { private static ClassLoaderWrapper classLoaderWrapper = new ClassLoaderWrapper();
Resources 通过对ClassLoaderWrapper进一步封装,使调用方无需关心到底调用的是那个类加载器来加载资源文件,为此提供了多个重载的获取资源的方法,分为如下5 类:
1)getResourceURL 从classpath读取文件,返回为URL
2)getResourceAsStream 从classpath读取返回为输入流
3)getResourceAsProperties 同上,返回为properties
4)getResourceAsReader 返回为Reader
5)getResourceAsFile 返回为File
每个方法都有相应的重载方法可提供外部的ClassLoader来加载资源,再看下ClassLoaderWrapper:
public class ClassLoaderWrapper { ClassLoader defaultClassLoader; ClassLoader systemClassLoader; ClassLoaderWrapper() { try { //当前系统AppClassLoader,负责加载classpath,是所有应用classloader的父亲 systemClassLoader = ClassLoader.getSystemClassLoader(); } catch (SecurityException ignored) { // AccessControlException on Google App Engine } } /* * Get a resource as a URL using the current class path * * @param resource - the resource to locate * @return the resource or null */ public URL getResourceAsURL(String resource) { return getResourceAsURL(resource, getClassLoaders(null)); } /* * Get a resource from the classpath, starting with a specific class loader * * @param resource - the resource to find * @param classLoader - the first classloader to try * @return the stream or null */ public URL getResourceAsURL(String resource, ClassLoader classLoader) { return getResourceAsURL(resource, getClassLoaders(classLoader)); } /* * Get a resource from the classpath * * @param resource - the resource to find * @return the stream or null */ public InputStream getResourceAsStream(String resource) { return getResourceAsStream(resource, getClassLoaders(null)); } /* * Get a resource from the classpath, starting with a specific class loader * * @param resource - the resource to find * @param classLoader - the first class loader to try * @return the stream or null */ public InputStream getResourceAsStream(String resource, ClassLoader classLoader) { return getResourceAsStream(resource, getClassLoaders(classLoader)); } /* * Find a class on the classpath (or die trying) * * @param name - the class to look for * @return - the class * @throws ClassNotFoundException Duh. */ public Class<?> classForName(String name) throws ClassNotFoundException { return classForName(name, getClassLoaders(null)); } /* * Find a class on the classpath, starting with a specific classloader (or die trying) * * @param name - the class to look for * @param classLoader - the first classloader to try * @return - the class * @throws ClassNotFoundException Duh. */ public Class<?> classForName(String name, ClassLoader classLoader) throws ClassNotFoundException { return classForName(name, getClassLoaders(classLoader)); } /* * Try to get a resource from a group of classloaders * * @param resource - the resource to get * @param classLoader - the classloaders to examine * @return the resource or null */ InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) { for (ClassLoader cl : classLoader) { if (null != cl) { // try to find the resource as passed InputStream returnValue = cl.getResourceAsStream(resource); // now, some class loaders want this leading "/", so we'll add it and try again if we didn't find the resource if (null == returnValue) { returnValue = cl.getResourceAsStream("/" + resource); } if (null != returnValue) { return returnValue; } } } return null; } /* * Get a resource as a URL using the current class path * * @param resource - the resource to locate * @param classLoader - the class loaders to examine * @return the resource or null */ URL getResourceAsURL(String resource, ClassLoader[] classLoader) { URL url; for (ClassLoader cl : classLoader) { if (null != cl) { // look for the resource as passed in... url = cl.getResource(resource); // ...but some class loaders want this leading "/", so we'll add it // and try again if we didn't find the resource if (null == url) { url = cl.getResource("/" + resource); } // "It's always in the last place I look for it!" // ... because only an idiot would keep looking for it after finding it, so stop looking already. if (null != url) { return url; } } } // didn't find it anywhere. return null; } /* * Attempt to load a class from a group of classloaders * * @param name - the class to load * @param classLoader - the group of classloaders to examine * @return the class * @throws ClassNotFoundException - Remember the wisdom of Judge Smails: Well, the world needs ditch diggers, too. */ Class<?> classForName(String name, ClassLoader[] classLoader) throws ClassNotFoundException { for (ClassLoader cl : classLoader) { if (null != cl) { try { Class<?> c = Class.forName(name, true, cl); if (null != c) { return c; } } catch (ClassNotFoundException e) { // we'll ignore this until all classloaders fail to locate the class } } } throw new ClassNotFoundException("Cannot find class: " + name); } //如果传入了外部classLoader则getResourceAsStream的时候调用默认使用,传入的第一个 //defaultClassLoader 在此类中未定义 //Thread.currentThread().getContextClassLoader() 创建当前线程的类加载器 // getClass().getClassLoader()加载当前类定义的类加载器默认是AppClassLoader 即ClassLoader.getSystemClassLoader //systemClassLoader ClassLoader[] getClassLoaders(ClassLoader classLoader) { return new ClassLoader[]{ classLoader, defaultClassLoader, Thread.currentThread().getContextClassLoader(), getClass().getClassLoader(), systemClassLoader}; } }
后面这三个类加载器在默认情况下都是同一个AppClassLoader的同一个实例,如图:
这样做的好处就是拓展性,即便我们提供的mybatis.xml文件在外部,假设自定义了一个URL的ClassLoader,对后面加载其他的mapper.xml也不会有任何影响。
2.SessionFactoryBuilder
SessionFactoryBuilder中全是各式各样的Build方法
public class SqlSessionFactoryBuilder { public SqlSessionFactory build(Reader reader) { return build(reader, null, null); } public SqlSessionFactory build(Reader reader, String environment) { return build(reader, environment, null); } public SqlSessionFactory build(Reader reader, Properties properties) { return build(reader, null, properties); } public SqlSessionFactory build(Reader reader, String environment, Properties properties) { try { XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties); return build(parser.parse()); } catch (Exception e) { throw ExceptionFactory.wrapException("Error building SqlSession.", e); } finally { ErrorContext.instance().reset(); try { reader.close(); } catch (IOException e) { // Intentionally ignore. Prefer previous error. } } } public SqlSessionFactory build(InputStream inputStream) { return build(inputStream, null, null); } public SqlSessionFactory build(InputStream inputStream, String environment) { return build(inputStream, environment, null); } public SqlSessionFactory build(InputStream inputStream, Properties properties) { return build(inputStream, null, properties); } public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { try { XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); return build(parser.parse()); } catch (Exception e) { throw ExceptionFactory.wrapException("Error building SqlSession.", e); } finally { ErrorContext.instance().reset(); try { inputStream.close(); } catch (IOException e) { // Intentionally ignore. Prefer previous error. } } } public SqlSessionFactory build(Configuration config) { return new DefaultSqlSessionFactory(config); }
最主要的还是最后两个构造方法,直接进入XMLConfigBuilder
3.XMLConfigBuilder构造方法
3.1 初始化
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) { this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props); }
3.2 XMLMapperEntityResolver初始化
public class XMLMapperEntityResolver implements EntityResolver { private static final String IBATIS_CONFIG_SYSTEM = "ibatis-3-config.dtd"; private static final String IBATIS_MAPPER_SYSTEM = "ibatis-3-mapper.dtd"; private static final String MYBATIS_CONFIG_SYSTEM = "mybatis-3-config.dtd"; private static final String MYBATIS_MAPPER_SYSTEM = "mybatis-3-mapper.dtd"; private static final String MYBATIS_CONFIG_DTD = "org/apache/ibatis/builder/xml/mybatis-3-config.dtd"; private static final String MYBATIS_MAPPER_DTD = "org/apache/ibatis/builder/xml/mybatis-3-mapper.dtd"; /* * Converts a public DTD into a local one * * @param publicId The public id that is what comes after "PUBLIC" * @param systemId The system id that is what comes after the public id. * @return The InputSource for the DTD * * @throws org.xml.sax.SAXException If anything goes wrong */ @Override public InputSource resolveEntity(String publicId, String systemId) throws SAXException { try { if (systemId != null) { String lowerCaseSystemId = systemId.toLowerCase(Locale.ENGLISH); if (lowerCaseSystemId.contains(MYBATIS_CONFIG_SYSTEM) || lowerCaseSystemId.contains(IBATIS_CONFIG_SYSTEM)) { return getInputSource(MYBATIS_CONFIG_DTD, publicId, systemId); } else if (lowerCaseSystemId.contains(MYBATIS_MAPPER_SYSTEM) || lowerCaseSystemId.contains(IBATIS_MAPPER_SYSTEM)) { return getInputSource(MYBATIS_MAPPER_DTD, publicId, systemId); } } return null; } catch (Exception e) { throw new SAXException(e.toString()); } } private InputSource getInputSource(String path, String publicId, String systemId) { InputSource source = null; if (path != null) { try { InputStream in = Resources.getResourceAsStream(path); source = new InputSource(in); source.setPublicId(publicId); source.setSystemId(systemId); } catch (IOException e) { // ignore, null is ok } } return source; } }
3. XPathParser 初始化
public class XPathParser { private final Document document; private boolean validation; private EntityResolver entityResolver; private Properties variables; private XPath xpath; public XPathParser(String xml) { commonConstructor(false, null, null); this.document = createDocument(new InputSource(new StringReader(xml))); } public XPathParser(Reader reader) { commonConstructor(false, null, null); this.document = createDocument(new InputSource(reader)); } public XPathParser(InputStream inputStream) { commonConstructor(false, null, null); this.document = createDocument(new InputSource(inputStream)); } public XPathParser(Document document) { commonConstructor(false, null, null); this.document = document; } public XPathParser(String xml, boolean validation) { commonConstructor(validation, null, null); this.document = createDocument(new InputSource(new StringReader(xml))); } public XPathParser(Reader reader, boolean validation) { commonConstructor(validation, null, null); this.document = createDocument(new InputSource(reader)); } public XPathParser(InputStream inputStream, boolean validation) { commonConstructor(validation, null, null); this.document = createDocument(new InputSource(inputStream)); } public XPathParser(Document document, boolean validation) { commonConstructor(validation, null, null); this.document = document; } public XPathParser(String xml, boolean validation, Properties variables) { commonConstructor(validation, variables, null); this.document = createDocument(new InputSource(new StringReader(xml))); } public XPathParser(Reader reader, boolean validation, Properties variables) { commonConstructor(validation, variables, null); this.document = createDocument(new InputSource(reader)); } public XPathParser(InputStream inputStream, boolean validation, Properties variables) { commonConstructor(validation, variables, null); this.document = createDocument(new InputSource(inputStream)); } public XPathParser(Document document, boolean validation, Properties variables) { commonConstructor(validation, variables, null); this.document = document; } public XPathParser(String xml, boolean validation, Properties variables, EntityResolver entityResolver) { commonConstructor(validation, variables, entityResolver); this.document = createDocument(new InputSource(new StringReader(xml))); } public XPathParser(Reader reader, boolean validation, Properties variables, EntityResolver entityResolver) { commonConstructor(validation, variables, entityResolver); this.document = createDocument(new InputSource(reader)); } public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) { commonConstructor(validation, variables, entityResolver); this.document = createDocument(new InputSource(inputStream)); } public XPathParser(Document document, boolean validation, Properties variables, EntityResolver entityResolver) { commonConstructor(validation, variables, entityResolver); this.document = document; } public void setVariables(Properties variables) { this.variables = variables; } public String evalString(String expression) { return evalString(document, expression); } public String evalString(Object root, String expression) { String result = (String) evaluate(expression, root, XPathConstants.STRING); result = PropertyParser.parse(result, variables); return result; } public Boolean evalBoolean(String expression) { return evalBoolean(document, expression); } public Boolean evalBoolean(Object root, String expression) { return (Boolean) evaluate(expression, root, XPathConstants.BOOLEAN); } public Short evalShort(String expression) { return evalShort(document, expression); } public Short evalShort(Object root, String expression) { return Short.valueOf(evalString(root, expression)); } public Integer evalInteger(String expression) { return evalInteger(document, expression); } public Integer evalInteger(Object root, String expression) { return Integer.valueOf(evalString(root, expression)); } public Long evalLong(String expression) { return evalLong(document, expression); } public Long evalLong(Object root, String expression) { return Long.valueOf(evalString(root, expression)); } public Float evalFloat(String expression) { return evalFloat(document, expression); } public Float evalFloat(Object root, String expression) { return Float.valueOf(evalString(root, expression)); } public Double evalDouble(String expression) { return evalDouble(document, expression); } public Double evalDouble(Object root, String expression) { return (Double) evaluate(expression, root, XPathConstants.NUMBER); } public List<XNode> evalNodes(String expression) { return evalNodes(document, expression); } public List<XNode> evalNodes(Object root, String expression) { List<XNode> xnodes = new ArrayList<XNode>(); NodeList nodes = (NodeList) evaluate(expression, root, XPathConstants.NODESET); for (int i = 0; i < nodes.getLength(); i++) { xnodes.add(new XNode(this, nodes.item(i), variables)); } return xnodes; } public XNode evalNode(String expression) { return evalNode(document, expression); } public XNode evalNode(Object root, String expression) { Node node = (Node) evaluate(expression, root, XPathConstants.NODE); if (node == null) { return null; } return new XNode(this, node, variables); } private Object evaluate(String expression, Object root, QName returnType) { try { return xpath.evaluate(expression, root, returnType); } catch (Exception e) { throw new BuilderException("Error evaluating XPath. Cause: " + e, e); } } private Document createDocument(InputSource inputSource) { // important: this must only be called AFTER common constructor try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setValidating(validation); factory.setNamespaceAware(false); factory.setIgnoringComments(true); factory.setIgnoringElementContentWhitespace(false); factory.setCoalescing(false); factory.setExpandEntityReferences(true); DocumentBuilder builder = factory.newDocumentBuilder(); builder.setEntityResolver(entityResolver); builder.setErrorHandler(new ErrorHandler() { @Override public void error(SAXParseException exception) throws SAXException { throw exception; } @Override public void fatalError(SAXParseException exception) throws SAXException { throw exception; } @Override public void warning(SAXParseException exception) throws SAXException { } }); return builder.parse(inputSource); } catch (Exception e) { throw new BuilderException("Error creating document instance. Cause: " + e, e); } } private void commonConstructor(boolean validation, Properties variables, EntityResolver entityResolver) { this.validation = validation; this.entityResolver = entityResolver; this.variables = variables; XPathFactory factory = XPathFactory.newInstance(); this.xpath = factory.newXPath(); } }
这个过程中会调用 commonConstructor进行 xpath和entityResolver的初始化,然后调用CreateDocument对document进行初始化,后面使用Document结合Xpath对xml进行解析。xpath是xml文档查询语言,EntityResolver 实现了EntityResolver 接口,此接口只有一个方法
public abstract InputSource resolveEntity (String publicId, String systemId) throws SAXException, IOException;
解析器在进行解析时会调用此接口进行xml的实现类进行dtd验证XMLMapperEntityResolver
将dtd文件指向jar包中的mapper.xml 和mybatis.config对应的dtd文件。
到此XmlPaser就创建完毕了,这里就是mybatis解析xml的方式DOM+Xpath。
4. 回到XMLConfigBuilder的构造方法
public XMLConfigBuilder(Reader reader, String environment, Properties props) { this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props); }执行完后,继续调用
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) { super(new Configuration()); ErrorContext.instance().resource("SQL Mapper Configuration"); this.configuration.setVariables(props); this.parsed = false; this.environment = environment; this.parser = parser; }
5.Configuration
Configuration 是Mybatis中的核心配置类,包含了mybatis所有的配置信息
继续进行初始化
public Configuration() { typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class); typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class); typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class); typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class); typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class); typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class); typeAliasRegistry.registerAlias("FIFO", FifoCache.class); typeAliasRegistry.registerAlias("LRU", LruCache.class); typeAliasRegistry.registerAlias("SOFT", SoftCache.class); typeAliasRegistry.registerAlias("WEAK", WeakCache.class); typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class); typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class); typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class); typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class); typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class); typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class); typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class); typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class); typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class); typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class); typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class); typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class); languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class); languageRegistry.register(RawLanguageDriver.class); }
Configuration中包含一个
protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();属性,其中
public class TypeAliasRegistry { private final Map<String, Class<?>> TYPE_ALIASES = new HashMap<String, Class<?>>(); public TypeAliasRegistry() { registerAlias("string", String.class); registerAlias("byte", Byte.class); registerAlias("long", Long.class); registerAlias("short", Short.class); registerAlias("int", Integer.class); registerAlias("integer", Integer.class); registerAlias("double", Double.class); registerAlias("float", Float.class); registerAlias("boolean", Boolean.class); registerAlias("byte[]", Byte[].class); registerAlias("long[]", Long[].class); registerAlias("short[]", Short[].class); registerAlias("int[]", Integer[].class); registerAlias("integer[]", Integer[].class); registerAlias("double[]", Double[].class); registerAlias("float[]", Float[].class); registerAlias("boolean[]", Boolean[].class); registerAlias("_byte", byte.class); registerAlias("_long", long.class); registerAlias("_short", short.class); registerAlias("_int", int.class); registerAlias("_integer", int.class); registerAlias("_double", double.class); registerAlias("_float", float.class); registerAlias("_boolean", boolean.class); registerAlias("_byte[]", byte[].class); registerAlias("_long[]", long[].class); registerAlias("_short[]", short[].class); registerAlias("_int[]", int[].class); registerAlias("_integer[]", int[].class); registerAlias("_double[]", double[].class); registerAlias("_float[]", float[].class); registerAlias("_boolean[]", boolean[].class); registerAlias("date", Date.class); registerAlias("decimal", BigDecimal.class); registerAlias("bigdecimal", BigDecimal.class); registerAlias("biginteger", BigInteger.class); registerAlias("object", Object.class); registerAlias("date[]", Date[].class); registerAlias("decimal[]", BigDecimal[].class); registerAlias("bigdecimal[]", BigDecimal[].class); registerAlias("biginteger[]", BigInteger[].class); registerAlias("object[]", Object[].class); registerAlias("map", Map.class); registerAlias("hashmap", HashMap.class); registerAlias("list", List.class); registerAlias("arraylist", ArrayList.class); registerAlias("collection", Collection.class); registerAlias("iterator", Iterator.class); registerAlias("ResultSet", ResultSet.class); } @SuppressWarnings("unchecked") // throws class cast exception as well if types cannot be assigned public <T> Class<T> resolveAlias(String string) { try { if (string == null) { return null; } // issue #748 String key = string.toLowerCase(Locale.ENGLISH); Class<T> value; if (TYPE_ALIASES.containsKey(key)) { value = (Class<T>) TYPE_ALIASES.get(key); } else { value = (Class<T>) Resources.classForName(string); } return value; } catch (ClassNotFoundException e) { throw new TypeException("Could not resolve type alias '" + string + "'. Cause: " + e, e); } } public void registerAliases(String packageName){ registerAliases(packageName, Object.class); } public void registerAliases(String packageName, Class<?> superType){ ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>(); resolverUtil.find(new ResolverUtil.IsA(superType), packageName); Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses(); for(Class<?> type : typeSet){ // Ignore inner classes and interfaces (including package-info.java) // Skip also inner classes. See issue #6 if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) { registerAlias(type); } } } public void registerAlias(Class<?> type) { String alias = type.getSimpleName(); Alias aliasAnnotation = type.getAnnotation(Alias.class); if (aliasAnnotation != null) { alias = aliasAnnotation.value(); } registerAlias(alias, type); } public void registerAlias(String alias, Class<?> value) { if (alias == null) { throw new TypeException("The parameter alias cannot be null"); } // issue #748 String key = alias.toLowerCase(Locale.ENGLISH); if (TYPE_ALIASES.containsKey(key) && TYPE_ALIASES.get(key) != null && !TYPE_ALIASES.get(key).equals(value)) { throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + TYPE_ALIASES.get(key).getName() + "'."); } TYPE_ALIASES.put(key, value); } public void registerAlias(String alias, String value) { try { registerAlias(alias, Resources.classForName(value)); } catch (ClassNotFoundException e) { throw new TypeException("Error registering type alias "+alias+" for "+value+". Cause: " + e, e); } } /** * @since 3.2.2 */ public Map<String, Class<?>> getTypeAliases() { return Collections.unmodifiableMap(TYPE_ALIASES); } }
默认注册了所有的基本类型、包装类型、集合类型等简写到class的映射。
在构造函数初始化之前还会对默认变量进行赋值,下面对Configuration中几个重要的变量进行初始化
反射工具包ReflectorFactory
public class DefaultReflectorFactory implements ReflectorFactory { private boolean classCacheEnabled = true; private final ConcurrentMap<Class<?>, Reflector> reflectorMap = new ConcurrentHashMap<Class<?>, Reflector>(); public DefaultReflectorFactory() { } @Override public boolean isClassCacheEnabled() { return classCacheEnabled; } @Override public void setClassCacheEnabled(boolean classCacheEnabled) { this.classCacheEnabled = classCacheEnabled; } @Override public Reflector findForClass(Class<?> type) { if (classCacheEnabled) { // synchronized (type) removed see issue #461 Reflector cached = reflectorMap.get(type); if (cached == null) { cached = new Reflector(type); reflectorMap.put(type, cached); } return cached; } else { return new Reflector(type); } } } public interface ReflectorFactory { boolean isClassCacheEnabled(); void setClassCacheEnabled(boolean classCacheEnabled); Reflector findForClass(Class<?> type); } public class Reflector { private final Class<?> type; private final String[] readablePropertyNames; private final String[] writeablePropertyNames; private final Map<String, Invoker> setMethods = new HashMap<String, Invoker>(); private final Map<String, Invoker> getMethods = new HashMap<String, Invoker>(); private final Map<String, Class<?>> setTypes = new HashMap<String, Class<?>>(); private final Map<String, Class<?>> getTypes = new HashMap<String, Class<?>>(); private Constructor<?> defaultConstructor; private Map<String, String> caseInsensitivePropertyMap = new HashMap<String, String>(); public Reflector(Class<?> clazz) { type = clazz; addDefaultConstructor(clazz); addGetMethods(clazz); addSetMethods(clazz); addFields(clazz); readablePropertyNames = getMethods.keySet().toArray(new String[getMethods.keySet().size()]); writeablePropertyNames = setMethods.keySet().toArray(new String[setMethods.keySet().size()]); for (String propName : readablePropertyNames) { caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName); } for (String propName : writeablePropertyNames) { caseInsensitivePropertyMap.put(propName.toUpperCase(Locale.ENGLISH), propName); } } // private void addDefaultConstructor(Class<?> clazz) { Constructor<?>[] consts = clazz.getDeclaredConstructors(); for (Constructor<?> constructor : consts) { if (constructor.getParameterTypes().length == 0) { //检查是否能访问私有方法、属性 if (canAccessPrivateMethods()) { try { constructor.setAccessible(true); } catch (Exception e) { // Ignored. This is only a final precaution, nothing we can do. } } //检查constructor 是否设置上能访问,否则defaultConstructor =null if (constructor.isAccessible()) { this.defaultConstructor = constructor; } } } } private void addGetMethods(Class<?> cls) { Map<String, List<Method>> conflictingGetters = new HashMap<String, List<Method>>(); Method[] methods = getClassMethods(cls); for (Method method : methods) { if (method.getParameterTypes().length > 0) { continue; } String name = method.getName(); if ((name.startsWith("get") && name.length() > 3) || (name.startsWith("is") && name.length() > 2)) { name = PropertyNamer.methodToProperty(name); // addMethodConflict(conflictingGetters, name, method); } } resolveGetterConflicts(conflictingGetters); } private void resolveGetterConflicts(Map<String, List<Method>> conflictingGetters) { //list中最多只能有 一个isB 一个getB ,分为返回值相同 返回值不同,返回值不同分为类型 //不同,和超类子类的返回值不同,所以getB有bool,返回类型A,返回类型B,isB也是三 //种,其中B和A有父子关系,一共有9种可能 for (Entry<String, List<Method>> entry : conflictingGetters.entrySet()) { Method winner = null; String propName = entry.getKey(); for (Method candidate : entry.getValue()) { if (winner == null) { winner = candidate; continue; } Class<?> winnerType = winner.getReturnType(); Class<?> candidateType = candidate.getReturnType(); if (candidateType.equals(winnerType)) {// 返回类型相等只有 A,B和bool三种情况 //两个类型相等 后者如果不是bool ,无论谁在前面都抛出异常说明is和get返回相同值 if (!boolean.class.equals(candidateType)) { throw new ReflectionException( "Illegal overloaded getter method with ambiguous type for property " + propName + " in class " + winner.getDeclaringClass() + ". This breaks the JavaBeans specification and can cause unpredictable results."); //返回类型相同,并且同时是bool但是后面这个是以is开头,交换 } else if (candidate.getName().startsWith("is")) { winner = candidate; } //返回值不同,且前者是后者的子类-->winner保持当前值 } else if (candidateType.isAssignableFrom(winnerType)) { // OK getter type is descendant //返回值不同,且前者是后者的父类->交换调整返回子类 } else if (winnerType.isAssignableFrom(candidateType)) { winner = candidate; } else { //抛出异常两个返回类型直接没有任何关联并且其中一个是boolean类型 throw new ReflectionException( "Illegal overloaded getter method with ambiguous type for property " + propName + " in class " + winner.getDeclaringClass() + ". This breaks the JavaBeans specification and can cause unpredictable results."); } } addGetMethod(propName, winner); } } private void addGetMethod(String name, Method method) { if (isValidPropertyName(name)) { getMethods.put(name, new MethodInvoker(method)); Type returnType = TypeParameterResolver.resolveReturnType(method, type); getTypes.put(name, typeToClass(returnType)); } } private void addSetMethods(Class<?> cls) { Map<String, List<Method>> conflictingSetters = new HashMap<String, List<Method>>(); Method[] methods = getClassMethods(cls); for (Method method : methods) { String name = method.getName(); if (name.startsWith("set") && name.length() > 3) { if (method.getParameterTypes().length == 1) { name = PropertyNamer.methodToProperty(name); addMethodConflict(conflictingSetters, name, method); } } } resolveSetterConflicts(conflictingSetters); } private void addMethodConflict(Map<String, List<Method>> conflictingMethods, String name, Method method) { List<Method> list = conflictingMethods.get(name); if (list == null) { list = new ArrayList<Method>(); conflictingMethods.put(name, list); } list.add(method); } private void resolveSetterConflicts(Map<String, List<Method>> conflictingSetters) { for (String propName : conflictingSetters.keySet()) { List<Method> setters = conflictingSetters.get(propName); Class<?> getterType = getTypes.get(propName); Method match = null; ReflectionException exception = null; for (Method setter : setters) { Class<?> paramType = setter.getParameterTypes()[0]; if (paramType.equals(getterType)) { // should be the best match match = setter; break; } if (exception == null) { try { match = pickBetterSetter(match, setter, propName); } catch (ReflectionException e) { // there could still be the 'best match' match = null; exception = e; } } } if (match == null) { throw exception; } else { addSetMethod(propName, match); } } } private Method pickBetterSetter(Method setter1, Method setter2, String property) { if (setter1 == null) { return setter2; } Class<?> paramType1 = setter1.getParameterTypes()[0]; Class<?> paramType2 = setter2.getParameterTypes()[0]; if (paramType1.isAssignableFrom(paramType2)) { return setter2; } else if (paramType2.isAssignableFrom(paramType1)) { return setter1; } throw new ReflectionException("Ambiguous setters defined for property '" + property + "' in class '" + setter2.getDeclaringClass() + "' with types '" + paramType1.getName() + "' and '" + paramType2.getName() + "'."); } private void addSetMethod(String name, Method method) { if (isValidPropertyName(name)) { setMethods.put(name, new MethodInvoker(method)); Type[] paramTypes = TypeParameterResolver.resolveParamTypes(method, type); setTypes.put(name, typeToClass(paramTypes[0])); } } private Class<?> typeToClass(Type src) { Class<?> result = null; if (src instanceof Class) { result = (Class<?>) src; } else if (src instanceof ParameterizedType) { result = (Class<?>) ((ParameterizedType) src).getRawType(); } else if (src instanceof GenericArrayType) { Type componentType = ((GenericArrayType) src).getGenericComponentType(); if (componentType instanceof Class) { result = Array.newInstance((Class<?>) componentType, 0).getClass(); } else { Class<?> componentClass = typeToClass(componentType); result = Array.newInstance((Class<?>) componentClass, 0).getClass(); } } if (result == null) { result = Object.class; } return result; } private void addFields(Class<?> clazz) { Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { if (canAccessPrivateMethods()) { try { field.setAccessible(true); } catch (Exception e) { // Ignored. This is only a final precaution, nothing we can do. } } if (field.isAccessible()) { if (!setMethods.containsKey(field.getName())) { // issue #379 - removed the check for final because JDK 1.5 allows // modification of final fields through reflection (JSR-133). (JGB) // pr #16 - final static can only be set by the classloader int modifiers = field.getModifiers(); if (!(Modifier.isFinal(modifiers) && Modifier.isStatic(modifiers))) { addSetField(field); } } if (!getMethods.containsKey(field.getName())) { addGetField(field); } } } if (clazz.getSuperclass() != null) { addFields(clazz.getSuperclass()); } } private void addSetField(Field field) { if (isValidPropertyName(field.getName())) { setMethods.put(field.getName(), new SetFieldInvoker(field)); Type fieldType = TypeParameterResolver.resolveFieldType(field, type); setTypes.put(field.getName(), typeToClass(fieldType)); } } private void addGetField(Field field) { if (isValidPropertyName(field.getName())) { getMethods.put(field.getName(), new GetFieldInvoker(field)); Type fieldType = TypeParameterResolver.resolveFieldType(field, type); getTypes.put(field.getName(), typeToClass(fieldType)); } } private boolean isValidPropertyName(String name) { return !(name.startsWith("$") || "serialVersionUID".equals(name) || "class".equals(name)); } /* * This method returns an array containing all methods * declared in this class and any superclass. * We use this method, instead of the simpler Class.getMethods(), * because we want to look for private methods as well. * * @param cls The class * @return An array containing all methods in this class */ //返回当前类、超类所有方法包括私有方法 private Method[] getClassMethods(Class<?> cls) { Map<String, Method> uniqueMethods = new HashMap<String, Method>(); Class<?> currentClass = cls; while (currentClass != null && currentClass != Object.class) {//循环直到Object addUniqueMethods(uniqueMethods, currentClass.getDeclaredMethods()); // we also need to look for interface methods - // because the class may be abstract Class<?>[] interfaces = currentClass.getInterfaces(); for (Class<?> anInterface : interfaces) { addUniqueMethods(uniqueMethods, anInterface.getMethods()); } currentClass = currentClass.getSuperclass(); } Collection<Method> methods = uniqueMethods.values(); return methods.toArray(new Method[methods.size()]); } private void addUniqueMethods(Map<String, Method> uniqueMethods, Method[] methods) { for (Method currentMethod : methods) { //Bridge方法是 1.5引入泛型后,class字节码为了兼容自动生成的方法 if (!currentMethod.isBridge()) { // //returnType#methodName:parameter1,parameter2... String signature = getSignature(currentMethod); // check to see if the method is already known // if it is known, then an extended class must have // overridden a method if (!uniqueMethods.containsKey(signature)) { if (canAccessPrivateMethods()) { try { currentMethod.setAccessible(true); } catch (Exception e) { // Ignored. This is only a final precaution, nothing we can do. } } uniqueMethods.put(signature, currentMethod); } } } } //returnType#methodName:parameter1,parameter2... private String getSignature(Method method) { StringBuilder sb = new StringBuilder(); Class<?> returnType = method.getReturnType(); if (returnType != null) { sb.append(returnType.getName()).append('#'); } sb.append(method.getName()); Class<?>[] parameters = method.getParameterTypes(); for (int i = 0; i < parameters.length; i++) { if (i == 0) { sb.append(':'); } else { sb.append(','); } sb.append(parameters[i].getName()); } return sb.toString(); } private static boolean canAccessPrivateMethods() { try { SecurityManager securityManager = System.getSecurityManager(); if (null != securityManager) { securityManager.checkPermission(new ReflectPermission("suppressAccessChecks")); } } catch (SecurityException e) { return false; } return true; } /* * Gets the name of the class the instance provides information for * * @return The class name */ public Class<?> getType() { return type; } public Constructor<?> getDefaultConstructor() { if (defaultConstructor != null) { return defaultConstructor; } else { throw new ReflectionException("There is no default constructor for " + type); } } public boolean hasDefaultConstructor() { return defaultConstructor != null; } public Invoker getSetInvoker(String propertyName) { Invoker method = setMethods.get(propertyName); if (method == null) { throw new ReflectionException("There is no setter for property named '" + propertyName + "' in '" + type + "'"); } return method; } public Invoker getGetInvoker(String propertyName) { Invoker method = getMethods.get(propertyName); if (method == null) { throw new ReflectionException("There is no getter for property named '" + propertyName + "' in '" + type + "'"); } return method; } /* * Gets the type for a property setter * * @param propertyName - the name of the property * @return The Class of the propery setter */ public Class<?> getSetterType(String propertyName) { Class<?> clazz = setTypes.get(propertyName); if (clazz == null) { throw new ReflectionException("There is no setter for property named '" + propertyName + "' in '" + type + "'"); } return clazz; } /* * Gets the type for a property getter * * @param propertyName - the name of the property * @return The Class of the propery getter */ public Class<?> getGetterType(String propertyName) { Class<?> clazz = getTypes.get(propertyName); if (clazz == null) { throw new ReflectionException("There is no getter for property named '" + propertyName + "' in '" + type + "'"); } return clazz; } /* * Gets an array of the readable properties for an object * * @return The array */ public String[] getGetablePropertyNames() { return readablePropertyNames; } /* * Gets an array of the writeable properties for an object * * @return The array */ public String[] getSetablePropertyNames() { return writeablePropertyNames; } /* * Check to see if a class has a writeable property by name * * @param propertyName - the name of the property to check * @return True if the object has a writeable property by the name */ public boolean hasSetter(String propertyName) { return setMethods.keySet().contains(propertyName); } /* * Check to see if a class has a readable property by name * * @param propertyName - the name of the property to check * @return True if the object has a readable property by the name */ public boolean hasGetter(String propertyName) { return getMethods.keySet().contains(propertyName); } public String findPropertyName(String name) { return caseInsensitivePropertyMap.get(name.toUpperCase(Locale.ENGLISH)); } }
工厂设计模式:DefaultReflectorFactory实现了ReflectorFactory接口,按类缓存Reflector,Reflector中包含当前类的所有元数据信息:getter、setter、可读、可写字段 ,以及getter,Setter对应的Invoker,Invoker是对反射的轻量级封装:
public interface Invoker { Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException; Class<?> getType(); }
Invoker接口有三个实现类:GetFieldInvoker、MethodInvoker、SetFieldInvoker,其中MethodInvoker:
public class MethodInvoker implements Invoker { private final Class<?> type; private final Method method; public MethodInvoker(Method method) { this.method = method; if (method.getParameterTypes().length == 1) { type = method.getParameterTypes()[0]; } else { type = method.getReturnType(); } } @Override public Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException { return method.invoke(target, args); } @Override public Class<?> getType() { return type; } }
这样做的好处就是:定义接口将设置值与反射隔离,只关注Invoker,而不用管是到底是怎样设置值。
TODO 后面补充TypeParameterResolver
初始化 DefaultObjectFactory
public interface ObjectFactory { /** * Sets configuration properties. * @param properties configuration properties */ void setProperties(Properties properties); /** * Creates a new object with default constructor. * @param type Object type * @return */ <T> T create(Class<T> type); /** * Creates a new object with the specified constructor and params. * @param type Object type * @param constructorArgTypes Constructor argument types * @param constructorArgs Constructor argument values * @return */ / <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs); /** * Returns true if this object can have a set of other objects. * It's main purpose is to support non-java.util.Collection objects like Scala collections. * * @param type Object type * @return whether it is a collection or not * @since 3.1.0 */ <T> boolean isCollection(Class<T> type); }
实现类
public class DefaultObjectFactory implements ObjectFactory, Serializable { private static final long serialVersionUID = -8855120656740914948L; @Override public <T> T create(Class<T> type) { return create(type, null, null); } @SuppressWarnings("unchecked") @Override public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) { //传入指定类型,构造参数类型、构造函数值 创建对象,后面两个参数其中一个为空则表示调用默认构造函数 //检查对象是否是集合对象 Class<?> classToCreate = resolveInterface(type); // we know types are assignable return (T) instantiateClass(classToCreate, constructorArgTypes, constructorArgs); } @Override public void setProperties(Properties properties) { // no props for default //如果要自定义对象工厂,可在config文件中指定property属性 } private <T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) { try { Constructor<T> constructor; if (constructorArgTypes == null || constructorArgs == null) { constructor = type.getDeclaredConstructor(); if (!constructor.isAccessible()) { constructor.setAccessible(true); } return constructor.newInstance(); } constructor = type.getDeclaredConstructor(constructorArgTypes.toArray(new Class[constructorArgTypes.size()])); if (!constructor.isAccessible()) { constructor.setAccessible(true); } return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()])); } catch (Exception e) { StringBuilder argTypes = new StringBuilder(); if (constructorArgTypes != null && !constructorArgTypes.isEmpty()) { for (Class<?> argType : constructorArgTypes) { argTypes.append(argType.getSimpleName()); argTypes.append(","); } argTypes.deleteCharAt(argTypes.length() - 1); // remove trailing , } StringBuilder argValues = new StringBuilder(); if (constructorArgs != null && !constructorArgs.isEmpty()) { for (Object argValue : constructorArgs) { argValues.append(String.valueOf(argValue)); argValues.append(","); } argValues.deleteCharAt(argValues.length() - 1); // remove trailing , } throw new ReflectionException("Error instantiating " + type + " with invalid types (" + argTypes + ") or values (" + argValues + "). Cause: " + e, e); } } protected Class<?> resolveInterface(Class<?> type) { Class<?> classToCreate; if (type == List.class || type == Collection.class || type == Iterable.class) { classToCreate = ArrayList.class; } else if (type == Map.class) { classToCreate = HashMap.class; } else if (type == SortedSet.class) { // issue #510 Collections Support classToCreate = TreeSet.class; } else if (type == Set.class) { classToCreate = HashSet.class; } else { classToCreate = type; } return classToCreate; } @Override public <T> boolean isCollection(Class<T> type) { return Collection.class.isAssignableFrom(type); }
DefaultObjectFactory具有创建普通对象和Collection对象的功能
创建 DefaultObjectWrapperFactory
public interface ObjectWrapperFactory { boolean hasWrapperFor(Object object); ObjectWrapper getWrapperFor(MetaObject metaObject, Object object); } public class DefaultObjectWrapperFactory implements ObjectWrapperFactory { @Override public boolean hasWrapperFor(Object object) { return false; } @Override public ObjectWrapper getWrapperFor(MetaObject metaObject, Object object) { throw new ReflectionException("The DefaultObjectWrapperFactory should never be called to provide an ObjectWrapper."); }
TODO 默认是空的 现在还不清楚这个有什么用
创建ProxyFactory
//TODO
创建 MapperRegistry
//TODO
创建InterceptorChain
//创建 TypeHandlerRegistry
等等
6.初始化父类初始化BaseBuilder
BaseBuilder 两个成员变量typeAliasRegistry和typeHandlerRegistry,可从别名找到类型,再从类型找到类型对应的处理器。
7.继续执行XMLConfigBuilder
初始化私有变量localReflectorFactory,在configuration中也有这样一个reflectorFactory的实例。
8.初始化ErrorContext并设置值
ErrorContext使用ThreadLocal 存储每个线程对应的错误信息
private static final ThreadLocal<ErrorContext> LOCAL = new ThreadLocal<ErrorContext>(); private ErrorContext stored; private String resource; private String activity; private String object; private String message; private String sql; private Throwable cause;
每个错误信息都包含:当前错误文件,当前activity,当前object ,错误消息,sql等,通过reset方法可重置当前线程错误信息上下文,最后通过toString格式化输出。
9. 设置Configuration变量 可覆盖原有配置信息
10.到此XMLConfigBuilder构造方法执行完毕,总结一下这个过程中大致做了哪些工作:
1)XPathParser 解析器的初始化,赋给XMLConfigBuilder的parser
2)初始化Configuration
发表评论
-
MyBatis原理(2)-执行流程 4 Mapper的执行
2018-09-07 11:15 702执行方式2: DeptMapper mapper ... -
MyBatis原理(2)-执行流程 3 处理结果集
2018-09-07 10:24 815DefaultResultSetHandler#handleR ... -
MyBatis原理(2)-执行流程 2
2018-08-31 17:47 485@Override public <E> L ... -
MyBatis原理(2)-执行流程 1 BoundSql生成
2018-08-31 17:09 1188MyBatis执行两种方式: 1. SqlSession ... -
MyBatis原理(1)-启动流程3- mapper 加载
2018-08-25 22:27 8231.接着上一篇文章解析mapper第一步 mapperEle ... -
MyBatis原理(1)-启动流程2
2018-08-24 17:21 4961.XMLConfigBuilder.parse ...
相关推荐
| 2.0.0版本|springboot启动| # 原理 Mybatis基于动态代理实现Mapper接口,实现快速开发SOAP的WebService接口服务 # 功能 #### 1.支持日志记录,黑白名单控制 #### 2.支持数据库配置velocity sql查询语句(支持...
Spring+SpringMVC+Mybatis框架集成公共模块,包括公共配置、MybatisGenerator扩展插件、通用BaseService、工具类等。 > zheng-admin 基于bootstrap实现的响应式Material Design风格的通用后台管理系统,`zheng`...
无论您是运行还是二次开发,部署过程若遇到问题,请及时私信交流,欢迎学习。 基于SpringBoot+Mybatis开发的Steam商城项目源码+项目说明+sql数据库.zip 项目的运行 在数据库中创建steam库,然后导入sql文件,修改...
7.1 启动流程(Springboot 1.50版本) 128 7.1.1 创建SpringApplication对象 129 7.1.2 运行run方法 130 7.1.3 编写事件监听机制 132 8 Spring Boot自定义starters 136 8.1 概述 136 8.2 步骤 137 9 更多Springboot...
├─补充1:拔高课程(Redis3.0持久化、集群、MySQL5.6优化、Tomcat7优化) │ │ 打开必读.txt │ │ │ ├─课前资料 │ │ ├─MySQL5.6优化 │ │ │ MySql5.6性能优化.docx │ │ │ │ │ ├─Redis集群 │ ...
─02. 源码框架专题 │ 2.1 Spring底层核心原理解析....│ 3.1Spring之启动过程源码解析.mp4 │ 3.2 Spring之配置类解析与扫描过程源码解析.mp4 │ 3.3 Spring之整合MyBatis底层源码解析.mp4 │ 256G视频绝对物超所值
│ Java面试题48.struts2的执行流程或者struts2的原理.mp4 │ Java面试题49.Struts2的拦截器是什么?你都用它干什么?.mp4 │ Java面试题50.Spring MVC的执行流程.mp4 │ Java面试题51.SpringMVC和Struts2的不同.mp4...
部署说明:本资源提供了详细的部署说明,包括如何配置数据库连接、如何启动项目等。此外,还介绍了如何在不同操作系统上进行部署,以满足不同开发者的需求。演示视频:本资源附带了一个演示视频,展示了系统的运行...
《Spring3.x企业应用开发实战》是在《精通Spring2.x——企业应用开发详解》的基础上,经过历时一年的重大调整改版而成的,本书延续了上一版本追求深度,注重原理,不停留在技术表面的写作风格,力求使读者在熟练...
部署说明:本项目提供了详细的部署说明,包括如何配置环境、如何启动项目等。通过阅读部署说明,开发者可以快速了解如何在自己的服务器上运行该项目。此外,部署说明还包含了一些注意事项和常见问题解决方案,帮助...
这是一个基于SSM(Spring、SpringMVC、MyBatis)框架和Vue前端技术栈的小学生课外知识学习网站的源码资源包。该资源包包含了完整的项目源码、部署说明、演示视频以及源码介绍,旨在帮助用户快速搭建和运行一个功能...
◦定时任务管理 通过配置以实现某时刻重复执行的系统任务,如配置每月最后一天进行库存清算任务,并且启动库存清算审批流程。 ◦系统日志管理 记录进入系统中的每个用户访问的每个功能 ◦数据源管理 可以设置多种...
《Spring3.x企业应用开发实战》是在《精通Spring2.x——企业应用开发详解》的基础上,经过历时一年的重大调整改版而成的,本书延续了上一版本追求深度,注重原理,不停留在技术表面的写作风格,力求使读者在熟练...
│ 第48节:VCL的子程序和Request流程.avi │ 第49节:VCL的变量和常见的应用片断.avi │ 第50节:使用CLI来管理Varnish.avi │ 第51节:Varnishd命令和运行期参数.avi │ 第52节:Varnish的日志操作.avi │ 第53节...
启动配置..................................................................................................................................... 9 配置空间...................................................
MinorGC 的过程(复制->清空->互换) ....................................................................................... 24 1:eden、servicorFrom 复制到 ServicorTo,年龄+1.................................