Spring:是一个开源的轻量级的应用框架,其目的是用于简化企业级应用程序开发,降低侵入性;其本质是管理软件中的对象,及创建对象和维护对象之间的关系。
Spring容器:用来管理对象;本身也是对象;是IOC的核心
在spring中,任何的Java类和JavaBean都被当做Bean处理,这些Bean通过容器管理和使用;Spring容器实现了IOC和AOP机制,这些机制可以简化Bean对象创建和Bean对象直接的解耦;Spring容器有BeanFactory和ApplicationContext两种类型。
JavaBean:一种简单规范的Java对象
什么是JavaBean?
满足如下规范的类:
1.有package
2.有默认构造器
3.实现序列化接口Serializable
4.有get/set方法
Spring容器的实例化:ApplicationContext集成自BeanFactory接口,拥有更多的企业级方法,推荐使用该类,实例方法如下:
//加载项目classpath下的配置文件实例化
ApplicationContext ctx = new ClassPathXmlApplicationContext("app.xml");
Spring容器对Bean(对象)的管理:
Bean的实例化:
1.构造器方式:
<!-- Springc创建对象 -->
<!-- 1.通过构造器创建对象:class是要创建的对象的类型 -->
<bean class="java.util.ArrayList" id="obj1"></bean>
//在junit的测试代码中测试
List list = (List) ctx.getBean("obj1");
System.out.println(list);
注:id或name属性用于指定Bean名称,用于从Spring中查找这个Bean对象;class用于指定Bean类型,会自动嗲用无参构造器创建对象。
2.静态工厂方法:
<!-- 2.通过静态工厂方法创建对象,调用某类的静态方法来创建对象;class:要调用的类,factory-method:要调用的静态方法 -->
<bean class="java.util.Calendar" factory-method="getInstance" id="obj2"></bean>
//测试代码
Calendar c = (Calendar) ctx.getBean("obj2");
System.out.println(c);
Date s = c.getTime();
System.out.println(s);
注:id属性用于指定Bean名称;class属性用于指定工厂类型;factory-method属性用于指定工厂中创建Bean对象的方法,必须用static修饰的方法。
3.实例工厂方法:
<bean factory-bean="obj2" factory-method="getTime" id="obj3"></bean>
Date d = (Date) ctx.getBean("obj3");
System.out.println(d);
注:id用于指定Bean名称;factory-bean属性用于指定工厂Bean对象;factory-method属性用于指定工厂中创建Bean对象的方法。
Bean的命名:
在spring容器中,每个Bean都需要有名字(即标识符),该名字可以用<bean>元素的id或者那么属性执行。
Bean的别名:为以定义好的Bean,在增加另外一个名字引用,可以使用<alias>指定;如:
<alias name="fromName" alias="toName>
Bean的作用域:
Spring在容器实例化Bean时,可以创建一下作用域的Bean对象:
singleton:在每个Speing IOC容器中一个bean对应一个对象实例,默认项;
prototype:一个bean定义对应多个对象实例;
request:在一次HTTP请求中,一个bean定义对应一个实例,仅限于Web环境;
session:在一个HTTP Session中,一个bean定义对应一个实例,仅限于Web环境;
global Session:在一个全局的HTTP Session中,一个bean定义对应一个实例;仅在基于prothet的Web应用中才有意义;
上面的Bean作用域,可以通过<bean>定义的scope属性指定;如下:
<bean class="java.util.HashMap" id="obj4" scope="singleton"></bean>
/**
* bean的作用域:
* 每个bean在容器中默认都是单例的
*/
@Test
public void test4(){
//1.创建Spring容器
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
//获取bean
Map map1 = (Map) ctx.getBean("obj4");
Map map2 = (Map) ctx.getBean("obj4");
System.out.println(map1==map2);
}
管理Bean的生命周期:就是让spring自动调用该bean的初始化和销毁方法
<bean class="bean.Example" id="exa" init-method="init" scope="singleton" lazy-init="true"></bean>
init-method:声明销毁方法,在容器关闭时自动调用
destory-method:声明销毁方法,在容器关闭时自动调用。但是只对单例的bean有效
//测试代码
/**
* 容器管理bean生命周期
*/
@Test
public void test5(){
//1.创建容器
//classPathXmlApplicationContext继承于AbstractApplicationContext
//后者实现了ApplicationContext。
//AbstractApplicationContext里面生命了关闭容器的方法close
AbstractApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
System.out.println("1-----");
//获取bean
Example exa = (Example) ctx.getBean("exa");
System.out.println(exa);
System.out.println("2---");
ctx.close();
}
Bean延迟实例化:适用于使用频率很低的单例对象
<bean id="exampleBean" lazy-init="true" class="com.foo.ExampleBean"/>
如果不想让 singleton bean在ApplicationContext初始化时就被提前实例化,可以使用延迟实例化。
lazy-init:延迟初始化,即在容器创建时并不创建bean,而是在获取bean才创建,将bean创建的时机推迟了,只对单例的bean有效。
Spring容器工作过程:
SpringIOC:
ioc全称是Intersion of Control,被译为控制反转;指的是程序中对象的获取方式发生反转,由最初的new方式创建,转变为由第三方框架创建、注入(DI),它降低了对象之间的耦合度。
Spring容器是采用DI方式石像IOC控制,IOC是Spring框架的基础和核心;
DI全称Dependency Injection,被称依赖注入;基本原理就是将一起工作具有关系的对象,通过构造方法参数或方法参数传入建立关联,因此容器的工作就是创建bean时注入那些依赖关系。
IOC是一种思想,而DI是实现IOC的主要技术途径;DI主要有两种注入方式,即Setter注入和构造器注入;
setter注入:通过调用无参构造器或无参static工厂方法实例化bean之后,调用该bean的setter方法,即可实现setter方式的注入
<!-- 1.setter注入:通过bean的set方法,给它注入参数 -->
<bean class="bean.Computer" id="computer">
<property name="mainboard" value="技嘉"/>
<property name="hdd" value="希捷"/>
<property name="ram" value="金士顿"/>
</bean>
/**
* 依赖注入 -setter注入
*/
@Test
public void test6(){
//创建容器
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
Computer com = (Computer) ctx.getBean("computer");
System.out.println(com);
System.out.println(com.getHdd()+";"+com.getHdd()+";"+com.getRam());
}
package bean;
public class Computer {
private String mainboard;
private String hdd;
private String ram;
public String getMainboard() {
return mainboard;
}
public void setMainboard(String mainboard) {
this.mainboard = mainboard;
}
public String getHdd() {
return hdd;
}
public void setHdd(String hdd) {
this.hdd = hdd;
}
public String getRam() {
return ram;
}
public void setRam(String ram) {
this.ram = ram;
}
}
构造器注入:基于构造器的注入是通过调用带参数的构造器来实现的,容器在bean被实例化的时候,根据参数类型执行相应的构造器;构造器注入,可以强制给bean注入某些参数,比Setter注入更严格,往往是为了强制注入这些参数。
xml和测试:
<bean class="bean.MobilePhone" id="phone">
<constructor-arg index="0" value="ARM"/>
<constructor-arg index="1" value="4G"/>
</bean>
@Test
public void test7(){
//创建容器
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
MobilePhone phone = (MobilePhone) ctx.getBean("phone");
System.out.println(phone);
System.out.println(phone.getCpu()+";"+phone.getRam());
}
实体类:
package bean;
import java.io.Serializable;
public class MobilePhone implements Serializable{
private String cpu;
private String ram;
public MobilePhone(String cpu, String ram) {
this.cpu = cpu;
this.ram = ram;
}
public String getCpu() {
return cpu;
}
public void setCpu(String cpu) {
this.cpu = cpu;
}
public String getRam() {
return ram;
}
public void setRam(String ram) {
this.ram = ram;
}
}
自动装配:Spring Ioc容器可以自动装配(autowire)相互协作bean之间的关联关系,autowire可以针对单个bean进行设置,autowire的方便在于减少了xml的注入配置;在xml配置文件中,可以在<bean/>元素中使用autowire属性指定自动装配规则,一共有5种类型值:
<!-- 自动装配:Spring在创建bean时,可以根据类型或名称,从容器中找到匹配的bean,设置给这个bean属性,需要创建对应的Student类 -->
<bean class="bean.Student" id="student" autowire="byType"></bean>
/**
* 自动装配
*/
@Test
public void test8(){
//创建容器
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
Student s = (Student) ctx.getBean("student");
System.out.println(s.getComputer().getMainboard());
System.out.println(s.getPhone().getCpu());
}
no:禁用自动装配;
byName:根据属性名自动装配,此选项将检查容器并根据名字查找与属性完全一致的bean,并将其与属性自动装配;
byType:如果容器中存在一个与指定属性类型相同的bean,那么将与该属性自动装配;
constructor:与byType的方式类似,不同之处在于它应用于构造参数
autodetect:通过bean类来决定是使用constructor还是byType方式进行自动装配,如果发现默认的构造器,那么将使用byType方式;
参数值注入:
<!-- 参数值注入 -->
<bean class="bean.Message" id="msg">
<!-- 注入基本值:基本类型、封装类型、String类型 -->
<property name="id" value="1"/>
<property name="name" value="zhangsan"/>
<property name="sal" value="50000.00"/>
<!-- 注入bean(前提是都在容器之中,必须由容器创建);name:要注入的属性的名称 ;ref:注入的bean的ID -->
<property name="phone" ref="phone"/>
<!-- 注入集合List、Set、Map、Properties -->
<property name="cities">
<list>
<value>北京</value>
<value>上海</value>
<value>广州</value>
</list>
</property>
<property name="score">
<map>
<entry key="张三" value="101KG"/>
<entry key="李四" value="102KG"/>
</map>
</property>
<property name="params">
<props>
<prop key="user">system</prop>
<prop key="password">dream</prop>
</props>
</property>
</bean>
/**
* 参数值注入
*/
@Test
public void test9(){
//创建容器
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
Message msg = (Message) ctx.getBean("msg");
System.out.println(msg.getId());
System.out.println(msg.getName());
System.out.println(msg.getSal());
System.out.println(msg.getPhone().getCpu());
System.out.println("城市:"+msg.getCities().get(0));
System.out.println("体重:"+msg.getScore().get("张三"));
System.out.println("参数:"+msg.getParams().getProperty("user"));
}
基于注解的依赖注入:
说到注解不得不聊一下组件扫描;组件扫描就是制定一个包路径,Spring会自动扫描该包及其子包所有组件类,当发现类定义前有特点的注解标记是,就将该组件纳入到Spring容器,等价于原有XML配置中的<bean>定义功能;组件扫描可以替代大量XML配置的<bean>定义。
指定组件扫描路径:
<!--使用组件臊面,首先需要在XML配置中指定扫描父级package路径-->
<context:component-scan base-package="org.example"/>
上面配置,容器会自动扫描org.example包及其子包下所有组件,并实例化为bean。
自动扫描的注解标记:只有在组件类定义前面有一下注解标记时,才会扫描到Spring容器中去。
@Component:通用注解
@Named:通用注解
@Repository:持久层组件注解
@Service:业务层组件注解
@Controller:控制层组件注解
自动扫描组件的命名:(两种)
@Component //默认id为小写开头的类名
public class ExampleBean implements Serializable{
}
@Component("example") //自定义id
public class ExampleBean implements Serializable{
}
自定组件的作用域:
@Component
@Scope("prototype")
public class ExampleBean implements Serializable{
}
注:通常受spring管理的组件,默认的作用域是“singleton”,如果需要其他的可用@Scope注解。
自定初始化和销毁的回调:
/**
* @author dream-it
* 演示 组件扫描
*/
//通用的注解,表示什么地方都能用,加了之后spring会将Student当bean来管理
//配置文件会自动生成一个bean,默认的ID是首字母小写之后的类名
@Component("stu")
//表示作用域
@Scope("singleton")
//延迟加载
@Lazy(true)
public class Student {
//初始化方法
@PostConstruct
public void init(){
System.out.println("Student的init()...");
}
//销毁方法
@PreDestroy
public void destory(){
System.out.println("Student的销毁");
}
public Student() {
System.out.println("Student的无参构造器...");
}
}
依赖注入(使用注解):主要由3中方式;
1.AutoWired和Qualifier(分为写在构造器和set方法前面两种)
@Autowired写在构造器前面,声明需要为其注入bean
@Qualifier写在参数前面,声明需要注入的bean的ID值
@Component("rest")
public class Restaurant {
//这两个注解也可以直接添加到属性前面
//此时对应的set方法可以不要,通过反射的机制来执行
@Autowired
//wt表示被注入的bean的ID
@Qualifier("wtm")
private Waiter wt;
测试类:
@Test
//测试@AutoWired来完成注解
public void test5(){
ApplicationContext ctx = new ClassPathXmlApplicationContext("app.xml");
Restaurant rest = ctx.getBean("rest",Restaurant.class);
System.out.println(rest);
Hotel hotel = ctx.getBean("hotel",Hotel.class);
System.out.println(hotel);
}
注意:注入的对象是单例时,@Qualifier可省略,此时,Spring按照类型匹配参数
@Autowired写在set方法前面,声明需要为其注入bean
@Qualifier写在参数前面,声明需要追的bean的ID值
@Component("rest")
public class Restaurant {
private Waiter wt;
@Autowired
public void setWt(@Qualifier("wt") Waiter wt) {
System.out.println("Res的set方法");
this.wt = wt;
}
2.@Inject注解:和@Autowired功能一直,但使用时需要额外导包
@Inject注解标记是Spring3.0开始增添的对JSR-330标准的支持,使用前需要添加JSR-330的jar包javax.inject-1.jar
@Inject注解用法和@Autowired一直,其中
1).@Inject等价于@Autowired;
2).@Named等价于@Qualifier
3.@Resource注解:(只能用于setter注入,但更简单,也更重要,因为一般都Setter注入)
@Component
public class Manager implements Serializable{
private Computer computer;
@Resource(name="computer")
public void setComputer(Computer computer){
this.computer = computer;
System.out.println("Manager");
}
}
注意:1.注入的对象单例时,(name=“computer”)可省略,此时,Spring安装类型匹配参数
2.@Resource也可以写在属性上,作用和写在set方法上类似,但只会执行一行代码:this.computer = computer;
总结:
Spring容器就相当于是一个大的抽象工厂,在程序中创建对象是很复杂的,所以才会有相应的设计模式。Spring容器就相当于把对象拿过来用框架帮你进行管理,这样就大大的降低程序的耦合度;并且Spring容器实现了IOC和AOP的机制,让创建对象和扩展都得变得容易的,也符合开闭原则。并且运用SpringIOC的DI方式注入对象,也大大的降低了程序的耦合度。