Spring学习04-初识Spring(IoC&DI)

Spring学习04-初识Spring(IoC&DI)

1.1Spring是什么

Spring框架是一个开放源代码的J2EE应用程序框架

这里罗列了Spring的六大定义。

1、Spring的核心是一个轻量级(Lightweight)的容器(Container)。
2、Spring是实现IoC(Inversion of Control)容器和非入侵性(No intrusive)的框架。
3、Spring提供AOP(Aspect-oriented programming)概念的实现方式。
4、Spring提供对持久层(Persistence)、事物(Transcation)的支持。
5、Spring供MVC Web框架的实现,并对一些常用的企业服务API(Application Interface)提供一致的模型封装。
6、Spring提供了对现存的各种框架(Structs、JSF、Hibernate、Ibatis、Webwork等)相整合的方案。

1.2 Spring quick start

这里推荐使用Maven项目工具创建Spring!

首先第一步创建Maven项目,只需要创建最普通的Maven项目即可,不需要使用骨架。

在这里插入图片描述

第二步,取个名字点击Finish即可

在这里插入图片描述

第三步,由于这里想要创建多个Module,所以建议把src全部删除

在这里插入图片描述

删除之后,我们就得到了一个非常干净的Maven项目。

导入依赖

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
<!--            使用spring-webmvc可以讲spring 6大核心一次性导入,不然需要写6个依赖-->
            <artifactId>spring-webmvc</artifactId> 
            <version>5.1.9.RELEASE</version>
        </dependency>
<!--        测试使用junit-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.1</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

下面还需要增加一些项目构建时候的配置

 <build>
     <!-- Java项目都要编译成class字节码文件运行,idea讲这些文件放在target中,每次编译都会更新,但不一定会把配置文件如mybatis中的mapper.xml复制一份放入target中,这段代码可以把所有的xml文件复制一份到target中。-->
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
<!--        由于SpringMVC支持的Java版本较低,所以在Maven中我们设定了使用Java8-->
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>8</source>
                    <target>8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

可以点击右边Maven查看jar包是否都导入成功。

在这里插入图片描述

到现在基本配置都已完成,可以开始了

现在创建Module,一样是普通maven项目,这里取名为Spring01-IoC

在这里插入图片描述

public class Hello {
    private String name;

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

    public void show(){
        System.out.println("Hello,"+ name );
    }
}

在resource目录下创建文件为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">

   <!--bean就是java对象 , 由Spring创建和管理-->
    <!-- 通过id找到bean, 每个bean对应了一个class,class的位置是从java目录下开始的-->
   <bean id="hello" class="Hello">
           <!-- property属性设置,spring会调用类中的set方法进行值的注入-->
           <!-- name属性必须是set方法后的名字-->
       <property name="name" value="Spring"/>
   </bean>
</beans>

使用junit测试

public class JunitTest {
    @Test
    public void test1(){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Hello hello = context.getBean("hello", Hello.class);
        hello.show();
    }
}
//Hello,Spring

ApplicationContext 相当于一个工厂,我们并没有手动的new实例化对象,而是告诉了ApplicationContext 我们需要得到hello的这个类的对象,它就返回一个对象。

quick start 就完成了!

1.3 什么是Spring IoC 和 DI

再讲之前,先举个例子

比如说,在A类对象中,有B类对象的依赖。A类对象中的B 直接耦合死 为 B b = new B(); 这时候B类的控制权完全在A类手上,就是控制权在调用方。

当有新的需求来临的时,需要修改B类为C类时,我们只能在A类中修改原代码。这就已经违背了开闭原则。

在这里插入图片描述

为了使A类与B类解耦,我们首先想到的就是使用set方法,对象的实例化过程并不由A类控制,又服务方控制,想要注入什么样的实例对象就注入什么样的。

此时控制权就已经不在A类手上,反转到服务方了,这就实现了控制反转,而实现控制反转的方式就是使用了依赖注入set方法。

将上图的需求,修改为使用 IoC 的调用代码方式。就是将代码的控制权从调用方修改为被调用方,意味着,代码的调用权转移给被调用方(我们也称为服务方),不用修改调用方的代码,只要修改配置文件就实现对象的切换。将 A 类调用 B 类的对象修改为 C 类的对象,修改的是被调用方的配置文件的代码,所以代码的调用权转移到了被调用方。通过控制反转,我们可以实现增加模块或者移除模块统一由配置文件关联,所以增加或者移除模块配置 XML 配置文件即可。

在这里插入图片描述

这里就可利用Java的多态,可以使用父类的继承类注入,也可以使用接口的实现类注入。

所以

控制反转(IoC)= 依赖注入 + 面向接口的编程思想的实现

其实在spring quick start中就是用了set方法注入name,name的名字并不由hello来控制,而是由服务方决定。

1.4 IoC 加载的扩展

1.引用set注入

现在我们用User类封装了了一个用户的一些信息,有user的Id,Name和Record(依赖),Record 又封装了一些信息。都分别写好了无参构造和全参构造和getter&setter方法,重写toString()方法。

public class User {
    private int UserId;
    private String UserName;
    private UserRecord userRecord;

    public User() {
    }

    public User(int userId, String userName, UserRecord userRecord) {
        UserId = userId;
        UserName = userName;
        this.userRecord = userRecord;
    }

    public int getUserId() {
        return UserId;
    }

    public void setUserId(int userId) {
        UserId = userId;
    }

    public String getUserName() {
        return UserName;
    }

    public void setUserName(String userName) {
        UserName = userName;
    }

    public UserRecord getUserRecord() {
        return userRecord;
    }

    public void setUserRecord(UserRecord userRecord) {
        this.userRecord = userRecord;
    }

    @Override
    public String toString() {
        return "User{" +
                "UserId=" + UserId +
                ", UserName='" + UserName + '\'' +
                ", userRecord=" + userRecord +
                '}';
    }
}
public class UserRecord {
    private int balance;
    private String LastLoginLocation;
    private String LastLoginTime;
    private int LoginTimes;

    public UserRecord() {
    }

    public UserRecord(int balance, String lastLoginLocation, String lastLoginTime, int loginTimes) {
        this.balance = balance;
        LastLoginLocation = lastLoginLocation;
        LastLoginTime = lastLoginTime;
        LoginTimes = loginTimes;
    }

    public int getBalance() {
        return balance;
    }

    public void setBalance(int balance) {
        this.balance = balance;
    }

    public String getLastLoginLocation() {
        return LastLoginLocation;
    }

    public void setLastLoginLocation(String lastLoginLocation) {
        LastLoginLocation = lastLoginLocation;
    }

    public String getLastLoginTime() {
        return LastLoginTime;
    }

    public void setLastLoginTime(String lastLoginTime) {
        LastLoginTime = lastLoginTime;
    }

    public int getLoginTimes() {
        return LoginTimes;
    }

    public void setLoginTimes(int loginTimes) {
        LoginTimes = loginTimes;
    }

    @Override
    public String toString() {
        return "UserRecord{" +
                "balance=" + balance +
                ", LastLoginLocation='" + LastLoginLocation + '\'' +
                ", LastLoginTime='" + LastLoginTime + '\'' +
                ", LoginTimes=" + LoginTimes +
                '}';
    }

创建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">

    <bean id="userRecord" class="com.dao.UserRecord">
        <property name="balance" value="100"/>
        <property name="loginTimes" value="1"/>
        <property name="lastLoginLocation" value="ShangHai"/>
        <property name="lastLoginTime" value="24:00"/>
    </bean>
    
     <bean id="user" class="com.dao.User">
        <property name="userId" value="1"/>
        <property name="userName" value="admin"/>
        <property name="userRecord" ref="userRecord"/>
    </bean>
</beans>

用property属性是使用set方法注入,value代表注入基本数据类型,ref代表注入引用数据类型,直接用生产该类的bean id即可。

下面来测试一下

    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        User user = (User) context.getBean("user");//默认范围的是object类,后面加了User.class就返回User类
        System.out.println(user);
    }
//User{UserId=1, UserName='admin', userRecord=UserRecord{balance=100, LastLoginLocation='ShangHai', LastLoginTime='24:00', LoginTimes=1}}
2.constructor注入

我们还写了构造方法,Spring也支持构造方法注入。

    <bean id="userRecord" class="com.dao.UserRecord">
        <constructor-arg index="0" value="10"/>
        <constructor-arg index="1" value="24:00"/>
        <constructor-arg index="2" value="ShangHai"/>
        <constructor-arg index="3" value="1"/>
    </bean>

index表示写构造方法时的参数顺序,测试后还是一样。

3.多种数据结构注入

Spring也支持很多数据结构的注入

这里又List,Map,Set,Array等属性。

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

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


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

    public void setHobbys(List<String> hobbys) {
        this.hobbys = hobbys;
    }

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

    public void setGames(Set<String> games) {
        this.games = games;
    }

    public void setWife(String wife) {
        this.wife = wife;
    }

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

    public void show(){
        System.out.println("name="+ name
                + ",books="
        );
        for (String book:books){
            System.out.print("<<"+book+">>\t");
        }
        System.out.println("\n爱好:"+hobbys);

        System.out.println("card:"+card);

        System.out.println("games:"+games);

        System.out.println("wife:"+wife);

        System.out.println("info:"+info);

    }
}
 <bean id="student" class="com.dao.Student">
        <property name="name" value="小明"/>
        <property name="books">
            <array>
                <value>西游记</value>
                <value>红楼梦</value>
                <value>水浒传</value>
            </array>
        </property>
        <property name="hobbys">
            <list>
                <value>听歌</value>
                <value>看电影</value>
                <value>爬山</value>
            </list>
        </property>
        <property name="card">
            <map>
                <entry key="中国邮政" value="456456456465456"/>
                <entry key="建设" value="1456682255511"/>
            </map>
        </property>
        <property name="games">
            <set>
                <value>LOL</value>
                <value>BOB</value>
                <value>COC</value>
            </set>
        </property>
        <property name="wife"><null/></property>
        <property name="info">
            <props>
                <prop key="学号">20190604</prop>
                <prop key="性别"></prop>
                <prop key="姓名">小明</prop>
            </props>
        </property>
    </bean>

测试一下

    @Test
    public void test1(){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Student student = (Student) context.getBean("student");
        student.show();
    }
/*name=小明,books=
<<西游记>>	<<红楼梦>>	<<水浒传>>	
爱好:[听歌, 看电影, 爬山]
card:{中国邮政=456456456465456, 建设=1456682255511}
games:[LOL, BOB, COC]
wife:null
info:{姓名=小明, 学号=20190604, 性别=男}
    * */
4.P命名空间

在beans属性中新加入 导入约束 : xmlns:p="http://www.springframework.org/schema/p"

就是简化版的properties.

<?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"  
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
    
 <!--P(属性: properties)命名空间 , 属性依然要设置set方法-->
<bean id="userRecord2" class="com.dao.UserRecord" p:balance="10" p:lastLoginLocation="BeiJing" p:loginTimes="10" p:lastLoginTime="10:00"/>
</beans>
5.C命名空间

导入约束 : xmlns:c="http://www.springframework.org/schema/c"

 <!--C(构造: Constructor)命名空间 , 属性依然要设置set方法-->
<bean id="userRecord2" class="com.dao.UserRecord" c:balance="10" c:lastLoginLocation="BeiJing" c:loginTimes="10" c:lastLoginTime="10:00"/>

测试代码和上面一样,就不多演示了。

6.bean属性中的其他参数
 <bean id="admin" class="com.dao.User" scope="singleton" lazy-init="false" autowire="byName"/>
Scope作用域

有四种属性

Singleton

当一个bean的作用域为Singleton,那么Spring IoC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。Singleton是单例类型

Prototype

当一个bean的作用域为Prototype,表示一个bean定义对应多个对象实例

Request

当一个bean的作用域为Request,表示在一次HTTP请求中,一个bean定义对应一个实例

Session

当一个bean的作用域为Session,表示在一个HTTP Session中,一个bean定义对应一个实例

lazy-init

设置bean对象是否懒加载,如果设为true,则应用第一次用到bean时才实例化对象,否则在初始化spring容器时加载*单例*bean对象。(*非单例不实例化**)*

abstract

设置bean是否为抽象类,默认abstract=“false”,如果设为true,将不能被实例化;

destroy-method

它的作用是在销毁bean之前可以执行指定的方法。注意:必须满足scope=“singleton”,并且destroy方法参数个数不能超过1,并且参数类型只能为boolean。

7.自动装配

自动装配直到是自动注入引用。

这个例子中User中有一个Dog的依赖。

public class Dog {
    public void sound(){
        System.out.println("wang- wang - wang");
    }
}

public class User {
    private Dog dog;

    public User(Dog dog) {
        this.dog = dog;
    }

    public User() {
    }

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

现在我们配置xml 注意autowire

<?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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="dog" class="com.dao.Dog"/>
    <bean id="user" class="com.dao.User" autowire="byName"/>
</beans>

测试

    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        User user = (User) context.getBean("user");
        user.runDog();
    }
//wang- wang - wang

autowire

byName

  1. 将查找其类中所有的set方法名,例如setCat,获得将set去掉并且首字母小写的字符串,即cat。

  2. 去spring容器中寻找是否有此字符串名称id的对象。

  3. 如果有,就取出注入;如果没有,就报空指针异常。

byType

  1. 同一类型的对象,在spring容器中唯一。如果不唯一,会报不唯一的异常。
8.使用注解开发

jdk1.5开始支持注解,spring2.5开始全面支持注解。

准备工作:利用注解的方式注入属性。

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/>
@Autowired&@Qualifier
  • @Autowired是按类型自动转配的,不支持id匹配。
  • 需要导入 spring-aop的包!
  • @Autowired是根据类型自动装配的,加上@Qualifier则可以根据byName的方式自动装配
  • @Qualifier不能单独使用。
<?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: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/>
    <bean id="dog" class="com.dao.Dog"/>
    <bean id="user" class="com.dao.User"/>
</beans>

在User类中加入注解

    @Autowired
    @Qualifier(value = "dog")
    private Dog dog;

测试代码不变。

@Resource
  • @Resource如有指定的name属性,先按该属性进行byName方式查找装配;
  • 其次再进行默认的byName方式进行装配;
  • 如果以上都不成功,则按byType的方式自动装配。
  • 都不成功,则报异常。

@Autowired与@Resource异同:

1、@Autowired与@Resource都可以用来装配bean。都可以写在字段上,或写在setter方法上。

2、@Autowired默认按类型装配(属于spring规范),默认情况下必须要求依赖对象必须存在,如果要允许null 值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果我们想使用名称装配可以结合@Qualifier注解进行使用

3、@Resource(属于J2EE复返,并不属于spring),默认按照名称进行装配,名称可以通过name属性进行指定。如果没有指定name属性,当注解写在字段上时,默认取字段名进行按照名称查找,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。

它们的作用相同都是用注解方式注入对象,但执行顺序不同。@Autowired先byType,@Resource先byName。

@Component

增加context属性,扫描指包下的注解。

<?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:component-scan base-package="com.pojo"/>
    
</beans>
@Component("user")
@Scope("singleton") //相当于bean 标签的scope
// 相当于配置文件中 <bean id="user" class="当前注解的类"/>
public class User {
    @Value("admin")
    // 相当于配置文件中 <property name="name" value="秦疆"/>
    public String name;
}
    @Test
    public void test(){
        ApplicationContext applicationContext =
                new ClassPathXmlApplicationContext("applicationContext.xml");
        User user = (User) applicationContext.getBean("user");
        System.out.println(user.name); //admin
    }

@Component三个衍生注解

为了更好的进行分层,Spring可以使用其它三个注解,功能一样,目前使用哪一个功能都一样。

  • @Controller:web层
  • @Service:service层
  • @Repository:dao层

写上这些注解,就相当于将这个类交给Spring管理装配了!

@Configuration &@Bean

编写一个实体类

@Component  //将这个类标注为Spring的一个组件,放到容器中!
public class Dog {
    @Value("dogg")
    public String name;
}

编写java配置文件

@Configuration //代表这是一个配置类 就等同于xml文件
//@Import(MyConfig2.class)  //导入合并其他配置类,类似于配置文件中的 inculde 标签
public class MyConfig {
    @Bean //通过方法注册一个bean,这里的返回值就Bean的类型,方法名就是bean的id!
    public Dog dog(){
        return new Dog();
    }
}

测试,此次加载用的java配置类加载

@Test
public void test2(){
   ApplicationContext applicationContext =
           new AnnotationConfigApplicationContext(MyConfig.class);
   Dog dog = (Dog) applicationContext.getBean("dog");
   System.out.println(dog.name);//dogg
}

Reference

主要材料《Spring 5核心原理与30个类手写实战》

ioc https://blog.csdn.net/weixin_40867255/article/details/91049459

ioc2:https://www.cnblogs.com/maigy/p/10808308.html

java:https://www.zhihu.com/question/353412842/answer/877602348

狂神笔记:https://mp.weixin.qq.com/s/VM6INdNB_hNfXCMq3UZgTQ

https://blog.csdn.net/a745233700/article/details/113840727

https://blog.csdn.net/weixin_44227923/article/details/87697194

spring bean 标签https://blog.csdn.net/ZixiangLi/article/details/87937819

狂神注解开发:https://mp.weixin.qq.com/s/dCeQwaQ-A97FiUxs7INlHw

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值