Spring 框架的最核心功能之一是 DI (Dependency Injection), 也就是依赖注入。java
DI 的底层原理是反射技术,对 JavaBean 的属性进行赋值,从而达到 A 到 B 模块的解耦。Spring 提供 DI 容器,对须要关联的 JavaBean、不须要关联的 JavaBean 的建立、销毁都要进行统一的调度和管理。git
在咱们的程序中,咱们将不使用 new 关键字的建立对象,而是交给 DI 容器管理生命周期,以及多个 JavaBean 之间的注入关系。从技术上说,就是使用反射将接口和实现相分离。github
但在此以前,咱们来看看如何使用 Spring 框架,以及几种建立 JavaBean 的方式。spring
先建立一个 Java Project,建立一个保存操做的类 Save数组
package save; public class Save { public void save() { System.out.println("save 方法!"); } }
一个测试类 Testapp
package test; import save.Save; public class Test { private Save save = new Save(); public Save getSave() { return save; } public void setSave(Save save) { this.save = save; } public static void main(String[] args) { Test test = new Test(); test.getSave().save(); } }
运行结果:框架
在 src 中建立 applicationContext.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="userinfo1" class="entity.Userinfo"></bean> </beans>
<bean> 标签告诉 Spring 要建立 entity 包下的 Userinfo 类的对象,放在 DI 容器里,在容器里的 id 是 userinfo1。Userinfo 以下:测试
package entity; public class Userinfo { public Userinfo() { System.out.println("类 Userinfo 被实例化 = " + this); } }
测试类 Test:this
package test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Test1 { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); } }
运行结果:
若是 applicationContext.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="userinfo1" class="entity.Userinfo"></bean> <bean id="userinfo2" class="entity.Userinfo"></bean> </beans>
若是经过 getBean(Userinfo.class) 方法获取对象的话就会抛出 NoUniqueBeanDefinitionException 异常,由于 Spring 找到了多个对象,不知道返回哪个。
package test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import entity.Userinfo; public class TestNoUnique { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Userinfo userinfo = context.getBean(Userinfo.class); } }
抛出如下异常 :
如何解决呢? 不要使用 getBean(Userinfo.class); , 使用 getBean(String id); 这样就能够得到 id 对应的惟一对象,而解决异常。
package test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import entity.Userinfo; public class Test1_1 { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Userinfo userinfo1 = (Userinfo)context.getBean("userinfo1"); Userinfo userinfo2 = (Userinfo)context.getBean("userinfo2"); System.out.println(userinfo1); System.out.println(userinfo2); } }
运行结果, 能够看到经过 id 获取的对象和 Spring 建立的对象是同一个对象:
总结: getBean(Userinfo.class) 适用于只有一个 Userinfo 类实例对象的状况,而有多个 Userinfo 类对象的时候就须要使用 getBean(String id) 方法。
使用 <context:component-scan base-package=""> 建立对象
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:component-scan base-package="entity"></context:component-scan> </beans>
使用 @Component 注解,这样 Userinfo 能被 <context:component-scan base-package="entity"> 识别并建立实例化对象。
package entity; import org.springframework.stereotype.Component; @Component public class Userinfo { public Userinfo() { System.out.println("Userinfo 构造方法执行了:" + this); } }
测试类 Test 类,运行结果:
package test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Test { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); } }
getBean(Userinfo.class) 的方式运行:
package test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import entity.Userinfo; public class Test2 { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); Userinfo userinfo = (Userinfo)context.getBean(Userinfo.class); System.out.println("main run " + userinfo); } }
咱们也能够看到经过 id 获取的对象和 Spring 建立的对象是同一个对象。
全注解配置法也称 JavaConfig 配置法。注解 @Configuration 起着和 <beans> 标签同样的全局配置做用,这样咱们就不须要 xml 配置方式就能建立 JavaBean 了。而注解 @Bean 就和 <bean> 标签的功能是同样的,用来声明 JavaBean。
@Bean 注解声明建立对象的方法是名称能够是任意的,但必须有返回值。
package tools; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import entity.Userinfo; @Configuration public class CreateBean { @Bean public Userinfo getUserinfo() { Userinfo userinfo1 = new Userinfo(); System.out.println("建立 userinfo1 = " + userinfo1); return userinfo1; } @Bean public Userinfo createUserinfo() { Userinfo userinfo2 = new Userinfo(); System.out.println("建立 userinfo2 = " + userinfo2); return userinfo2; } }
package entity; import org.springframework.stereotype.Component; public class Userinfo { public Userinfo() { System.out.println("Userinfo 构造方法执行了:" + this); } }
package test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class Test4 { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("tools"); } }
运行结果:
使用全注解发生 NoUniqueBeanDefinitionException 异常,以及解决方法
将 Test4 类的代码加上一行代码
package test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import entity.Userinfo; public class Test4 { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("tools"); Userinfo userinfo = context.getBean(Userinfo.class); } }
解决办法是给 @Bean 添加一个 name
package tools; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import entity.Userinfo; @Configuration public class CreateBean { @Bean(name = "userinfo1") public Userinfo getUserinfo() { Userinfo userinfo1 = new Userinfo(); System.out.println("建立 userinfo1 = " + userinfo1); return userinfo1; } @Bean(name = "userinfo2") public Userinfo createUserinfo() { Userinfo userinfo2 = new Userinfo(); System.out.println("建立 userinfo2 = " + userinfo2); return userinfo2; } }
而后经过 getBean(String id) 方法得到对象,和 xml 方式是同样的:
package test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import entity.Userinfo; public class Test4 { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("tools"); Userinfo userinfo1 = (Userinfo)context.getBean("userinfo1"); Userinfo userinfo2 = (Userinfo)context.getBean("userinfo2"); System.out.println("main userinfo1 = " + userinfo1); System.out.println("main userinfo2 = " + userinfo2); } }
运行结果:
使用 @ComponentScan 注解的 basePackages = “” 建立对象,它和 <context:component-scan base-package=""> </context:component-scan>
做用是相同的,能够进行类扫描,而且建立对象。
扫描 entity 包的类 CreateBean
package tools; import java.util.Date; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan(basePackages = "entity") public class CreateBean { @Bean public Date getUserinfo() { Date nowDate = new Date(); System.out.println("建立 nowDate = " + nowDate); return nowDate; } }
package entity; import org.springframework.stereotype.Component; @Component public class Userinfo { public Userinfo() { System.out.println("Userinfo 构造方法执行了:" + this.hashCode()); } }
package test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import tools.CreateBean; public class Test { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(CreateBean.class); } }
运行结果:
咱们能够看到 entity 下的 Userinfo 类建立了一个实例对象。
使用 @ComponentScan(basePackages = "") 扫描多个包
package tools; import java.util.Date; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan(basePackages = {"entity1" , "entity2"}) public class CreateBean { @Bean public Date createDate() { Date nowDate = new Date(); System.out.println("createDate " + nowDate.getTime()); return nowDate; } }
package entity1; import org.springframework.stereotype.Component; @Component public class Userinfo { public Userinfo() { System.out.println("Userinfo 构造方法执行了:" + this); } }
package entity2; import org.springframework.stereotype.Repository; @Repository public class Bookinfo { public Bookinfo() { System.out.println("Bookinfo 的构造方法被调用了 " + this); } }
package test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import tools.CreateBean; public class Test4 { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(CreateBean.class); } }
运行结果:
使用 @ComponentScan(basePackageClasses = "") 扫描多个类
package tools; import java.util.Date; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import entity1.entity2.Entity2222; import entity1.entity2.entity3.Userinfo; @Configuration //@ComponentScan(basePackageClasses = { Entity2222.class, Userinfo.class}) @ComponentScan(basePackageClasses = { Userinfo.class}) @ComponentScan(basePackageClasses = { Entity2222.class}) public class CreateBean { @Bean public Date createDate() { Date nowDate = new Date(); System.out.println("createDate " + nowDate.getTime()); return nowDate; } }
package entity1.entity2; import org.springframework.stereotype.Repository; @Repository public class Entity2222 { public Entity2222() { System.out.println("Entity2222 构造方法执行了:" + this.hashCode()); } }
package entity1.entity2.entity3; import org.springframework.stereotype.Repository; @Repository public class Bookinfo { public Bookinfo() { System.out.println("Bookinfo 的构造方法被调用了 " + this.hashCode()); } }
package entity1.entity2.entity3; import org.springframework.stereotype.Component; @Component public class Userinfo { public Userinfo() { System.out.println("Userinfo 构造方法执行了:" + this.hashCode()); } }
package test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import tools.CreateBean; public class Test4 { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(CreateBean.class); } }
运行结果:
能够分行写,也能够写成一个数组
//@ComponentScan(basePackageClasses = { Entity2222.class, Userinfo.class}) @ComponentScan(basePackageClasses = { Userinfo.class}) @ComponentScan(basePackageClasses = { Entity2222.class})
若是只写 @ComponentScan, 那么扫描的将是 @Configuration 注解配置类所在包及其子包下的全部组件。
@Component 注解表明都是一个能够扫描的组件, @Repository 注解表明也是一个组件,通常表明的是 DAO 层的组件,也就是数据访问层的组件。它们俩均可以被 @ComponentScan 组件扫描。
总结:这篇博客的例子基原本自 JavaEE 核心框架第二版。之前并无记录 Spring 建立 JavaBean 的方式,因此记录下来,方便之后学习。