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]
老师在讲课