- 控制反转(loC,Inversion of Control),是一个概念,是一种思想。指的是将传统上由程序代码直接操纵的对象调用权交给容器,通过容器来实现对象的装配和管理。控制反转就是对对象控制权的转移,从程序代码本身反转到了外部容器。
- loC是一个概念,是一种思想,其实现方式多种多样。当前比较流行的实现方式有两种:依赖注入和依赖查找。依赖注入方式应用更为广泛。
1、依赖查找:(Dependency Lookup,DL,容器提供回调接口和上下文环境给组件,程序代码则徐亚哦提供具体的查找方式。比较经典的是依赖于JNDI服务接口(Java Naming and Directory Interface)的查找。
2、依赖注入:Dependency Injection,DI,程序代码不做定位查询,这些工作由容器自行完成。 - 依赖注入DI是指程序运行过程中,若需要调用另一个对象协助时,无须在代码中创建被调用者,而是依赖于外部容器,由外部容器创建后传递给程序。
- Spring的依赖注入对调用者与被调用者几乎没有任何要求,完全支持POJO之间依赖关系的管理。
- 依赖注入是目前最优秀的解耦方式。依赖注入让Spring的Bean之间以配置文件的方式组织在一起,而不是以硬编码的方式耦合在一起。
1 Spring的第一个程序
- 在普通三层架构的基础上,将程序修改为Spring框架程序。
- 举例:springDemo
1.1 导入jar包
- 首先,导入Spring程序开发的四个基本jar包。
- 其次,导入日志相关的Jar包。
- 在依赖库spring-framework-3.0.2.RELEASE-dependencies.zip解压目录下:\org.apache.commons\com.springsource.org.apache.commons.logging\1.1.1下的com.springsource.org.apache.commons.logging-1.1.1.jar文件。该文件只是日志记录的实现规范,并没有具体的实现,相当于的slf4j.jar作用。
- 这里日志的实现使用log4j,故还需要log4j.jar。在依赖库解压目录下:\org.apache.log4j\com.springsource.org.apache.log4j\1.2.15中的com.springsource.org.apache.log4j-1.2.15.jar。
- 最后,导入JUnit测试Jar包junit-4.9.jar即可。
- Spring基本编程,共需要7个Jar包即可。
1.2 定义接口和实体类
package com.eason.spring4.service; public interface IStudentService { void some(); }
package com.eason.spring4.service.impl; import com.eason.spring4.service.IStudentService; public class StudentServiceImpl implements IStudentService { @Override public void some() { System.out.println("执行some()方法"); } }
1.3 创建Spring配置文件
- spring配置文件的文件名可以随意,但是Spring建议的名称为applicationContext.xml。文件约束在%SPRING_HOME%\docs\spring-framework-reference\html\xsd-configuration.html文件中。
- 注意,Spring配置文件中使用的约束文件为xsd文件。若Eclipse中没有自动提示功能,则需要将约束要查找的域名地址指向本地的xsd文件。相关的xsd文件在Spring框架解压目录下的schema目录的相关子目录中。
- 这里需要的是spring-beans.xsd约束文件,故需要在beans子目录中查找相应版本的约束文件。
:用于定义一个实例对象。一个实例对应一个bean元素。 - id:该属性是Bean实例的唯一标识,程序通过id属性访问Bean,Bean与Bean之间的依赖关系也是通过id属性关联的。
- class:指定该Bean所属的类,注意这里只能是类,而不是接口。
1.4 定义测试类
@org.junit.Test public void test() { //获取容器 ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); //从容器中获取对象 IStudentService service = (IStudentService) context.getBean("studentService"); service.some(); }
1.4.1 ApplicationContext接口容器
- 若Spring配置文件存放在项目的类路径下,则使用ClassPathXmlApplicationContext实现类进行加载。
2、配置文件在本地目录中: - 若Spring配置文件存放在本地磁盘目录中,则使用FileSystemApplicationContext实现类进行加载。
3、配置文件在项目根目录下: - 若Spring配置文件存放在项目的根目录下,同样使用FileSystemXmlApplicationContext实现类进行加载。
- 下面是存放在项目根路径下的情况,该配置文件与src目录同级,而非在src中。
2 Bean的装配
- Bean的装配,即Bean对象的创建,容器根据代码要求创建Bean对象后再传递给代码的过程,称之为Bean的装配。
2.1 默认装配方式
- 代码通过getBean()方式从容器获取指定的Bean实例,容器首先会调用Bean类的无参构造器,创建空值的实例对象。
2.2 动态工厂Bean
- 有些时候,项目中需要工厂类来创建Bean实例,而不是像前面例子中似的,直接由Spring容器来装配Bean实例。使用工厂模式创建Bean实例,就会使得工厂类和要创建的Bean类耦合在一起。
2.2.1 将动态工厂Bean作为普通Bean使用
- 将动态工厂Bean作为普通Bean来使用是指,在配置文件中注册过动态工厂Bean后,测试类直接通过getBean()获取到工厂对象,再由工厂对象调用其相应方法创建相应的目标对象。配置文件中无需注册目标对象的Bean。因为目标对象的创建不由Spring容器来管理。
1、定义工厂类:public class ServiceFactory { public IStudentService getStudentService() { return new StudentServiceImpl(); } }
2、定义配置文件:
3、编写测试代码:
@org.junit.Test public void test() { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); //从Spring容器中获取factory ServiceFactory factory = (ServiceFactory) context.getBean("studentServiceFactory"); IStudentService studentService = factory.getStudentService(); studentService.some(); }
- 但是这样做的缺点是,不仅工厂类和目标类耦合到了一起,测试类和工厂类也耦合在了一起。
2.2.2 使用Spring的动态工厂Bean
- Spring对于使用动态工厂来创建的Bean,有专门的属性定义。factory-bean指定相应的工厂Bean,由factory-method指定创建所用的方法。此时配置文件中至少有两个Bean的定义:工厂类的Bean,与工厂类所要创建的目标类Bean。而测试类中不再需要获取工厂Bean对象,而是可以直接获取Bean对象,实现测试类和工厂类间的解耦。
1、修改配置文件:2、编写测试代码:
@org.junit.Test public void test() { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); //从Spring容器中获取factory IStudentService studentService = (IStudentService) context.getBean("studentService"); studentService.some(); }
2.3 静态工厂Bean
- 使用工厂模式中的静态工厂来创建实例Bean。
- 此时需要注意的是,静态工厂无需工厂实例,所以不需要定义静态工厂
。 - 而对于工厂所要创建的Bean,其不是由自己的类创建的,所以无需定义自己的类。但是其是由工厂类创建的,所以需要指定所用的工厂类。故class属性指定的是工厂类而非自己的类。当然,还需要通过factory-method属性指定工厂方法。
- 修改配置文件:
2.4 容器中Bean的作用域
- 当通过Spring容器创建一个Bean实例时,不仅可以完成Bean的实例化,还可以通过scope属性,为Bean指定特定的作用域。Spring支持5种作用域。
1、singleton:单态模式。即在整个Spring容器中,使用singleton定义的Bean将是单例的,只有一个实例,默认为单态的。
2、prototype:原型模式。即每次使用getBean方法获取的同一个的实例都是一个新的实例。
3、request:对于每次HTTP请求,都将会产生一个不同的Bean实例。
4、session:对于每次不同的HTTP session,都将产生一个不同的Bean实例。 - 注意:
1、对于scope的值request、session与global session,只有在Web应用中使用Spring时,该作用域才有效。
2、对于scope为singleton的单例模式,该bean是在容器被创建时即被装配好的。
3、对于scope为prototype的原型模式,Bean实例是在代码中使用该bean实例时才进行装配的。2.5 Bean后处理器
- Bean后处理器是一种特殊的Bean,容器中所有的Bean在初始化时,均会自动执行该类的两个方法。由于该Bean是由其他Bean自动调用执行的,不是程序员手工调用,故此Bean无需id属性。
- 需要做的是,在Bean后处理器类方法中,只要对Bean类与Bean类中的方法进行判断,就可以实现对指定的Bean的指定方法进行功能扩展和增强。方法返回的Bean对象,即是增强过的对象。
- 代码中需要自定义Bean后处理器类。该类就是实现了接口BeanPostProcessor的类。该接口中包含两个方法,分别在目标Bean初始化完毕之前和之后执行。它们的返回值为:功能被扩展或者增强后的Bean对象。
- Bean初始化完毕后一个标志:一个方法被执行。即当该方法被执行时,表示该Bean被初始化完毕。所以Bean后处理器中两个方法的执行,是在这个方法之前之后执行。这个方法在后面将会讲到。
- public Object postProcessBeforeInitialization(Ojbect bean, String beanId) throws BeansException,该方法会在目标bean初始化完毕之前由容器自动调用。
- public Object postProcessAfterInitialization(Object bean, String beanId) throws BeansException,第二个参数是该Bean实例的id属性值。若Bean没有id就是name属性值。
- 举例:程序中有一个业务接口IService,其由两个业务方法some()与other()。有两个Bean:StudentServiceImpl与TeacherServiceImpl,均实现了IService接口。要求对StudentServiceImpl的some()方法进行增强,输出其开始执行时间和执行结束时间。
public interface IService { void some(); void other(); }
//此类为待增强的Bean,即目标类 public class StudentServiceImpl implements IService { @Override public void some() { System.out.println(this.getClass().getSimpleName() + ",执行some()方法"); } @Override public void other() { System.out.println(this.getClass().getSimpleName() + ",执行other()方法"); } }
public class TeacherServiceImpl implements IService { @Override public void some() { System.out.println(this.getClass().getSimpleName() + ",执行some()方法"); } @Override public void other() { System.out.println(this.getClass().getSimpleName() + ",执行other()方法"); } }
public class MyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { //只对StudentServiceImpl实现类的some()方法进行加强,使用动态代理进行加强 if("studentService".equals(beanName)) { Object proxy = Proxy.newProxyInstance(bean.getClass().getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if("some".equals(method.getName())) { System.out.println("目标方法执行开始时间:" + System.currentTimeMillis()); //执行目标方法 Object result = method.invoke(bean, args); System.out.println("目标方法执行结束时间:" + System.currentTimeMillis()); return result; } return method.invoke(bean, args); } }); return proxy; } return bean; } @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { //即使不对Bean进行增强,也要是方法返回bean,不能够为默认的null值。 //否则将抛出NullPointerException异常 System.out.println("执行postProcessBeforeInitialization()"); return bean; } }
@org.junit.Test public void test() { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); IService studentService = (IService) context.getBean("studentService"); studentService.some(); studentService.other(); IService teacherService = (IService) context.getBean("teacherService"); teacherService.some(); teacherService.other(); }
2.6 制定Bean的生命始末
- 可以为Bean定制初始化后的生命行为,也可以为Bean定制销毁前的生命行为。
- 举例:首先,这些方法需要在Bean类中事先定义好,是方法名随意的public void方法。
public class StudentServiceImpl implements IService { @Override public void some() { System.out.println(this.getClass().getSimpleName() + ",执行some()方法"); } @Override public void other() { System.out.println(this.getClass().getSimpleName() + ",执行other()方法"); } public void setUp() { System.out.println("初始化完毕,执行后续工作..."); } public void tearDown() { System.out.println("对象将销毁,进行资源释放..."); } }
- 其次,在配置文件中
标签中增加如下属性:
1、init-method:指定初始化方法的方法名;
2、destroy-method:指定销毁方法的方法名; - 注意,若要看到Bean的destroy-method的执行结果,需要满足两个条件:
1、Bean为singleton,即单例;2、要确保容器关闭。接口ApplicationContext没有close()方法,但是其实现类有。所以,可以将ApplicationContext强转为其实现类对象,或者直接创建的就是实现类对象。@org.junit.Test public void test() { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); IService studentService = (IService) context.getBean("studentService"); studentService.some(); studentService.other(); //关闭容器对象 context.close(); }
2.7 Bean的生命周期
- Bean实例从创建到最后销毁,需要经过很多过程,执行很多生命周期方法。
1、调用无参构造器,创建实例对象。
2、调用参数的setter,为属性注入值。
3、若Bean实现了BeanNameAware接口,则会执行接口方法setBeanName(String beanId),使得Bean类可以获取其在容器中的id名称。
4、若Bean实现了BeanFactoryAware接口,则执行接口方法setBeanFactory(BeanFactory factory),使得Bean类可以获取到BeanFactory对象。
5、若定义并注册了Bean后处理器BeanPostProcessor,则执行接口方法postProcessBeforeInitialization()。
6、若Bean实现了InitializingBean接口,则执行接口方法afterPropertiesSet()。该方法在Bean的所有属性的set方法执行完毕后执行,是Bean初始化结束的标志,即Bean实例化结束。
7、若设置了init-method方法,则执行。
8、若定义并注册了Bean后处理器BeanPostProcessor,则执行接口方法postProcessAfterInitialization()。
9、执行业务方法。
10、若Bean实现了DisposableBean接口,则执行接口方法destroy()。
11、若设置了destroy-method方法,则执行。2.8
标签的id属性与name属性 - 一般情况下,命名
使用id属性,而不使用name属性。在没有id属性的情况下,name属性与id属性作用是相同的。但是当 中含有一些特殊字符时,就需要使用name属性了。 - id的命名需要满足XML对ID属性命名规范:必须以字母开头,可以包含字母、数字、下划线、连字符、冒号。
- naem属性值则可以包含各种字符。
3 基于XML的DI
3.1 注入分类
- Bean实例在调用无参构造器创建了空值对象后,就要对Bean对象的属性进行初始化。初始化时由容器自动完成的,称之为注入。根据注入方式的不同,常用的有两类:设值注入、构造注入。
- 还有另外一种,实现特定接口注入。由于这种方式采用侵入式编程,会污染代码,所以几乎不用。
3.1.1 设值注入
- 设值注入指的是,通过setter方法传入被调用者的实例。这种注入方式简单、直观。因而在Spring的依赖注入中大量使用。
public class Student { private String name; private int age; //setter and getter() @Override public String toString() { return "Student [name=" + name + ", age=" + age + "]"; } }
@org.junit.Test public void test() { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Student student = (Student) context.getBean("student"); System.out.println(student.toString()); }
-
当指定bean的某属性值为另一个bean的实例时,通过ref指定它们间的引用关系。ref的值必须为某bean的id值。
public class Student { private String name; private int age; private School school; @Override public String toString() { return "Student [name=" + name + ", age=" + age + ", school=" + school + "]"; } }
public class School { private String name; @Override public String toString() { return "School [name=" + name + "]"; } }
@org.junit.Test public void test() { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Student student = (Student) context.getBean("student"); System.out.println(student.toString()); }
- 对于其他对象的引用,除了
标签的ref属性外,还可以使用标签。 3.1.2 构造注入
- 构造注入是指,在构造调用者实例的同时,完成被调用者的实例化。即,使用构造器设置依赖关系。
public Student(String name, int age, School school) { super(); this.name = name; this.age = age; this.school = school; }
标签中用于指定参数的属性有: - name:指定参数名称;
- index:指明该参数对应着构造器的第几个参数,从0开始。不过,该属性不要也行,但是要注意,若参数类型相同,或者之间有包含关系,则需要保证赋值顺序要与构造器中的参数顺序一致。
-
另外,type属性可用于指定其类型。基本类型直接写类型关键字即可,非基本类型需要写全限定性类名。
3.2 集合属性注入
public class Student { private String name; //getter and setter() @Override public String toString() { return "student [name=" + name + "]"; } }
public class MyCollections { private Student[] students; private List
students2; private Set mySet; private Map myMap; private Properties properties; //getter and setter() } 北京大学 清华大学 1008611 @org.junit.Test public void test() { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); MyCollections cols = (MyCollections) context.getBean("myCollections"); Student[] students = cols.getStudents(); List
list = cols.getStudents2(); Set mySet = cols.getMySet(); Map myMap = cols.getMyMap(); Properties properties = cols.getProperties(); System.out.println(students[0]); System.out.println(list.size()); System.out.println(mySet.isEmpty()); System.out.println(myMap.get("weight")); System.out.println(properties.get("广州移动")); } 3.3 对于域属性的自动注入
- 对于域属性的注入,也可不在配置文件中显示的注入。可以通过
标签设置autowire属性值,为域属性进行隐式自动注入。根据自动注入判断标准的不同,可以分为两种: - byName:根据名称自动注入;
- byType:根据类型自动注入;
3.3.1 byName方式自动注入
- 当配置文件中被调用者Bean的id值与代码中调用者Bean类的属性名相同时,可使用byName方式,让容器自动将被调用者Bean注入给调用者Bean。容器是通过调用者的Bean类的属性名与配置文件的被调用者bean的id进行比较而实现自动注入的。
public class Student { private String name; private School mySchool; //setter and getter @Override public String toString() { return "Student [name=" + name + ", mySchool=" + mySchool + "]"; } }
public class School { private String name; //setter and getter() @Override public String toString() { return "School [name=" + name + "]"; } }
@org.junit.Test public void test() { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Student student = (Student) context.getBean("myStudent"); System.out.println(student); }
3.3.2 byType方式自动
- 使用byType方式自动注入,要求:配置文件中被调用者bean的class属性指定的类,要与代码中调用者Bean类的某域属性类型同源。即要么相同,要么有is-a关系(子类,或者是实现类)。但是这样的同源的被调用bean只能有一个。多于一个,容器就不知道该匹配哪一个了。
3.4 使用SPEL注入
- SPEL,Spring Expression Language,即Spring EL表达式语言。即,在Spring配置文件中为Bean的属性注入值时,可直接使用SPEL表达式计算的结果。SPEL表达式以#开头,后跟一对大括号。用法:
。其文档中有其用法举例。在Spring框架解压目录\docs\spring-framework-reference\htmlsingle\index.html中。Ctrl+F,对SpEL进行检索。第一个检索结果中9.4.1所链接的位置即有用法举例。 - 举例说明:
public class Student { private String studentName; private int studentAge; //setter and getter() @Override public String toString() { return "Student [studentName=" + studentName + ", studentAge=" + studentAge + "]"; } }
public class Person { private String personName; private int personAge; //setter and getter() //业务方法,计算年龄(若年龄大于25岁,则按25岁计算) public int computeAge() { return personAge > 25 ? 25 : personAge; } @Override public String toString() { return "Person [personName=" + personName + ", personAge=" + personAge + "]"; } }
@org.junit.Test public void test() { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Student student = (Student) context.getBean("myStudent"); System.out.println(student); }
- 其他用法:
1、,引用另一个bean。指定school的值为另一个Bean实例mySchool。
2、,使用指定属性,并使用其方法。指定schoolName值为mySchool的name属性值,并将其字母均转换成大写字母(toUpperCase()方法)。 3.5 使用内部Bean注入
- 若不希望代码直接访问某个bean,即,在代码中通过getBean方法获取该Bean实例,则可将该Bean的定义放入调用者bean定义的内部。
3.6 使用同类抽象Bean注入
- 当若干Bean实例同属于一个类,且这些实例的属性值有相同值时,可以使用抽象Bean,以此简化配置文件。
-
抽象Bean是用于让其他bean继承的,这个bean在Bean类中是不能通过getBean方法获取的。设置abstract属性为true来指明该bean为抽象bean,默认值为false。不过,该bean不为抽象bean时,也可被继承。只不过在应用中,用于被继承的bean一般为抽象bean。
public class Student { private String name; private double score; private String department; private String school; //setter and getter() @Override public String toString() { return "Student [name=" + name + ", score=" + score + ", department=" + department + ", school=" + school + "]"; } }
@org.junit.Test public void test() { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Student student1 = (Student) context.getBean("myStudent1"); System.out.println(student1); Student student2 = (Student) context.getBean("myStudent2"); System.out.println(student2); }
3.7 使用异类抽象Bean注入
-
当若干不同的类对象具有相同的属性,且其值也相同时,可使用异类抽象Bean。
public class Student { private String name; private double score; private String department; private String school; ·//setter and getter() @Override public String toString() { return "Student [name=" + name + ", score=" + score + ", department=" + department + ", school=" + school + "]"; } }
public class Teacher { private String name; private double score; private String department; private String school; //setter and getter() @Override public String toString() { return "Teacher [name=" + name + ", score=" + score + ", department=" + department + ", school=" + school + "]"; } }
@org.junit.Test public void test() { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Student student = (Student) context.getBean("student"); System.out.println(student); Teacher teacher = (Teacher) context.getBean("teacher"); System.out.println(teacher); }
3.8 为应用指定多个Spring配置文件
- 在实际应用里,随着应用规模的增加,系统中Bean数量也大量增加,导致配置文件变得非常庞大、臃肿。为了避免这种情况的产生,提高配置文件的可读性和可维护性,可以将Spring配置文件分解成多个配置文件。
3.8.1 平等关系的配置文件
- 将配置文件分解为地位平等的多个配置文件,并将所有配置文件的路径定义为一个String数组,将其作为容器初始化参数出现。其将与可变参的容器构造器匹配。
- 各配置文件间为并列关系,不分主次。
@org.junit.Test public void test() { String[] resourceFiles = {"com/eason/spring4/po/spring-base.xml", "com/eason/spring4/po/spring-student.xml", "com/eason/spring4/po/spring-teacher.xml"}; ApplicationContext context = new ClassPathXmlApplicationContext(resourceFiles); Student student = (Student) context.getBean("student"); System.out.println(student); Teacher teacher = (Teacher) context.getBean("teacher"); System.out.println(teacher); }
3.8.2 包含关系的配置文件
- 各配置文件中有一个总文件,总配置文件将各其他子文件通过
引入。在Java代码中只需要使用总配置文件对容器进行初始化即可。
@org.junit.Test public void test() { ApplicationContext context = new ClassPathXmlApplicationContext("com/eason/spring4/po/spring-total.xml"); Student student = (Student) context.getBean("student"); System.out.println(student); Teacher teacher = (Teacher) context.getBean("teacher"); System.out.println(teacher); }
- 也可以使用通配符。但是,此时要求父配置文件名不能满足所能匹配的格式,否则将出现循环递归包含。就本例而言,父配置文件不能匹配spring-*.xml的格式,即不能起名为spring-total.xml。
4 基于注解的DI
- 对于DI使用注解,将不再需要在Spring配置文件中声明Bean实例。Spring中使用注解,需要在原有Spring运行环境基础上再做一些改变,完成以下三个步骤。
1、导入AOP的Jar包。因为注解的后台实现用到了AOP编程。
2、需要更换配置文件头,即添加相应的约束。约束在%SPRING_HOME%\docs\spring-framework-reference\html\xsd-configuration.html文件中。
3、需要在Spring配置文件中配置组件扫描器,用于在指定的基本包中扫描注解。4.1 定义Bean@Component
- 需要在类上使用注解@Component,该注解的value属性用于指定该bean的id值。
@Component("myStudent") public class Student { }
- 另外,Spring还提供了3个功能基本和@Component等效的注解:
1、@Repository:用于对DAO实现类进行注解;2、@Service:用于对Service实现类进行注解;3、@Controller:用于对Controller实现类进行注解; - 之所以创建这三个功能与@Component等效的注解,是为了以后对其进行功能上的扩展,使它们不再等效。
4.2 Bean的作用域@Scope
- 需要在类上使用注解@Scope,其value属性用于指定作用域,默认为singleton。
@Scope("prototype") @Component("myStudent") public class Student { }
4.3 基本类型属性注入@Value
- 需要在属性上使用注解@Value,该注解的value属性用于指定要注入的值。
- 使用该注解完成属性注入时,类中无需setter。当然,若属性有setter,则也可将其加到setter上。
@Value("balabala") private String name; @Value("94.5") private double score; @Value("操作部") private String department;
4.4 按照类型注入域属性@Autowired
- 需要在域属性上使用注解@Autowired,该注解默认使用按照类型自动装配Bean的方式。
- 使用该注解完成属性注入时,类中无需setter。当然,若属性有setter,则可以将其加到setter上。
@Autowired private String school;
4.5 按照名称注入域属性@Autowired与@Qualifier
- 需要在域属性上联合使用注解@Autowired与@Qualifier。@Qualifier的value属性用于指定要匹配的Bean的id值。同样类中无需setter,也可加到setter上。
@Qualifier("mySchool") @Autowired private School school;
4.6 域属性注解@Resource
- Spring提供了对JSR-250规范中定义@Resource标准注解的支持。@Resource注解既可以按照名称匹配的Bean,也可以按照类型匹配Bean。使用该注解,要求JDK必须是6以及以上版本。
4.6.1 按照类型注入域属性
- @Resource注解若不带任何参数,则会按照类型进行Bean的匹配注入。
@Resource private School school;
4.6.2 按照名称注入域属性
- @Resource注解指定其name属性,则name的值即为按照名称进行匹配的Bean的id。
@Resource(name="mySchool") private School school;
4.7 Bean的生命始末@PostConstruct与@PreDestroy
- 在方法上使用@PostConstruct,与原来的init-method等效。在方法上使用@PreDestroy,与destroy-method等效。
@PostConstruct public void setUp() { System.out.println("Bean初始化后,执行..."); } @PreDestroy public void tearDown() { System.out.println("Bean销毁前,执行..."); }
4.8 使用JUnit4测试Spring
- 使用Spring的JUnit4对Spring代码进行测试,将不再需要在程序代码中直接写入创建Spring容器,以及从Spring容器中通过getBean()获取对象。这些工作将由JUnit4注解,配合着域属性的自动注入注解共同完成。
4.8.1 导入Jar包
-
除了junit-4.9.jar外,还需要导入Spring框架的解压目录中的Spring与JUnit4的整合Jar:spring-test-4.2.1.RELEASE.jar。
4.8.2 定义实体类
public class Student { private String name; private double score; private School school; //setter and getter() @Override public String toString() { return "Student [name=" + name + ", score=" + score + ", school=" + school + "]"; } }
public class School { private String name; //setter and getter() @Override public String toString() { return "School [name=" + name + "]"; } }
4.8.3 定义Spring配置文件
4.8.4 定义测试类
@RunWith(SpringJUnit4Cla***unner.class) @ContextConfiguration(locations="classpath:com/eason/spring4/application-context.xml") public class Test { @Autowired private Student student; @org.junit.Test public void test() { System.out.println(student); } }
- 在类头添加的两个注解:1、@RunWith(SpringJUnit4Cla***unner.class):拥有指定运行环境;2、@ContextConfiguration(locations=""):用于指定配置文件位置;
4.9 注解与XML共同使用
- 注解的好处是,配置方便,直观。但是其弊端也是显而易见的:以硬编码的方式写入到Java代码中,其修改时需要编译代码的。
- XML配置方式的最大好处是,对其所作修改,无需编译代码,只需重启服务器即可将新的配置加载。
- 若注解与XML同用,XML的优先级要高于注解。这样做的好处是,需要对某个Bean做修改时,只需要修改配置文件即可。当然,此时Bean类要有setter或者构造器。(注解的方式可以不需要setter或者构造器,直接编写在属性跟前即可。)