Spring
spring概述:
Spring 是分层的 Java SE/EE 应用 full-stack 轻量级开源框架,以 IoC(Inverse Of Control:反转控制)
和 AOP(Aspect Oriented Programming:面向切面编程)为内核,提供了展现层 SpringMVC 和
持久层 Spring JDBC 以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多
著名的第三方框架和类库,逐渐成为使用最多的 Java EE 企业应用开源框架。
spring优势:
方便解耦,简化开发:通过 Spring 提供的 IoC 容器,可以将对象间的依赖关系交由 Spring 进行控制,避免硬编码所造成的过度程序耦合。
AOP 编程的支持:通过 Spring 的 AOP 功能,方便进行面向切面的编程
声明式事务的支持
方便程序的测试
方便集成各种优秀框架:提供了对各种优秀框架(Struts、Hibernate、Hessian、 Quartz等)的直接支持。
降低 JavaEE API 的使用难度:Spring 对 JavaEE API(如 JDBC、 JavaMail、远程调用等)进行了薄薄的封装层
Java 源码是经典学习范例:各种设计模式
程序的耦合:
耦合性(Coupling),也叫耦合度,是对模块间关联程度的度量。耦合的强弱取决于模块间接口的复杂性、调
用模块的方式以及通过界面传送数据的多少。模块间的耦合度是指模块之间的依赖关系,包括控制关系、调用关
系、数据传递关系。模块间联系越多,其耦合性越强,同时表明其独立性越差。
软件设计中通常用耦合度和内聚度作为衡量模块独立程度的标准。 划分模块的一个准则就是高内聚低耦合。
耦合分类:
(1) 内容耦合。当一个模块直接修改或操作另一个模块的数据时,或一个模块不通过正常入口而转入另
一个模块时,这样的耦合被称为内容耦合。
(2) 公共耦合。两个或两个以上的模块共同引用一个全局数据项,这种耦合被称为公共耦合。在具有大
量公共耦合的结构中,确定究竟是哪个模块给全局变量赋了一个特定的值是十分困难的。
(3) 外部耦合 。一组模块都访问同一全局简单变量而不是同一全局数据结构,而且不是通过参数表传
递该全局变量的信息,则称之为外部耦合。
(4) 控制耦合 。一个模块通过接口向另一个模块传递一个控制信号,接受信号的模块根据信号值而进
行适当的动作,这种耦合被称为控制耦合。
(5) 标记耦合 。若一个模块 A 通过接口向两个模块 B 和 C 传递一个公共参数,那么称模块 B 和 C 之间
存在一个标记耦合。
(6) 数据耦合。模块之间通过参数来传递数据,那么被称为数据耦合。数据耦合是最低的一种耦合形
式,系统中一般都存在这种类型的耦合,因为为了完成一些有意义的功能,往往需要将某些模块的输出数据作为另
一些模块的输入数据。
(7) 非直接耦合 。两个模块之间没有直接关系,它们之间的联系完全是通过主模块的控制和调用来实现的。
一个简单的例子解释依赖:
下面1、2同为创建数据库连接前的注册数据库驱动的操作。对于1,程序使用了new关键字,是编译器依赖,如果mysql的jar包不在,那么连编译都通不过。而2中,"com.mysql.jdbc.Driver"只是一个字符串,假如mysql的jar包不在,那么编译期是可以通过的,尽管有运行时异常(如果没有jar包的话)。
1、DriverManager.registerDriver(new com.mysql.jdbc.Driver());
2、Class.forName("com.mysql.jdbc.Driver");
我们实际开发中, 应该尽量做到:编译期不依赖,运行时才依赖。这就是所谓的解耦: 降低程序间的依赖关系。
解耦的思路:
第一步:使用反射来创建对象,而避免使用new关键字。
第二步:通过读取配置文件来获取要创建的对象全限定类名
使用工厂模式解耦dao和service
我们在开发中,有些依赖关系是必须的,有些依赖关系可以通过优化代码来解除的。比如下面的代码,业务层调用持久层,并且此时业务层在依赖持久层的接口和实现类。如果此时没有持久层实现类,编译将不能通过。 这种编译期依赖关系,应该在我们开发中杜绝。 我们需要优化代码解决。
public class AccountServiceImpl implements IAccountService {
private IAccountDao accountDao = new AccountDaoImpl();
}
我们可以写一个Bean对象的工厂来代替编译期的new关键字:
先解释一下bean:
Bean:在计算机英语中,有可重用组件的含义。
JavaBean:用java语言编写的可重用组件。
javabean > 实体类
它就是创建我们的service和dao对象的。
我们在BeanFactory需要的操作:
第一个:需要一个配置文件来配置我们的service和dao配置的内容:唯一标识=全限定类名(key=value)
第二个:通过读取配置文件中配置的内容,反射创建对象
我的配置文件可以是xml也可以是properties
public class BeanFactory {
//定义一个Properties对象
private static Properties props;
//定义一个Map,用于存放我们要创建的对象。我们把它称之为容器
private static Map<String,Object> beans;
//使用静态代码块为Properties对象赋值
static {
try {
//实例化对象
props = new Properties();
//使用类加载器,获取properties文件的流对象
InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
props.load(in);
//实例化容器
beans = new HashMap<String,Object>();
//取出配置文件中所有的Key
Enumeration keys = props.keys();
//遍历枚举
while (keys.hasMoreElements()){
//取出每个Key
String key = keys.nextElement().toString();
//根据key获取value
String beanPath = props.getProperty(key);
//反射创建对象
Object value = Class.forName(beanPath).newInstance();
//把key和value存入容器中
beans.put(key,value);
}
}catch(Exception e){
throw new ExceptionInInitializerError("初始化properties失败!");
}
}
/**
* 根据Bean的名称获取bean对象
* @param beanName
* @return
* */
public static Object getBean(String beanName){
Object bean = null;
try {
String beanPath = props.getProperty(beanName);
// System.out.println(beanPath);
bean = Class.forName(beanPath).newInstance();//每次都会调用默认构造函数创建对象
}catch (Exception e){
e.printStackTrace();
}
return bean;
}
}
这里我们在resource目录下写一个配置文件 bean.properties
accountService=cn.figo.service.impl.AccountServiceImpl
accountDao=cn.figo.dao.impl.AccountDaoImpl
这样工厂类通过读取bean配置文件,可以通过bean的名字得到bean的类路径,将二者存到一个容器map中,然后根据反射Class.forName(beanPath).newInstance(),在运行时创建一个对象。
这样在service中调用dao时,就可以通过BeanFactory来创建dao对象
public class AccountServiceImpl implements IAccountService {
// private IAccountDao accountDao = new AccountDaoImpl();
private IAccountDao accountDao = (IAccountDao)BeanFactory.getBean("accountDao");
// private int i = 1;
public void saveAccount(){
int i = 1;
accountDao.saveAccount();
System.out.println(i);
i++;
}
}
在需要service对象时,也可以通过同样的方式来获取对象:
/**
* 模拟一个表现层,用于调用业务层
*/
public class Client {
public static void main(String[] args) {
//IAccountService as = new AccountServiceImpl();
for(int i=0;i<5;i++) {
IAccountService as = (IAccountService) BeanFactory.getBean("accountService");
System.out.println(as);
as.saveAccount();
}
}
}
可以看出,我们这样创建的对象时多例的,每次创建service对象时,会调用代码中的
BeanFactory.getBean("accountService");
而getBean()方法中,又会使用代码
Class.forName(beanPath).newInstance();
每次创建对象时,都会调用默认构造函数创建对象,这样多例由于每次都会创建对象,效率低。因此我们需要考虑单例。
在运行时使用对象时,从beans中取就行了,这时beans中只有一个对象,因为在静态代码块中,类BeanFactory加载时就创建了一个service对象和一个dao对象,保存在了bean这个map中。
public static Object getBean(String beanName){
return beans.get(beanName);
}
这时就是单例的