和我一起迎接明天的太阳吧

klaus08

焦虑源于行动的匮乏

2021-07-24 Spring

Spring优点

  • spring是一个开源的买南非框架
  • spring是一个轻量级的、非入侵式的框架
  • IOC、AOP
  • 支持事务处理,对框架整合的支持

spring就是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架。

IOC

控制反转,就是获得依赖对象的方式反转了,通过 xml 或注解获取指定对象。

经常说的DI(依赖注入)就是IOC的一种实现方式。

IOC创建对象的方式

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

  2. 使用有参构造函数

    <!-- 下标赋值 -->
    <bean id="exampleBean" class="examples.ExampleBean">
        <constructor-arg index="0" value="7500000"/>
        <constructor-arg index="1" value="42"/>
    </bean>
    
    <!-- 类型赋值 但同类型两个参数会歧义-->
    <bean id="exampleBean" class="examples.ExampleBean">
        <constructor-arg type="int" value="7500000"/>
        <constructor-arg type="java.lang.String" value="42"/>
    </bean>
    
    <!-- 参数名赋值 -->
    <bean id="exampleBean" class="examples.ExampleBean">
        <constructor-arg name="years" value="7500000"/>
        <constructor-arg name="ultimateAnswer" value="42"/>
    </bean>
    
    <!-- 引用 -->
    <bean id="beanOne" class="x.y.ThingOne">
        <constructor-arg ref="beanTwo"/>
        <constructor-arg ref="beanThree"/>
    </bean>
    <bean id="beanTwo" class="x.y.ThingTwo"/>
    <bean id="beanThree" class="x.y.ThingThree"/>
    

依赖注入(DI)

1. 构造器注入

就是上文中四种构造器。

2. Set方式注入

  • 依赖:bean对象的创建依赖于容器。
  • 注入:bean对象中的所有属性由容器来注入。

对象

Student.java

package com.klaus.pojo;

import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

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

注入

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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="student" class="com.klaus.pojo.Student">
        <!-- 普通值注入 -->
        <property name="name" value="刘佳伟"/>
        <!-- Bean注入, ref-->
        <property name="address" ref="address"/>
        <!-- 数组注入 -->
        <property name="books">
            <array>
                <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="2018012966"/>
            </map>
        </property>
        <!-- set -->
        <property name="games">
            <value>三国杀</value>
            <value>LOL</value>
        </property>
        <!-- null 注入 -->
        <property name="wife">
            <null/>
        </property>
        <!-- properties -->
        <property name="info">
            <props>
                <prop key="status">student</prop>
            </props>
        </property>

    </bean>

    <bean id="address" class="com.klaus.pojo.Address"/>
</beans>

3. 拓展方式注入

3.1 p命名空间

p标签可以直接注入。

不过需要加上xmlns:p="http://www.springframework.org/schema/p"

user.java

package com.klaus.pojo;

public class User {
    String name;

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

    public String getName() {
        return name;
    }

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

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"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- p ~ property -->
    <bean id="user" class="com.klaus.pojo.User" p:name="刘佳伟"/>

</beans>

3.2 c命名空间

必须有有参构造函数。

使用时需要加上xmlns:c="http://www.springframework.org/schema/c"xml 约束。

user.java

package com.klaus.pojo;

public class User {
    String name;
    int age;

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

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

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"
       xmlns:c="http://www.springframework.org/schema/c"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="user" class="com.klaus.pojo.User" c:age="18" c:name="刘佳伟"/>

</beans>

Bean的作用域

  1. 单例模式
    默认作用域,全局创建的只有这一个对象。

    例如:

    <?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:c="http://www.springframework.org/schema/c"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            https://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="user" class="com.klaus.pojo.User" c:age="18" c:name="刘佳伟" />
    
    </beans>
    
    @Test
    public void test2(){
        ApplicationContext context =
                new ClassPathXmlApplicationContext("user.xml");
        User user1 = context.getBean("user", User.class);
        User user2 = context.getBean("user", User.class);
        System.out.println(user1 == user2);
    
    }
    

    输出结果为true


  2. 原型模式

    每次从容器中get的都是新的对象。

    然后同样的测试示例,bean的作用范围改成prototype结果就是false。

    <bean id="user" class="com.klaus.pojo.User" c:age="18" c:name="刘佳伟" scope="prototype"/>
    
  3. 其余的request、session、application在web应用中。

Bean的自动装配

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

在Spring中由三种装配方式:

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

测试环境

// Cat.java
package com.klaus.pojo;

public class Cat {
    public void shout(){
        System.out.println("喵喵喵");
    }
}
// Dog.java
package com.klaus.pojo;

public class Dog {
    public void shout(){
        System.out.println("汪汪汪");
    }
}
// People.java
package com.klaus.pojo;

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

    public Dog getDog() {
        return dog;
    }

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

    public Cat getCat() {
        return cat;
    }

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

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "People{" +
                "dog=" + dog +
                ", cat=" + cat +
                ", name='" + name + '\'' +
                '}';
    }
}
<!-- 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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

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

    <bean id="people" class="com.klaus.pojo.People">
        <property name="name" value="刘佳伟"/>
        <property name="cat" ref="cat"/>
        <property name="dog" ref="dog"/>
    </bean>

</beans>

byName自动装配

**工作机制:**自动在容器上下文中查找,和自己对象 set 方法后面的值对应的 Bean id。

如下,依然可以正确运行。

<bean id="people" class="com.klaus.pojo.People" autowire="byName">
    <property name="name" value="刘佳伟"/>
</bean>

byType自动装配

**工作机制:**自动在容器上下文查找 和自己对象属性类型相同的 Bean。所以要保证类型唯一,否则会出错。

<!-- byType可以不需要id -->
<bean class="com.klaus.pojo.People" autowire="byType">

注:

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

使用注解实现自动装配

配置注解的支持<context:annotation-config/>

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

    <context:annotation-config/>

</beans>

@Autowired

直接在属性上使用即可;

使用 Autowired 可以不用编写 set 方法,前提是自动装配的属性在容器中存在,且名字符合 byName (还是通过byName实现的)。

当环境如下时

public class People {
    @Autowired
    private Dog dog;
    @Autowired
    private Cat cat;
    private String name;
    ...
}
<bean id="dog11" class="com.klaus.pojo.Dog"/>
<bean id="dog111" class="com.klaus.pojo.Dog"/>

这时候可以使用@Qualifier(value = "dog11")来指定。

public class People {
    @Autowired
    @Qualifier(value = "dog11")
    private Dog dog;
    @Autowired
    private Cat cat;
    private String name;
    ...
}
@Nullable	字段标记了这个注解,说明这个字段为null
@Autowired的require属性为false,说明这个对象可以为null,否则不允许为空

@Resourse

使用这个注解时,会先根据byName查找,然后byType,都找不到时才会报错。

<bean id="cat1" class="com.klaus.pojo.Cat"/>
<bean id="cat" class="com.klaus.pojo.Cat"/>
public class People {
    @Autowired
    @Qualifier(value = "dog11")
    private Dog dog;
    @Resource
    private Cat cat;

​ 这时,程序可以运行,通过byName的方式。

<bean id="cat1" class="com.klaus.pojo.Cat"/>
public class People {
    @Autowired
    @Qualifier(value = "dog11")
    private Dog dog;
    @Resource
    private Cat cat;

​ 这时,程序也可以运行,通过byType的方式。

<bean id="dog11" class="com.klaus.pojo.Dog"/>
<bean id="dog111" class="com.klaus.pojo.Dog"/>

​ 如果同样出现上面这种情况,使用@Resourse(name = "dog11")来指定。

public class People {
    @Autowired
    @Qualifier(value = "dog11")
    private Dog dog;
    @Resource(name = "cat1")
    private Cat cat;
    private String name;
    ...
}

下面是文档结构,个人感觉就是,IOC 容器会根据 People 里的类在beans.xml中已创建的实例中寻找对应的实例。寻找方总的来说有两种一是根据类名,二是根据类的种别,比如说Cat是一类,Dog是一类,如果同名的找不到,而同类型的又有很多个(如上面的 3 ),此时就要用特殊注解属性指定具体名称。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LBugCzAu-1627118636805)(https://gitee.com/man-ljw/PicBed/raw/master/6V@]93VA)][2IL5((FUVM0{T.png)


@Resource和@Autowired:

同:

  • 都是用来自动装配的,都可以放在属性上
  • @Autowired 通过byType的方式实现装配,要求该对象必须存在
  • @Resource 默认通过byName,如果找不到名字,则通过byType实现。如果两个都找不到,则报错

异:

  • @Autowired 通过byType的方式,@Resource 通过byName

使用注解开发

bean

@Component,放在类前

package com.klaus.pojo;

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

@Component
public class User {

    public String name ;

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

属性注入

@Value(“klaus”),放在属性前,或者属性的 set 方法前

package com.klaus.pojo;

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

@Component
public class User {
//     相当于 <property name="name" value="klaus"/>
//    @Value("klaus")
    public String name ;

    @Value("klaus")
    public void setName(String name) {
        this.name = name;
    }
}

@Component 衍生的注解

@Component有几个衍生注解,按mvc分为三层:

  • dao层 [ @Repository ]
  • service层 [ @Service ]
  • controller层 [ @Controller ]

上述四个注解都是一样的、同级的,都是把某个类注册到Spring容器中,装配Bean。

自动装配

@Autowired

@Nullable

@Resource

作用域

@Scope("singleton")   // 放在类前,还是
package com.klaus.pojo;

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

// 单例模式 ,相当于<bean id="user" class="com.klaus.pojo.User" scope="prototype" />
@Scope("singleton")
public class User {

    public String name ;

    @Value("klaus")
    public void setName(String name) {
        this.name = name;
    }
}

小结

xml于注解:

  • xml 适用范围更广。
  • 注解维护更复杂

使用Java方式进行配置


完全不使用 Spring 的 xml 配置,全权交给 Java 做。

文件结构:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KiDM0i4s-1627118636807)(https://gitee.com/man-ljw/PicBed/raw/master/)]}]RV04GR6PCLKH5OXH{ZSB.png)

实体类 User.java

package com.klaus.pojo;

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

// 这个类注册到容器中,被Spring接管
@Component
public class User {
    private String name;

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

    public String getName() {
        return name;
    }
// 属性注入
    @Value("klaus")
    public void setName(String name) {
        this.name = name;
    }
}

配置类 MyConfigure.java

package com.klaus.configure;

import com.klaus.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

// 代表这是一个配置类,本身就是一个组件
// 和之前的 beans.xml 一样
@Configuration

@ComponentScan("com.klaus.pojo")

// 把MyConfig2里的配置导入
@Import(MyConfig2.class)

public class MyConfigure {

    // 相当于之前的 <bean   />
    // 方法名字等于之前的 id,方法的返回值就是 bean 中class属性
    @Bean
    public User getUser(){
        return new User();
    }
}

配置类MyConfig2.java

package com.klaus.configure;

import org.springframework.context.annotation.Configuration;

@Configuration
public class MyConfig2 {

}

测试类 MyTest.java

import com.klaus.configure.MyConfigure;
import com.klaus.pojo.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MyTest {
    public static void main(String[] args) {
        // 如果完全使用配置方式去做,只能通过 AnnotationConfigApplicationContext 获取容器
        // 通过配置类的 class 加载
        ApplicationContext context =
                new AnnotationConfigApplicationContext(MyConfigure.class);
        User getUser = context.getBean("getUser", User.class);
        System.out.println(getUser);

    }
}

posted @ 2021-07-24 17:25  klaus08  阅读(36)  评论(0)    收藏  举报