学习Spring
在学习Spring 的时候,我们先不直接去学,我们先来模拟一下Spring的一些功能,然后再去学,这样就比好容易理解
1.建立一个项目。。。名字叫springTest_IOC(这样取名的目的是为了直接明了,看见标题就知道这个是干什么的)
2.创建了一个项目,第一步不是去考虑用什么设计模式或者其他,首先肯定要用一个实体,我们先来建立一个用户实体
package
com.bjsxt.model;
public
class
User {
private
String
userName
;
private
String
password
;
public
String getUserName() {
return
userName
;
}
public
void
setUserName(String userName) {
this
.
userName
= userName;
}
public
String getPassword() {
return
password
;
}
public
void
setPassword(String password) {
this
.
password
= password;
}
}
3.我们应该分层
一般有这几层:model(实体层),service(服务层,控制层),dao(数据库管理层,数据交换层),一般又把service分为service(应用接口层)和serviceImpl(应用实现层),dao也同样dao和daoImpl
源码:
package
com.test.dao;
import
com.test.model.User;
public
interface
UserDAO {
public
void
save(User u);
}
package
com.test.dao.Impl;
import
com.test.dao.UserDAO;
import
com.test.model.User;
public
class
UserDAOImpl
implements
UserDAO{
@Override
public
void
save(User u) {
System.
out
.println(
"save user"
);
}
}
package
com.test.service;
import
com.test.dao.UserDAO;
import
com.test.dao.Impl.UserDAOImpl;
import
com.test.model.User;
public
class
UserService {
private
UserDAO
userDAO
=
new
UserDAOImpl();
public
UserDAO getUserDA0() {
return
userDA0
;
}
public
void
setUserDAO(UserDAO userDAO) {
this
.
userDAO
= userDAO;
}
public
void
addUser(User user ){
userDAO
.save(user);
};
}
4.spring 读取文件时通过*.xml格式的文件来读取的
我们首先来看先怎样读取文件
xml文件
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
?>
<
result
>
<
value
>
<
no
>
A1234
</
no
>
<
addr
>
成都
</
addr
>
</
value
>
<
value
>
<
no
>
B1234
</
no
>
<
addr
>
上海
</
addr
>
</
value
>
</
result
>
我们可以通过四种方法来读取xml文件
这里的xml文件需要注意的是:如果你发现你复制一段xml文件到xml里面去,却没有变色,则说明这段xml文件有误
我这里先介绍一种JDOM
JDOM的目的是成为Java特定文档模型,它简化与XML的交互并且比使用DOM实现更快。由于是第一个Java特定模型,JDOM一直得到大力推广和促进。正在考虑通过“Java规范请求JSR-102”将它最终用作“Java标准扩展”。从2000年初就已经开始了JDOM开发。
JDOM与DOM主要有两方面不同。首先,JDOM仅使用具体类而不使用接口。这在某些方面简化了API,但是也限制了灵活性。第二,API大量使用了Collections类,简化了那些已经熟悉这些类的Java开发者的使用。
JDOM文档声明其目的是“使用20%(或更少)的精力解决80%(或更多)Java/XML问题”(根据学习曲线假定为20%)。JDOM对于大多数Java/XML应用程序来说当然是有用的,并且大多数开发者发现API比DOM容易理解得多。JDOM还包括对程序行为的相当广泛检查以防止用户做任何在XML中无意义的事。然而,它仍需要您充分理解XML以便做一些超出基本的工作(或者甚至理解某些情况下的错误)。这也许是比学习DOM或JDOM接口都更有意义的工作。
JDOM自身不包含解析器。它通常使用SAX2解析器来解析和验证输入XML文档(尽管它还可以将以前构造的DOM表示作为输入)。它包含一些转换器以将JDOM表示输出成SAX2事件流、DOM模型或XML文本文档。JDOM是在Apache许可证变体下发布的开放源码。
JDOM与DOM主要有两方面不同。首先,JDOM仅使用具体类而不使用接口。这在某些方面简化了API,但是也限制了灵活性。第二,API大量使用了Collections类,简化了那些已经熟悉这些类的Java开发者的使用。
JDOM文档声明其目的是“使用20%(或更少)的精力解决80%(或更多)Java/XML问题”(根据学习曲线假定为20%)。JDOM对于大多数Java/XML应用程序来说当然是有用的,并且大多数开发者发现API比DOM容易理解得多。JDOM还包括对程序行为的相当广泛检查以防止用户做任何在XML中无意义的事。然而,它仍需要您充分理解XML以便做一些超出基本的工作(或者甚至理解某些情况下的错误)。这也许是比学习DOM或JDOM接口都更有意义的工作。
JDOM自身不包含解析器。它通常使用SAX2解析器来解析和验证输入XML文档(尽管它还可以将以前构造的DOM表示作为输入)。它包含一些转换器以将JDOM表示输出成SAX2事件流、DOM模型或XML文本文档。JDOM是在Apache许可证变体下发布的开放源码。
代码:
import
java.util.List;
import
org.jdom2.Document;
import
org.jdom2.Element;
import
org.jdom2.input.SAXBuilder;
public
class
MyXMLReader {
public
static
void
main(String[] args)
throws
Exception {
long
lasting=System. currentTimeMillis();
SAXBuilder builder=
new
SAXBuilder();
Document doc=builder.build(MyXMLReader.
class
.getClassLoader().getResourceAsStream(
"text.xml"
));
//读取文件
Element foo=doc.getRootElement();
//获取根元素
System.
out
.println(foo);
List allChildren=foo.getChildren();
//获取所有的子元素
for
(
int
i=0;i<allChildren.size();i++){
System.
out
.println(
"号码"
+((Element)allChildren.get(i)).getChild(
"no"
).getText());
System.
out
.println(
"地址"
+((Element)allChildren.get(i)).getChild(
"addr"
).getText());
}
}
}
结果:
[Element: <result/>]
号码A1234
地址成都
号码B1234
地址上海
5.加测试,怎么样加喃?
第一,选择你要测试的类,右键-->New-->Juint test-->选择Junit 4--->finish---最好建立一个包叫测试包,然后把测试方法统一的放到里面去。
源码:
package
com.test.test;
import
static
org.junit.Assert.*;
import
org.junit.Test;
public
class
UserServiceTest {
@Test
public
void
testAddUser() {
fail(
"Not yet implemented"
);
}
}
6.写自己的测试方法(修改上面的测试方法)
public
class
UserServiceTest {
@Test
public
void
testAddUser() {
UserService service=
new
UserService();
User user=
new
User();
service.addUser(user);
}
}
结果:
save user
7.下面我们来模仿spring的xml配置
我们来建立一个beans.xml文件
源码:
<
beans
>
<!-- class 是全路径,包名加类名 -->
<
bean
id
=
"u"
class
=
"com.test.dao.impl.UserDAOImpl"
>
</
bean
>
<!--
<bean id="userService" class="com.test.service.UserService">
<property name="userDAO" bean="u"/>
</bean>
-->
</
beans
>
8.我们来建立一个spring的类,主要是为了模拟spring。在用这个类来实现一个beanFactory(bean工厂)接口。
我们在做这一步的时候也要修改UseeService的源码:
修改:
public
class
UserService {
//private UserDAO userDao=new UserDAOImpl();原来是这样的
private
UserDAO
userDao
;
//现在是这样,通过配置文件来实例化
public
UserDAO getUserDao() {
return
userDao
;
}
public
void
setUserDao(UserDAO userDao) {
this
.
userDao
= userDao;
}
public
void
addUser(User user ){
userDao
.save(user);
};
}
源码:
package
com.test.spring;
import
java.util.HashMap;
import
java.util.List;
import
java.util.Map;
import
org.jdom2.Document;
import
org.jdom2.Element;
import
org.jdom2.input.SAXBuilder;
import
com.test.util.MyXMLReader;
public
class
ClassPathXmlApplicationContext
implements
BeanFactory{
private
Map<String, Object>
beans
=
new
HashMap<String,Object>();
//容器,用户来装bean
public
ClassPathXmlApplicationContext()
throws
Exception{
SAXBuilder builder=
new
SAXBuilder();
Document doc=builder.build(MyXMLReader.
class
.getClassLoader().getResourceAsStream(
"beans.xml"
));
//读取文件
Element root=doc.getRootElement();
//获取根元素
System.
out
.println(root);
List allChildren=root.getChildren();
//获取所有的子元素
for
(
int
i=0;i<allChildren.size();i++){
Element element=(Element)allChildren.get(i);
//获取一个子元素
String id=element.getAttributeValue(
"id"
);
//子元素的属性id
String clazz=element.getAttributeValue(
"class"
);
System.
out
.println(id+
":"
+clazz);
Object o=Class.forName(clazz).newInstance();
//通过反射来产生一个实体
beans
.put(id, o);
//放入bean中
}
}
@Override
public
Object getBean(String name) {//从beans中获取一个元素
return
beans
.get(name);
}
}
public
class
UserServiceTest {
@Test
public
void
testAddUser()
throws
Exception{
// UserService service=new UserService();原来的做法
// User user=new User();
// service.addUser(user);
BeanFactory beans=
new
ClassPathXmlApplicationContext();
//实例化bean工厂
UserService service=
new
UserService();
UserDAO userDAO=(UserDAO)beans.getBean(
"u"
);
//通过工厂是实例化
service.setUserDao(userDAO);
User user=
new
User();
service.addUser(user);
}
}
运行:
结果:
[Element: <beans/>]
u:com.test.dao.impl.UserDAOImpl
save user
9.上面体现了一点SPring的好处,但是它的最好的还没体现,就是自动装配,上面的beans源码不是下面的一段被注释了嘛
<
beans
>
<!-- class 是全路径,包名加类名 -->
<
bean
id
=
"u"
class
=
"com.test.dao.impl.UserDAOImpl"
>
</
bean
>
<!--
<bean id="userService" class="com.test.service.UserService">
<property name="userDAO" bean="u"/>
</bean>
-->
</
beans
>
红色的一段就是自动装配
修改代码:
//这个类取这样的名字都是为了模仿spring,因为spring里面也有这样的类
public
class
ClassPathXmlApplicationContext
implements
BeanFactory{
private
Map<String, Object>
beans
=
new
HashMap<String,Object>();
//容器,用户来装bean
public
ClassPathXmlApplicationContext()
throws
Exception{
SAXBuilder builder=
new
SAXBuilder();
Document doc=builder.build(MyXMLReader.
class
.getClassLoader().getResourceAsStream(
"beans.xml"
));
//读取文件
Element root=doc.getRootElement();
//获取根元素
System.
out
.println(root);
List allChildren=root.getChildren();
//获取所有的子元素
for
(
int
i=0;i<allChildren.size();i++){
Element element=(Element)allChildren.get(i);
//获取一个子元素
String id=element.getAttributeValue(
"id"
);
//子元素的属性id
String clazz=element.getAttributeValue(
"class"
);
System.
out
.println(id+
":"
+clazz);
Object o=Class.forName(clazz).newInstance();
//通过反射来产生一个实体
beans
.put(id, o);
//放入bean中
//下面是才增加的方法
for(Element propertyElement:(List<Element>)element.getChildren("property" )){
String name=propertyElement.getAttributeValue("name" );//取出name属性userDAO
String bean=propertyElement.getAttributeValue("bean" );//u
Object beanObject= beans.get(bean); //取出K为bean的值
String methodName="set" +name.substring(0, 1).toUpperCase()+name.substring(1);//拼凑set方法名字
Method m=o.getClass().getMethod(methodName, beanObject.getClass().getInterfaces()[0]);//取出方法,传递参数(setUserDAO(UserDAOImpl实现了一个借口)就变成了setUserDAO(UserDAO))
m.invoke(o, beanObject);
}
}
}
@Override
public
Object getBean(String name) {
return
beans
.get(name);
}
}
public
class
UserServiceTest {
@Test
public
void
testAddUser()
throws
Exception{
// UserService service=new UserService();
// User user=new User();
// service.addUser(user);
BeanFactory beans=
new
ClassPathXmlApplicationContext();
//实例化bean工厂
// UserService service=new UserService();
// UserDAO userDAO=(UserDAO)beans.getBean("u");//通过工厂是实例化
//
// service.setUserDao(userDAO);
UserService service=(UserService)beans.getBean(
"userService"
);//现在直接工厂来实例化
User user=
new
User();
service.addUser(user);
}
}
运行:
结果:
[Element: <beans/>]
u:com.test.dao.impl.UserDAOImpl
userService:com.test.service.UserService
save user
上面就是模拟spring的IOC的注入功能
这里解释一下什么叫IOC(DI)
IOC:控制反转
例如:原来我们创建一个叫userDAO的实例是这样创建的
UserDAO userDAO=new UserDAO();
但是现在我们不用这样创建,我们只需要配置一下文件,让spring 来帮我们创建,本来是我们
控制 创建,但是现在是spring 来控制创建,我们把控制权交给了spring 所以叫控制反转
DI:依赖注入
比如:userService里面的userDAO是依赖容器帮我注入进来的。
这样做的好处:解耦和,就是修改代码很容易,只需要修改一下配置文件就OK了(也就是书上说的更加灵活)