设计模式
发表于|更新于|Java
|总字数:2.2k|阅读时长:10分钟|浏览量:
创建型模式
简单工厂
Pay.java
1 | public interface Pay { |
SimpleFactory.java
1 | public class SimpleFactory { |
WXPay.java
微信支付
1 | public class WXPay implements Pay { |
ZFBPay.java
支付宝支付
1 | public class ZFBPay implements Pay { |
Test.java
1 | /** |
行为模式
策略模式
基础
Strategy
1 | public interface Strategy { |
ConcreteStrategyA
1 | public class ConcreteStrategyA implements Strategy { |
ConcreteStrategyB
1 | public class ConcreteStrategyB implements Strategy { |
ConcreteStrategyC
1 | public class ConcreteStrategyC implements Strategy { |
Context
1 | public class Context { |
Test.java
1 | public class Test { |
应用
MemberStrategy.java
1 | /** |
PrimaryMemberStrategy.java 初级会员
1 | public class PrimaryMemberStrategy implements MemberStrategy { |
IntermediateMemberStrategy.java 中级会员
1 | public class IntermediateMemberStrategy implements MemberStrategy { |
AdvancedMemberStrategy.java 高级会员
1 | public class AdvancedMemberStrategy implements MemberStrategy { |
Price.java
1 | public class Price { |
Test.java
1 | public class Test { |
结构模式
装饰模式
mybatis的执行期就用的是装饰模式
1 | // 抽象构件角色 |
模板模式
1 | public interface Lifecycle { |
责任链模式
1 | public abstract class Handler { |
Tomcat的过滤器也使用到了责任链ApplicationFilterChain,具体的还没有读,先写完这个模式,比模板模式感觉有点复杂
参考的是:https://juejin.im/post/6844903702260629512#heading-11,
感觉举的例子的业务显示还不是特别的好理解
也参考下这个:https://www.cnblogs.com/tanshaoshenghao/p/10741160.html
适配器模式
SpringMVC的DispatchServlet的例子
具体实现
1 | /** |
处理器
1 | /** |
main方法
1 | /** |
参考
相关推荐

2022-11-19
Mybatis
教程 参考: 官方文档 源码 Provider的使用1.一定要注意type的类名.class 和 method方法名,还要注意形参也得是一样的 2.Provider的方法,大概就三个方法sql.SELECT,sql.WHERE,sql.FROM 3.SQL 对象里的方法名跟别的不一样,小写的不行,idea也识别不到,要用大写,比如SLELECT 4.Provider里返回的是String 注解 一对多查询(多个参数) 注解 一对多查询(多个参数)123456789101112131415161718192021222324252627282930313233343536373839404142@SelectProvider(method="queryPageFloors",type=BarterGroupProvider.class)@Results({ @Result(property = "userId",column="user_id"), @Result(property = "floorNum",column="floor_num"), @Result(property = "floorSecond",column="id"), @Result(property = "say",column="note"), @Result(property = "joinOrPublish",column="join_or_publish"), @Result(property="categorys",javaType = List.class,column="id",many=@Many(select="com.ecloud.hobay.marketing.service.infrastructure.mapper.bartergroup.BarterGroupMapper.queryCategory")), @Result(property="productIds",javaType = List.class,column="id",many=@Many(select="com.ecloud.hobay.marketing.service.infrastructure.mapper.bartergroup.BarterGroupMapper.queryProducts")), @Result(property="beforeResult",javaType = List.class,column="id",many=@Many(select="com.ecloud.hobay.marketing.service.infrastructure.mapper.bartergroup.BarterGroupEvaluationMapper.queryAll")), @Result(property="barterGroupUser",javaType = BarterGroupUser.class,column="{user_id=user_id,id = barter_group_id }",many=@Many(select="com.ecloud.hobay.marketing.service.infrastructure.mapper.bartergroup.BarterGroupMapper.isHead")), @Result(property="futureResultNum",column="id",many=@Many(select="com.ecloud.hobay.marketing.service.infrastructure.mapper.bartergroup.BarterGroupMapper.futureResultNum"))})List<QueryFloors> queryFloors3(Page<QueryFloors> page);/** * 分类 * @param id * @return */@Select("SELECT * FROM ecloud_marketing.barter_group_category WHERE barter_group_details_id = #{id} limit 0,7 ")List<BarterGroupCategory> queryCategory(@Param("id") Long id);/** * 产品 * @param id * @return */@Select("SELECT product_id from ecloud_marketing.barter_group_product_category WHERE barter_group_details_id = #{id} limit 0,3 ")List<Long> queryProducts(@Param("id") Long id);/** * 团员 * @param userId * @param id * @return */@Select("SELECT * FROM ecloud_marketing.barter_group_user WHERE user_id =#{user_id} and barter_group_id = #{id} and status = 1")BarterGroupUser isHead(Map<String,Object> map);@Select("SELECT count(*) from ecloud_marketing.barter_group_evaluation WHERE barter_group_details_id = #{id}")Integer futureResultNum(@Param("id") Long id); 1234567891011121314151617181920212223242526272829303132333435363738public class QueryFloors implements Serializable { @ApiModelProperty("我有的产品的id") List<ProductBarter> list; @ApiModelProperty("封装前的评论数据") private List<BarterGroupEvaluation> beforeResult; @ApiModelProperty("封装后的评论数据有分页给功能") private Page<BarterGroupEvaluation> beforeResultPage; @ApiModelProperty("封装后的评论数据") private List<BarterGroupEvaluationResult> futureResult; @ApiModelProperty("剩余评论条数") private Integer futureResultNum; @ApiModelProperty("分类") List<BarterGroupCategory> categorys; @ApiModelProperty(value="楼层数") private Integer floorNum; @ApiModelProperty(value="楼层id") private Long floorSecond; @ApiModelProperty(value="要说的") private String say; @ApiModelProperty(value="加入或者是发布") private Integer joinOrPublish; @ApiModelProperty("会员表") private BarterGroupUser barterGroupUser; @ApiModelProperty(value="加入时间") private Long joinData;} 注解 一对多查询(一个参数) 标签mybatis的xml的常用标签: include和sql标签 12345678<sql id="query_column"> id,user_no</sql><select id="test"> select <include refid="query_column"></include> from user_info</select> where标签 12345678910<select id="test"> select * from user_info where <if test="userName != null and userName != ''"> user_name = #{userName} </if> <if test="password != null and password != ''"> and password = #{password} </if></select> 如果userName= null,则sql语句就变成 1select * from user_info where and password = #{password} where标签可以去除where语句中的第一个and 或 or。 1234567891011<select id="test"> select * from user_info <where> <if test="userName != null and userName != ''"> and user_name = #{userName} </if> <if test="password != null and password != ''"> and password = #{password} </if> </where></select> set标签 123456789101112<update id="myupdate"> update user_info <set> <if test="userName != null"> user_name = #{userName}, </if> <if test="password != null"> password = #{password,jdbcType=VARCHAR}, </if> </set> where id = #{id,jdbcType=INTEGER} </update> trim标签 where标签只能去除第一个and或or,不够灵活,trim标签功能更加强大。 trim标签的属性如下: prefix 在trim标签内容前面加上前缀 suffix 在trim标签内容后面加上后缀 prefixOverrides 移除前缀内容。即 属性会忽略通过管道分隔的文本序列,多个忽略序列用 “|” 隔开。它带来的结果就是所有在 prefixOverrides 属性中指定的内容将被移除。 suffixOverrides 移除前缀内容。 12345678<trim prefix="where" prefixOverrides="and"> <if test="userId != null"> user_id=#{userId} </if> <if test="pwd != null and pwd !=''"> user_id=#{userId} </if></trim> foreach标签 foreach元素的属性主要有item,index,collection,open,separator,close. collection 要做foreach的对象,作为入参时,List对象默认用”list”代替作为键,数组对象有”array”代替作为键,Map对象没有默认的键。当然在作为入参时可以使用@Param(“keyName”)来设置键,设置keyName后,list,array将会失效。 除了入参这种情况外,还有一种作为参数对象的某个字段的时候。举个例子:如果User有属性List ids。入参是User对象,那么这个collection = “ids”.如果User有属性Ids ids;其中Ids是个对象,Ids有个属性List id;入参是User对象,那么collection = “ids.id“,必填 item 合中元素迭代时的别名,必选 index 在list和数组中,index是元素的序号,在map中,index是元素的key,可选 open foreach代码的开始符号,一般是(和close=”)”合用。常用在in(),values()时。可选 separator 元素之间的分隔符,例如在in()的时候,separator=”,”会自动在元素中间用“,“隔开,避免手动输入逗号导致sql错误,如in(1,2,)这样,可选。 close foreach代码的关闭符号,一般是)和open=”(“合用。常用在in(),values()时。可选 choose,when,otherwise标签 功能有点类似于Java中的 swicth - case - default 12345678910111213141516<select id="getUser" resultType="com.cat.pojo.User"> SELECT * FROM user <where> <choose> <when test="id != null and test.trim() != '' "> id = #{id} </when> <when test="name != null and name.trim() != '' "> name = #{name} </when> <otherwise> age = 17 </otherwise> </choose></where></select> if标签 123456<!-- 判断字符串--><if test="item != null and item != ''"></if><!-- 如果是Integer类型的需要把and后面去掉或是加上or--><if test="item != null"></if><if test="item != null and item != '' or item == 0"></if> 存过/函数存过 12345678<select id="pNextSupperUsers" parameterType="map" statementType="CALLABLE" resultType="vo.UserAgentVO"> { call p_next_supper_users( #{type,mode=IN,jdbcType=INTEGER}, #{userId,mode=IN,jdbcType=BIGINT} ) }</select> 123456List<UserAgentVO> pNextSupperUsers(Map<String, Object> param);Map<String, Object> param = new HashMap<>();param.put("type", 1);param.put("userId", userId);List<UserAgentVO> list = userInfoMapper.pNextSupperUsers(param); 函数 1234SELECT fn_next_user_count ( 1, u.id ) AS teamCount,FROMuser_info 源码参考 b站博学谷 架构设计 启动测试方法123456789101112131415161718192021# 第一种调用方法public static void test(){ String resource = "com/analyze/mybatis/mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); Map map = sqlSession.selectOne("com.analyze.mybatis.mapper.UserMapper.getUA"); sqlSession.close();}# 第二种调用方法public static void test2(){ String resource = "com/analyze/mybatis/mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); Map map = userMapper.getUA(); sqlSession.close();} 下面的代码,大概意思就是能加载的配置文件的信息,解释 InputStream inputStream = Resources.getResourceAsStream(resource);这行代码的作用 读取mybatis的配置文件12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849// org.apache.ibatis.io.Resources#getResourceAsStream(java.lang.String)public static InputStream getResourceAsStream(String resource) throws IOException { return getResourceAsStream(null, resource);}// org.apache.ibatis.io.Resources#getResourceAsStream(java.lang.ClassLoader, java.lang.String)public static InputStream getResourceAsStream(ClassLoader loader, String resource) throws IOException { InputStream in = classLoaderWrapper.getResourceAsStream(resource, loader); if (in == null) { throw new IOException("Could not find resource " + resource); } return in;}// org.apache.ibatis.io.ClassLoaderWrapper#getResourceAsStream(java.lang.String, java.lang.ClassLoader)public InputStream getResourceAsStream(String resource, ClassLoader classLoader) { return getResourceAsStream(resource, getClassLoaders(classLoader));}// org.apache.ibatis.io.ClassLoaderWrapper#getClassLoadersClassLoader[] getClassLoaders(ClassLoader classLoader) { return new ClassLoader[]{ classLoader, defaultClassLoader, Thread.currentThread().getContextClassLoader(), getClass().getClassLoader(), systemClassLoader};}// org.apache.ibatis.io.ClassLoaderWrapper#getResourceAsStream(java.lang.String, java.lang.ClassLoader[])// 找到一个可以用的ClassloaderInputStream getResourceAsStream(String resource, ClassLoader[] classLoader) { for (ClassLoader cl : classLoader) { if (null != cl) { // try to find the resource as passed InputStream returnValue = cl.getResourceAsStream(resource); if (null == returnValue) { returnValue = cl.getResourceAsStream("/" + resource); } if (null != returnValue) { return returnValue; } } } return null;} 下面的代码,解释SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);,用来解析全局配置文件和解析mapper文件 下面是解析全局配置文件 解析全局配置文件123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188// org.apache.ibatis.session.SqlSessionFactoryBuilder#build(java.io.InputStream)public SqlSessionFactory build(InputStream inputStream) { return build(inputStream, null, null);}// org.apache.ibatis.session.SqlSessionFactoryBuilder#build(java.io.InputStream, java.lang.String, java.util.Properties)public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { try { // 1.创建XpathParse解析器对象,根据inputStream解析成Document对象 // 2.创建全局配置Configuration对象 // 使用构建者模式,好处降低偶尔,分离复杂对象的创建 // 构建XMLConfig XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); // 根据Xpath解析xml配置文件,将配置文件封装到Configuration对象 // 返回DefaultSqlSessionFactory对象 // parse() 就是配置文件解析完成了 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. } }}// org.apache.ibatis.session.SqlSessionFactoryBuilder#build(org.apache.ibatis.session.Configuration)// 最终返回public SqlSessionFactory build(Configuration config) { return new DefaultSqlSessionFactory(config);}// org.apache.ibatis.builder.xml.XMLConfigBuilder#XMLConfigBuilder(java.io.InputStream, java.lang.String, java.util.Properties)public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) { // 点this,查看代码 this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);}// org.apache.ibatis.builder.xml.XMLConfigBuilder#XMLConfigBuilder(org.apache.ibatis.parsing.XPathParser, java.lang.String, java.util.Properties)private XMLConfigBuilder(XPathParser parser, String environment, Properties props) { // 创建Configuration对象,并通过TypeAliasRegistry注册一些Mybatis内部相关类的别名 super(new Configuration()); ErrorContext.instance().resource("SQL Mapper Configuration"); this.configuration.setVariables(props); this.parsed = false; this.environment = environment; this.parser = parser;}// org.apache.ibatis.parsing.XPathParser#XPathParser(java.io.InputStream, boolean, java.util.Properties, org.xml.sax.EntityResolver)public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) { commonConstructor(validation, variables, entityResolver); // 创建解析器 this.document = createDocument(new InputSource(inputStream));}// org.apache.ibatis.parsing.XPathParser#createDocument// 不用太关系,只是创建一个解析器的对象,顺便检查xml文档有没有写错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 { } }); // 解析器的源码了,跟mybatis没有关系了 return builder.parse(inputSource); } catch (Exception e) { throw new BuilderException("Error creating document instance. Cause: " + e, e); } }// org.apache.ibatis.builder.xml.XMLConfigBuilder#parsepublic Configuration parse() { if (parsed) { // 每一个配置文件,只能解析一次 throw new BuilderException("Each XMLConfigBuilder can only be used once."); } parsed = true; // 从根节点开始解析,最终封装到Configuration对象 parseConfiguration(parser.evalNode("/configuration")); return configuration;}// org.apache.ibatis.parsing.XPathParser#evalNode(java.lang.String)public XNode evalNode(String expression) { return evalNode(document, expression);}// org.apache.ibatis.parsing.XPathParser#evalNode(java.lang.Object, java.lang.String)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);}// org.apache.ibatis.builder.xml.XMLConfigBuilder#parseConfiguration// 可以配置这些信息:https://mybatis.org/mybatis-3/zh/configuration.htmlprivate void parseConfiguration(XNode root) { try { //issue #117 read properties first propertiesElement(root.evalNode("properties")); Properties settings = settingsAsProperties(root.evalNode("settings")); loadCustomVfs(settings); loadCustomLogImpl(settings); typeAliasesElement(root.evalNode("typeAliases")); // 插件 pluginElement(root.evalNode("plugins")); objectFactoryElement(root.evalNode("objectFactory")); objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); reflectorFactoryElement(root.evalNode("reflectorFactory")); settingsElement(settings); // read it after objectFactory and objectWrapperFactory issue #631 // 重点 environmentsElement(root.evalNode("environments")); databaseIdProviderElement(root.evalNode("databaseIdProvider")); typeHandlerElement(root.evalNode("typeHandlers")); // 重点 mapperElement(root.evalNode("mappers")); } catch (Exception e) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); }}// org.apache.ibatis.builder.xml.XMLConfigBuilder#mapperElementprivate void mapperElement(XNode parent) throws Exception { if (parent != null) { for (XNode child : parent.getChildren()) { if ("package".equals(child.getName())) { String mapperPackage = child.getStringAttribute("name"); configuration.addMappers(mapperPackage); } else { String resource = child.getStringAttribute("resource"); String url = child.getStringAttribute("url"); String mapperClass = child.getStringAttribute("class"); // 三个值是互斥的 if (resource != null && url == null && mapperClass == null) { ErrorContext.instance().resource(resource); InputStream inputStream = Resources.getResourceAsStream(resource); XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments()); mapperParser.parse(); } else if (resource == null && url != null && mapperClass == null) { ErrorContext.instance().resource(url); // 专门用来解析mapper映射文件 InputStream inputStream = Resources.getUrlAsStream(url); // 重点 XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments()); mapperParser.parse(); } else if (resource == null && url == null && mapperClass != null) { Class<?> mapperInterface = Resources.classForName(mapperClass); configuration.addMapper(mapperInterface); } else { throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one."); } } } }} 解释下environment作用,就是<environment id="development">这里的,不同的环境不同的配置 1234567891011121314151617181920212223<configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://192.168.3.12:3306/orangedb"/> <property name="username" value="root"/> <property name="password" value="abcd2022"/> </dataSource> </environment> <environment id="test"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://192.168.3.12:3306/orangedb"/> <property name="username" value="root"/> <property name="password" value="abcd2022"/> </dataSource> </environment> </environments></configuration> 用法:如下代码 1SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream,"development"); 下面是解析mapper文件,配置的属性,参考:https://mybatis.org/mybatis-3/zh/sqlmap-xml.html 解析mapper配置文件123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223// org.apache.ibatis.builder.xml.XMLMapperBuilder#XMLMapperBuilder(java.io.InputStream, org.apache.ibatis.session.Configuration, java.lang.String, java.util.Map<java.lang.String,org.apache.ibatis.parsing.XNode>)// 这个方法,前面已经用过了 public XMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource, Map<String, XNode> sqlFragments) { this(new XPathParser(inputStream, true, configuration.getVariables(), new XMLMapperEntityResolver()), configuration, resource, sqlFragments); }// org.apache.ibatis.builder.xml.XMLMapperBuilder#parsepublic void parse() { // mapper映射文件是否已经加载过 if (!configuration.isResourceLoaded(resource)) { // 从根节点解析 configurationElement(parser.evalNode("/mapper")); // 标记已经解析 configuration.addLoadedResource(resource); // 为命名空间绑定映射 bindMapperForNamespace(); } // parsePendingResultMaps(); parsePendingCacheRefs(); parsePendingStatements();}// org.apache.ibatis.builder.xml.XMLMapperBuilder#configurationElementprivate void configurationElement(XNode context) { try { // 获取命名空间 String namespace = context.getStringAttribute("namespace"); // 命名空间不能为空 if (namespace == null || namespace.equals("")) { throw new BuilderException("Mapper's namespace cannot be empty"); } // 设置当前的命名空间的值 // 构建mappedStatement对象 builderAssistant.setCurrentNamespace(namespace); cacheRefElement(context.evalNode("cache-ref")); cacheElement(context.evalNode("cache")); parameterMapElement(context.evalNodes("/mapper/parameterMap")); resultMapElements(context.evalNodes("/mapper/resultMap")); sqlElement(context.evalNodes("/mapper/sql")); // 重点 buildStatementFromContext(context.evalNodes("select|insert|update|delete")); } catch (Exception e) { throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e); }}// org.apache.ibatis.builder.xml.XMLMapperBuilder#buildStatementFromContext(java.util.List<org.apache.ibatis.parsing.XNode>)private void buildStatementFromContext(List<XNode> list) { if (configuration.getDatabaseId() != null) { buildStatementFromContext(list, configuration.getDatabaseId()); } // 没有配置过getDatabaseId,所以走这里 buildStatementFromContext(list, null);}// org.apache.ibatis.builder.xml.XMLMapperBuilder#buildStatementFromContext(java.util.List<org.apache.ibatis.parsing.XNode>, java.lang.String)private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) { for (XNode context : list) { final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId); try { statementParser.parseStatementNode(); } catch (IncompleteElementException e) { configuration.addIncompleteStatement(statementParser); } }}// org.apache.ibatis.builder.xml.XMLStatementBuilder#parseStatementNodepublic void parseStatementNode() { String id = context.getStringAttribute("id"); String databaseId = context.getStringAttribute("databaseId"); // 没有设置过databaseId if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) { return; } String nodeName = context.getNode().getNodeName(); // 解析Sql命令类型是什么,确实是 Select,update,insert,delete 类型 SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH)); boolean isSelect = sqlCommandType == SqlCommandType.SELECT; boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect); boolean useCache = context.getBooleanAttribute("useCache", isSelect); boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false); // Include Fragments before parsing XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant); includeParser.applyIncludes(context.getNode()); String parameterType = context.getStringAttribute("parameterType"); Class<?> parameterTypeClass = resolveClass(parameterType); // 配置语言驱动 String lang = context.getStringAttribute("lang"); LanguageDriver langDriver = getLanguageDriver(lang); // Parse selectKey after includes and remove them. processSelectKeyNodes(id, parameterTypeClass, langDriver); // Parse the SQL (pre: <selectKey> and <include> were parsed and removed) KeyGenerator keyGenerator; String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX; keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true); if (configuration.hasKeyGenerator(keyStatementId)) { keyGenerator = configuration.getKeyGenerator(keyStatementId); } else { keyGenerator = context.getBooleanAttribute("useGeneratedKeys", configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType)) ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE; } // 替换占位符?,保存#{}里面的内容 SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass); StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString())); Integer fetchSize = context.getIntAttribute("fetchSize"); Integer timeout = context.getIntAttribute("timeout"); String parameterMap = context.getStringAttribute("parameterMap"); String resultType = context.getStringAttribute("resultType"); Class<?> resultTypeClass = resolveClass(resultType); String resultMap = context.getStringAttribute("resultMap"); String resultSetType = context.getStringAttribute("resultSetType"); ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType); if (resultSetTypeEnum == null) { resultSetTypeEnum = configuration.getDefaultResultSetType(); } String keyProperty = context.getStringAttribute("keyProperty"); String keyColumn = context.getStringAttribute("keyColumn"); String resultSets = context.getStringAttribute("resultSets"); // 通过构建者助手,创建mappedstatement对象 builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered, keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets); }// org.apache.ibatis.builder.MapperBuilderAssistant#addMappedStatement(java.lang.String, org.apache.ibatis.mapping.SqlSource, org.apache.ibatis.mapping.StatementType, org.apache.ibatis.mapping.SqlCommandType, java.lang.Integer, java.lang.Integer, java.lang.String, java.lang.Class<?>, java.lang.String, java.lang.Class<?>, org.apache.ibatis.mapping.ResultSetType, boolean, boolean, boolean, org.apache.ibatis.executor.keygen.KeyGenerator, java.lang.String, java.lang.String, java.lang.String, org.apache.ibatis.scripting.LanguageDriver, java.lang.String)public MappedStatement addMappedStatement( String id, SqlSource sqlSource, StatementType statementType, SqlCommandType sqlCommandType, Integer fetchSize, Integer timeout, String parameterMap, Class<?> parameterType, String resultMap, Class<?> resultType, ResultSetType resultSetType, boolean flushCache, boolean useCache, boolean resultOrdered, KeyGenerator keyGenerator, String keyProperty, String keyColumn, String databaseId, LanguageDriver lang, String resultSets) { if (unresolvedCacheRef) { throw new IncompleteElementException("Cache-ref not yet resolved"); } id = applyCurrentNamespace(id, false); boolean isSelect = sqlCommandType == SqlCommandType.SELECT; // 创建MappedStatement对象 MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType) .resource(resource) .fetchSize(fetchSize) .timeout(timeout) .statementType(statementType) .keyGenerator(keyGenerator) .keyProperty(keyProperty) .keyColumn(keyColumn) .databaseId(databaseId) .lang(lang) .resultOrdered(resultOrdered) .resultSets(resultSets) .resultMaps(getStatementResultMaps(resultMap, resultType, id)) .resultSetType(resultSetType) .flushCacheRequired(valueOrDefault(flushCache, !isSelect)) .useCache(valueOrDefault(useCache, isSelect)) .cache(currentCache); ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id); if (statementParameterMap != null) { statementBuilder.parameterMap(statementParameterMap); } // 封装好MappedStatement,并返回 MappedStatement statement = statementBuilder.build(); configuration.addMappedStatement(statement); return statement; }// org.apache.ibatis.builder.MapperBuilderAssistant#applyCurrentNamespacepublic String applyCurrentNamespace(String base, boolean isReference) { if (base == null) { return null; } if (isReference) { // is it qualified with any namespace yet? if (base.contains(".")) { return base; } } else { // is it qualified with this namespace yet? if (base.startsWith(currentNamespace + ".")) { return base; } if (base.contains(".")) { throw new BuilderException("Dots are not allowed in element names, please remove it from " + base); } } // namespacename+点+方法名 return currentNamespace + "." + base; } 到这里,配置文件就解析完成了,下面是创建SqlSessionFactory对象 SqlSession1234 // org.apache.ibatis.session.SqlSessionFactoryBuilder#build(org.apache.ibatis.session.Configuration)public SqlSessionFactory build(Configuration config) { return new DefaultSqlSessionFactory(config);} 下面是mybatis创建sql的流程 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475// /Users/anthony/.m2/repository/org/mybatis/mybatis/3.5.2/mybatis-3.5.2-sources.jar!/org/apache/ibatis/builder/xml/XMLStatementBuilder.java:96// 占位符是如果进行替换的?动态sql如果进行的解析String lang = context.getStringAttribute("lang");LanguageDriver langDriver = getLanguageDriver(lang);SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);// org.apache.ibatis.builder.xml.XMLStatementBuilder#getLanguageDriverprivate LanguageDriver getLanguageDriver(String lang) { Class<? extends LanguageDriver> langClass = null; if (lang != null) { langClass = resolveClass(lang); } return configuration.getLanguageDriver(langClass);}// org.apache.ibatis.session.Configuration#getLanguageDriverpublic LanguageDriver getLanguageDriver(Class<? extends LanguageDriver> langClass) { if (langClass == null) { return languageRegistry.getDefaultDriver(); } languageRegistry.register(langClass); return languageRegistry.getDriver(langClass);}// org.apache.ibatis.scripting.LanguageDriverRegistry#getDefaultDriver// 打断点可以看到是:XMLLanguageDriverpublic LanguageDriver getDefaultDriver() { return getDriver(getDefaultDriverClass());}public Class<? extends LanguageDriver> getDefaultDriverClass() { return defaultDriverClass;}// org.apache.ibatis.scripting.xmltags.XMLLanguageDriver#createSqlSource(org.apache.ibatis.session.Configuration, org.apache.ibatis.parsing.XNode, java.lang.Class<?>)public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) { XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType); return builder.parseScriptNode();}// org.apache.ibatis.scripting.xmltags.XMLScriptBuilder#XMLScriptBuilder(org.apache.ibatis.session.Configuration, org.apache.ibatis.parsing.XNode, java.lang.Class<?>)public XMLScriptBuilder(Configuration configuration, XNode context, Class<?> parameterType) { super(configuration); this.context = context; this.parameterType = parameterType; // 初始化动态sql的节点处理器结合 initNodeHandlerMap();}// org.apache.ibatis.scripting.xmltags.XMLScriptBuilder#initNodeHandlerMap private void initNodeHandlerMap() { nodeHandlerMap.put("trim", new TrimHandler()); nodeHandlerMap.put("where", new WhereHandler()); nodeHandlerMap.put("set", new SetHandler()); nodeHandlerMap.put("foreach", new ForEachHandler()); nodeHandlerMap.put("if", new IfHandler()); nodeHandlerMap.put("choose", new ChooseHandler()); nodeHandlerMap.put("when", new IfHandler()); nodeHandlerMap.put("otherwise", new OtherwiseHandler()); nodeHandlerMap.put("bind", new BindHandler()); }// org.apache.ibatis.scripting.xmltags.XMLScriptBuilder#parseScriptNode// 解析sql,还有参数类型和结果集类型public SqlSource parseScriptNode() { MixedSqlNode rootSqlNode = parseDynamicTags(context); SqlSource sqlSource; if (isDynamic) { sqlSource = new DynamicSqlSource(configuration, rootSqlNode); } else { sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType); } return sqlSource;} 全部就解析完成了,接下来 看看session = sqlSessionFactory.openSession(); 创建事务对象 创建了执行器对象CasheingExecutor 创建DefaultSqlSession对象 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768// org.apache.ibatis.session.defaults.DefaultSqlSessionFactory#openSession()@Overridepublic SqlSession openSession() { return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);}// org.apache.ibatis.session.Configuration#getDefaultExecutorType// protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;public ExecutorType getDefaultExecutorType() { return defaultExecutorType;}// org.apache.ibatis.session.defaults.DefaultSqlSessionFactory#openSessionFromDataSource// 参数一:执行器,参数二:隔离级别private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null; try { // 获取数据源环境信息 final Environment environment = configuration.getEnvironment(); // 获取事务工厂 final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); // 获取JdbcTransaction或者ManagedTransaction // ManagedTransaction 就相当于没有事务 tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); // 创建Executor执行器 final Executor executor = configuration.newExecutor(tx, execType); // 创建DefaultSqlSession return new DefaultSqlSession(configuration, executor, autoCommit); } catch (Exception e) { closeTransaction(tx); // may have fetched a connection so lets call close() throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e); } finally { ErrorContext.instance().reset(); }}// org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory#newTransaction(java.sql.Connection)@Overridepublic Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) { return new JdbcTransaction(ds, level, autoCommit);}// org.apache.ibatis.session.Configuration#newExecutor(org.apache.ibatis.transaction.Transaction, org.apache.ibatis.session.ExecutorType) public Executor newExecutor(Transaction transaction, ExecutorType executorType) { executorType = executorType == null ? defaultExecutorType : executorType; executorType = executorType == null ? ExecutorType.SIMPLE : executorType; Executor executor; if (ExecutorType.BATCH == executorType) { executor = new BatchExecutor(this, transaction); } else if (ExecutorType.REUSE == executorType) { executor = new ReuseExecutor(this, transaction); } else { executor = new SimpleExecutor(this, transaction); } // 如果允许缓存,会通过CachingExecutor去代理一层 if (cacheEnabled) { executor = new CachingExecutor(executor); } // 拦截器插件 executor = (Executor) interceptorChain.pluginAll(executor); return executor; }// org.apache.ibatis.executor.CachingExecutor#CachingExecutorpublic CachingExecutor(Executor delegate) { this.delegate = delegate; delegate.setExecutorWrapper(this);} 调用具体的API进行查询用启动类中的方法,对比可以发现两段代码不同之处为: 1234Map map = sqlSession.selectOne("com.analyze.mybatis.mapper.UserMapper.getUA");UserMapper userMapper = sqlSession.getMapper(UserMapper.class);Map map = userMapper.getUA(); 在查看DefaultSqlSession中的selectOne方法,会执行以下的调用链 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205// org.apache.ibatis.session.defaults.DefaultSqlSession#selectOne(java.lang.String, java.lang.Object)@Overridepublic <T> T selectOne(String statement, Object parameter) { // Popular vote was to return null on 0 results and throw exception on too many. List<T> list = this.selectList(statement, parameter); if (list.size() == 1) { return list.get(0); } else if (list.size() > 1) { throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size()); } else { return null; }}// org.apache.ibatis.session.defaults.DefaultSqlSession#selectList(java.lang.String, java.lang.Object)// RowBounds 分页对象public <E> List<E> selectList(String statement, Object parameter) { return this.selectList(statement, parameter, RowBounds.DEFAULT);}// org.apache.ibatis.session.defaults.DefaultSqlSession#selectList(java.lang.String, java.lang.Object, org.apache.ibatis.session.RowBounds)@Overridepublic <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) { try { // 根据传入的statementId,获取mapperStatement对象 MappedStatement ms = configuration.getMappedStatement(statement); // 调用执行器的方法 return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER); } catch (Exception e) { throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e); } finally { ErrorContext.instance().reset(); }}// org.apache.ibatis.executor.CachingExecutor#query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler)// 注意这里是CachingExecutor,默认的@Overridepublic <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException { // 获取带问号的sql语句,比如 select * from user_info where id = ? BoundSql boundSql = ms.getBoundSql(parameterObject); // 生成缓存key CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql); return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);}// org.apache.ibatis.executor.CachingExecutor#query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler, org.apache.ibatis.cache.CacheKey, org.apache.ibatis.mapping.BoundSql)@Overridepublic <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { // 二级缓存 // usermapper的标签里可以设置<Cache>缓存标签 Cache cache = ms.getCache(); if (cache != null) { // 刷新二级缓存,在<select> 标签里可以配置flushcache flushCacheIfRequired(ms); // 在<select> 标签里可以配置usecache if (ms.isUseCache() && resultHandler == null) { // 判断是不是存过 ensureNoOutParams(ms, boundSql); @SuppressWarnings("unchecked") // 从二级缓存中查询数据 List<E> list = (List<E>) tcm.getObject(cache, key); // 如果从二级缓存没有查询到数据 if (list == null) { // 委托给BaseExecutor执行 list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql); // 将查询结果保存到二级缓存中,这里只是存到map集合中,没有真正存到二级缓存中 tcm.putObject(cache, key, list); // issue #578 and #116 } return list; } } return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);}// org.apache.ibatis.executor.BaseExecutor#query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler, org.apache.ibatis.cache.CacheKey, org.apache.ibatis.mapping.BoundSql)@Overridepublic <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId()); if (closed) { throw new ExecutorException("Executor was closed."); } // 如果配置了FlushCacheRequired为true,则会在执行器执行之前就清空本地一级缓存 if (queryStack == 0 && ms.isFlushCacheRequired()) { // 清空缓存 clearLocalCache(); } List<E> list; try { queryStack++; // 从一级缓存中获取数据 list = resultHandler == null ? (List<E>) localCache.getObject(key) : null; if (list != null) { // 如果有数据,则处理本地缓存结果给输出参数 // 还是处理存过 handleLocallyCachedOutputParameters(ms, key, parameter, boundSql); } else { // 没有缓存结果,则从数据库查询结果 list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql); } } finally { queryStack--; } if (queryStack == 0) { for (DeferredLoad deferredLoad : deferredLoads) { deferredLoad.load(); } // issue #601 deferredLoads.clear(); if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) { // issue #482 clearLocalCache(); } } return list;}// org.apache.ibatis.executor.BaseExecutor#queryFromDatabaseprivate <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException { List<E> list; // 向本地缓存中存入一个ExecutionPlaceholder的枚举类占位 localCache.putObject(key, EXECUTION_PLACEHOLDER); try { // 执行查询 list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql); } finally { // 执行玩移除这个key localCache.removeObject(key); } localCache.putObject(key, list); if (ms.getStatementType() == StatementType.CALLABLE) { localOutputParameterCache.putObject(key, parameter); } return list;}// org.apache.ibatis.executor.SimpleExecutor#doQuery@Overridepublic <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { Statement stmt = null; try { // 获取全局配置文件实例 Configuration configuration = ms.getConfiguration(); // new一个statementHandler实例 StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql); // 准备处理器,主要包括创建statement以及动态参数的设置 stmt = prepareStatement(handler, ms.getStatementLog()); // 执行真正的数据库操作调用 return handler.query(stmt, resultHandler); } finally { closeStatement(stmt); }}// org.apache.ibatis.session.Configuration#newStatementHandlerpublic StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { // 创建路由功能的StatementHanlder,根据MappedStatement中的StetementType StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql); // 插件机制:对核心对象进行拦截 statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler); return statementHandler;}// org.apache.ibatis.executor.statement.RoutingStatementHandler#RoutingStatementHandlerpublic RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { switch (ms.getStatementType()) { case STATEMENT: delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql); break; case PREPARED: delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql); break; case CALLABLE: delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql); break; default: throw new ExecutorException("Unknown statement type: " + ms.getStatementType()); }}// org.apache.ibatis.executor.SimpleExecutor#prepareStatementprivate Statement prepareStatement(StatementHandler handler, Log statementLog){ Statement stmt; // 获取代理后,增加日志功能的Connection对象 Connection connection = getConnection(statementLog); // 创建Statement对象 stmt = handler.prepare(connection, transaction.getTimeout()); // 参数化处理 handler.parameterize(stmt); return stmt;}// org.apache.ibatis.executor.statement.PreparedStatementHandler#query @Overridepublic <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { PreparedStatement ps = (PreparedStatement) statement; ps.execute(); return resultSetHandler.handleResultSets(ps);} 解析结果1234567891011121314151617181920212223242526272829303132333435363738// org.apache.ibatis.executor.resultset.DefaultResultSetHandler#handleResultSets@Overridepublic List<Object> handleResultSets(Statement stmt) throws SQLException { ErrorContext.instance().activity("handling results").object(mappedStatement.getId()); final List<Object> multipleResults = new ArrayList<>(); int resultSetCount = 0; ResultSetWrapper rsw = getFirstResultSet(stmt); List<ResultMap> resultMaps = mappedStatement.getResultMaps(); int resultMapCount = resultMaps.size(); validateResultMapsCount(rsw, resultMapCount); while (rsw != null && resultMapCount > resultSetCount) { ResultMap resultMap = resultMaps.get(resultSetCount); handleResultSet(rsw, resultMap, multipleResults, null); rsw = getNextResultSet(stmt); cleanUpAfterHandlingResultSet(); resultSetCount++; } String[] resultSets = mappedStatement.getResultSets(); if (resultSets != null) { while (rsw != null && resultSetCount < resultSets.length) { ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]); if (parentMapping != null) { String nestedResultMapId = parentMapping.getNestedResultMapId(); ResultMap resultMap = configuration.getResultMap(nestedResultMapId); handleResultSet(rsw, resultMap, null, parentMapping); } rsw = getNextResultSet(stmt); cleanUpAfterHandlingResultSet(); resultSetCount++; } } return collapseSingleResultList(multipleResults);} 缓存1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253// org.apache.ibatis.executor.CachingExecutor#createCacheKey// CacheKey重新了 hacode和equals方法@Overridepublic CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) { // delegate 是具体类型的执行器的应用 // 默认是SimpleExecutor return delegate.createCacheKey(ms, parameterObject, rowBounds, boundSql);}// org.apache.ibatis.executor.BaseExecutor#createCacheKey@Overridepublic CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) { if (closed) { throw new ExecutorException("Executor was closed."); } // 通过构造器创建 CacheKey cacheKey = new CacheKey(); // id cacheKey.update(ms.getId()); // 分页参数 cacheKey.update(rowBounds.getOffset()); cacheKey.update(rowBounds.getLimit()); // sql cacheKey.update(boundSql.getSql()); List<ParameterMapping> parameterMappings = boundSql.getParameterMappings(); TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry(); // mimic DefaultParameterHandler logic for (ParameterMapping parameterMapping : parameterMappings) { if (parameterMapping.getMode() != ParameterMode.OUT) { Object value; String propertyName = parameterMapping.getProperty(); if (boundSql.hasAdditionalParameter(propertyName)) { value = boundSql.getAdditionalParameter(propertyName); } else if (parameterObject == null) { value = null; } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) { value = parameterObject; } else { MetaObject metaObject = configuration.newMetaObject(parameterObject); value = metaObject.getValue(propertyName); } // 参数的值 cacheKey.update(value); } } if (configuration.getEnvironment() != null) { // issue #176 // 当前环境的值 cacheKey.update(configuration.getEnvironment().getId()); } return cacheKey;} 面试题

2022-11-19
Mycat
主从复制启动1 .下载jre包 2 下载mycat包 3 打包镜像 Dockerfile 1234567891011121314FROM centos# 解压这个包到home下面ADD server-jre-8u251-linux-x64.tar.gz /home/ADD ./Mycat-server-1.6.7.4-release-20200105164103-linux.tar.gz /homeENV WORKPATH /home/mycat/WORKDIR $WORKPATHENV JAVA_HOME /home/jdk1.8.0_251ENV CLASSPATH $JAVA_HOME/dt.jar:$JAVA_HOME/lib/tools.jarENV PATH $PATH:$JAVA_HOME/BIN:$CATALINA_HOME/lib:$CATALINA_HOME/bin# 暴露8066端口EXPOSE 8066CMD /home/mycat/bin/mycat console 把mycat里的conf 和 logs 从容器中拷贝出来 再挂在两个目录,并启动 123456789101112131415docker run -it \ --name mycat \ -p 8066:8066 \ -v /home/anthony/mycat/conf:/home/mycat/conf/ \ -v /home/anthony/mycat/logs:/home/mycat/logs/ \ java1234/mycat:1.0Running Mycat-server...Removed stale pid file: /home/mycat/logs/mycat.pidwrapper | --> Wrapper Started as Consolewrapper | Launching a JVM...jvm 1 | Wrapper (Version 3.2.3) http://wrapper.tanukisoftware.orgjvm 1 | Copyright 1999-2006 Tanuki Software, Inc. All Rights Reserved.jvm 1 |jvm 1 | MyCAT Server startup successfully. see logs in logs/mycat.log 配置 schems.xml 定义逻辑库,表,分片节点等内容 rule.xml 定义分片规则 server.xml 定义用户以及系统相关变量 安装mysql5.7123# 从容器中拷贝这些文件docker cp xxx:/etc/mysql/mysql.conf.d/ /home/anthony/mysqldocker cp xxx:/var/log/ /home/anthony/mysql 启动的时候映射这些 创建网络12345678910# 可以查看容器的虚拟ipdocker inspect ac7# 查看网络docker network ls# 自定义一个网络模式# extnetwork 是网络模式的名字# 172.20.0.1 是网关docker network create --subnet=172.20.0.0/16 extnetwork mysql,mycat指定网路,重建容器12345678910111213141516171819202122232425262728# 指定网络模式和ip地址--net extnetwork --ip 172.20.0.2docker run -itd \ --name mysql-master \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD=123456 \ -v /home/anthony/mysql/mysql.conf.d/:/etc/mysql/conf.d/ \ -v /home/anthony/mysql/log/:/var/log/ \ --net extnetwork --ip 172.20.0.2 \ mysql:5.7docker run -itd \ --name mysql-slave \ -p 3307:3306 \ -e MYSQL_ROOT_PASSWORD=123456 \ -v /home/anthony/mysql2/mysql.conf.d/:/etc/mysql/conf.d/ \ -v /home/anthony/mysql2/log/:/var/log/ \ --net extnetwork --ip 172.20.0.3 \ mysql:5.7docker run -it \ --name mycat \ -p 8066:8066 \ -v /home/anthony/mycat/conf:/home/mycat/conf/ \ -v /home/anthony/mycat/logs:/home/mycat/logs/ \ --net extnetwork --ip 172.20.0.4 \ java1234/mycat:1.0 主从配置主,在[mysqld] 12345678910111213# 主服务器ID,必须唯一server-id=2# 开启和设置二进制日志文件名称log_bin=mysql-bin# 要同步的数据库binlog-do-db=db_java1234# 不需要同步的数据库binlog-ignore_db=mysqlbinlog-ignore_db=information_schemabinlog-ignore_db=performation_schemabinlog_ignore_db=sys# 设置logbin格式,mysql默认采用statement,建议使用mixedbinlog_format=MIXED 从,在[mysqld] 12345678910111213141516# 主服务器ID,必须唯一server-id=3# 这行是从库需要添加的relay-log=mysql-relay# 开启和设置二进制日志文件名称log_bin=mysql-bin# 要同步的数据库binlog-do-db=db_java1234# 不需要同步的数据库binlog-ignore_db=mysqlbinlog-ignore_db=information_schemabinlog-ignore_db=performation_schemabinlog_ignore_db=sys# 设置logbin格式,mysql默认采用statement,建议使用mixedbinlog_format=MIXED 测试主从还没有生效 12345678910mysql> show master status;+------------------+----------+--------------+--------------------------------------------------+-------------------+| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |+------------------+----------+--------------+--------------------------------------------------+-------------------+| mysql-bin.000003 | 357 | db_java1234 | mysql,information_schema,performation_schema,sys | |+------------------+----------+--------------+--------------------------------------------------+-------------------+1 row in set (0.01 sec)mysql> show slave status;Empty set 设置用户名在master执行 12345678910111213# 创建用户名为slave1# 172.20.0.3从库ip# 123456 密码CREATE USER 'slave1'@'172.20.0.3' IDENTIFIED BY '123456';# 语法:GRANT privileges ON databasename.tablename TO 'username'@'host'# 172.20.0.3 从库# 两个权限:REPLICATION和SLAVE# *.* 所有的库和表GRANT REPLICATION SLAVE ON *.* TO 'slave1'@'172.20.0.3';# 刷新权限FLUSH PRIVILEGES; 在slave执行 1234CHANGE MASTER TO MASTER_HOST='172.20.0.2',MASTER_USER='slave1',MASTER_PASSWORD='123456',MASTER_LOG_FILE='mysql-bin.000003',MASTER_LOG_POS=964;# 开启主从start slave; 在从库执行 1show slave status; 主要看Slave_io_running =yes 主要看Slave_sql_running =yes 读写分离scheml.xmlschema标签1234567<schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100" randomDataNode="dn1"> <!-- auto sharding by id (long) --> <!--splitTableNames 启用<table name 属性使用逗号分割配置多个表,即多个表使用这个配置--> <table name="travelrecord,address" dataNode="dn1,dn2,dn3" rule="auto-sharding-long" splitTableNames ="true"/> <!-- <table name="oc_call" primaryKey="ID" dataNode="dn1$0-743" rule="latest-month-calldate" /> --></schema> name: 配置逻辑库的名称 checkSQLschema: 当执行select * from T_Account.user 为true时,mycat会把sql转成select * from user false 会报错 sqlMaxLimit:最大查询每页条数,如果sql指定了limit,就以sql为准,没有,就以配置为准 randomDataNode:这个先不用,先改成datanote='dn1' datanote标签123<dataNode name="dn1" dataHost="localhost1" database="db1" /><dataNode name="dn2" dataHost="localhost1" database="db2" /><dataNode name="dn3" dataHost="localhost1" database="db3" /> name:数据分片节点名称 dataHost:定义该数据分片节点属于哪台数据库主机 database:指定哪个数据库,db_java1234 datahost标签123456789<dataHost name="localhost1" maxCon="1000" minCon="10" balance="3" writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100"> <heartbeat>select user()</heartbeat> <!-- can have multi write hosts --> <writeHost host="hostM1" url="localhost:3306" user="root" password="123456"> </writeHost> <!-- <writeHost host="hostM2" url="localhost:3316" user="root" password="123456"/> --></dataHost> name:跟datanote标签的datahost对应 maxCon 指定每个读写实例连接池的的最大链接,write合readhost标签都会使用这个属性的值来实例化出连接池的最大连接数 min 最小的连接 balance: 0 不开启读写分离,所有读操作都发送到当前可用的writehost上 1 2 所有读操作都随机在writehost合readehost上分发 3 所有读请求随机分发到writehost合readhos 双组双从第二套主从1.在mysql添加中添加 123log-slave-update=1auto-increment-increment=2auto-increment-offset=1 2.把mysql文件复制一份mysql3 3.把mysql3的配置文件修修改这些配置 123# 主服务器ID,必须唯一server-id=5auto-increment-offset=2 4.再创建两个容器,m2 和 s2 master 和 master同步就是相互slave 测试读写分离插入语句 1insert into test valus(@@hostname)

2023-07-15
SpringBoot
定时器定时器的数值放到配置文件,如果使用@Scheduled(fixedDelay = 类中的变量)这种方式试过不行 123@Scheduled(fixedDelayString = "${fixedDelayString}")public void open() {} 1fixedDelayString=30000 RocketMQ下载源码或者二进制包 12345678# 源码安装# 解压文件unzip rocketmq-all-5.2.0-source-release.zipcd rocketmq-all-5.2.0-source-release/# 编译mvn -Prelease-all -DskipTests -Dspotbugs.skip=true clean install -U# 编译后的二进制的目录,配置文件在这里cd ./distribution 修改配置文件 1234567# 修改配置文件# 修改内存配置,虚拟机的内存太小了# vim ./bin/runserver.shJAVA_OPT="${JAVA_OPT} -server -Xms512m -Xmx521m -Xmn256g -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=320m"# vim ./bin/runbroker.shJAVA_OPT="${JAVA_OPT} -server -Xms512m -Xmx512m" 启动 123456# 第一步.启动Name Serversh bin/mqnamesrvThe Name Server boot success...# 第二步.启动Brokersh bin/mqbroker -n localhost:9876 The broker[%s, 172.30.30.233:10911] boot success... 测试发送消息 12345export NAMESRV_ADDR=localhost:9876# 发送消息sh bin/tools.sh org.apache.rocketmq.example.quickstart.Producer# 接收消息sh bin/tools.sh org.apache.rocketmq.example.quickstart.Consumer 关闭服务12sh bin/mqshutdown brokersh bin/mqshutdown namesrv 监控1234567# 记得checkout release-rocketmq-console-1.0.0分支git clone https://github.com/apache/rocketmq-externals/tree/release-rocketmq-console-1.0.0# 修改配置文件里的rocketMQ远程地址NameSvrAddrList = 192.168.254.124:9876# 或者rocketmq.config.namesrvAddr=192.168.254.124:9876 代码单个生产者消费者12345678910111213141516171819202122232425262728293031public class Provider { public static void main(String[] args) throws Exception { DefaultMQProducer producer = new DefaultMQProducer("group1"); producer.setNamesrvAddr("192.168.254.190:9876"); producer.start(); String msg = "什么鬼,咋莫名其妙就收到了"; Message message = new Message("topic1", "tag1", msg.getBytes()); SendResult sendResult = producer.send(message); System.out.printf("%s%n", sendResult); producer.shutdown(); }}public class Consumer { public static void main(String[] args) throws MQClientException { DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("group1"); consumer.setNamesrvAddr("192.168.254.190:9876"); consumer.subscribe("topic1", "*"); consumer.registerMessageListener((MessageListenerConcurrently) (list, consumeConcurrentlyContext) -> { // 业务逻辑 list.forEach(one->{ byte[] body = one.getBody(); System.out.println(new String(body)); }); return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; }); consumer.start(); System.out.println("消费者启起来了"); }} 单个生产者多个消费者相同的Topic,相同的Group-负载均衡默认就是负载均衡,多个消费者,每个人收一条信息,不重复 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253public class Provider { public static void main(String[] args) throws Exception { DefaultMQProducer producer = new DefaultMQProducer("group2"); producer.setNamesrvAddr("192.168.254.190:9876"); producer.start(); for (int i = 0; i < 10; i++) { String msg = "默认接受者是负载均衡"+i; Message message = new Message("topic2", "tag1", msg.getBytes()); SendResult sendResult = producer.send(message); System.out.printf("%s%n", sendResult); } producer.shutdown(); }}public class Consumer { public static void main(String[] args) throws MQClientException { // 谁来收 DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("group2"); // 从哪里收 consumer.setNamesrvAddr("192.168.254.190:9876"); // 监听队列 consumer.subscribe("topic2", "*"); // 监听器 consumer.registerMessageListener((MessageListenerConcurrently) (list, consumeConcurrentlyContext) -> { // 业务逻辑 list.forEach(one->{ byte[] body = one.getBody(); System.out.println(new String(body)); }); return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; }); consumer.start(); System.out.println("消费者启起来了"); }}// 第一个消费者默认接受者是负载均衡0默认接受者是负载均衡1默认接受者是负载均衡4默认接受者是负载均衡5默认接受者是负载均衡8默认接受者是负载均衡9// 第二个消费者默认接受者是负载均衡2默认接受者是负载均衡3默认接受者是负载均衡6默认接受者是负载均衡7 相同的Topic,相同的Group-轮训只是添加了消费者的一段代码:consumer.setMessageModel(MessageModel.BROADCASTING); 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051public class Provider { public static void main(String[] args) throws Exception { // 谁来发 DefaultMQProducer producer = new DefaultMQProducer("group3"); // 发给谁 producer.setNamesrvAddr("192.168.254.190:9876"); producer.start(); for (int i = 0; i < 10; i++) { String msg = "默认接受者是负载均衡"+i; Message message = new Message("topic3", "tag1", msg.getBytes()); SendResult sendResult = producer.send(message); System.out.printf("%s%n", sendResult); } producer.shutdown(); }}public class Consumer { public static void main(String[] args) throws MQClientException { DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("group3"); consumer.setNamesrvAddr("192.168.254.190:9876"); consumer.subscribe("topic3", "*"); consumer.setMessageModel(MessageModel.BROADCASTING); // 监听器 consumer.registerMessageListener((MessageListenerConcurrently) (list, consumeConcurrentlyContext) -> { // 业务逻辑 list.forEach(one->{ byte[] body = one.getBody(); System.out.println(new String(body)); }); return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; }); consumer.start(); System.out.println("消费者启起来了"); }}// 两个消费者都是收到相同的消息默认接受者是负载均衡0默认接受者是负载均衡1默认接受者是负载均衡2默认接受者是负载均衡3默认接受者是负载均衡4默认接受者是负载均衡5默认接受者是负载均衡6默认接受者是负载均衡7默认接受者是负载均衡8默认接受者是负载均衡9 不同的Topic,不同的Group 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788// 消费者1public class Consumer { public static void main(String[] args) throws MQClientException { DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("group4"); consumer.setNamesrvAddr("192.168.254.190:9876"); consumer.subscribe("topic4", "*"); // 监听器 consumer.registerMessageListener((MessageListenerConcurrently) (list, consumeConcurrentlyContext) -> { // 业务逻辑 list.forEach(one->{ byte[] body = one.getBody(); System.out.println(new String(body)); }); return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; }); consumer.start(); System.out.println("消费者启起来了"); }}// 消费者2public class Consumer { public static void main(String[] args) throws MQClientException { DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("group4"); consumer.setNamesrvAddr("192.168.254.190:9876"); consumer.subscribe("topic4", "*"); // 监听器 consumer.registerMessageListener((MessageListenerConcurrently) (list, consumeConcurrentlyContext) -> { // 业务逻辑 list.forEach(one->{ byte[] body = one.getBody(); System.out.println(new String(body)); }); return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; }); consumer.start(); System.out.println("消费者启起来了"); }}// 消费者3public class Consumer { public static void main(String[] args) throws MQClientException { DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("group4-1"); consumer.setNamesrvAddr("192.168.254.190:9876"); consumer.subscribe("topic4", "*"); // 监听器 consumer.registerMessageListener((MessageListenerConcurrently) (list, consumeConcurrentlyContext) -> { // 业务逻辑 list.forEach(one->{ byte[] body = one.getBody(); System.out.println(new String(body)); }); return ConsumeConcurrentlyStatus.CONSUME_SUCCESS; }); consumer.start(); System.out.println("消费者启起来了"); }}// 消费者1默认接受者是负载均衡2默认接受者是负载均衡3默认接受者是负载均衡6默认接受者是负载均衡7// 消费者2默认接受者是负载均衡0默认接受者是负载均衡1默认接受者是负载均衡4默认接受者是负载均衡5默认接受者是负载均衡8默认接受者是负载均衡9// 消费者3默认接受者是负载均衡0默认接受者是负载均衡1默认接受者是负载均衡2默认接受者是负载均衡3默认接受者是负载均衡4默认接受者是负载均衡5默认接受者是负载均衡6默认接受者是负载均衡7默认接受者是负载均衡8默认接受者是负载均衡9 Swagger123456<!-- 引入Swagger3依赖 --><dependency> <groupId>io.springfox</groupId> <artifactId>springfox-boot-starter</artifactId> <version>3.0.0</version></dependency> 1234567891011121314151617181920212223242526272829303132333435363738import io.swagger.annotations.ApiOperation;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import springfox.documentation.builders.ApiInfoBuilder;import springfox.documentation.builders.PathSelectors;import springfox.documentation.builders.RequestHandlerSelectors;import springfox.documentation.oas.annotations.EnableOpenApi;import springfox.documentation.service.ApiInfo;import springfox.documentation.service.Contact;import springfox.documentation.spi.DocumentationType;import springfox.documentation.spring.web.plugins.Docket;/** * Swagger配置类 */@EnableOpenApi@Configurationpublic class SwaggerConfig { @Bean public Docket docket(){ return new Docket(DocumentationType.OAS_30) .apiInfo(apiInfo()).enable(true) .select() //apis: 添加swagger接口提取范围 .apis(RequestHandlerSelectors.basePackage("com.example.study.controller")) .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) .paths(PathSelectors.any()) .build(); } private ApiInfo apiInfo(){ return new ApiInfoBuilder() .title("XX项目接口文档") .description("XX项目描述") .contact(new Contact("作者", "作者URL", "作者Email")) .version("1.0") .build(); }} 123# 原因是在springboot 2.6.0中将SpringMVC 默认路径匹配策略从AntPathMatcher# 更改为PathPatternParser,导致出错,解决办法是切换回原先的AntPathMatcherspring.mvc.pathmatch.matching-strategy=ant_path_matcher knife4j12345<dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>knife4j-spring-boot-starter</artifactId> <version>3.0.3</version></dependency> application.properties 12spring.mvc.pathmatch.matching-strategy=ant_path_matcherknife4j.enable: true 12345678910111213141516171819@RestController@RequestMapping("/test")@Api(tags = "测试2")// 排序@ApiSort(value = 1)public class TestController2 { @GetMapping("/3") @ApiOperation("3") public String method(){ return "hello wolrd"; } @GetMapping("/4") @ApiOperation("4") public String method2(){ return "hello wolrd"; }} Log4j2Log4j2日志所有级别级别从上到下 ,级别从低到高,报错(更重要)这种,级别最高,info,debug相对不怎么重要 all trace debug info warn error fail Log4j2.xml配置例子 抄网上的示例实际开发用的1234info级别输出到info.logwarn级别输出warn.logerror级别输出到error.logdebug级别输出到debug.log 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164<?xml version="1.0" encoding="UTF-8"?><Configuration> <!--<Configuration status="WARN" monitorInterval="30"> --> <properties> <property name="LOG_HOME">./service-logs</property> </properties> <Appenders> <!--*********************控制台日志***********************--> <Console name="consoleAppender" target="SYSTEM_OUT"> <!--设置日志格式及颜色--> <PatternLayout pattern="%style{%d{ISO8601}}{bright,green} %highlight{%-5level} [%style{%t}{bright,blue}] %style{%C{}}{bright,yellow}: %msg%n%style{%throwable}{red}" disableAnsi="false" noConsoleNoAnsi="false"/> </Console> <!--*********************文件日志***********************--> <!--all级别日志--> <RollingFile name="allFileAppender" fileName="${LOG_HOME}/all.log" filePattern="${LOG_HOME}/$${date:yyyy-MM}/all-%d{yyyy-MM-dd}-%i.log.gz"> <!--设置日志格式--> <PatternLayout> <pattern>%d %p %C{} [%t] %m%n</pattern> </PatternLayout> <Policies> <!-- 设置日志文件切分参数 --> <!--<OnStartupTriggeringPolicy/>--> <!--设置日志基础文件大小,超过该大小就触发日志文件滚动更新--> <SizeBasedTriggeringPolicy size="100 MB"/> <!--设置日志文件滚动更新的时间,依赖于文件命名filePattern的设置--> <TimeBasedTriggeringPolicy/> </Policies> <!--设置日志的文件个数上限,不设置默认为7个,超过大小后会被覆盖;依赖于filePattern中的%i--> <DefaultRolloverStrategy max="100"/> </RollingFile> <!--debug级别日志--> <RollingFile name="debugFileAppender" fileName="${LOG_HOME}/debug.log" filePattern="${LOG_HOME}/$${date:yyyy-MM}/debug-%d{yyyy-MM-dd}-%i.log.gz"> <Filters> <!--过滤掉info及更高级别日志--> <ThresholdFilter level="info" onMatch="DENY" onMismatch="NEUTRAL"/> </Filters> <!--设置日志格式--> <PatternLayout> <pattern>%d %p %C{} [%t] %m%n</pattern> </PatternLayout> <Policies> <!-- 设置日志文件切分参数 --> <!--<OnStartupTriggeringPolicy/>--> <!--设置日志基础文件大小,超过该大小就触发日志文件滚动更新--> <SizeBasedTriggeringPolicy size="100 MB"/> <!--设置日志文件滚动更新的时间,依赖于文件命名filePattern的设置--> <TimeBasedTriggeringPolicy/> </Policies> <!--设置日志的文件个数上限,不设置默认为7个,超过大小后会被覆盖;依赖于filePattern中的%i--> <DefaultRolloverStrategy max="100"/> </RollingFile> <!--info级别日志--> <RollingFile name="infoFileAppender" fileName="${LOG_HOME}/info.log" filePattern="${LOG_HOME}/$${date:yyyy-MM}/info-%d{yyyy-MM-dd}-%i.log.gz"> <Filters> <!--过滤掉warn及更高级别日志--> <ThresholdFilter level="warn" onMatch="DENY" onMismatch="NEUTRAL"/> </Filters> <!--设置日志格式--> <PatternLayout> <pattern>%d %p %C{} [%t] %m%n</pattern> </PatternLayout> <Policies> <!-- 设置日志文件切分参数 --> <!--<OnStartupTriggeringPolicy/>--> <!--设置日志基础文件大小,超过该大小就触发日志文件滚动更新--> <SizeBasedTriggeringPolicy size="100 MB"/> <!--设置日志文件滚动更新的时间,依赖于文件命名filePattern的设置--> <TimeBasedTriggeringPolicy interval="1" modulate="true" /> </Policies> <!--设置日志的文件个数上限,不设置默认为7个,超过大小后会被覆盖;依赖于filePattern中的%i--> <!--<DefaultRolloverStrategy max="100"/>--> </RollingFile> <!--warn级别日志--> <RollingFile name="warnFileAppender" fileName="${LOG_HOME}/warn.log" filePattern="${LOG_HOME}/$${date:yyyy-MM}/warn-%d{yyyy-MM-dd}-%i.log.gz"> <Filters> <!--过滤掉error及更高级别日志--> <ThresholdFilter level="error" onMatch="DENY" onMismatch="NEUTRAL"/> </Filters> <!--设置日志格式--> <PatternLayout> <pattern>%d %p %C{} [%t] %m%n</pattern> </PatternLayout> <Policies> <!-- 设置日志文件切分参数 --> <!--<OnStartupTriggeringPolicy/>--> <!--设置日志基础文件大小,超过该大小就触发日志文件滚动更新--> <SizeBasedTriggeringPolicy size="100 MB"/> <!--设置日志文件滚动更新的时间,依赖于文件命名filePattern的设置--> <TimeBasedTriggeringPolicy/> </Policies> <!--设置日志的文件个数上限,不设置默认为7个,超过大小后会被覆盖;依赖于filePattern中的%i--> <DefaultRolloverStrategy max="100"/> </RollingFile> <!--error及更高级别日志--> <RollingFile name="errorFileAppender" fileName="${LOG_HOME}/error.log" filePattern="${LOG_HOME}/$${date:yyyy-MM}/error-%d{yyyy-MM-dd}-%i.log.gz"> <!--设置日志格式--> <PatternLayout> <pattern>%d %p %C{} [%t] %m%n</pattern> </PatternLayout> <Policies> <!-- 设置日志文件切分参数 --> <!--<OnStartupTriggeringPolicy/>--> <!--设置日志基础文件大小,超过该大小就触发日志文件滚动更新--> <SizeBasedTriggeringPolicy size="100 MB"/> <!--设置日志文件滚动更新的时间,依赖于文件命名filePattern的设置--> <TimeBasedTriggeringPolicy/> </Policies> <!--设置日志的文件个数上限,不设置默认为7个,超过大小后会被覆盖;依赖于filePattern中的%i--> <DefaultRolloverStrategy max="100"/> </RollingFile> <!--json格式error级别日志--> <RollingFile name="errorJsonAppender" fileName="${LOG_HOME}/error-json.log" filePattern="${LOG_HOME}/error-json-%d{yyyy-MM-dd}-%i.log.gz"> <JSONLayout compact="true" eventEol="true" locationInfo="true"/> <Policies> <SizeBasedTriggeringPolicy size="100 MB"/> <TimeBasedTriggeringPolicy interval="1" modulate="true"/> </Policies> </RollingFile> </Appenders> <Loggers> <!-- 根日志设置 --> <Root level="debug"> <AppenderRef ref="allFileAppender" level="all"/> <AppenderRef ref="consoleAppender" level="debug"/> <AppenderRef ref="debugFileAppender" level="debug"/> <AppenderRef ref="infoFileAppender" level="info"/> <AppenderRef ref="warnFileAppender" level="warn"/> <AppenderRef ref="errorFileAppender" level="error"/> <AppenderRef ref="errorJsonAppender" level="error"/> </Root> <!--spring日志--> <Logger name="org.springframework" level="debug"/> <!--druid数据源日志--> <Logger name="druid.sql.Statement" level="warn"/> <!-- mybatis日志 --> <Logger name="com.mybatis" level="warn"/> <Logger name="org.hibernate" level="warn"/> <Logger name="com.zaxxer.hikari" level="info"/> <Logger name="org.quartz" level="info"/> <Logger name="com.andya.demo" level="debug"/> </Loggers></Configuration>123error级别 打印error.loginfo级别 打印info,error 日志到info.logdebug级别 打印info,error,debug日志到debug.log 基本结构是: Configuration properties 属性配置,可以在这里配置全局变量,可以在xml别的地方引入 Appenders 具体配置日志框架该如何收集日志的动作 Console 控制台输出 RollingFile 滚动日志文件 Loggers Root 理论上只有一个Root,level属性,是全局日志级别,如果AppenderRef没有配置level,就使用全局级别 AppenderRef 引用具体配置的动作,level 没有的话,就使用全局级别,就像CSS属性那样,标签里的属性优先级最高 Logger 单独配置一些类,level 没有的话,就使用全局级别,就像CSS属性那样,标签里的属性优先级最高 过滤器的具体使用方法 1234<Filters> <!--过滤掉info及更高级别日志--> <ThresholdFilter level="info" onMatch="DENY" onMismatch="NEUTRAL"/></Filters> level指定日志等级的阈值。 onMatch 定义当日志事件等级等于或高于 level 属性指定的等级时的行为。 ACCEPT:接受日志事件,让它通过过滤器。 DENY:拒绝日志事件,不让它通过过滤器。 NEUTRAL:对日志事件不做决定,继续应用其他的过滤规则。 onMismatch 定义当日志事件等级低于 level 属性指定的等级时的行为。 ACCEPT:接受日志事件,让它通过过滤器。 DENY:拒绝日志事件,不让它通过过滤器。 NEUTRAL:对日志事件不做决定,继续应用其他的过滤规则。 123456789if 日志的级别 >= level配置的级别 ACCEPT 接受日志 DENY 拒绝日志 NEUTRAL 对日志事件不做决定,继续应用其他的过滤规则if 日志的级别 < level配置的级别 ACCEPT 接受日志 DENY 拒绝日志 NEUTRAL 对日志事件不做决定,继续应用其他的过滤规则 还是没有搞清楚 onMismatch=”NEUTRAL” 的作用是怎样的 使用ChatGPT提供的demo,没有验证 info级别 记录到info.logerror级别 记录到error.loginfo级别和error级别 记录到dev.log 1234567891011121314151617181920212223242526272829<Configuration> <Appenders> <!-- Info 级别日志的 Appender --> <File name="InfoAppender" fileName="logs/info.log"> <ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/> <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %logger{36} - %msg%n"/> </File> <!-- Error 级别日志的 Appender --> <File name="ErrorAppender" fileName="logs/error.log"> <ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/> <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %logger{36} - %msg%n"/> </File> <!-- 同时记录 Info 和 Error 级别日志的 Appender --> <File name="DevAppender" fileName="logs/dev.log"> <Filters> <ThresholdFilter level="INFO" onMatch="ACCEPT" onMismatch="DENY"/> <ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="NEUTRAL"/> </Filters> <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %logger{36} - %msg%n"/> </File> </Appenders> <Loggers> <Root level="DEBUG"> <AppenderRef ref="InfoAppender"/> <AppenderRef ref="ErrorAppender"/> <AppenderRef ref="DevAppender"/> </Root> </Loggers></Configuration> 只是引入web框架只是引入web框架 1234<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency> 123456789101112@GetMapping("/test")public String test(){ // 格式化当前日期时间 String formattedDateTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); log.debug("当前日期时间:{}", formattedDateTime); log.info("当前日期时间:{}", formattedDateTime); log.error("当前日期时间:{}", formattedDateTime); log.warn("当前日期时间:{}", formattedDateTime); log.trace("当前日期时间:{}", formattedDateTime); return formattedDateTime;} 12345# 控制台打印INFO 64710 --- [nio-8080-exec-1] c.e.s.controller.IndexController : 当前日期时间:2024-03-03 21:37:40ERROR 64710 --- [nio-8080-exec-1] c.e.s.controller.IndexController : 当前日期时间:2024-03-03 21:37:40WARN 64710 --- [nio-8080-exec-1] c.e.s.controller.IndexController : 当前日期时间:2024-03-03 21:37:40# 为什么控制台没有打印 trace和debug日志

2022-11-19
Spring Security
Spring Security1234<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId></dependency> 登录业务 1234567891011121314151617181920212223242526272829303132333435363738@Servicepublic class LoginService { public static Map<String, RedisUser> map = new HashMap<>(); @Resource AuthenticationManager authenticationManager; public String login(SysUser sysUser) { // AuthenticationManager 进行用户认证 UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(sysUser.getUsername(), sysUser.getPassword()); Authentication authenticate = authenticationManager.authenticate(usernamePasswordAuthenticationToken); // 如果认证不通过,给出对应的提示 if (Objects.isNull(authenticate)) { throw new RuntimeException("登录失败"); } // 如果认证通过,使用username给jwt生成一个 RedisUser principal = (RedisUser) authenticate.getPrincipal(); map.put(principal.getUsername(), principal); return createJWT("1234567", 9990000, principal.getUsername()); } public void logout() { UsernamePasswordAuthenticationToken authentication = (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication(); RedisUser principal = (RedisUser) authentication.getPrincipal(); String username = principal.getUsername(); // 从redis里删除 map.remove(username); }} 123456789101112131415161718192021222324@RestController@RequestMapping("/")public class LoginController { @Resource LoginService loginService; @PostMapping("/user/login") public ResponseEntity<HashMap> login(@RequestParam("username") String username,@RequestParam("password") String password){ SysUser sysUser = new SysUser(); sysUser.setUsername(username); sysUser.setPassword(password); HashMap<String, Object> stringObjectHashMap = new HashMap<>(); stringObjectHashMap.put("token", loginService.login(sysUser)); return ResponseEntity.status(200).body(stringObjectHashMap); } @PostMapping("/user/logout") public ResponseEntity<String> logout(){ loginService.logout(); return ResponseEntity.ok("success"); }} 验证账户 12345678910111213141516171819202122232425262728293031323334353637383940414243import com.example.spirngsecutirylearn.pojo.RedisUser;import com.example.spirngsecutirylearn.pojo.SysUser;import org.springframework.security.core.userdetails.UserDetails;import org.springframework.security.core.userdetails.UserDetailsService;import org.springframework.security.core.userdetails.UsernameNotFoundException;import org.springframework.stereotype.Service;import java.util.ArrayList;@Servicepublic class TestService implements UserDetailsService { static String user = "anthony"; static String password = "$2a$10$yyR9WuT9JY/bpe1VPU0yguqlv0lWpgzTD9NEetf2.n8y7NXIa1rfm"; /** * DaoAuthenticationProvier 会调用这个方法查询用户,并且返回UserDetails对象 * @param username * @return * @throws UsernameNotFoundException */ @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // 从数据库查询用户信息 SysUser sysUser = new SysUser(); if (username.equals(user)) { sysUser.setUsername(user); sysUser.setPassword(password); }else { // 没有查到用户 throw new RuntimeException("没有查到用户信息"); } // 查询对应的权限信息 ArrayList<String> strings = new ArrayList<>();// strings.add("test"); strings.add("admin"); // 返回封装的信息 return new RedisUser(sysUser,strings); }} 缓存的用户对象 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263package com.example.spirngsecutirylearn.pojo;import lombok.AllArgsConstructor;import lombok.Data;import org.springframework.security.core.GrantedAuthority;import org.springframework.security.core.authority.SimpleGrantedAuthority;import org.springframework.security.core.userdetails.UserDetails;import java.util.Collection;import java.util.List;import java.util.stream.Collectors;@Datapublic class RedisUser implements UserDetails { SysUser sysUser; List<String> list; public RedisUser(SysUser sysUser, List<String> list) { this.sysUser = sysUser; this.list = list; } /** * 权限 * @return */ @Override public Collection<? extends GrantedAuthority> getAuthorities() { return list.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList()); } @Override public String getPassword() { return sysUser.getPassword(); } @Override public String getUsername() { return sysUser.getUsername(); } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; }} 认证失败的回调 123456789101112131415161718192021222324252627282930313233package com.example.spirngsecutirylearn.handler;import cn.hutool.json.JSONUtil;import org.springframework.http.ResponseEntity;import org.springframework.security.core.AuthenticationException;import org.springframework.security.web.AuthenticationEntryPoint;import org.springframework.stereotype.Component;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;@Componentpublic class AuthException implements AuthenticationEntryPoint { @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { ResponseEntity<String> responseEntity = ResponseEntity.ok().body("认证失败,请重新登录"); String str = JSONUtil.toJsonStr(responseEntity); /// 处理异常 extracted(response,str); } private static void extracted(HttpServletResponse response,String string) throws IOException { response.setStatus(200); response.setContentType("application/json"); response.setCharacterEncoding("utf-8"); response.getWriter().println(string); }} 授权失败的回调 123456789101112131415161718192021222324252627282930313233package com.example.spirngsecutirylearn.handler;import cn.hutool.json.JSONUtil;import org.springframework.http.ResponseEntity;import org.springframework.security.access.AccessDeniedException;import org.springframework.security.core.AuthenticationException;import org.springframework.security.web.AuthenticationEntryPoint;import org.springframework.security.web.access.AccessDeniedHandler;import org.springframework.stereotype.Component;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;@Componentpublic class AccessException implements AccessDeniedHandler { @Override public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { ResponseEntity<String> responseEntity = ResponseEntity.ok().body("权限不租"); String str = JSONUtil.toJsonStr(responseEntity); /// 处理异常 extracted(response,str); } private static void extracted(HttpServletResponse response,String string) throws IOException { response.setStatus(200); response.setContentType("application/json"); response.setCharacterEncoding("utf-8"); response.getWriter().println(string); }} 开启注解权限控制和自定义注解 1234567891011121314151617181920@RestController@RequestMapping("/test")public class TestController { @GetMapping("/hello") @PreAuthorize("hasAnyAuthority('test')") public ResponseEntity<String> hello(){ return ResponseEntity.ok("hello server"); } /** * 自定义校验权限 * @return */ @GetMapping("/hello2") @PreAuthorize("@anthony.hasAnyAuthority('test')") public ResponseEntity<String> hello2(){ return ResponseEntity.ok("hello server2"); }} 配置 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100package com.example.spirngsecutirylearn.cofig;import com.example.spirngsecutirylearn.filter.JwtFilter;import com.example.spirngsecutirylearn.handler.AccessException;import com.example.spirngsecutirylearn.handler.AuthException;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.http.HttpMethod;import org.springframework.security.authentication.AuthenticationManager;import org.springframework.security.config.annotation.authentication.configuration.EnableGlobalAuthentication;import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;import org.springframework.security.config.annotation.web.builders.HttpSecurity;import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;import org.springframework.security.config.http.SessionCreationPolicy;import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;import org.springframework.security.crypto.password.PasswordEncoder;import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;import org.springframework.security.web.authentication.logout.LogoutFilter;import javax.annotation.Resource;@Configuration@EnableGlobalMethodSecurity(prePostEnabled = true)public class SecurityConfig extends WebSecurityConfigurerAdapter { @Resource JwtFilter jwtFilter; @Resource AuthException authException; @Resource AccessException accessException; @Override @Bean public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Override protected void configure(HttpSecurity httpSecurity) throws Exception { httpSecurity // CSRF禁用,因为不使用session .csrf().disable() // 基于token,所以不需要session .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() // 过滤请求 .authorizeRequests() // 对于登录login 注册register 验证码captchaImage 允许匿名访问 .antMatchers("/user/login").anonymous() // 静态资源,permitAll 有没有登录都访问// .antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()// .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll() // 除上面外的所有请求全部需要鉴权认证 .anyRequest().authenticated();// .and()// .headers().frameOptions().disable(); // 添加Logout filter// httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler);// // 添加JWT filter httpSecurity.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);// // 添加CORS filter// httpSecurity.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class);// httpSecurity.addFilterBefore(corsFilter, LogoutFilter.class); // 添加自定义的认证和授权的自定义失败处理 httpSecurity.exceptionHandling().authenticationEntryPoint(authException).accessDeniedHandler(accessException); httpSecurity.cors(); } /** * 密码加密的规则 * @return */ @Bean public PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } /** * BCryptPasswordEncoder 的测试方法 * @param args */ public static void main(String[] args) { BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder(); // 加密 String encode = bCryptPasswordEncoder.encode("123456"); System.out.println(encode); // 解密 boolean matches = bCryptPasswordEncoder.matches("123456", encode); System.out.println(matches); }} 自定义注解 123456789101112131415161718@Component("anthony")public class TestExpress { public boolean hasAnyAuthority(String authority){ Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); RedisUser principal = (RedisUser) authentication.getPrincipal();// List<String> list = (List<String>)principal.getAuthorities(); List<SimpleGrantedAuthority> authorities = (List<SimpleGrantedAuthority>) principal.getAuthorities(); for (SimpleGrantedAuthority simpleGrantedAuthority : authorities) { if (simpleGrantedAuthority.getAuthority().equals(authority)) { return true; } } return false; }} 找Java包的路径 12# mac/usr/libexec/java_home -V

2023-09-24
代码片段
谷歌验证码12345<dependency> <groupId>com.warrenstrange</groupId> <artifactId>googleauth</artifactId> <version>1.5.0</version></dependency> 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253package utils;import com.warrenstrange.googleauth.GoogleAuthenticator;import com.warrenstrange.googleauth.GoogleAuthenticatorKey;import java.io.UnsupportedEncodingException;import java.net.URLEncoder;public class GoogleAuthUtil { /** * 生成key */ public static String generaKey(){ GoogleAuthenticator gAuth = new GoogleAuthenticator(); GoogleAuthenticatorKey key = gAuth.createCredentials(); return key.getKey(); } /** * 生成二维码 * @param key 生成的key * @param issuer app上title名字 服务名称 如: Google Github 印象笔记 * @param account app上title名字(括号里的) 用户账户 如: [email protected] 138XXXXXXXX */ public static String createGoogleAuthQRCodeData(String key, String account,String issuer) throws UnsupportedEncodingException { return String.format("otpauth://totp/%s?secret=%s&issuer=%s", URLEncoder.encode(issuer + ":" + account, "UTF-8").replace("+", "%20"), URLEncoder.encode(key, "UTF-8").replace("+", "%20"), URLEncoder.encode(issuer, "UTF-8").replace("+", "%20")); } public static void main(String[] args) throws UnsupportedEncodingException { String s = generaKey(); System.out.println(s); String googleAuthQRCodeData = createGoogleAuthQRCodeData(s, "anthony", "V16"); System.out.println(googleAuthQRCodeData); } /** * 检查 * @param key 生成的key * @param code 用户输入的面膜 */ public static boolean check(String key,Integer code){ GoogleAuthenticator gAuth = new GoogleAuthenticator(); // 根据用户密钥和用户输入的密码,验证是否一致。 boolean isCodeValid = gAuth.authorize(key, code); return isCodeValid; }} 图片验证码12345<dependency> <groupId>com.github.whvcse</groupId> <artifactId>easy-captcha</artifactId> <version>1.6.2</version> </dependency> 1234567891011121314151617181920@ApiOperation("获取验证码")@PostMapping("/getVerify")public Response<Map<String, String>> getVerify() { try { SpecCaptcha specCaptcha = new SpecCaptcha(130, 48, 6); specCaptcha.setFont(new Font("Default", Font.PLAIN, 32)); String verCode = specCaptcha.text().toLowerCase(); String key = UUID.randomUUID().toString(); Map<String, String> stringStringHash = new HashMap<>(); stringStringHash.put("uuid", key); stringStringHash.put("img", specCaptcha.toBase64()); key = "login_code:" + key; redisDao.setString(key, verCode); redisDao.expire(key, 2, TimeUnit.MINUTES); return Response.successData(stringStringHash); } catch (Exception e) { throw new RuntimeException("验证码生成失败", e); }} 华为云上传图片12345<dependency> <groupId>com.huaweicloud</groupId> <artifactId>esdk-obs-java-bundle</artifactId> <version>3.23.9.1</version></dependency> 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768import com.obs.services.ObsClient;import com.obs.services.ObsConfiguration;import com.obs.services.exception.ObsException;import com.obs.services.model.*;import java.io.IOException;import java.io.InputStream;public class SimpleMultipartUploadSample { // 通常一个链接是 https://桶的名字.obs.ap-southeast-1.myhuaweicloud.com/资源路径 private static final String endPoint = "https://obs.ap-southeast-1.myhuaweicloud.com"; private static final String ak = "xxxx"; private static final String sk = "xxxx"; private static ObsClient obsClient; private static String bucketName = "桶的名字"; public static void upload(String objectKey, InputStream file) throws IOException { ObsConfiguration config = new ObsConfiguration(); config.setSocketTimeout(30000); config.setConnectionTimeout(10000); config.setEndPoint(endPoint); try { obsClient = new ObsClient(ak, sk, config); System.out.println("Step 1: initiate multipart upload \n"); InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest(); request.setBucketName(bucketName); request.setObjectKey(objectKey); InitiateMultipartUploadResult result = obsClient.initiateMultipartUpload(request); System.out.println("Step 2: upload part \n"); UploadPartResult uploadPartResult = obsClient.uploadPart(bucketName, objectKey, result.getUploadId(), 1, file); System.out.println("Step 3: complete multipart upload \n"); CompleteMultipartUploadRequest completeMultipartUploadRequest = new CompleteMultipartUploadRequest(); completeMultipartUploadRequest.setBucketName(bucketName); completeMultipartUploadRequest.setObjectKey(objectKey); completeMultipartUploadRequest.setUploadId(result.getUploadId()); PartEtag partEtag = new PartEtag(); partEtag.setPartNumber(uploadPartResult.getPartNumber()); partEtag.setEtag(uploadPartResult.getEtag()); completeMultipartUploadRequest.getPartEtag().add(partEtag); obsClient.completeMultipartUpload(completeMultipartUploadRequest); } catch (ObsException e) { System.out.println("Response Code: " + e.getResponseCode()); System.out.println("Error Message: " + e.getErrorMessage()); System.out.println("Error Code: " + e.getErrorCode()); System.out.println("Request ID: " + e.getErrorRequestId()); System.out.println("Host ID: " + e.getErrorHostId()); } finally { if (obsClient != null) { try { obsClient.close(); } catch (IOException e) { } } } }}

2023-12-20
Tron
波场官方trident-java文档 m1 mac Android protobuf 遇到的问题 tron中文文档 trc20离线签名的demo,有trxdent-java,就不需要这个了,使用http的方式可以看看 trc20离线签名的demo,有trxdent-java,就不需要这个了,使用http的方式可以看看,另外一个demo 要继续学习tron可以看看 打包依赖 下载源文件trident-java 这里选择main分支 进入trident-java文件夹,可以看到有build.gradle,如果是mac系统,修改一下依赖 1234567891011121314protoc { // 原来的依赖 artifact = 'com.google.protobuf:protoc:3.12.0' // 修改之后的依赖 artifact = 'com.google.protobuf:protoc:3.12.0:osx-x86_64' } plugins { grpc { // 原来的依赖 artifact = 'io.grpc:protoc-gen-grpc-java:1.31.0' // 修改之后的依赖 artifact = 'io.grpc:protoc-gen-grpc-java:1.31.0:osx-x86_64' } } IDEA打包 打包之后,在每个子模块的build/libs/xxx-jar 下面的操作就可以跟着开发文档了,创建项目 pom.xml 123456789101112131415161718192021222324252627<dependency> <groupId>org.tron.trident</groupId> <artifactId>abi</artifactId> <version>0.8.0</version> <scope>system</scope> <systemPath>${project.basedir}/src/main/resources/jar/abi-0.8.0.jar</systemPath></dependency><dependency> <groupId>org.tron.trident</groupId> <artifactId>utils</artifactId> <version>0.8.0</version> <scope>system</scope> <systemPath>${project.basedir}/src/main/resources/jar/utils-0.8.0.jar</systemPath></dependency><dependency> <groupId>org.tron.trident</groupId> <artifactId>core</artifactId> <version>0.8.0</version> <scope>system</scope> <systemPath>${project.basedir}/src/main/resources/jar/core-0.8.0.jar</systemPath></dependency><dependency> <groupId>io.grpc</groupId> <artifactId>grpc-all</artifactId> <version>1.48.1</version></dependency> 查询余额的代码 123456789101112131415public static String privateKey = "私钥"; public static String apiKey = "apiKey";//波场申请 @Test public void getCount() { //生成密钥对 KeyPair keyPair = new KeyPair(privateKey); //地址转换 System.out.println(keyPair.toHexAddress()); ApiWrapper wrapper = ApiWrapper.ofMainnet(privateKey,apiKey); //账号查询 Account account = wrapper.getAccount("账户地址"); //查询余额 System.out.println(account.getBalance()); System.out.println(account.getCreateTime()); } 转账示例 123456789101112131415161718192021public static String transferTrc(String fromAddress, String toAddress, String amount, String contractAddress, String privateKey) { ApiWrapper wrapper = ApiWrapper.ofNile(privateKey); String ret = ""; try { //根据合约地址获取封装好的合约 Contract contract = wrapper.getContract(contractAddress); //创建一个TRC20合约对象 Trc20Contract token = new Trc20Contract(contract, fromAddress, wrapper); //获取想要转移的数量 BigInteger sunAmountValue = Convert.toSun(amount, Convert.Unit.TRX).toBigInteger(); //设置最大手续费 long maxTrx = Convert.toSun("17", Convert.Unit.TRX).longValue(); ret = token.transfer(toAddress, sunAmountValue.longValue(), 0, "", maxTrx); System.out.println("哈希:" + ret); } catch (Exception e) { System.out.println("转移失败:" + e.getMessage().toString()); } finally { wrapper.close(); } return ret;}
评论