对Spring ioc的介绍及使用,与下文相连:
Spring ioc
DI(Dependency Injection)依赖注入,指容器复制创建和维护对象之间的依赖关系,而不是通过对象本身复制自己的创建和解决自己的依赖。控制反转是通过依赖注入实现的。
其实Ioc和DI在Spring中是一个等同的概念。如果非要咬文嚼字的话,控制反转是依赖注入的一部分,或者说是同一个行为偏重点不同的俩个称呼。
他们是从不能的角度阐述同一个功能,描述的对象不同而已。依赖注入是从程序本身来说,控制反转是从容器来说的。
关键点:“谁依赖谁?为什么需要依赖?谁注入谁?注入了什么?”
容器:IOC
组件:(某一个特定的类:例如:UserService)
资源:(组件依赖的内容:例如:mailService)
谁依赖于谁:应用程序依赖于IOC容器
为什么需要依赖:应用程序需要IOC容器提供组件需要的外部资源
谁注入谁:IOC容器注入应用程序需要的资源,组件依赖的资源
注入了什么:注入了某个对象所需要的外部资源(包括对象,常量数据,资源)
给定对象Person,并给定其有参构造函数
public class Person {
private String name;
private int id;
public Person(String name,int id){
this.id = id;
this.name = name;
}
//省略set和get方法
}
在appLication.xml配置文件中配置相应属性
<bean id="person4" class="com.tulun.bean.Person">
<constructor-arg name="id" value="1"/>
<constructor-arg name="name" value="wu"/>
bean>
通过< constructor-arg >标签注入属性
测试(App,java)
//基于有参构造函数注入依赖
public void demo5(){
//获取IOC容器
ApplicationContext context = new ClassPathXmlApplicationContext("application.xml");
//在容器中获取需要的对象
Person person = (Person) context.getBean("person4");
System.out.println(person);
}
对应的对象类提供set()方法
public class Person {
private String name;
private int id;
public Person(){
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", id=" + id +
", person1=" + person1 +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
在application.xml文件中使用property标签配置属性
<bean id="person5" class="com.tulun.bean.Person">
<property name="name" value="li"/>
<property name="id" value="2"/>
bean>
对象类中存在自定义类型的属性
public class Person {
private String name;
private int id;
//自定义属性
private Person1 person1;
public void setPerson1(Person1 person1) {
this.person1 = person1;
}
//省略set和get方法
}
application.xml配置文件中的配置
<bean id="person" class="com.tulun.bean.Person1"/>
<bean id="person5" class="com.tulun.bean.Person">
<property name="name" value="li"/>
<property name="id" value="2"/>
<property name="person1" ref="person"/>
bean>
通过set方法注入依赖使用< property >标签,name属性对应的是类中属性名 value对应是赋予的值(只针对基本类型,作为String)
自定类型的注入使用的是ref标签
注入数据处理基本类型,自定义类型,Spring中还支持List、map、set、array等类型的数据注入
例如:
在对象类中添加对应类型属性
private List<String> ls;
private Map<String,String> ms;
public void setLs(List<String> ls) {
this.ls = ls;
}
public void setMs(Map<String, String> ms) {
this.ms = ms;
}
相应配置文件
<property name="ls">
<list>
<value>12value>
<value>13value>
list>
property>
<property name="ms">
<map>
<entry key="" value=""/>
<entry key=" " value=""/>
map>
property>
xml配置文件(application1.xml)
开启注解扫描
<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-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:component-scan base-package="com.tulun"/>
beans>
在对应的依赖上添加注解@Autowired
@Service(value = "userService")
//service注解是装配bean
public class UserService1 {
@Autowired
//Aotowired是用来注入依赖的
private MailService mailService;
}
@Value注入普通的类型属性
@Resource 注入的是对象类型
@Autowired 注入对象类型
@Component(value = "user")
public class User12 {
@Value("2")
private Integer id;
@Value("zhangsan")
private String name;
private String passwd;
private String address;
public User12() {
}
// 有参构造函数
public User12(Integer id, String name, String passwd, String address) {
this.id = id;
this.name = name;
this.passwd = passwd;
this.address = address;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPasswd() {
return passwd;
}
public void setPasswd(String passwd) {
this.passwd = passwd;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("{");
sb.append("\"id\":")
.append(id);
sb.append(",\"name\":\"")
.append(name).append('\"');
sb.append(",\"passwd\":\"")
.append(passwd).append('\"');
sb.append(",\"address\":\"")
.append(address).append('\"');
sb.append(",\"person\":")
.append(person);
sb.append('}');
return sb.toString();
}
}
@Service(value = "mailService")
public class MailService {
public void sendLoginMail(User12 user) {
System.err.println(String.format("Hi, %s! ", user.getName()));
}
public void sendRegistrationMail(User12 user) {
System.err.println(String.format("Welcome, %s!", user.getName()));
}
}
@Service(value = "userService")
public class UserService1 {
@Autowired
private MailService mailService;
private List<User12> users = new ArrayList<User12>();
public User12 login(String name, String password) {
for (User12 user : users) {
if (user.getName().equalsIgnoreCase(name) && user.getPasswd().equals(password)) {
mailService.sendLoginMail(user);
return user;
}
}
throw new RuntimeException("login failed.");
}
public User12 getUser(long id) {
for (User12 user : users) {
if (user.getId() == id) {
return user;
}
}
return null;
}
public User12 register(String name, String password, String address) {
User12 user = new User12(users.size()+1, name, password, address);
users.add(user);
mailService.sendLoginMail(user);
mailService.sendRegistrationMail(user);
return user;
}
}
@Resource和@Autowired都是用来做bean的注入时使用的,@Resource和@Autowired在有时是可以互相替换换使用的,但是有的时候是不可以的?
功能点:
@Resource和@Autowired都作为bean的注入使用的,在接口仅有一个实现类时,两个注解的修饰效果相同,可以相互替换
不同点:
@Resource是Java自己的注解,
@Resource注解有两个属性比较重要,一个是name,一个是type
spring中使用时name属性解析为按照bean中名称,使用type时解析为类型,如果没有给定属性值,spring的反射机制通过byName来自动注入属性
@Autowired是spring提供的注解,在spring2.5版本后引入的只根据type去进行注入,不用name ,如果涉及到type无法识别注入对象时,仅使用@Autowired是无法完成注入的,需要借助其他的注解一块才能完成
在spring的依赖的解析过程:
1、ApplicationContext通过配置的元数据来创建和初始化,这些元数据描述了所有的bean,
元数据的信息是可以通过注解,xml或者是Java代码来描述
2、对于每一个bean,他的依赖用属性、构造函数或者是静态工厂方法等形式来表达,bean被创建好之后这些依赖会提供给他
3、每一个属性或者构造方法都要被设置的值的实际定义,或者是对容器中的另一个bean的引用
4、每个属性或者构造方法的值的实际定义都会转化为当前实例bean的实际的值
在容器创建的过程中,spring容器会验证每一个bean的配置,在实际创建bean之前,bean的属性不会被设置,单例和被设置为首先加载的bean会在容器初始化后就创建出来,
其他的bean只会在需要的时候才会创建,创建bean过程可能会引起一系列的bean被创建,
循环依赖:
A->B
B->A
A ->B->C->F
D->E->C->B
高频的问题:循环依赖的问题
解决途径是:这些类可以通过setter注入,避免使用构造方法注入
创建顺序:
A B
构造函数构造A ,依赖B 创建顺序:B-A
Set方法创建A, 注入B 创建顺序:A-B