美文网首页
Maven项目&Spring IOC基础

Maven项目&Spring IOC基础

作者: 乘风破浪的姐姐 | 来源:发表于2021-12-29 18:33 被阅读0次

在使用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);
     }

相关文章

网友评论

      本文标题:Maven项目&Spring IOC基础

      本文链接:https://www.haomeiwen.com/subject/kdfrqrtx.html