1:为了降低Java开发的复杂性,Spring采取了4种关键策略
通过POJO的轻量级和最小侵入性编程;
通过依赖注入和面向接口实现松耦合;
基于切面和惯例进行声明式编程;
通过切面和模板减少样板式代码;
Spring竭力避免因自身的API而弄乱应用代码,不会强迫你实现Spring规范的接口或者继承Spring规范的类。相反,在基于Spring构建的应用中,他的类通常没有任何痕迹表明你使用了Spring。最坏的场景,一个类或许会使用Spring注解,但它依旧是POJO.
从这点看,配置还是注解都是各有利弊,配置虽然略显繁琐,但与代码独立。注解虽然便于快速开发,但还是具有一定耦合性。
通过POJO的轻量级和最小侵入性编程;
通过依赖注入和面向接口实现松耦合;
基于切面和惯例进行声明式编程;
通过切面和模板减少样板式代码;
Spring竭力避免因自身的API而弄乱应用代码,不会强迫你实现Spring规范的接口或者继承Spring规范的类。相反,在基于Spring构建的应用中,他的类通常没有任何痕迹表明你使用了Spring。最坏的场景,一个类或许会使用Spring注解,但它依旧是POJO.
从这点看,配置还是注解都是各有利弊,配置虽然略显繁琐,但与代码独立。注解虽然便于快速开发,但还是具有一定耦合性。
例1:
public class Student1 implements Person{ private Teacher teacher; public Student1() { this.teacher = new Teacher();//与Teacher紧耦合 } public void study() { teacher.teach(); } }
例2:
public class Student implements Person{ private Teacher teacher; public Student(Teacher teacher) { this.teacher = teacher; } public void study() { teacher.teach(); } }
这两个例子简略的展示了紧耦合所带来的限制,例2并没有自行的传递一个对象,而是在构造的时候将任务作为构造器参数传入,这是依赖注入的方式之一,既构造器注入
再扩展一下,假如例1中的teacher是体育老师,或者美术老师,而例2中的teacher是所有的老师,那么例1的劣势更将凸显出来,而例2却能够相应不同的teacher的操作,实现了没有与任何特定的teacher发生耦合,他并不关心是哪个具体的teacher,这就是DI【依赖注入】所带来的最大收益——松耦合。如果一个对象只通过接口【而不是具体实现或初始化过程】来表明依赖关系,那么这种依赖就能够在对象本身毫不知情的情况下,用不同的具体实现进行代替。
但是,耦合具体两面性。一方面,紧密耦合的代码难以测试,难以复用,难以理解,并且典型的表现出“打地鼠”的bug特征,修复完一个bug,将会出现其他bug。另一方面,一定程度的耦合又是必须的,完全没有耦合的代码什么也做不了。为了完成有实际意义的功能,不同的类必须以适当的方式进行交互,总而言之,耦合是必须的,但应当被小心谨慎的使用。
那么,怎样将特定的teacher传递给student呢,创建应用组件之间协作的行为通常称为装配。
Spring有多种装配bean的方式,采用XML是很常见的。以下为例展示:
再扩展一下,假如例1中的teacher是体育老师,或者美术老师,而例2中的teacher是所有的老师,那么例1的劣势更将凸显出来,而例2却能够相应不同的teacher的操作,实现了没有与任何特定的teacher发生耦合,他并不关心是哪个具体的teacher,这就是DI【依赖注入】所带来的最大收益——松耦合。如果一个对象只通过接口【而不是具体实现或初始化过程】来表明依赖关系,那么这种依赖就能够在对象本身毫不知情的情况下,用不同的具体实现进行代替。
但是,耦合具体两面性。一方面,紧密耦合的代码难以测试,难以复用,难以理解,并且典型的表现出“打地鼠”的bug特征,修复完一个bug,将会出现其他bug。另一方面,一定程度的耦合又是必须的,完全没有耦合的代码什么也做不了。为了完成有实际意义的功能,不同的类必须以适当的方式进行交互,总而言之,耦合是必须的,但应当被小心谨慎的使用。
那么,怎样将特定的teacher传递给student呢,创建应用组件之间协作的行为通常称为装配。
Spring有多种装配bean的方式,采用XML是很常见的。以下为例展示:
package com.spring.dao; public interface TeacherDao { public void teach(); }
Teacher实现类:
import com.spring.dao.TeacherDao; public class TeacherDaoImple implements TeacherDao{ public void teach() { System.out.println("老师在讲课"); } }
Student类:
import com.spring.dao.TeacherDao; public class StudentService{ private TeacherDao teacherDao; public void setTeacherDao(TeacherDao teacherDao) { this.teacherDao = teacherDao; } public void lean() { teacherDao.teach(); } }
XML配置:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="studentService" class="com.spring.service.StudentService"> <property name="teacherDao" ref="teacher"></property> </bean> <bean id="teacher" class="com.spring.dao.imple.TeacherDaoImple"></bean> </beans>
测试类:
import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.spring.service.StudentService; public class StudentTest { private ApplicationContext atc = new ClassPathXmlApplicationContext("Student.xml"); @Test public void test1(){ StudentService stu = atc.getBean(StudentService.class); stu.lean(); } }测试结果:
信息: Loading XML bean definitions from class path resource [Student.xml] 老师在讲课
Spring还支持使用JAVA来描述配置。其作用与XML是相同的。
import javax.annotation.Resource; import org.springframework.stereotype.Component; @Component(value="student") public class Student implements Person{ @Resource private Teacher teacher; public Student(Teacher teacher) { this.teacher = teacher; } public void study() { teacher.teach(); } }
尽管Student依赖与Teacher,但是他并不知道传递给他的是什么具体类型的teacher,也不知道它来自何方,只有Spring通过他的配置,能够了解这些组成部分是如何装配起来的,这样的话,就可以在不改变所依赖的类的情况下,修改依赖关系。
这里即通过Student.xml文件创建了Spring应用上下文。
这里即通过Student.xml文件创建了Spring应用上下文。
这里需要注意的是,Student类中需要有一个实际可使用的默认构造方法,如果声明了一个有参的构造方法,那么还需要声明一个无参的构造方法,否则无法自动创建bean。
有参的构造方法时,没有声明无参构造:
public class StudentService{ private TeacherDao teacherDao; public StudentService(TeacherDao teacherDao) { super(); this.teacherDao = teacherDao; } public void setTeacherDao(TeacherDao teacherDao) { this.teacherDao = teacherDao; } public void lean() { teacherDao.teach(); } }
测试类执行结果:
警告: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'studentService' defined in class path resource [Student.xml]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.spring.service.StudentService]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.spring.service.StudentService.<init>()
所以建议保持始终显式的声明一个无参构造:
public class StudentService{ private TeacherDao teacherDao; public StudentService() { super(); } public StudentService(TeacherDao teacherDao) { super(); this.teacherDao = teacherDao; } public void setTeacherDao(TeacherDao teacherDao) { this.teacherDao = teacherDao; } public void lean() { teacherDao.teach(); } }
测试类执行结果:
信息: Loading XML bean definitions from class path resource [Student.xml] 老师在讲课