Spring详解

1. IOC

1.1 拓展

现代化的Java开发,就是基于Spring的开发

  • SpringBoot
    • 快速开发的脚手架
    • 基于SpringBoot可以快速开发微服务
    • 约定大于配置
  • SpringCloud
    • SpringCloud是基于SpringBoot实现的

学习SpringBoot的前提是掌握Spring以及SpringMVC,SpringBoot起到承上启下 的作用;

弊端:配置十分繁琐,人称:“配置地狱”;

1.2 IOC理论推导

问题展示

对象包:(接口)

package com.kangzhu.dao;
public interface UserDao {
    public void getUser();
}

对象包:(实现1)

package com.kangzhu.dao;
public class UserDaoImpl implements UserDao{
    @Override
    public void getUser() {
        System.out.println("I am the user");
    }
}

对象包:(实现2)

package com.kangzhu.dao;
public class UserDaoMysqlImpl implements UserDao{
    @Override
    public void getUser() {
        System.out.println("I am the user found by Mysql");
    }
}

服务包:(接口)

package com.kangzhu.service;
public interface UserService {
    public void getUser();
}

服务包:(实现)

package com.kangzhu.service;
import com.kangzhu.dao.UserDao;
import com.kangzhu.dao.UserDaoImpl;
import com.kangzhu.dao.UserDaoMysqlImpl;

public class UserServiceImpl implements UserService{
//    private UserDao userDao = new UserDaoImpl();
    private UserDao userDao = new UserDaoMysqlImpl();
    @Override
    public void getUser() {
//        业务层调用dao层
        userDao.getUser();
    }
}

客户端(测试类)

import com.kangzhu.service.UserServiceImpl;
public class MyTest {
    public static void main(String[] args) {
//        用户实际调用的是业务层
        UserServiceImpl userService = new UserServiceImpl();
        userService.getUser();
    }
}

问题在哪呢?

客户端的需求可能会影响我们的代码,如果说客户端想要通过普通的方式查询用户,服务包:(实现)中需要引入对象包(实现1),如果客户端想要通过Mysql的方式查询用户,需要引入对象包(实现2);

需要根据客户端的需求修改源代码,如果代码量很大,修改一次的成本就会十分昂贵!

解决方法(set注入)

改变服务实现类

package com.kangzhu.service;
import com.kangzhu.dao.UserDao;
public class UserServiceImpl implements UserService{
    private UserDao userDao = null;
    // set注入
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
    @Override
    public void getUser() {
        userDao.getUser();
    }
}

客户端:

import com.kangzhu.dao.UserDaoMysqlImpl;
import com.kangzhu.service.UserServiceImpl;

public class MyTest {
    public static void main(String[] args) {
//        用户实际调用的是业务层
        UserServiceImpl userService = new UserServiceImpl();
        userService.setUserDao(new UserDaoMysqlImpl());
        userService.getUser();
    }
}
  • 之前,程序是主动对象,控制权在程序员手上;
  • 使用了set注入后,程序不再具有主动性,而是变成了接收对象;

这种思想从本质上解决了问题,程序员不用再去管理对象的创建;系统的耦合性降低,程序员可以专注在业务实现上;

这就是IOC的原型!

IOC的本质

  • 控制反转,是一种设计思想;
  • 所谓控制反转就是:获得依赖对象的方式反转了;

2. HelloSpring

  1. 编写实体类

    package com.kangzhu.pojo;
    
    public class Hello {
        private String str;
        public String getStr() {
            return str;
        }
        public void setStr(String str) {
            this.str = str;
        }
        @Override
        public String toString() {
            return "Hello{" +
                    "str='" + str + '\'' +
                    '}';
        }
    }
    
  2. 添加配置文件(名称任意,这里是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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    
    <!--使用Spring来创建对象,在Spring这些都称为Bean
        类型 变量名 = new 类型();
    
        id = 变量名
        class = new的对象
        property 相当于给对象中的属性设置一个值
    -->
        <bean id="hello" class="com.kangzhu.pojo.Hello">
            <property name="str" value="Spring"/>
        </bean>
    </beans>
    
  3. 测试

    import com.kangzhu.pojo.Hello;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class MyTest {
        public static void main(String[] args) {
    //        获取Spring的上下文对象
            ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
    //        我们的对象都在Spring中管理了,我们要使用,直接去里面取出来就可以了
            Hello hello = (Hello) context.getBean("hello");
            System.out.println(hello.toString());
        }
    }
    

控制:传统应用程序对象是由程序控制创建的,使用Spring后,对象是由Spring来创建的;

反转:程序本身不创建对象,而变成被动的接收对象

3. IOC创建对象方式

  1. 使用无参构造创建对象,默认

  2. 使用有参构造创建对象

    • 下标赋值

      <bean id="user1" class="com.kangzhu.pojo.User">
          <constructor-arg index="0" value="kangzhu1"/>
      </bean>
      
    • 根据参数类型赋值

      <!--但是假设有两个参数的类型相同,这种方式不适用-->
      <bean id="user2" class="com.kangzhu.pojo.User">
      <constructor-arg type="java.lang.String" value="kangzhu2"/>
      </bean>
      
    • 根据参数名赋值

      <bean id="user3" class="com.kangzhu.pojo.User">
          <constructor-arg name="name" value="kangzhu3"/>
      </bean>
      

4. Spring配置

4.1 别名

<alias name="user" alias="aliasUser"/>

这里的name是bean中的id,也就是对象名,alias是别名;

4.2 Bean的配置

id:bean的唯一标识符,也就是对象名;

class:bean对象所对应的权限命名(包名.类名);

name:也是别名,而且name更高级,可以同时取多个别名(不同别名间通过空格,逗号,分号等等进行分割);

4.3 import

import一般用于团队开发使用,它可以将多个配置文件导入合并为一个;

假设项目中有多个人开发,这三个人负责不同的类开发,不同的类需要注册在不同的bean中,我们可以利用import将所有人的beans.xml合并为一个总的;

  • programmer1 --> beans1.xml
  • programmer2 --> beans2.xml
  • programmer3 --> beans3.xml

总的是applicationContext.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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <import resource="beans1.xml"/>
    <import resource="beans2.xml"/>
    <import resource="beans3.xml"/>

</beans>

如果说遇到相同的对象名,后面的会覆盖前面的;

5. 依赖注入

  • 依赖:bean对象的创建依赖于容器;

  • 注入:bean对象中的所有属性由容器注入;

5.1 构造器注入

上面的IOC创建对象方式中的使用有参构造创建对象就是构造器注入;

5.2 Set方式注入

这里创建两个实体类:

package com.kangzhu.pojo;

import java.util.*;

public class Student {
    private String name;
    private Address address;
    private String[] books;
    private List<String> hobbies;
    private Map<String, String> card;
    private Set<String> games;
    private String wife;
    private Properties info;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    public String[] getBooks() {
        return books;
    }

    public void setBooks(String[] books) {
        this.books = books;
    }

    public List<String> getHobbies() {
        return hobbies;
    }

    public void setHobbies(List<String> hobbies) {
        this.hobbies = hobbies;
    }

    public Map<String, String> getCard() {
        return card;
    }

    public void setCard(Map<String, String> card) {
        this.card = card;
    }

    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;
    }

    public Properties getInfo() {
        return info;
    }

    public void setInfo(Properties info) {
        this.info = info;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", address=" + address +
                ", books=" + Arrays.toString(books) +
                ", hobbies=" + hobbies +
                ", card=" + card +
                ", games=" + games +
                ", wife='" + wife + '\'' +
                ", info=" + info +
                '}';
    }
}
package com.kangzhu.pojo;

public class Address {
    private String address;
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
}

根据数据类型来看set注入:

<?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 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="address" class="com.kangzhu.pojo.Address"/>
    <bean id="student" class="com.kangzhu.pojo.Student">
<!--        第一种,基本数据类型注入-->
        <property name="name" value="kangzhu"/>

<!--        第二种,引用注入-->
        <property name="address" ref="address"/>

<!--        第三种,数组注入-->
        <property name="books">
            <array>
                <value>哈利波特</value>
                <value></value>
                <value>箱子里的人</value>
            </array>
        </property>

<!--        第四种,list注入-->
        <property name="hobbies">
            <list>
                <value>电影</value>
                <value>打游戏</value>
            </list>
        </property>

<!--        第五种,map注入-->
        <property name="card">
            <map>
                <entry key="学生卡" value="123456"/>
                <entry key="身份证" value="123456"/>
            </map>
        </property>

        <property name="games">
            <set>
                <value>LOL</value>
                <value>COC</value>
                <value>BOB</value>
            </set>
        </property>

<!--        注入为null值-->
        <property name="wife">
            <null/>
        </property>

<!--        properties注入-->
        <property name="info">
            <props>
                <prop key="学号">123</prop>
                <prop key="姓名">糠猪</prop>
            </props>
        </property>
    </bean>
</beans>

5.3 拓展方式注入

可以使用p命名空间和c命名空间进行注入:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"

       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

<!--    p命名空间注入,可以直接注入属性的值-->
    <bean id="user" class="com.kangzhu.pojo.User" p:name="糠猪" p:age="18"/>

<!--    c命名空间注入-->
    <bean id="user2" class="com.kangzhu.pojo.User" c:name="糠猪2" c:age="18"/>
</beans>

note

  1. p命名空间和c命名空间不能直接使用,需要导入xml约束:

    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:c="http://www.springframework.org/schema/c"
    
  2. p代表properties,相当于set注入,c代表constructor,相当于构造器注入;

6. bean作用域

  1. The Singleton Scope(默认)

    单例模式,全局共享一个实例,显式的设置单例模式

    <bean id="user" class="com.kangzhu.pojo.User" p:name="糠猪" p:age="18" scope="singleton"/>
    

    测试:

    @Test
    public void test() {
        ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("userBeans.xml");
        User user1 = (User) classPathXmlApplicationContext.getBean("user");
        User user2 = (User) classPathXmlApplicationContext.getBean("user");
        System.out.println(user1 == user2);
    }
    

    返回结果式true;

  2. The Prototype Scope

    原型模式

    <bean id="user" class="com.kangzhu.pojo.User" p:name="糠猪" p:age="18" scope="prototype"/>
    

    每次getBean的时候都会返回一个新的对象,同样的测试返回结果式false;

  3. 其余的request、session、application这些只能在web开发中使用到;

7. Bean的自动装配

  • 自动装配是Spring满足bean依赖注入的一种方式;
  • Spring会在上下文中自动寻找,并自动给bean装配属性;

在Spring中有三种装配的方式:

  1. 在xml中显示的配置
  2. 在Java中显示配置
  3. 隐式的自动装配

7.1 测试环境搭建

实体类有People、Cat、Dog:

package com.kangzhu.pojo;

public class People {
    private Cat cat;
    private Dog dog;
    private String name;

    public Cat getCat() {
        return cat;
    }

    public void setCat(Cat cat) {
        this.cat = cat;
    }

    public Dog getDog() {
        return dog;
    }

    public void setDog(Dog dog) {
        this.dog = dog;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "People{" +
                "cat=" + cat +
                ", dog=" + dog +
                ", name='" + name + '\'' +
                '}';
    }
}
package com.kangzhu.pojo;

public class Cat {
    public void shout() {
        System.out.println("喵喵喵");
    }
}
package com.kangzhu.pojo;

public class Dog {
    public void shout() {
        System.out.println("汪汪汪");
    }
}

配置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
       http://www.springframework.org/schema/context 
       http://www.springframework.org/schema/context/spring-context.xsd">

    <bean id="cat" class="com.kangzhu.pojo.Cat"/>
    <bean id="dog" class="com.kangzhu.pojo.Dog"/>

    <bean id="people" class="com.kangzhu.pojo.People">
        <property name="name" value="kanghzu"/>
        <property name="dog" ref="dog"/>
        <property name="cat" ref="cat"/>
    </bean>
</beans>

7.2 byName自动装配

<bean id="people" class="com.kangzhu.pojo.People" autowire="byName">
    <property name="name" value="kanghzu"/>
</bean>

会自动在容器上下文中查找,和自己对象set方法后面的值对应的beanId:

public void setCat(Cat cat) {
    this.cat = cat;
}

public void setDog(Dog dog) {
    this.dog = dog;
}

7.3 byType自动装配

<bean id="people" class="com.kangzhu.pojo.People" autowire="byType">
    <property name="name" value="kanghzu"/>
</bean>

会自动在容器上下文中查找,和自己对象属性类型相同的bean(这里bean里面的id也可以省略);

假设有两个Dog类型会报错,因此必须保证类型全局唯一;

小结

  • byName的时候,需要保证所有的bean的id唯一,并且这个bean的id需要和自动注入的属性的set方法的值一致;
  • byType的时候,需要保证所有的bean的class唯一,并且这个bean需要和自动注入的属性的类型一致;

7.4 使用注解实现自动装配

jdk1.5开始支持的注解,Spring2.5开始支持注解;

注解的配置bi

使用注解需要:

  1. 导入约束,context约束;

  2. 配置注解的支持:

    <?xml version="1.0" encoding="UTF-8"?>
    <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.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context.xsd">
    
        <!--开启注解支持-->
        <context:annotation-config/>
    
    </beans>
    

    @Autowired

    直接在属性上使用即可:(People的部分代码)

    public class People {
        @Autowired
        private Cat cat;
        @Autowired
        private Dog dog;
        private String name;
    }
    

    同时因为是使用反射实现自动装配,所以可以不写相应的set方法;

    默认情况是byType, 类型相同看neme,如果没有匹配的name的话会报错;

    也可以显示指定:

    // 指定IOC容器中的对象
    @Qualifier(value="dog")
    

    其它:

    // 如果某个字段标记了这个注解,说明这个字段可以为null
    @Nullable
    
    //如果显示定义了@Autowired的require属性为false,说明这个对象可以为null,否则不允许为空
    @Autowired(required = false)
    

8. 使用注解开发

在Spring4之后,要使用注解开发,必须要保证aop的包导入了;

使用注解需要导入context约束,增加注解的支持;

  1. bean

    package com.kangzhu.pojo;
    
    import org.springframework.stereotype.Component;
    //@Component:组件,放在类上,说明这个类被Spring管理了,就是bean
    @Component
    //等价于<bean id="user" class="com.kangzhu.pojo.User"/>
    public class User {
        @Value("kangzhu")
        public String name;
    //    相当与<property name="name" value="kangzhu"/>
    }
    
  2. 衍生注解

    mvc三层架构中:

    • dao: @Repository
    • service: @Service
    • controller: @Controller

    这三个注解和@Component功能是一样的;

  3. 自动装配(上面)

  4. 作用域(@Scope)

    package com.kangzhu.pojo;
    
    import org.springframework.stereotype.Component;
    
    @Component
    @Scope("singleton") // 单例模式
    @Scope("prototype") // 原型模式
    public class User {
        @Value("kangzhu")
        public String name;
    }
    
  5. 最佳时间(见仁见智)

    • xml来管理bean;
    • 注解只负责完成属性的注入;

9. 使用Java的方式配置Spring

完全不使用Spring的xml配置!

Javaconfig是Spring的一个子项目,在Spring4之后,它成为了一个核心功能;

实体类

package com.kangzhu.pojo;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class User {
    @Value("kangzhu")
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                '}';
    }
}

配置类

package com.kangzhu.config;

import com.kangzhu.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

//这个也会被Spring容器托管,注册到容器中,因为它本来就是一个@Component
//@Configuration代表这是一个配置类,和applicationContext.xml一样
@Configuration
public class Config {
//    这个方法的名字就相当于bean标签中的id属性
    @Bean
    public User getUser() {
        return new User();
    }
}

测试类

import com.kangzhu.config.Config;
import com.kangzhu.pojo.User;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MyTest {
    @Test
    public void test() {
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(Config.class);
//        取的话需要使用方法名
        User user = (User) annotationConfigApplicationContext.getBean("getUser");
        System.out.println(user.getName());
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值