在使用Spring IOC之前,先来看看原来的写法
1、先创建一个Maven项目,新增接口:UserDao
package com.sc.dao;
public interface UserDao {
void getUser();
}
2、增加接口实现类:UserDaoImpl、UserDao2Impl 、UserDao3Impl
package com.sc.dao;
public class UserDaoImpl implements UserDao {
@Override
public void getUser() {
System.out.println("用户默认信息");
}
}
package com.sc.dao;
public class UserDao2Impl implements UserDao {
@Override
public void getUser() {
System.out.println("用户UserDao2Impl信息");
}
}
package com.sc.dao;
public class UserDao3Impl implements UserDao {
@Override
public void getUser() {
System.out.println("用户UserDao3Impl信息");
}
}
3、新增接口:UserService
package com.sc.service;
public interface UserService {
void getUser();
}
4、新增实现类:UserServiceImpl
package com.sc.service;
import com.sc.dao.UserDao;
public class UserServiceImpl implements UserService {
UserDao userDao;
public void setUserDao(UserDao userDao){
this.userDao = userDao;
}
@Override
public void getUser() {
userDao.getUser();
}
}
5、测试类:MyTest
public class MyTest {
public static void main(String[] args) {
UserServiceImpl userService = new UserServiceImpl();
userService.setUserDao(new UserDao2Impl());
userService.getUser();
userService.setUserDao(new UserDao3Impl());
userService.getUser();
userService.setUserDao(new UserDaoImpl());
userService.getUser();
}
}
运行结果:
image.png
上述这种方式 ,在UserServiceImpl 中 利用set创建UserDao 对象, 这样程序员不再去管理对象的创建了 , 程序调用时由用户选择调用的接口,而程序员则更多的去关注业务的实现 . 耦合性大大降低 . 这就是IOC的原型 !
IOC(Inversion of Control)控制反转,是一种设计思想,DI(依赖注入)是实现IOC的一种方法。没有IOC的程序中 , 面向对象编程时 , 对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,而控制反转是将对象的创建转移给第三方,也就是:获得依赖对象的方式反转了。
Spring容器在初始化时先读取配置文件,根据配置文件或元数据创建与组织对象存入容器中,程序使用时再从IOC容器中取出需要的对象即可。
控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。
以下以student、address为例,以set的方式测试DI(依赖注入)
1、POJO 实体类
Address.class
package com.sc.pojo;
public class Address {
private String address;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Address{" +
"address='" + address + '\'' +
'}';
}
}
Student.class
package com.sc.pojo;
import java.util.*;
public class Student {
private String username;
private String[] hobbys;
private List<String> books;
private Map<String,String> friends;
private Address address;
private Properties info;
private Set<String> games;
private String wife;
@Override
public String toString() {
return "Student{" +
"username='" + username + '\'' +
", hobbys=" + Arrays.toString(hobbys) +
", books=" + books +
", friends=" + friends +
", address=" + address.toString() +
", info=" + info +
", games=" + games +
", wife='" + wife + '\'' +
'}';
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String[] getHobbys() {
return hobbys;
}
public void setHobbys(String[] hobbys) {
this.hobbys = hobbys;
}
public List<String> getBooks() {
return books;
}
public void setBooks(List<String> books) {
this.books = books;
}
public Map<String, String> getFriends() {
return friends;
}
public void setFriends(Map<String, String> friends) {
this.friends = friends;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public Properties getInfo() {
return info;
}
public void setInfo(Properties info) {
this.info = info;
}
public Set<String> getGames() {
return games;
}
public void setGames(Set<String> games) {
this.games = games;
}
public String getWife() {
return wife;
}
public void setWife(String wife) {
this.wife = wife;
}
}
2、在resources目录下创建配置文件:beans.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="address" class="com.sc.pojo.Address" >
<property name="address" value="上海"/>
</bean>
<bean id="student" class="com.sc.pojo.Student" autowire="byName">
<property name="username" value="sundy"/>
<!--注意: 这里的name并不是属性 , 而是set方法后面的那部分 , 首字母小写-->
<!--引用另外一个bean ,要用 ref,相接赋值时用 value-->
<property name="address" ref="address"/>
<property name="books">
<list>
<value>西游记</value>
<value>红楼梦</value>
<value>三国演义</value>
</list>
</property>
<property name="friends">
<map>
<entry key="1" value="AA"/>
<entry key="2" value="BB"/>
<entry key="3" value="CC"/>
</map>
</property>
<property name="games" >
<set>
<value>英雄联盟</value>
<value>水果消消乐</value>
<value>俄罗斯方块</value>
</set>
</property>
<property name="hobbys">
<array>
<value>羽毛球</value>
<value>网球</value>
<value>画画</value>
</array>
</property>
<property name="wife">
<null/>
</property>
<property name="info">
<props>
<prop key="age" >20</prop>
<prop key="sex">女</prop>
<prop key="high">165</prop>
</props>
</property>
</bean>
</beans>
这里也可以通过P标签或C标签的方式注入,以P标签为例。
先要导入约束:
xmlns:p="http://www.springframework.org/schema/p"
这里不需要有参构造器
<bean id="student" class="com.sc.pojo.Student" p:username="sundy" autowire="byName"/>
3、测试类中新增方法
@Test
public void test(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Student student = context.getBean("student", Student.class);
System.out.println(student);
}
上述方法中,先通过ClassPathXmlApplicationContext解析beans.xml文件, 生成管理相应的Bean对象并放入Spring容器中。
然后从Spring容器中获取spring配置文件中该对象bean的id,即可得到Student的对象。
输出:
image.png
IOC通过有参构造方法创建对象的方式有三种。
User.class
public class User {
private String name;
public User(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
public void info(){
System.out.println("name="+ name );
}
}
beans.xml 有三种方式编写
第一种根据index参数下标设置
<bean id="user" class="com.sc.pojo.User">
<!-- index指构造方法 , 下标从0开始 -->
<constructor-arg index="0" value="sc"/>
</bean>
第二种根据参数名字设置
<bean id="user" class="com.sc.pojo.User">
<!-- name指参数名 -->
<constructor-arg name="name" value="sc2"/>
</bean>
第三种根据参数类型设置(不推荐使用)
<bean id="user" class="com.sc.pojo.User">
<constructor-arg type="java.lang.String" value="sc3"/>
</bean>
测试
@Test
public void testT(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
User user = (User) context.getBean("user");
user.info();
}
经验证:在配置文件加载的时候,管理的对象就已经初始化了。
多个配置文件可以使用import标签导入
<import resource="{path}/beans.xml"/>
下面介绍一下Spring中Bean的自动装配
Spring的自动装配需要从两个角度来实现:
组件扫描(component scanning):spring会自动发现应用上下文中所创建的bean;
自动装配(autowiring):spring自动满足bean之间的依赖,即IOC/DI;
实体类Cat.class
public class Cat {
public void shout() {
System.out.println("喵喵~");
}
}
实体类Dog.class
public class Dog {
public void shout() {
System.out.println("汪汪~");
}
}
实体类User.class
public class User {
private Cat cat;
private Dog dog;
private String message;
}
编写Spring配置文件 beans.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="dog" class="com.sc.pojo.Dog"/>
<bean id="cat" class="com.sc.pojo.Cat"/>
<bean id="user" class="com.sc.pojo.User">
<property name="cat" ref="cat"/>
<property name="dog" ref="dog"/>
<property name="message" value="sc"/>
</bean>
</beans>
测试
public class MyTest {
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
User user = context.getBean("user",User.class);
user.getCat().shout();
user.getDog().shout();
}
}
1、byName (按名称自动装配)
修改beans.xml配置,增加一个属性 autowire="byName"
<bean id="user" class="com.sc.pojo.User" autowire="byName">
<property name="message" value="sc"/>
</bean>
测试可成功输出。
若将 cat 的bean id修改为 cat123,就会报错:空指针NullPointerException。
所以:
当一个bean节点带有 autowire byName的属性时:
1)先查找其类中所有的set方法名,例如setCat,获得将set去掉并且首字母小写的字符串,即cat。
2)再去spring容器中寻找是否有此字符串名称id的对象。
3)如果有,就取出注入;如果没有,就报空指针异常。
2、autowire byType (按类型自动装配)
修改beans.xml配置 : autowire="byType"
<bean id="dog" class="com.sc.pojo.Dog"/>
<bean id="cat2" class="com.sc.pojo.Cat"/>
<bean id="user" class="com.sc.pojo.User" autowire="byType">
<property name="message" value="sc"/>
</bean>
测试可成功输出。
若再加一行
<bean id="cat3" class="com.sc.pojo.Cat"/>
报错NoUniqueBeanDefinitionException
所以使用autowire byType需要保证:同一类型的对象,在spring容器中唯一。
3、使用注解自动装配
1)在spring配置文件中引入context文件头
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
2)开启属性注解支持!
<context:annotation-config/>
此时配置文件内容
<context:annotation-config/>
<bean id="dog" class="com.sc.pojo.Dog"/>
<bean id="cat" class="com.sc.pojo.Cat"/>
<bean id="user" class="com.sc.pojo.User"/>
3)将User类中的set方法去掉,使用@Autowired注解
package com.sc.pojo1;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import javax.annotation.Resource;
public class User {
@Autowired
@Qualifier("dog2")
private Dog dog;
@Resource(name = "cat2")
private Cat cat;
@Value("sundy")
private String message;
public Dog getDog() {
return dog;
}
public Cat getCat() {
return cat;
}
public String getMessage() {
return message;
}
@Override
public String toString() {
return "User{" +
"dog=" + dog +
", cat=" + cat +
", message='" + message + '\'' +
'}';
}
}
@Autowired是根据类型自动装配的,加上@Qualifier则可以根据byName的方式自动装配
@Qualifier不能单独使用。 <bean id="dog2" class="com.sc.pojo1.Dog"/>配置文件中id='dog2'应和@Qualifier("dog2") 一致,否则报错
@Resource如有指定的name属性,先按该属性进行byName方式查找装配;
其次再进行默认的byName方式进行装配;
如果以上都不成功,则按byType的方式自动装配。
都不成功,则报异常。
@Autowired与@Resource都可以用来装配bean。都可以写在字段上,或写在setter方法上。
@Autowired默认按类型装配(属于spring规范),默认情况下必须要求依赖对象必须存在,如果要允许null 值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果想使用名称装配可以结合@Qualifier注解进行使用。
@Resource(属于J2EE复返),默认按照名称进行装配,名称可以通过name属性进行指定。如果没有指定name属性,当注解写在字段上时,默认取字段名进行按照名称查找,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。
它们的作用相同都是用注解方式注入对象,但执行顺序不同。@Autowired先byType,@Resource先byName。
配置扫描哪些包下的注解
<!--指定注解扫描包-->
<context:component-scan base-package="com.sc.pojo"/>
在指定包下编写类,增加注解
@Component("user")
// 相当于配置文件中 <bean id="user" class="当前注解的类"/>
public class User {
public String name = "sc";
}
为了更好的进行分层,Spring可以使用其它三个注解,功能一样,目前使用哪一个功能都一样。
@Controller:web层
@Service:service层
@Repository:dao层
写上上述这些注解,就相当于将这个类交给Spring管理装配了
4、使用javaconfig的方式配置Spring
实体类:
package com.sc.pojo3;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component//将这个类标注为Spring的一个组件,放到容器中!
public class User2 {
private String name ;
public String getName() {
return name;
}
@Value("sundy2")
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
配置类:
package com.sc.pojo3;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
//完全不使用xml配置
@Configuration//代表这是一个配置类
public class JavaConfig {
@Bean//通过方法注册一个bean,这里的返回值就Bean的类型,方法名就是bean的id!
public User2 getUser(){
return new User2();
}
}
测试:
@Test
public void test5(){
ApplicationContext context = new AnnotationConfigApplicationContext(JavaConfig.class);
User2 user = context.getBean("getUser", User2.class);
System.out.println(user.getName());
System.out.println(user);
}
网友评论