美文网首页
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