Spring框架
IOC容器
理解IOC容器和DI依赖注入
IOC容器 Inverser Of Control反转控制 就是把原来在程序中创建HelloService对象的控制权限交给Spring管理,简单的说 就是HelloService对象控制器被反转到Spring框架内
DI: 依赖注入 在Spring框架负责创建Bean对象的时候 动态的将数据注入到Bean的组件
IOC容器装配Bean的方式(XML配置方式)
IOC容器装配Bean的方式就是实例化该对象的方式,Spring提供了三种装配Bean的方式
- 使用类的构造函数进行初始化 (默认是无参的构造函数)
- 使用静态工厂方法:利用factory-method=属性指定静态工厂方法
- 使用实例化工厂方法 (工厂模式)
/**
* 通过3种方式获取Bean对象
* a.根据获取Bean对象不同的方式定义相关内容
* 根据构造器获取
* 利用静态工厂方法获取
* 利用实例化工厂方法获取
* b.在applicationContext.xml中进行配置
* c.获取Bean对象进行测试
* url : "spring/applicationContext.xml" 指定核心配置文件位置
* ApplicationContext ac = new ClassPathXmlApplicationContext(url);
* BeanClass bean = ac.getBean("id");
*/
a.构造函数初始化
<1>定义Bean实体,提供无参构造函数
Bean.java:
/**
* 方式1:利用构造器获取
*/
public class Bean {
// 定义一个无参的构造方法,系统有默认的无参构造器
}
<2>在核心配置文件applicationContext.xml中定义bean对象
<!--
a.构造函数初始化
<bean id="" class=""></bean>
id:实例对象名称
class:对应类全名
-->
<bean id="bean" class="com.guigu.spring.b_instance.Bean"></bean>
<3>编写代码获取指定对象进行测试
/**
* 方式1:通过构造器获取Bean对象
*/
@Test
public void testGetBean1() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring/applicationContext.xml");
Bean bean =(Bean) applicationContext.getBean("bean");
System.out.println("方式1:"+bean);
}
<4>查看测试结果
b.使用静态工厂方法
<1>定义Bean实体,工厂方法设计模式
ClientService.java:
/**
* 方式2:利用静态工厂方法获取 此处类似于单例模式
*/
public class ClientService {
// a.构造函数私有化
private ClientService() {
}
// b.提供一个静态的私有的属于自己的全局变量进行初始化
private static ClientService clientService = new ClientService();
// c.通过提供公有方法返回该对象
public static ClientService createInstance() {
return clientService;
}
}
<2>在核心配置文件applicationContext.xml中定义bean对象
<!--
b.使用静态工厂方法
<bean id="" class="" factory-method=""></bean>
id:实例对象名称
class:对应类全名
factory-method:返回该实例对象的工厂方法名
-->
<bean id="clientService" class="com.guigu.spring.b_instance.ClientService" factory-method="createInstance"></bean>
<3>编写代码获取指定对象进行测试
<4>查看测试结果
c.使用实例化工厂方法
<1>定义Bean实体,实例化工厂方法设计模式
UserService.java:
/**
* 方式3:实例化工厂方法
* a.先创建一个实体类xxx
* b.通过xxxFactory进行实例化并返回某个对象
*/
public class UserService {
}
UserServiceFactory.java:
public class UserServiceFactory {
// 创建UserService 是通过UserServiceFactory 完成实例化
private static UserService userService = new UserService();
// 提供共有方法返回实例化对象
public UserService createUserServiceInstance() {
return userService;
}
}
<2>在核心配置文件applicationContext.xml中定义bean对象
<!--
c.使用实例化工厂方法
<bean id="xxxFactory" class="xxxFactory工厂类全名"></bean>
<bean id="实例对象名称" factory-bean="xxxFactory" factory-method="调用工厂方法名"></bean>
-->
<bean id="userServiceFactory" class="com.guigu.spring.b_instance.UserServiceFactory"></bean>
<bean id="userService" factory-bean="userServiceFactory" factory-method="createUserServiceInstance"></bean>
<3>编写代码获取指定对象进行测试
<4>查看测试结果
Bean的其他属性配置——作用域
定义Bean的时候可以指定以下方式
- singleton
- prototype
<1>定义实体类product
/**
* 定义Product类,提供无参构造方法
*/
public class Product {
public Product() {
System.out.println("product实例化......");
}
}
<2>核心配置文件进行Bean配置
<!-- a.单例模式配置:scope="singleton" -->
<bean id="product1" class="com.guigu.spring.c_scope.Product" scope="singleton"></bean>
<!-- b.多例模式配置:scope="prototype" -->
<bean id="product2" class="com.guigu.spring.c_scope.Product" scope="prototype"></bean>
<!-- c.还有其他作用域设置需根据实际项目需求进行选择,详细参考实际文档 -->
<3>编写测试文件进行测试
public class ProductTest {
@Test
public void testSingletonScope() {
/**
* 单例模式测试结果显示:
* product被实例化1次,且均为执行同一个对象(指向对象地址相同)
*/
ApplicationContext ac =new ClassPathXmlApplicationContext("spring/applicationContext.xml");
Product p1 = (Product) ac.getBean("product1");
Product p2 = (Product) ac.getBean("product1");
System.out.println(p1);
System.out.println(p2);
}
@Test
public void testPrototypeScope() {
/**
* 多例模式测试结果显示:
* product被实例化多次,且均指向不同的对象(指向对象的地址不同)
*/
ApplicationContext ac =new ClassPathXmlApplicationContext("spring/applicationContext.xml");
Product p1 = (Product) ac.getBean("product2");
Product p2 = (Product) ac.getBean("product2");
System.out.println(p1);
System.out.println(p2);
}
}
Singleton测试结果:
Prototype测试结果:
Bean属性依赖注入
Bean属性的注入分为构造器注入、setter方法注入
步骤:
1.定义实体对象
2.在核心配置文件中定义相关属性
3.编写测试文件进行测试
a.第一种构造器注入
实体类定义如下:
public class Car {
// 定义Car类的两个属性
private String carType ;
private String carPrice;
// 提供带参数的构造器
public Car(String carType, String carPrice) {
super();
this.carType = carType;
this.carPrice = carPrice;
}
@Override
public String toString() {
return "Car [carType=" + carType + ", carPrice=" + carPrice + "]";
}
}
<1>使用构造函数的参数类型进行注入
在核心配置文件中定义相关属性:
编写测试代码:
测试结果:
<2>构造函数的参数的索引、下标
在核心配置文件中定义相关属性:
编写测试代码:
测试结果:
<3>构造函数参数的名称
在核心配置文件中定义相关属性:
编写测试代码:
测试结果:
b.Setter方法注入
<1>Setter方法注入简单数据
创建Cellphone实体:
public class Cellphone {
// 定义Cellphone的两个属性
private String phoneName;
private String phonePrice;
// 提供setter方法
public void setPhoneName(String phoneName) {
this.phoneName = phoneName;
}
public void setPhonePrice(String phonePrice) {
this.phonePrice = phonePrice;
}
@Override
public String toString() {
return "Cellphone [phoneName=" + phoneName + ", phonePrice=" + phonePrice + "]";
}
}
核心文件applicationContext.xml配置:
代码测试:
测试结果:
<2>Setter方法注入复杂对象
在上述内容的基础上定义Person实体拥有Car、Cellphone属性:
public class Person {
// 定义Person的3个属性
private String name;
private Car car;
private Cellphone phone;
// 提供setter方法
public void setName(String name) {
this.name = name;
}
public void setCar(Car car) {
this.car = car;
}
public void setPhone(Cellphone phone) {
this.phone = phone;
}
@Override
public String toString() {
return "Person [name=" + name + ", car=" + car + ", phone=" + phone + "]";
}
}
核心文件applicationContext.xml配置:
代码测试:
测试结果:
<3>集合属性的注入
定义测试对象CollectionBean:
public class CollectionBean {
// 定义CollectionBean的四个属性
private List<String> list;
private Set<Integer> set;
private Map<String,String> map;
private Properties properties;
// 提供相应的setter方法
public void setList(List<String> list) {
this.list = list;
}
public void setSet(Set<Integer> set) {
this.set = set;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
@Override
public String toString() {
return "CollectionBean [list=" + list + ", set=" + set + ", map=" + map + ", properties=" + properties + "]";
}
}
核心文件applicationContext.xml配置:
<bean id="collectionBean" class="com.guigu.spring.d_di.CollectionBean">
<!-- 作为参考,此处注入的均为普通数据类型的集合元素,可以注入其他的bean元素 -->
<!--
a.list集合属性的注入
<property name="属性名称">
<list>
<value>注入值:普通数据</value>
</list>
</property>
-->
<property name="list">
<list>
<value>list项1</value>
<value>list项2</value>
<value>list项3</value>
</list>
</property>
<!--
b.set集合属性的注入
<property name="属性名称">
<set>
<value>注入值:普通数据</value>
</set>
</property>
-->
<property name="set">
<set>
<value>1</value>
<value>2</value>
<value>3</value>
</set>
</property>
<!--
c.map集合属性的注入
<property name="属性名称">
<map>
<entry key="key值" value="value值"></entry>
</map>
</property>
-->
<property name="map">
<map>
<entry key="username" value="hahabibu"></entry>
<entry key="password" value="000000"></entry>
</map>
</property>
<!--
d.properties集合属性的注入
<property name="属性名称">
<props>
<prop key="key值">value值</prop>
</props>
</property>
-->
<property name="properties">
<props>
<prop key="company">杭州归谷</prop>
<prop key="address">浙江杭州</prop>
</props>
</property>
</bean>
代码测试:
测试结果:
<4>P命名空间的使用
核心配置文件中引入P标签:
<?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:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
创建实体类Computer:
public class Computer {
// 提供Computer的两个属性
private String cname;
private String cprice;
// 提供setter方法
public void setCname(String cname) {
this.cname = cname;
}
public void setCprice(String cprice) {
this.cprice = cprice;
}
@Override
public String toString() {
return "Computer [cname=" + cname + ", cprice=" + cprice + "]";
}
}
核心配置文件中进行配置:
编写测试代码:
测试结果:
多个xml配置文件
方式一 :在类中执行的时候添加多个文件(不推荐使用)
appliicationContext.xml定义如上述所示,appliicationContext2.xml定义如下
@Test
public void testXML1() {
ApplicationContext ac =new ClassPathXmlApplicationContext("spring/applicationContext.xml","spring/applicationContext2.xml");
Person p1 = (Person) ac.getBean("p1");
System.out.println(p1);
}
方式二 :在一个总的文件中引入不同的配置文件,然后在代码中使用这个总的配置文件
applicationContext_all.xml:
// 调用总的文件以引入不同的配置文件
@Test
public void testXML2() {
ApplicationContext ac =new ClassPathXmlApplicationContext("spring/applicationContext_all.xml");
Person p2 = (Person) ac.getBean("p2");
System.out.println(p2);
}
IOC容器装配Bean(注解的方式)
a.使用注解定义Bean
<1>定义配置文件
引入context命名空间
配置Spring的自动扫描和要扫描的包
applicationContext.xml:
<!-- 配置注解bean自动扫描 -->
<context:annotation-config/>
<!-- 定义具体扫描的包 -->
<context:component-scan base-package="com.guigu.spring.a_beandefinition,com.guigu.spring.b_scope">
</context:component-scan>
<2>需要在Bean对象中定义@Component 声明组件
<3>测试代码
<4>测试结果
除了@Component外,Spring提供了3个功能基本和@Component等效的注解
- @Repository 用于对DAO实现类进行标注
- @Service 用于对Service实现类进行标注
- @Controller 用于对Controller实现类进行标注
这三个注解是为了让标注类本身的用途清晰,Spring已对其增强
@Repository测试
测试结果:
@Service测试
测试结果:
@Controller测试
测试结果:
@Autowired默认是根据类型自动注入
- 通过@Autowired的required属性,设置一定要找到匹配的Bean
- 使用@Qualifier指定注入Bean的名称
- 使用Qualifier 指定Bean名称后,注解Bean必须指定相同名称
@Autowired(required=false)默认是true 如果注入不成功就会报错 False 空指针
如果存在多个相同的类型注入就会报错,可以再使用类型的前提下再次明确使用哪个Bean对象就是使用
@Qualifier("userService") 明确指定使用哪个Bean对象
使用@Resource可以优化上方两个注解:
@Resource(name="userService")
等价于
@Autowired(required=false)
@Qualifier("userService")
b.Bean的范围
<1>在applicationContext.xml中声明要进行扫描的包
<2>定义Car类进行作用范围测试
<3>编写测试文件
<4>测试结果分析
如果指定作用范围为prototype多例模式显示如下内容
如果指定作用范围为singleton单例模式则显示如下内容
c.注册Bean的注解
Spring3.0以JavaConfig为核心,提供使用Java类定义Bean信息的方法
- @Configuration 指定POJO类为Spring提供Bean定义信息
- @Bean 提供一个Bean定义信息
<1>定义model对象Cellphone、Product
Cellphone.java:
public class Cellphone {
private String cname;
private String cprice;
public String getCname() {
return cname;
}
public void setCname(String cname) {
this.cname = cname;
}
public String getCprice() {
return cprice;
}
public void setCprice(String cprice) {
this.cprice = cprice;
}
@Override
public String toString() {
return "Cellphone [cname=" + cname + ", cprice=" + cprice + "]";
}
}
Product.java:
public class Product {
private String pname;
private String pprice;
public String getPname() {
return pname;
}
public void setPname(String pname) {
this.pname = pname;
}
public String getPprice() {
return pprice;
}
public void setPprice(String pprice) {
this.pprice = pprice;
}
@Override
public String toString() {
return "Product [pname=" + pname + ", pprice=" + pprice + "]";
}
}
<2>定义POJO类定义Bean对象
BeanConfig.java:
/**
* Spring3.0以JavaConfig为核心,提供使用Java类定义Bean信息的方法
* @Configuration 指定POJO类为Spring提供Bean定义信息
* @Bean 提供一个Bean定义信息
*/
// 代表这个Bean对象是一个Bean的配置注解
@Configuration
public class BeanConfig {
/**
* 方式1:@Bean
* 如果没有指定name属性,则表示方法名即为对应Bean的id
* 此处为initCellphone
*/
@Bean
public Cellphone initCellphone() {
Cellphone cp = new Cellphone();
cp.setCname("诺基亚");
cp.setCprice("5000");
return cp;
}
/**
* 方式2:@Bean("xxx")
* 指定name属性表示该bean对象的id为指定的内容
*/
@Bean(name="product")
public Product initProduct() {
Product p = new Product();
p.setPname("电冰箱");
p.setPprice("10000");
return p;
}
}
<3>编写测试类进行测试
<4>测试结果
d.xml配置和注解配置综合案例
<1>dao层、Service层定义
CustomerDAO .java:
public class CustomerDAO {
}
OrderDAO .java:
public class OrderDAO {
}
CustomerService.java:
public class CustomerService {
// 1.使用注解的方式定义OrderDAO对象
@Autowired(required=false)
@Qualifier("orderDAO")
private OrderDAO orderDAO;
// 2.使用xml配置方式定义Customer对象,需根据需求提供相应的构造器或setter方法
private CustomerDAO customerDAO;
public void setCustomerDAO(CustomerDAO customerDAO) {
this.customerDAO = customerDAO;
}
@Override
public String toString() {
return "CustomerService [orderDAO=" + orderDAO + ", customerDAO=" + customerDAO + "]";
}
}
<2>核心配置文件applicationContext.xml配置CustomerDAO
<3>编写测试文件
<4>测试结果