单例模式你不一定懂
常见单例模式
首先我们来看看常见的单例模式都哪些实现方式:
饿汉式:
|
|
饿汉式中singleton在java虚拟机加载字节码的时候就已经被分配了空间所以就不存在线程安全问题。
懒汉式(延迟加载)
实现方式一
|
|
方式一虽然不存在线程安全问题,但是同步是在整个方法上进行的,效率低下,其实只需要第一次创建实例的时候需要同步,创建成功后就不需要再同步了。
实现方式二
12345678910111213141516
public class Singleton{ private volatile static Singleton singleton=null; private Singleton(){} public static Singleton getInstance(){ if(singleton==null){ synchronized(Singleton.class){ if(singleton==null){ singleton=new Singleton(); } } } return singleton; } }
要注意的一点是一定要volatile,当一个线程对singleton进行分配空间,初始化,调用构造函数的时候是在自己的线程工作栈中,其他线程是看不到这个过程的,Java虚拟机一切都是按引用的值复制的.向主存储区同步其实就是把线程工作存储区的这个已经构造好的对象有压缩堆地址值COPY给主存储区的那个变量。不加volatile,有可能发生另一个线程会获取到还没有实例化的singleton引用。
参考文献:
Double-checked Locking (DCL) and how to fix it
实现方式三
利用内部内延迟加载的特性。内部类也是个单独的Class文件,只有用到的时候才会动态加载
实现方式三
静态方法块
数据库索引以及数据库优化
聚集索引和非聚集索引
聚集索引存储记录是物理上连续存在,而非聚集索引是逻辑上的连续,物理存储并不连续。就像字段,聚集索引是连续的,a后面肯定是b,非聚集索引就不连续了,就像图书馆的某个作者的书,有可能在第1个货架上和第10个货架上。还有一个小知识点就是:聚集索引一个表只能有一个,而非聚集索引一个表可以存在多个。
索引建立的原则
定义主键的数据列一定要建立索引。
定义有外键的数据列一定要建立索引。
对于经常查询的数据列最好建立索引。
对于需要在指定范围内的快速或频繁查询的数据列;
经常用在WHERE子句中的数据列。
经常出现在关键字order by、group by、distinct后面的字段,建立索引。如果建立的是复合索引,索引的字段顺序要和这些关键字后面的字段顺序一致,否则索引不会被使用。
对于那些查询中很少涉及的列,重复值比较多的列不要建立索引。
对于定义为text、image和bit的数据类型的列不要建立索引。
对于经常存取的列避免建立索引
限制表上的索引数目。对一个存在大量更新操作的表,所建索引的数目一般不要超过3个,最多不要超过5个。索引虽说提高了访问速度,但太多索引会影响数据的更新操作。
对复合索引,按照字段在查询条件中出现的频度建立索引。在复合索引中,记录首先按照第一个字段排序。对于在第一个字段上取值相同的记录,系统再按照第二个字段的取值排序,以此类推。因此只有复合索引的第一个字段出现在查询条件中,该索引才可能被使用,因此将应用频度高的字段,放置在复合索引的前面,会使系统最大可能地使用此索引,发挥索引的作用。
SQL的优化
有大量重复值、且经常有范围查询(between, >,< ,>=,< =)和order by、group by发生的列,可考虑建立群集索引;
经常同时存取多列,且每列都含有重复值可考虑建立组合索引;
组合索引要尽量使关键查询形成索引覆盖,其前导列一定是使用最频繁的列。遇到以下情况,执行计划不会选择覆盖查询:
- select选择的字段中含有不在索引中的字段 ,也即索引没有覆盖全部的列。
- where 条件中不能含有对索引进行like的操作。
任何对列的操作都将导致表扫描,它包括数据库函数、计算表达式等等,查询时要尽可能将操作移至等号右边。
要善于使用存储过程,它使SQL变得更加灵活和高效。
存储过程减少了网络传输、处理及存储的工作量,且经过编译和优化,执行速度快,易于维护,且表的结构改变时,不影响客户端的应用程序
使用存储过程,视图,函数有助于减少应用程序中SQL复制的弊端,因为现在只在一个地方集中处理SQL
- IN、NOT IN 操作符
IN和EXISTS 性能有外表和内表区分的,但是在大数据量的表中推荐用EXISTS 代替IN 。
Not IN 不走索引的是绝对不能用的,可以用NOT EXISTS 代替
IS NULL 或IS NOT NULL操作
索引是不索引空值的,所以这样的操作不能使用索引,可以用其他的办法处理,例如:数字类型,判断大于0,字符串类型设置一个默认值,判断是否等于默认值即可<> 操作符(不等于)
不等于操作符是永远不会用到索引的,因此对它的处理只会产生全表扫描。 用其它相同功能的操作运算代替,如 a<>0 改为 a>0 or a<0 a<="">’’ 改为 a>’’ 0>用全文搜索搜索文本数据,取代like搜索,全文搜索始终优于like搜索
- 在查询中不要使用 select *
- where使用原则
- 第一个原则:在where子句中应把最具限制性的条件放在最前面。
- 第二个原则:where子句中字段的顺序应和索引中字段顺序一致。12345select field3,field4 from tb where field2 like 'R%' 快select field3,field4 from tb where field2 like '%R' 慢,不使用索引select field3,field4 from tb where field1>='sdf' 快 可以迅速定位索引select field3,field4 from tb where field1>'sdf' 慢
范式
第一范式:属性(字段)的原子性约束,要求属性具有原子性,不可再分割;
第二范式:记录的惟一性约束,要求记录有惟一标识,每条记录需要有一个属性来做为实体的唯一标识。
第三范式:属性(字段)冗余性的约束,即任何字段不能由其他字段派生出来,在通俗点就是:主键没有直接关系的数据列必须消除(消除的办法就是再创建一个表来存放他们,当然外键除外)
数据库设计的实用原则是:在数据冗余和处理速度之间找到合适的平衡点
满足范式的表一定是规范化的表,但不一定是最佳的设计。很多情况下会为了提高数据库的运行效率,常常需要降低范式标准:适当增加冗余,达到以空间换时间的目的
主键和外键的必要性
主键与外键的设计,在全局数据库的设计中,占有重要地位。 因为:主键是实体的抽象,主键与外键的配对,表示实体之间的连接。
主键:根据第二范式,需要有一个字段去标识这条记录,主键无疑是最好的标识,但是很多表也不一定需要主键,但是对于数据量大,查询频繁的数据库表,一定要有主键,主键可以增加效率、防止重复等优点。
主键的选择也比较重要,一般选择总的长度小的键,小的键的比较速度快,同时小的键可以使主键的B树结构的层次更少。
主键的选择还要注意组合主键的字段次序,对于组合主键来说,不同的字段次序的主键的性能差别可能会很大,一般应该选择重复率低、单独或者组合查询可能性大的字段放在前面。
外键:外键作为数据库对象,很多人认为麻烦而不用,实际上,外键在大部分情况下是很有用的,理由是:外键是最高效的一致性维护方法
数据库的一致性要求,依次可以用外键、CHECK约束、规则约束、触发器、客户端程序,一般认为,离数据越近的方法效率越高。
谨慎使用级联删除和级联更新,级联删除和级联更新作为SQL SERVER 2000当年的新功能,在2005作了保留,应该有其可用之处。我这里说的谨慎,是因为级联删除和级联更新有些突破了传统的关于外键的定义,功能有点太过强大,使用前必须确定自己已经把握好其功能范围,否则,级联删除和级联更新可能让你的数据莫名其妙的被修改或者丢失。从性能看级联删除和级联更新是比其他方法更高效的方法。
分割你的表,减小表尺寸
如果你发现某个表的记录太多,例如超过一千万条,则要对该表进行水平分割。水平分割的做法是,以该表主键的某个值为界线,将该表的记录水平分割为两个表。
如果你若发现某个表的字段太多,例如超过八十个,则垂直分割该表,将原来的一个表分解为两个表
字段设计原则
字段是数据库最基本的单位,其设计对性能的影响是很大的。需要注意如下:
A、数据类型尽量用数字型,数字型的比较比字符型的快很多。
B、 数据类型尽量小,这里的尽量小是指在满足可以预见的未来需求的前提下的。
C、 尽量不要允许NULL,除非必要,可以用NOT NULL+DEFAULT代替。
D、少用TEXT和IMAGE,二进制字段的读写是比较慢的,而且,读取的方法也不多,大部分情况下最好不用。
E、 自增字段要慎用,不利于数据迁移
减少持有行级锁的时间
比如对于一个秒杀系统,成功秒杀一件商品需要先在商品表里减库存再将购买记录插入到购买记录表:
减库存 update product set number=number-1 where product.id=id
插入购买记录:insert into sales(id,..,..,) value (...);
这两个sql是在事务管理下的,是原子的,减库存的行级锁需要等到插入操作执行完之后才能释放锁。所以整个过程都必须持有行级锁,由于减库存一个高热点的操作,如果持有锁的时间太长会降低秒杀的效率。为了降低持有锁的时间,可以将插入操作放到update前面。
MyBatis入门
配置
settings
useColumnLabel 使用列标签代替列名。不同的驱动在这方面会有不同的表现, 具体可参考相关驱动文档或通过测试这两种不同的模式来观察所用驱动的结果。
useGeneratedKeys 允许 JDBC 支持自动生成主键,需要驱动兼容。 如果设置为 true 则这个设置强制使用自动生成主键,尽管一些驱动不能兼容但仍可正常工作(比如 Derby)。
mapUnderscoreToCamelCase 是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射。
sqlMap
select
resultType 从这条语句中返回的期望类型的类的完全限定名或别名。注意如果是集合情形,那应该是集合可以包含的类型,而不能是集合本身。使用 resultType 或 resultMap,但不能同时使用。
resultMap
constructor - 类在实例化时,用来注入结果到构造方法中
idArg - ID 参数;标记结果作为 ID 可以帮助提高整体效能
arg - 注入到构造方法的一个普通结果
id – 一个 ID 结果;标记结果作为 ID 可以帮助提高整体效能
result – 注入到字段或 JavaBean 属性的普通结果
association – 一个复杂的类型关联;许多结果将包成这种类型
嵌入结果映射 – 结果映射自身的关联,或者参考一个
collection – 复杂类型的集
嵌入结果映射 – 结果映射自身的集,或者参考一个
discriminator – 使用结果值来决定使用哪个结果映射
case – 基于某些值的结果映射
嵌入结果映射 – 这种情形结果也映射它本身,因此可以包含很多相同的 元素,或者它可以参照一个外部的结果映射。
自动映射
当自动映射查询结果时,MyBatis会获取sql返回的列名并在java类中查找相同名字的属性(忽略大小写)。 这意味着如果Mybatis发现了ID列和id属性,Mybatis会将ID的值赋给id。
通常数据库列使用大写单词命名,单词间用下划线分隔;而java属性一般遵循驼峰命名法。 为了在这两种命名方式之间启用自动映射,需要将 mapUnderscoreToCamelCase设置为true。
With this result map both Blog and Author will be auto-mapped. But note that Author has an id property and there is a column named id in the ResultSet so Author’s id will be filled with Blog’s id, and that is not what you were expecting. So use the FULL option with caution.
Regardless of the auto-mapping level configured you can enable or disable the automapping for an specific ResultMap by adding the attribute autoMapping to it:
缓存
MyBatis 包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制。MyBatis 3 中的缓存实现的很多改进都已经实现了,使得它更加强大而且易于配置。
默认情况下是没有开启缓存的,除了局部的 session 缓存,可以增强变现而且处理循环 依赖也是必须的。要开启二级缓存,你需要在你的 SQL 映射文件中添加一行:
<cache/>
字面上看就是这样。这个简单语句的效果如下:
映射语句文件中的所有 select 语句将会被缓存。
映射语句文件中的所有 insert,update 和 delete 语句会刷新缓存。
缓存会使用 Least Recently Used(LRU,最近最少使用的)算法来收回。
根据时间表(比如 no Flush Interval,没有刷新间隔), 缓存不会以任何时间顺序 来刷新。
缓存会存储列表集合或对象(无论查询方法返回什么)的 1024 个引用。
缓存会被视为是 read/write(可读/可写)的缓存,意味着对象检索不是共享的,而 且可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
在线课堂搭建注意事项
RealTimeClassRoom修改的文件
将服务器环境搭建好之后需要修改以下几个文件:
property.properties
123456789101112131415161718//mysql链接,如果是远程数据库,修改为远程数据库的链接和端口mysql.url=jdbc\:mysql\://127.0.0.1\:3307/zzkt_ofbiz?&useUnicode\=true&characterEncoding\=utf-8&autoReconnect\=truemysql.drivername=com.mysql.jdbc.Drivermysql.username=rootmysql.password=****mysql2.url=jdbc\:mysql\://127.0.0.1\:3307/ofbiz?&useUnicode\=true&characterEncoding\=utf-8&autoReconnect\=truemysql2.username=rootmysql2.password=****//mongo的url也需要更改,用远程的也可以mongo.url=115.28.233.32mongo.port=27017mongo.dbname=RealTimeClassUseRecordCollectionName=UserOPRecordmongo.username=rootmongo.password=123456mongo.maxconn=5000mongo.minconn=100class.aheadoftime=30platform.properties
12345678//修改为服务器所在域名加上端口号DOMAIN=http\://sso.zzkt.com\:8080//不用修改PLAT_APP_NAME=eduportal///不用修改PLAT_PHOTO_NAME=app///修改为服务器所在域名CERTIFICATION_SERVER=sso.zzkt.comaodianyun.properties
1234567//修改为自己奥点运平台的参数AODIANYUN_LIST_URL=http\://openapi.aodianyun.com/v2/VOD.GetVodListACCESS_ID=ACCESS_KEY=APPNAME=ssktPUBLISH_URL=PLAY_URL=web.xml
123456789101112131415161718192021222324252627282930313233343536373839404142<filter><filter-name>CASFilter</filter-name><!-- <filter-class>org.jasig.cas.client.authentication.AuthenticationFilter</filter-class> --><filter-class>cn.edu.pku.ss.casfilter.AuthenticationFilterWithExcludeUrl</filter-class><init-param><param-name>casServerLoginUrl</param-name><param-value>http://sso.zzkt.com:8080/cas/login</param-value><!-- <param-value>http://online.sunhopeww.com:8080/cas/login</param-value> --><!--这里的server是服务端的IP --></init-param><init-param><!--本程序所在的URL --><param-name>serverName</param-name><param-value>http://sso.zzkt.com:8080</param-value><!-- <param-value>http://online.sunhopeww.com:8080</param-value> --></init-param><init-param><param-name>exclusions</param-name><param-value>/register/checkUsername.action,/search/famousCourseSearch.action,/studentUCenter/studentUCenter.action,/teacherUCenter/teacherUCenter.action,/register/doRegister.action,/register/register.action,/index/queryIndex.action,/iframe/queryIframeCourse.action,/courseShow/getCourse.action,/studentCourseManage/chooseCourseForWS.action,/sskt_logout/sskt_logout.action,/courseManage/approveCourse.action,/liveshow/apiLiveShowAction.action,/apicourse/getCourseInforByID.action,/apicourse/getCourseInforByTeacherID.action,/apicourse/getUnderWayCourseInfor.action,/yitiku/loginYiTiKu.action,/apicourse/getCourseListByCatagory.action,/apicourse/getCourseInforByProductID.action,/apiteacher/applyCourseAction.action,/user/getAllClassesInfoByTeacherID.action,/user/getAllStudentInfoByGradeIDAndClassID.action,/user/getAllStudentInfoByTeacherID.action</param-value></init-param></filter><!-- 该过滤器负责对Ticket的校验工作,必须启用它 --><filter><filter-name>CAS Validation Filter</filter-name><filter-class>org.jasig.cas.client.validation.Cas20ProxyReceivingTicketValidationFilter</filter-class><init-param><param-name>casServerUrlPrefix</param-name><param-value>http://sso.zzkt.com:8080/cas</param-value><!-- <param-value>http://online.sunhopeww.com:8080/cas</param-value> --></init-param><init-param><param-name>serverName</param-name><param-value>http://sso.zzkt.com:8080</param-value><!-- <param-value>http://online.sunhopeww.com:8080</param-value> --></init-param></filter><filter-mapping><filter-name>CAS Validation Filter</filter-name><url-pattern>*.action</url-pattern></filter-mapping>webroot/static-new/js/ownjs/course-detail.js
1234567//未登录点击购买课程按钮$("#notLoginPurchase").click(function(){var courseId = $("#courseId").val();alert("请您先登录");window.location.href='http://sso.zzkt.com:8080/cas/login?service=http://sso.zzkt.com:8080/RealTimeClassRoom/courseShow/getCourse.action?courseId='+courseId+'&&pamCasLoginId=1';// window.location.href='http://online.sunhopeww.com:8080/cas/login?service=http://online.sunhopeww.com:8080/RealTimeClassRoom/courseShow/getCourse.action?courseId='+courseId+'&&pamCasLoginId=1';});
CAS要修改的文件
web-inf/depolyerConfigContext.xml
123456789101112<bean id="primaryAuthenticationHandler"class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler"><property name="dataSource" ref="dataSource"></property><property name="sql" value="select password from sskt_user where userId=?"></property><property name="passwordEncoder" ref="passwordEncoder"/></bean><bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"><property name="driverClassName"><value>com.mysql.jdbc.Driver</value></property><property name="url"><value>jdbc:mysql://127.0.0.1:3307/zzkt_ofbiz</value></property><property name="username"><value>root</value></property><property name="password"><value>mysql123</value></property></bean>/cas/WEB-INF/view/jsp/default/ui/casLogoutView.jsp
12345function doSomeLogout(){//getLabelsGet("http://121.42.149.42:6301/logout");getLabelsGet("http://sso.zzkt.com:8080/RealTimeClassRoom/sskt_logout/sskt_logout.action");//getLabelsGet("http://121.42.149.42:6300/logout");}
CAS单点登录配置
生成证书
- 新建一个cas文件夹
用以下命令生成server.keystore
123456789101112131415jinyaoyuan:cas jinyaoyuan$ keytool -genkey -alias server -keyalg RSA -keypass mysql123 -storepass mysql123 -keystore server.keystore您的名字与姓氏是什么?[Unknown]: zzkt.sso.com您的组织单位名称是什么?[Unknown]: zzkt.sso.com您的组织名称是什么?[Unknown]: zzkt.sso.com您所在的城市或区域名称是什么?[Unknown]: beijing您所在的省/市/自治区名称是什么?[Unknown]: beijing该单位的双字母国家/地区代码是什么?[Unknown]: cnCN=zzkt.sso.com, OU=zzkt.sso.com, O=zzkt.sso.com, L=beijing, ST=beijing, C=cn是否正确?[否]: Y其中名字与姓氏一定要是cas服务器对应的域名,不能是IP
导出证书
12jinyaoyuan:cas jinyaoyuan$ keytool -export -alias server -storepass mysql123 -file server.cer -keystore server.keystore存储在文件 <server.cer> 中的证书导入证书
123456789101112131415161718192021jinyaoyuan:cas jinyaoyuan$ keytool -import -trustcacerts -alias server -file server.cer -keystore cacerts -storepass mysql123所有者: CN=zzkt.sso.com, OU=zzkt.sso.com, O=zzkt.sso.com, L=beijing, ST=beijing, C=cn发布者: CN=zzkt.sso.com, OU=zzkt.sso.com, O=zzkt.sso.com, L=beijing, ST=beijing, C=cn序列号: 4406dd99有效期开始日期: Sun Jun 12 16:19:09 CST 2016, 截止日期: Sat Sep 10 16:19:09 CST 2016证书指纹:MD5: 6F:AB:1B:6D:30:45:14:42:B9:09:4A:12:61:AA:42:CBSHA1: F8:2E:22:34:A4:E4:C6:8E:61:7D:90:AC:99:2A:08:AC:A3:4C:1A:1CSHA256: B5:17:93:49:60:F6:17:6C:0A:93:2A:5F:00:C5:E7:F7:33:B1:CE:D3:DC:A0:9F:C5:43:FF:68:CD:67:A2:F8:9A签名算法名称: SHA256withRSA版本: 3扩展:#1: ObjectId: 2.5.29.14 Criticality=falseSubjectKeyIdentifier [KeyIdentifier [0000: 9E F9 E0 9A 0A F4 E8 9C B4 24 C6 85 44 C3 30 F7 .........$..D.0.0010: 5F 24 B5 4C _$.L]]是否信任此证书? [否]: Y证书已添加到密钥库中完成上述步骤后cas文件下会生成server.keystore、server.cer、cacerts三个文件,将生成的cacerts文件替换到$JAVA_HOME/jre/lib/security/cacerts文件,替换前最好备份一下,这个文件有可能影响其他程序的正常运行。
-genkey
在用户主目录中创建一个默认文件”.keystore”,还会产生一个mykey的别名,mykey中包含用户的公钥、私钥和证书
-alias 产生别名
-keystore 指定密钥库的名称(产生的各类信息将不在.keystore文件中
-keyalg 指定密钥的算法
-validity 指定创建的证书有效期多少天
-keysize 指定密钥长度
-storepass 指定密钥库的密码
-keypass 指定别名条目的密码
-dname 指定证书拥有者信息
-list 显示密钥库中的证书信息
-v 显示密钥库中的证书详细信息
-export 将别名指定的证书导出到文件
-file 参数指定导出到文件的文件名
-delete 删除密钥库中某条目
-keypasswd 修改密钥库中指定条目口令
-import 将已签名数字证书导入密钥库
CAS配置
将生成的server.keystore放入%TOMCAT_HOME%\conf下。修改%TOMCAT_HOME%\conf\server.xml文件
<Connector port="8443" protocol="org.apache.coyote.http11.Http11Protocol" maxThreads="150" SSLEnabled="true" scheme="http" secure="true" clientAuth="false" sslProtocol="TLS" keystoreFile="./conf/server.keystore" keystorePass="mysql123"/>
mongoDB搭建与配置
下载及安装
1.1 在下面的地址选择适合自己的系统下载:
MongDB下载官网
1.2 将zip文件解压放到盘符的根目录(如C:或D:),为了方便建议文件夹命名尽量简短如(d:\mongodb)
1.3 创建数据库文件的存放位置,比如d:/mongodb/data/db。启动mongodb服务之前需要必须创建数据库文件的存放文件夹,否则命令不会自动创建,而且不能启动成功。
1.4 打开cmd(windows键+r输入cmd)命令行,进入D:\mongodb\bin目录(如图先输入d:进入d盘然后输入cd d:\mongodb\bin),
输入如下的命令启动mongodb服务:
D:/mongodb/bin>mongod --dbpath D:\mongodb\data\db
安装服务
2.1 在d:\mongodb\data下新建文件夹log(存放日志文件)并且新建文件mongodb.log
2.2 进入:D:/mongodb/bin输入
mongod -dbpath "d:\mongodb\data\db" -logpath "d:\mongodb\data\log\mongodb.log" -install -serviceName "MongoDB"(这里必须以管理员身份运行)
此时服务已经安装成功,运行
>net start mongodb (开启服务)
>net stop mongodb (关闭服务)
2.3 删除MongoDB Service
mongod -dbpath "d:\mongodb\data\db" -logpath "d:\mongodb\data\log\mongodb.log" -remove -serviceName "MongoDB";
基本操作
以下命令都是要进入mongodb shell才能操作,cmd进入:D:/mongodb/bin输入下mongo
3.1 新建数据库RealTimeClass
use RealTimeClass;
3.2 新建用户并给与权限
db.createUser({"user":"root","pwd":"123456","roles":[{role:"dbOwner",db:"RealTimeClass"}]});
注意一点,帐号是跟着库走的,所以在指定库里授权,必须也在指定库里验证(auth)。
|
|
转:Spring如何加载XSD文件(org.xml.sax.SAXParseException: Failed to read schema document错误的解决方法)
本文原文连接: http://blog.csdn.net/bluishglc/article/details/7596118 ,转载请注明出处!
有时候你会发现过去一直启动正常的系统,某天启动时会报出形如下面的错误:
org.xml.sax.SAXParseException: schema_reference.4: Failed to read schema document 'http://www.springframework.org/schema/beans/spring-beans-2.0.xsd', because 1) could not find the document; 2) the document could not be read; 3) the root element of the document is not <xsd:schema>.
很显然,spring xml配置文件中指定的xsd文件读取不到了,原因多是因为断网或spring的官网暂时无法连接导致的。 你可以通过在浏览器输入xsd文件的URL,如:http://www.springframework.org/schema/beans/spring-beans-2.0.xsd 进行确认。
关于这个问题,网上有两种常见的解决方法,第一种简单有效,但是工作量大,即:把所有spring配置文件中url形式的xsd路径转换成指向本地xsd文件的classpath形式的路径,例如:classpath:org/springframework/beans/factory/xml/spring-beans-2.5.xsd ,再有一种方法就是在本机搭建web服务器,按URL创建相应文件夹,放入对应xsd文件,在本机hosts文件中加入”127.0.0.1 www.springframework.org”.实际上,这两种方法都属于“歪打正着”式的方法,直正弄明白这一问题还需要从spring的XSD文件加载机制谈起。
首先:你必须知道一点:spring在加载xsd文件时总是先试图在本地查找xsd文件(spring的jar包中已经包含了所有版本的xsd文件),如果没有找到,才会转向去URL指定的路径下载。这是非常合理的做法,并不像看上去的那样,每次都是从站点下载的。事实上,假如你的所有配置是正确定的,你的工程完全可以在断网的情况下启动而不会报上面的错误。Spring加载xsd文件的类是PluggableSchemaResolver,你可以查看一下它的源码来验证上述说法。另外,你可以在log4j.xml文件中加入:
<logger name="org.springframework.beans.factory.xml">
<level value="all" />
</logger>
通过日志了解spring是何加载xsd文件的。
接下来,问题就是为什么spring在本地没有找到需要的文件,不得不转向网站下载。关于这个问题,其实也非常简单。在很多spring的jar包里,在META-INF目录下都有一个spring.schemas,这是一个property文件,其内容类似于下面:
http\://www.springframework.org/schema/beans/spring-beans-2.0.xsd=org/springframework/beans/factory/xml/spring-beans-2.0.xsd
http\://www.springframework.org/schema/beans/spring-beans-2.5.xsd=org/springframework/beans/factory/xml/spring-beans-2.5.xsd
http\://www.springframework.org/schema/beans/spring-beans-3.0.xsd=org/springframework/beans/factory/xml/spring-beans-3.0.xsd
....
实际上,这个文件就是spring关于xsd文件在本地存放路径的映射,spring就是通过这个文件在本地(也就是spring的jar里)查找xsd文件的。那么,查找不到的原因排除URL输入有误之外,可能就是声明的xsd文件版本在本地不存在。一般来说,新版本的spring jar包会将过去所有版本(应该是自2.0以后)的xsd打包,并在spring.schemas文件中加入了对应项,出现问题的情况往往是声明使用了一个高版本的xsd文件,如3.0,但依赖的spring的jar包却是2.5之前的版本,由于2.5版本自然不可能包含3.0的xsd文件,此时就会导致spring去站点下载目标xsd文件,如遇断网或是目标站点不可用,上述问题就发生了。
但是,在实现开发中,出现上述错误的几率并不高,最常见的导致这一问题的原因其实与使用了一个名为“assembly”的maven打包插件有关。很多项目需要将工程连同其所依赖的所有jar包打包成一个jar包,maven的assembly插件就是用来完成这个任务的。但是由于工程往往依赖很多的jar包,而被依赖的jar又会依赖其他的jar包,这样,当工程中依赖到不同的版本的spring时,在使用assembly进行打包时,只能将某一个版本jar包下的spring.schemas文件放入最终打出的jar包里,这就有可能遗漏了一些版本的xsd的本地映射,进而出现了文章开始提到的错误。如果你的项目是打成单一jar的,你可以通过检查最终生成的jar里的spring.schemas文件来确认是不是这种情况。而关于这种情况,解决的方法一般是推荐使用另外一种打包插件shade,它确实是一款比assembly更加优秀的工具,在对spring.schemas文件处理上,shade能够将所有jar里的spring.schemas文件进行合并,在最终生成的单一jar包里,spring.schemas包含了所有出现过的版本的集合!
以上就是spring加载XSD文件的机制和出现问题的原因分析。实际上,我们应该让我们工程在启动时总是加载本地的xsd文件,而不是每次去站点下载,做到这一点就需要你结合上述提及的种种情况对你的工程进行一番检查。
Spring AOP相关内容
Spring IOC相关内容
BeanFactory和ApplicationContext
- BeanFactory在初始化容器时并未实例化Bean,而是在第一次访问才实例化目标,ApplicationContext在初始化应用上下文时就实例化了所有单实例的Bean。
- ApplicationContext:XMlApplicationContext(ClassPathXmlApplicationContext、FileSystemApplicationContext)
AnnotationConfigApplicationContext
WebApplicationContext.
Bean的相关知识点
1.Bean的作用域
singleton:当一个bean的作用域为singleton, 那么Spring IoC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。
注意:Singleton作用域是Spring中的缺省作用域。要在XML中将bean定义成singleton,可以这样配置:
prototype:一个bean定义对应多个对象实例。Prototype作用域的bean会导致在每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)时都会创建一个新的bean实例。根据经验,对有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。
request:在一次HTTP请求中,一个bean定义对应一个实例;即每次HTTP请求将会有各自的bean实例, 它们依据某个bean定义创建而成。该作用域仅在基于web的Spring ApplicationContext情形下有效。
考虑下面bean定义:
<bean id="loginAction" class=cn.csdn.LoginAction" scope="request"/>
针对每次HTTP请求,Spring容器会根据loginAction bean定义创建一个全新的LoginAction bean实例, 且该loginAction bean实例仅在当前HTTP request内有效,因此可以根据需要放心的更改所建实例的内部状态, 而其他请求中根据loginAction bean定义创建的实例,将不会看到这些特定于某个请求的状态变化。 当处理请求结束,request作用域的bean实例将被销毁。
session:在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。
考虑下面bean定义:
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>
针对某个HTTP Session,Spring容器会根据userPreferences bean定义创建一个全新的userPreferences bean实例, 且该userPreferences bean仅在当前HTTP Session内有效。 与request作用域一样,你可以根据需要放心的更改所创建实例的内部状态,而别的HTTP Session中根据userPreferences创建的实例, 将不会看到这些特定于某个HTTP Session的状态变化。 当HTTP Session最终被废弃的时候,在该HTTP Session作用域内的bean也会被废弃掉。
global session:在一个全局的HTTP Session中,一个bean定义对应一个实例。典型情况下,仅在使用portlet context的时候有效。该作用域仅在基于web的Spring ApplicationContext情形下有效。
考虑下面bean定义:
<bean id="userPreferences" class="com.foo.UserPreferences" scope="globalSession"/>
global session作用域类似于标准的HTTP Session作用域,不过它仅仅在基于portlet的web应用中才有意义。Portlet规范定义了全局Session的概念,它被所有构成某个portlet web应用的各种不同的portlet所共享。在global session作用域中定义的bean被限定于全局portlet Session的生命周期范围内。
请注意:假如你在编写一个标准的基于Servlet的web应用,并且定义了一个或多个具有global session作用域的bean,系统会使用标准的HTTP Session作用域,并且不会引起任何错误。
2.Bean的命名
- Spring配置文件中不能出现两个相同的id的Bean,但是可以出现两个相同name的Bean,如果有多个相同name的Bean,通过getBean(BeanName)将返回最后申明的那个Bean.
3.Bean的注入方式
- Bean的注入方式: 1. 属性注入 2.构造函数注入(注意参数的顺序和类型)
- ref属性:bean,local,parent
4.Bean的自动装配
5.注解
- Component、Service、Controller、Repository、Import、Configuration、Bean、Autowired、ImportResource、PostConstruct、PreDestory
6.过滤表达式
7.Bean的配置方式有哪几种
- 基于xml的配置
- 基于注解的配置
- 基于Java类的配置