Spring动态切换多数据源解决方案

 

Spring动态配置多数据源,即在大型应用中对数据进行切分,并且采用多个数据库实例进行管理,这样可以有效提高系统的水平伸缩性。而这样的方案就会不同于常见的单一数据实例的方案,这就要程序在运行时根据当时的请求及系统状态来动态的决定将数据存储在哪个数据库实例中,以及从哪个数据库提取数据。

 

Spring配置多数据源的方式和具体使用过程。 
Spring对于多数据源,以数据库表为参照,大体上可以分成两大类情况: 
一是,表级上的跨数据库。即,对于不同的数据库却有相同的表(表名和表结构完全相同)。 
二是,非表级上的跨数据库。即,多个数据源不存在相同的表。

 

1、根据用户的选择,使用不同的数据源。

 

2、解决思路锁定:将sessionFactory的属性dataSource设置成不同的数据源,以达到切换数据源的目的。

 

3、问题产生:因为整个项目用的几乎都是单例模式,当多个用户并发访问数据库的时候,会产生资源争夺的问题。即项目启动时候,所有的bean都被装载到内存,并且每个bean都只有一个对象。正因为只有一个对象,所有的对象属性就如同静态变量(静态变量跟单例很相似,常用静态来实现单例)。整个项目系统的dataSource只有一个,如果很多用户不断的去改变dataSource的值,那必然会出现资源的掠夺问题,造成系统隐患。

 

4、多资源共享解决思路:同一资源被抢夺的时候,通常有两种做法,a、以时间换空间 b、以空间换时间。

 

5、线程同步机制就是典型的“以时间换空间”,采用排队稍等的方法,一个个等待,直到前面一个用完,后面的才跟上,多人共用一个变量,用synchronized锁定排队。   

 

6、“ThreadLocal”就是典型的“以空间换时间”,她可以为每一个人提供一份变量,因此可以同时访问并互不干扰。

 

7、言归正传:sessionFactory的属性dataSource设置成不用的数据源,首先不能在配置文件中写死,我们必须为她单独写一个类,让她来引用这个类,在这个类中再来判断我们到底要选择哪个数据源。

 

 

 

spring + mybatis 多数据源切换

 

DbContextHolder.java

 

 1 package com.easyway.stage.commons;  
 2   
 3 public class DbContextHolder  
 4 {  
 5   
 6     // ThreadLocal是线程安全的,并且不能在多线程之间共享。  
 7     private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();  
 8   
 9     public static void setDbType(String dbType)  
10     {  
11         contextHolder.set(dbType);  
12     }  
13   
14     public static String getDbType()  
15     {  
16         return ((String) contextHolder.get());  
17     }  
18   
19     public static void clearDbType()  
20     {  
21         contextHolder.remove();  
22     }  
23   
24 }  

 

 

MultiDataSource.java

 

 1 package com.easyway.stage.commons;  
 2   
 3 import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;  
 4   
 5 public class MultiDataSource extends AbstractRoutingDataSource  
 6 {  
 7   
 8     @Override  
 9     protected Object determineCurrentLookupKey()  
10     {  
11         return  DbContextHolder.getDbType();  
12     }  
13   
14 }  

 

 

Xml代码:

applicationContext.xml

 1 <?xml version="1.0" encoding="UTF-8"?>  
 2 <beans  
 3     xmlns="http://www.springframework.org/schema/beans"  
 4     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"  
 5     xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jdbc="http://www.springframework.org/schema/jdbc"  
 6     xmlns:context="http://www.springframework.org/schema/context"  
 7     xsi:schemaLocation="         
 8       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
 9       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd  
10       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd  
11       http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.0.xsd  
12       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">  
13           
14     <context:annotation-config/>  
15   
16     <!-- 数据源 -->    
17     <bean id="parentDataSource" class="com.alibaba.druid.pool.DruidDataSource">  
18         <!-- 配置初始化大小、最小、最大 -->  
19         <property name="initialSize" value="1" />  
20         <property name="maxActive" value="20" />   
21         <property name="minIdle" value="1" />  
22           
23         <!-- 配置获取连接等待超时的时间60s -->  
24         <property name="maxWait" value="60000" />   
25           
26         <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->  
27         <property name="timeBetweenEvictionRunsMillis" value="60000" />  
28         <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->    
29         <property name="minEvictableIdleTimeMillis" value="300000" />    
30          
31         <property name="validationQuery" value="SELECT 'x'" />    
32         <property name="testWhileIdle" value="true" />    
33         <property name="testOnBorrow" value="false" />    
34         <property name="testOnReturn" value="false" />    
35           
36         <!-- 打开PSCache,并且指定每个连接上PSCache的大小 -->  
37         <property name="poolPreparedStatements" value="true" />  
38         <property name="maxPoolPreparedStatementPerConnectionSize" value="20" />  
39           
40         <!-- 配置监控统计拦截的filters -->  
41         <property name="filters" value="wall,stat,slf4j" />  
42           
43         <!-- 对于长时间不使用的连接强制关闭  -->  
44         <property name="removeAbandoned" value="true" />  
45         <!-- 超过30分钟开始关闭空闲连接 -->  
46         <property name="removeAbandonedTimeout" value="1800" />   
47         <!-- 关闭abanded连接时输出错误日志 -->   
48         <property name="logAbandoned" value="true" />    
49     </bean>    
50     <bean id="local" parent="parentDataSource" init-method="init" destroy-method="close">      
51         <property name="url" value="${local.jdbc.url}" />    
52         <property name="username" value="${local.jdbc.username}" />    
53         <property name="password" value="${local.jdbc.password}" />      
54     </bean>  
55     <bean id="server" parent="parentDataSource" init-method="init" destroy-method="close">      
56         <property name="url" value="${jdbc.url}" />    
57         <property name="username" value="${jdbc.username}" />    
58         <property name="password" value="${jdbc.password}" />      
59     </bean>  
60   
61     <bean id="dataSource" class="com.autrade.stage.commons.MultiDataSource">  
62         <property name="targetDataSources">  
63             <map key-type="java.lang.String">  
64                 <entry value-ref="local" key="local"></entry>  
65                 <entry value-ref="server" key="server"></entry>  
66             </map>  
67         </property>  
68         <!-- 默认使用server的数据源 -->  
69         <property name="defaultTargetDataSource" ref="server"></property>  
70     </bean>    
71   
72     <!-- MyBatis -->  
73     <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">  
74         <property name="dataSource" ref="dataSource" />  
75         <property name="configLocation" value="classpath:resources/mybatis/myBatisConfig.xml" />  
76         <property name="mapperLocations" value="classpath:resources/mybatis/mapper/*.xml"/>  
77     </bean>  
78     <bean class="org.mybatis.spring.SqlSessionTemplate">  
79       <constructor-arg ref="sqlSessionFactory"/>  
80     </bean>  
81     <!-- MyBatis -->  
82       
83     <!-- 配置事务管理对象-->  
84     <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
85         <property name="dataSource" ref="dataSource"/>  
86     </bean>  
87     <!-- 将所有具有@Transactional注解的Bean自动配置为声明式事务支持 -->  
88     <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>  
89   
90     <!-- 自定义的拦截器 -->  
91     <bean id="methodAdvisor" class="com.easyway.app.interceptor.InjectorManager" />  
92   
93     <aop:config proxy-target-class="true">  
94         <aop:pointcut id="baseMethods"  
95             expression="execution(* com.easyway.app.service..*.*(..))" />  
96         <aop:advisor advice-ref="methodAdvisor" pointcut-ref="baseMethods" />  
97     </aop:config>  
98         
99 </beans>  

 

Test.java测试类

 

 1 package com.easyway.stage.test;  
 2   
 3 import javax.sql.DataSource;  
 4   
 5 import org.apache.ibatis.session.SqlSession;  
 6 import org.apache.ibatis.session.SqlSessionFactory;  
 7 import org.mybatis.spring.SqlSessionFactoryBean;  
 8 import org.springframework.context.ApplicationContext;  
 9 import org.springframework.context.support.ClassPathXmlApplicationContext;  
10 import org.springframework.core.io.FileSystemResource;  
11 import org.springframework.core.io.Resource;  
12   
13 import com.easyway.stage.commons.DbContextHolder;  
14   
15 public class Test  
16 {  
17   
18     /** 
19      * @param args 
20      */  
21     public static void main(String[] args)  
22     {  
23         ApplicationContext appContext = new ClassPathXmlApplicationContext("client-beans.xml");  
24   
25         DbContextHolder.setDbType("local");  
26         String res = "resources/mybatis/myBatisConfig.xml";  
27         DataSource datasource = (DataSource) appContext.getBean("dataSource");  
28   
29         SqlSessionFactoryBean bean = new SqlSessionFactoryBean();  
30         bean.setDataSource(datasource);  
31         Resource resource = new FileSystemResource(res);  
32         bean.setConfigLocation(resource);  
33         try  
34         {  
35             SqlSessionFactory sessionfactory = bean.getObject();  
36             SqlSession session = sessionfactory.openSession();  
37             User user = session.selectOne("com.easyway.mybatis.mapper.findOne");  
38             System.out.println(user.getName());  
39         }  
40         catch (Exception e)  
41         {  
42             e.printStackTrace();  
43         }  
44   
45         DbContextHolder.setDbType("server");  
46         String res1 = "resources/mybatis/myBatisConfig.xml";  
47         DataSource datasource1 = (DataSource) appContext.getBean("dataSource");  
48   
49         SqlSessionFactoryBean bean1 = new SqlSessionFactoryBean();  
50         bean1.setDataSource(datasource1);  
51         Resource resource1 = new FileSystemResource(res1);  
52         bean1.setConfigLocation(resource1);  
53   
54         try  
55         {  
56             SqlSessionFactory sessionfactory = bean.getObject();  
57             SqlSession session = sessionfactory.openSession();  
58             User user = session.selectOne("com.easyway.mybatis.mapper.findOne");  
59             System.out.println(user.getName());  
60         }  
61         catch (Exception e)  
62         {  
63             e.printStackTrace();  
64         }  
65   
66     }  
67   
68 }  

 

注意:当切换数据源时,需要在service层之外,如果需要在service层中切换非默认数据源,则不能开启事务,而且下次使用时,线程仍然绑定,此时若需要使用默认数据源,则需要显示的手动切换数据源,否则会出现xxx.table doesnt exist的问题。

 

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------

 

 

 

Spring动态配置多数据源,即在大型应用中对数据进行切分,并且采用多个数据库实例进行管理,这样可以有效提高系统的水平伸缩性。而这样的方案就会不同于常见的单一数据实例的方案,这就要程序在运行时根据当时的请求及系统状态来动态的决定将数据存储在哪个数据库实例中,以及从哪个数据库提取数据。

       Spring2.x以后的版本中采用Proxy模式,就是我们在方案中实现一个虚拟的数据源,并且用它来封装数据源选择逻辑,这样就可以有效地将数据源选择逻辑从Client中分离出来。Client提供选择所需的上下文(因为这是Client所知道的),由虚拟的DataSource根据Client提供的上下文来实现数据源的选择。

实现

具体的实现就是,虚拟的DataSource仅需继承AbstractRoutingDataSource实现determineCurrentLookupKey()在其中封装数据源的选择逻辑。
 
一、动态配置多数据源
1. 数据源的名称常量类:
[java] view plain copy
 
 print?
  1. /** 
  2.  * 动态配置多数据源 
  3.  * 数据源的名称常量类 
  4.  * @author LONGHUI_LUO 
  5.  * 
  6.  */  
  7. public class DataSourceConst {  
  8.     public static final String TEST="test";  
  9.     public static final String USER="User";  
  10. }  

2. 建立一个获得和设置上下文环境的类,主要负责改变上下文数据源的名称:
 
[java] view plain copy
 
 print?
  1. /** 
  2.  * 获得和设置上下文环境 主要负责改变上下文数据源的名称 
  3.  *  
  4.  * @author LONGHUI_LUO 
  5.  *  
  6.  */  
  7. public class DataSourceContextHolder {  
  8.     private static final ThreadLocal contextHolder = new ThreadLocal(); // 线程本地环境  
  9.   
  10.     // 设置数据源类型  
  11.     public static void setDataSourceType(String dataSourceType) {  
  12.         contextHolder.set(dataSourceType);  
  13.     }  
  14.   
  15.     // 获取数据源类型  
  16.     public static String getDataSourceType() {  
  17.         return (String) contextHolder.get();  
  18.     }  
  19.   
  20.     // 清除数据源类型  
  21.     public static void clearDataSourceType() {  
  22.         contextHolder.remove();  
  23.     }  
  24.   
  25. }  


3. 建立动态数据源类,注意,这个类必须继承AbstractRoutingDataSource,且实现方法determineCurrentLookupKey,该方法返回一个Object,一般是返回字符串:

 
[java] view plain copy
 
 print?
  1. /** 
  2.  * 建立动态数据源 
  3.  *  
  4.  * @author LONGHUI_LUO 
  5.  *  
  6.  */  
  7. public class DynamicDataSource extends AbstractRoutingDataSource {  
  8.   
  9.  protected Object determineCurrentLookupKey() {  
  10.   // 在进行DAO操作前,通过上下文环境变量,获得数据源的类型  
  11.   return DataSourceContextHolder.getDataSourceType();  
  12.  }  
  13.   
  14. }  


4. 编写spring的配置文件配置多个数据源

[html] view plain copy
 
 print?
  1.         <!-- 数据源相同的内容 -->  
  2. <bean  
  3.         id="parentDataSource"  
  4.         class="org.apache.commons.dbcp.BasicDataSource"  
  5.         destroy-method="close">  
  6.         <property  
  7.             name="driverClassName"  
  8.             value="com.microsoft.sqlserver.jdbc.SQLServerDriver" />  
  9.         <property name="username" value="sa" />  
  10.         <property name="password" value="net2com" />  
  11. </bean>  
[html] view plain copy
 
 print?
  1. <!-- start以下配置各个数据源的特性 -->  
  2. <bean parent="parentDataSource" id="testDataSource">   
  3.         <propertynamepropertyname="url" value="jdbc:sqlserver://localhost:1433;databaseName=test" />  
  4. </bean>   
  5. <bean parent="parentDataSource" id="UserDataSource">   
  6.             <property  
  7.             name="url"  
  8.             value="jdbc:sqlserver://localhost:1433;databaseName=User" />  
  9. </bean>   
[html] view plain copy
 
 print?
  1. <!-- end 配置各个数据源的特性 -->  


5. 编写spring配置文件配置多数据源映射关系

[html] view plain copy
 
 print?
  1. <bean class="com.xxxx.datasouce.DynamicDataSource" id="dataSource">  
  2.     <property name="targetDataSources">   
  3.        <map key-type="java.lang.String">   
  4.            <entry value-ref="testDataSource" key="test"></entry>  
  5.            <entry value-ref="UserDataSource" key="User"></entry>  
  6.        </map>   
  7.     </property>   
  8.     <property name="defaultTargetDataSource" ref="testDataSource" ></property>  
  9. </bean>  

        在这个配置中第一个property属性配置目标数据源,<map key-type="Java.lang.String">中的key-type必须要和静态键值对照类DataSourceConst中的值的类型相 同;<entry key="User" value-ref="userDataSource"/>中key的值必须要和静态键值对照类中的值相同,如果有多个值,可以配置多个< entry>标签。第二个property属性配置默认的数据源。

 

动态切换是数据源

[java] view plain copy
 
 print?
  1. DataSourceContextHolder.setDataSourceType(DataSourceConst.TEST);  


 

该方案的优势

       首先,这个方案完全是在spring的框架下解决的,数据源依然配置在spring的配置文件中,sessionFactory依然去配置它的dataSource属性,它甚至都不知道dataSource的改变。唯一不同的是在真正的dataSource与sessionFactory之间增加了一个MultiDataSource。
其次,实现简单,易于维护。这个方案虽然我说了这么多东西,其实都是分析,真正需要我们写的代码就只有MultiDataSource、SpObserver两个类。MultiDataSource类真正要写的只有getDataSource()和getDataSource(sp)两个方法,而SpObserver类更简单了。实现越简单,出错的可能就越小,维护性就越高。
最后,这个方案可以使单数据源与多数据源兼容。这个方案完全不影响BUS和DAO的编写。如果我们的项目在开始之初是单数据源的情况下开发,随着项目的进行,需要变更为多数据源,则只需要修改spring配置,并少量修改MVC层以便在请求中写入需要的数据源名,变更就完成了。如果我们的项目希望改回单数据源,则只需要简单修改配置文件。这样,为我们的项目将增加更多的弹性。

该方案的缺点

       没有能够解决多用户访问单例“sessionFactory”时共享“dataSource”变量,导致产生争抢“dataSource”的结果,本质类似于操作系统中的“生产者消费者”问题。因此当多用户访问时,多数据源可能会导致系统性能下降的后果。

转载于:https://www.cnblogs.com/wyb628/p/7240061.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/369874.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Appscan_web安全测试工具 (含修改启动浏览器的方法)

安全测试应该是测试中非常重要的一部分&#xff0c;但他常常最容易被忽视掉。 尽管国内经常出现各种安全事件&#xff0c;但没有真正的引起人们的注意。不管是开发还是测试都不太关注产品的安全。当然&#xff0c;这也不能怪我们苦B的“民工兄弟”。因为公司的所给我们的时间与…

传说之下地图素材_【开阔眼界】地图上的史记——名著轻松读

购买链接&#xff1a;https://j.youzan.com/Zns-38我读了无数次《史记》&#xff0c;每次都是读了开头没几页就放弃&#xff0c;文言文看不懂&#xff0c;翻译的白话文内容又太枯燥&#xff0c;现在居然有图画版的史记&#xff0c;还附有说明&#xff0c;真是太好了&#xff0c…

使用执行程序和ThreadPoolExecutor的Java线程池示例

线程池管理工作线程池&#xff0c;它包含一个队列&#xff0c;使任务等待执行。 线程池管理可运行线程的集合&#xff0c;工作线程从队列中执行可运行线程。 java.util.concurrent.Executors提供java.util.concurrent.Executor接口的实现&#xff0c;以在Java中创建线程池。 让…

H5_ 多媒体video,autio使用示例

1 <!DOCTYPE html>2 <html lang"en">3 <head>4 <meta charset"UTF-8">5 <title>AV</title>6 </head>7 <body>8 <div class"content">9 <p>注意:audio标签设置…

mysql中ibdata1过大的问题

ibdata1文件是什么&#xff1f; 答&#xff1a; ibdata1是一个用来构建innodb系统表空间的文件&#xff0c;这个文件包含了innodb表的元数据、撤销记录、修改buffer和双写buffer。如果file-per-table选项打开的话&#xff0c;该文件则不一定包含所有表的数据。当innodb_file_pe…

go int 转切片_「快学 Go 语言」第 4 课——低调的数组

数组就是一篇连续的内存&#xff0c;几乎所有的计算机语言都有数组&#xff0c;只不过 Go 语言里面的数组其实并不常用&#xff0c;这是因为数组是定长的静态的&#xff0c;一旦定义好长度就无法更改&#xff0c;而且不同长度的数组属于不同的类型&#xff0c;之间不能相互转换…

走向REST:将Tomcat与Spring和JAX-RS嵌入(Apache CXF)

这篇文章是上一篇文章的逻辑延续。 唯一的区别是我们将使用的容器&#xff1a;它将是我们的老兄Apache Tomcat而不是Jetty 。 令人惊讶的是&#xff0c;嵌入最新的Apache Tomcat 7非常容易&#xff0c;因此现在让我展示一下。 我不会重复完整的上一篇文章 &#xff0c;因为除了…

移动端下,点击a标签背景高亮问题的解决方法

在制作某个移动端项目的时候&#xff0c;发现当我们点击链接&#xff0c;input标签或者div盒子的时候&#xff0c;整个标签会出现颜色块并会闪烁一下&#xff0c;不仅影响美观&#xff0c;而且会极大降低了用户体验。  解决方案&#xff1a;-webkit-tap-highlight-color: tra…

Sublime Text 3 Mac常用快捷键与注意事项

大多数情况下容易忘记的快捷键&#xff0c;在此整理了一下。 编辑快捷键&#xff1a;cmd L&#xff1a;选择行&#xff08;重复按下将下一行加入选择&#xff09;&#xff1b;cmd D&#xff1a;选择词&#xff08;重复按下时多重选择相同的词进行多重编辑&#xff09;&#xff…

Python 基础知识整理-2

条件语句  if else 循环语句  while  for 循环之break continue break语句用来终止循环语句&#xff0c;即循环条件没有False条件或者序列还没被完全递归完&#xff0c;也会停止执行循环语句。 break语句用在while和for循环中。 continue 语句用来告诉Python跳过当前循…

js实现oss批量下载文件_js下载文件到本地各种方法总结

一、此方法火狐有些版本是不支持的window.location.href https://*****.oss-cn-**.aliyuncs.com/*********;二、为了解决火狐有些版本不支持,可以改成这种方式window.locationhttps://*****.oss-cn-**.aliyuncs.com/*********;三、该方法IE和火狐都可以,url表示要下载的文件路径…

Couchbase 101:从Java应用程序创建视图(MapReduce)

使用Couchbase 2.0开发新应用程序时&#xff0c;有时需要从代码中动态创建视图。 例如&#xff0c;在安装应用程序&#xff0c;编写一些测试时&#xff0c;可能会需要此方法&#xff1b;或者&#xff0c;在构建框架时&#xff0c;您也可以使用它&#xff0c;并希望动态创建视图…

函数的参数详解

# 函数的参数定义函数的时候&#xff0c;我们把参数的名字和位置确定下来&#xff0c;函数的接口定义就算完成了。 对于函数的调用者来说&#xff0c;只需要知道如何传递正确的参数&#xff0c;以及函数将返回什么样的值就够了 函数内政部的复杂逻辑被封装起来&#xff0c;调用…

gRPC之grpcurl

1、grpcurl grpcurl项目地址 &#xff1a;https://github.com/fullstorydev/grpcurl 一般情况下测试 gRPC 服务&#xff0c;都是通过客户端来直接请求服务端。如果客户端还没准备好的话&#xff0c;也可以使用 BloomRPC (https://appimage.github.io/BloomRPC/)这样的 GUI 客…

算法初步——two pointers

什么是 two pointers   以一个例子引入&#xff1a;给定一个递增的正整数序列和一个正整数 M&#xff0c;求序列中的两个不同位置的数 a 和 b&#xff0c;使得它们的和恰好为 M&#xff0c;输出所有满足条件的方案。 本题的一个最直观的想法是&#xff0c;使用二重循环枚举序…

H5 _拖放使用

1 <!DOCTYPE html>2 <html lang"en">3 <head>4 <meta charset"UTF-8">5 <title>拖放API</title>6 <style>7 [iddragme]{8 width: 100px;9 height: 100px; 10 …

从XaaS到Java EE – 2012年哪一种该死的云最适合我?

您是否曾经想过要让Java EE在某个地方启动和运行需要什么&#xff1f; 是的 多年。 从托管我自己的主机开始&#xff0c;转到一些托管产品 &#xff0c;最后偶然发现了PaaS运动。 老实说&#xff0c;我并没有太认真。 我只是想把我的东西放到某个地方&#xff0c;而不在乎解决…

正方体最快最简单画_素描新手入门第一幅画可不只是“正方体”

很多素描教程都把正方体作为入门第一幅画学习内容。这种现象也成了约定俗成的规矩但是&#xff0c;学过画画的人大概都知道有很多人画了多年石膏几何形、静物、人头像甚至半身像全身像。到最后落得只会画这些学过的东西。这就说明学习出了问题。绘画练习一定要弄清楚每个物体练…

重写Alert和confirm方法去除地址显示

//重写alert方法&#xff0c;去掉地址显示window.alertfunction(name){var iframedocument.createElement("IFRAME");iframe.style.display"none";iframe.setAttribute("src",data:text/plain,); document.documentElement.appendChild(iframe);…

VS2015配置内核WDK7600环境,32位下.

VS2015配置内核WDK7600环境,32位下. 学习内核驱动的编写,就要会配置环境.不然总是用记事本编写.比较不方便. 环境配置如下. 1.首先下载WDK7600, 课堂资料代码中已经上传.链接&#xff1a;https://pan.baidu.com/s/1o9PjpUU 密码&#xff1a;k5sp 2.VS2015下载. 这个网络上有很多…