Spring框架-控制反轉(IoC)

1.Spring框架-控制反轉

1.1spring簡介:

Spring是一個開源框架,Spring是於2003 年興起的一個輕量級的Java 開發框架,由Rod Johnson 在其著作Expert One-On-One J2EE Development and Design中闡述的部分理念和原型衍生而來。它是爲了解決企業應用開發的複雜性而創建的。框架的主要優勢之一就是其分層架構,分層架構允許使用者選擇使用哪一個組件,同時爲 J2EE 應用程序開發提供集成的框架。Spring使用基本的JavaBean來完成以前只可能由EJB完成的事情。然而,Spring的用途不僅限於服務器端的開發。從簡單性、可測試性和松耦合的角度而言,任何Java應用都可以從Spring中受益。Spring的核心就是一個輕量級的控制反轉(IoC)和麪向切面(AOP)的框架!

1.2spring特點:
  • Spring是一個開源的免費的框架(容器)
  • Spring是一個輕量級的、非入侵式的框架
  • 控制反轉(IOC) , 面向切面編程(AOP)
  • 支持事務的處理,Spring提供了一致的事務管理接口,可向下擴展到(使用一個單一的數據庫,例如)本地事務並擴展到全局事務(例如,使用 JTA)。
  • 降低API開發難度對JavaEEAPI(JDBC、JavaMail、遠程調用等),都提供了封裝,使這些API應用難度大大降低。
  • 對框架整合的支持
1.3spring組成

在這裏插入圖片描述

2.hellospring

2.1導入spring相關的jar包
 <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.2.0.RELEASE</version>
        </dependency>
        <!-- 單元測試jar包-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
            <scope>test</scope>
 </dependency>
2.2具體項目場景
Dao層接口及實現類

UserDao接口

public interface UserDao {
    public void getUser();
}

UserDao接口的多個實現類

public class UserDaoImpl implements UserDao {
    public void getUser() {
        System.out.println("獲取默認數據!");
    }
}

public class UserDaoMysqlImpl  implements UserDao{
    public void getUser() {
        System.out.println("獲取Mysql的數據!");
    }
}

public class UserDaoOracleImpl implements UserDao {
    public void getUser() {
        System.out.println("獲取Oracle的數據!");
    }
}

public class UserDaoServerImpl implements UserDao {
    public void getUser() {
        System.out.println("獲取Server的數據!");
    }
}
service層接口及實現類

UserService 接口

public interface UserService {
    public void getUser();
}

UserServiceImpl實現類

public class UserServiceImpl implements UserService {
    private UserDao userDao;
    public void setUserDao(UserDao userDao){
        this.userDao = userDao;
    }
    public void getUser() {
        userDao.getUser();
    }
}
配置文件

spring容器創建對象、設置屬性。

<?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    對象名
class 實例的全限定類名
-->
<bean id="userService" class="com.li.service.UserServiceImpl"/>
</beans>
測試
public class MyTest {
    @Test
    public void test() {
        //獲得spring容器
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        //獲得需要的對象
        UserServiceImpl userService = context.getBean("userService", UserServiceImpl.class);
        //需要使用那個類就創建  
        userService.setUserDao(new UserDaoMysqlImpl());
        userService.getUser();
    }
}

3.spring配置

配置文件

<?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="user" class="com.li.pojo.User">
    property name="name" value="小明"/>
  </bean>
 <!-- Spring的配置說明-->
    <!--alias  別名的設置 -->
    <alias name="user" alias="newuser"></alias>
<!-- bean的配置 name可以是多箇中間的符號可以是分號空格逗號-->
  <bean id="user" class="com.li.pojo.User" name="u2;u3 u4,u6">
    <property name="name" value="小明"/>
  </bean>

<!-- import 多個配置導入合成一個-->
</beans>

4.依賴注入(構造器、setter、其它)

4.1構造器注入
實體類
public class User {
    private String name;
    public User(){
       System.out.println("User的無參構造");
    }
    public User(String name){
        this.name= name;
    }
    public String getName(){
        return name;
    }
    public void setName(String name){
        this.name = name;
    }
    public void show() {
        System.out.println("name="+name);
    }
}
配置文件
<?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">
    <!--IOC創建對象的方式 當配置文件bean標籤創建的時候 User已經被實例化了
    -->
    <!-- 使用無參構造創建對象 默認!-->
    <bean id="user" class="com.li.pojo.User">
    <property name="name" value="小明"/>
    </bean>
<!-- 有參構造創建對象的三種方式-->
    <!-- 方式一:通過索引賦值-->
    <bean id="user" class="com.li.pojo.User">
    <constructor-arg index="0" value="小紅"/>
    </bean>

    <!-- 方式二:通過參數類型匹配 多個參數不推薦使用-->
    <bean id="user" class="com.li.pojo.User">
    <constructor-arg type="java.lang.String" value="開心"/>
    </bean>

    <!-- 方式三:通過name 進行賦值-->
    <bean id="user" class="com.li.pojo.User" name="u5">
        <constructor-arg name="name" value="歡喜"/>
    </bean>

 </bean>

4.2 set注入
實體類(模擬所有的set注入方式)
public class Student {
    private String name;
    private Address address;
    private String[] books;
    private List<String> hobbys;
    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> getHobbys() {
        return hobbys;
    }

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

    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.toString() +
                ", books=" + Arrays.toString(books) +
                ", hobbys=" + hobbys +
                ", card=" + card +
                ", games=" + games +
                ", wife='" + wife + '\'' +
                ", info=" + info +
                '}';
    }
}

   //Address實體類 
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 + '\'' +
                '}';
    }
}
配置文件
<?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="address" class="com.li.pojo.Address">
        <property name="address" value="西安"/>
    </bean>

<bean name="student" class="com.li.pojo.Student">
    <!-- 注入方式:-->
    <!-- 普通注入-->
    <property name="name" value="李瑞"/>

    <!--bean注入 主要是應用類型的屬性-->
    <property name="address" ref="address"/>

    <!--數組注入 -->
    <property name="books">
        <array>
            <value>英語四級</value>
            <value>英語六級</value>
            <value>計算機二級</value>
        </array>
    </property>

    <!-- List注入 -->
    <property name="hobbys">
        <list>
            <value>敲代碼</value>
            <value>聽音樂</value>
            <value>打籃球</value>
        </list>
    </property>

    <!-- Map注入 -->
    <property name="card">
        <map>
            <entry key="身份證" value="789456123456454658"/>
            <entry key="銀行卡" value="1321312314564454"/>
        </map>
    </property>

    <!-- set注入 -->
    <property name="games">
        <set>
            <value>和平精英</value>
            <value>王者榮耀</value>
            <value>球球作戰</value>
        </set>
    </property>

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

    <!-- properties注入 -->
    <property name="info">
        <props>
            <prop key="學號">20183306</prop>
            <prop key="宿舍號">12號樓226</prop>
        </props>
    </property>
</bean>
</beans>
測試
Student{name='李瑞', 
address=Address{address='西安'}, 
books=[英語四級, 英語六級, 計算機二級], 
hobbys=[敲代碼, 聽音樂, 打籃球], 
card={身份證=789456123456454658,
 銀行卡=1321312314564454}, 
 games=[和平精英, 王者榮耀, 球球作戰], 
 wife='null', 
 info={學號=20183306, 宿舍號=12號樓226}}
4.3其它注入
實體類
package com.li.pojo;

/**
 * @Author: Lenovo
 * @CreateTime: 2020-05-27 17:48
 * @Description:西部開源教育科技有限公司
 */
public class User {
    private String name;
    private int age;

    public User() {
    }

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

配置文件
<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- p、c命名空間需要導入各自xml約束 -->
<!--xmlns:p="http://www.springframework.org/schema/p"-->
<!--xmlns:c="http://www.springframework.org/schema/c"-->
    <bean id="user" class="com.li.pojo.User" p:name="小華" p:age="18"/>

    <bean id="user2" class="com.li.pojo.User" c:name="小李" c:age="20"/>

</beans>
4.4 bean作用域

bean作用域主要是singleton(單例模式)、prototype、web層面(request、session application、websocket)。

先看看單例模式實現方式

public class Singleton {
    public Singleton(){

    }
    //創建 靜態的 唯一的 全局的對象
    private static Singleton singleton = new Singleton();
    //定義公開的靜態方法
    public static Singleton getSingleton(){
        return singleton;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            System.out.println(Singleton.getSingleton());
        }
        System.out.println("===========================");
        for (int i = 0; i < 5; i++) {
            Singleton singleton = new Singleton();
            System.out.println(singleton);
        }

    }
}

測試:在這裏插入圖片描述

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="user" class="com.li.pojo.User" p:name="小華" p:age="18" scope="singleton"/>

    <bean id="user2" class="com.li.pojo.User" c:name="小李" c:age="20" scope="prototype"/>
</beans>

測試:

 @Test
    public void test2(){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext2.xml");
        User user = context.getBean("user",User.class);
        User user2 = context.getBean("user", User.class);
        System.out.println(user.hashCode());//265119009
        System.out.println(user2.hashCode());//265119009
        System.out.println(user == user2);//true
    }
   public void test2(){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext2.xml");
        User user = context.getBean("user2",User.class);
        User user2 = context.getBean("user2", User.class);
        System.out.println(user.hashCode());//1988859660
        System.out.println(user2.hashCode());//1514160588
        System.out.println(user == user2);//false
    }

結論
singleton作用域:創建bean的時候對象已經被實例了,並且只有一個對象
prototype作用域:每次去容器取得對象都不同。

5.Bean的自動裝配

場景:一個人有兩個寵物!

public class People {
    public Dog dog;
    public Cat cat;
    public 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 + '\'' +
                '}';
    }
}

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

public class Cat {
    public void shout(){
        System.out.println("miao~");
    }
}


測試:

public class MyTest {
        @Test
        public void test() {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        People people = context.getBean("people", People.class);
        people.getCat().shout();
        people.getDog().shout();

    }
}

5.1 自動裝配前:
<!--
如果類的屬性中有引用類型需要使用ref標籤引用
byName、 byType 可以簡化xml代碼不用ref標籤
-->
 <bean id="cat" class="com.li.pojo.Cat"/>
    <bean id="dog" class="com.li.pojo.Dog"/>
    <bean id="people" class="com.li.pojo.People">
        <property name="cat" ref="cat"/>
        <property name="dog" ref="dog"/>
 </bean>
5.2 自動裝配後:
<!-- byType就是bean的class類型 id也就是對象名隨意,沒有id也可以-->
    <bean  class="com.li.pojo.Cat"/>
    <bean  class="com.li.pojo.Dog"/>
    <bean  class="com.li.pojo.People" autowire="byType">
    </bean>

<!--byName根據id尋找  尋找bean中的peopel對象 -->
    <bean id="cat" class="com.li.pojo.Cat"/>
    <bean id="dog" class="com.li.pojo.Dog"/>
    <bean id="people" class="com.li.pojo.People" autowire="byName">
    </bean>

注意點:com.li.pojo.dog 的合格 bean 可用只能匹配單個bean 簡單說:就是cat、dog的id名只能有一個,否則報錯. Could not autowire. There is more than one bean of 'Dog' type. Beans: dog,dog2. Properties: 'dog' less... (Ctrl+F1) Inspection info:Checks autowiring problems in a Spring bean defined in XML context

6.註解開發

使用註解需要掃描指定的包,註解纔可以使用

<?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/>
    <context:component-scan base-package="com.li"/>
</beans>

註解:就是一個類,使用@註解名稱
開發中:使用註解 取代 xml配置文件。

  1. @Component 位置:取代 類上
    <bean id="user" class="com.li.pojo.User"/>
  2. web開發,提供3個@Component註解衍生註解(功能一樣)
    @Repository :dao層 位置:類上
    @Service:service層 位置:類上
    @Controller:web層 位置:類上
  3. 依賴注入,給屬性設值,使用setter注入
    普通值: @Value("") 位置:屬性上
@Value("小李")
    public String name;

引用值:
@Autowired 位置:屬性、set方法上 使用@Autowired也可以不寫set方法

    @Autowired
    public Dog dog;
    @Autowired
    public Cat cat;

@Resource 位置:屬性

    @Resource
    public Dog dog;
    @Resource
    public Cat cat;

@Autowired 與@Resource可以解決byName 、byType只能匹配單個bean的困擾,也就是說可以引用多個bean,如果沒有符合的id會直接通過類尋找

 <bean id="cat" class="com.li.pojo.Cat"/>
    <bean id="cat22" class="com.li.pojo.Cat"/>
    <bean id="cat44" class="com.li.pojo.Cat"/>
    <bean id="dog" class="com.li.pojo.Dog"/>
    <bean id="dog33" class="com.li.pojo.Dog"/>
    <bean id="people" class="com.li.pojo.People">
    </bean>

如果想要指定唯一的一個id匹配就使用@Qualifier(value=" ")或者 @Resource(name = " ")

    //方式一:	
    @Autowired
    @Qualifier(value = "dog33")
    public Dog dog;
    @Autowired
    @Qualifier(value = "cat22")
    public Cat cat;
	//方式二:
   @Resource(name = "dog33")
    public Dog dog;
    @Resource(name = "cat22")
    public Cat cat;

作用域:
@Scope("singleton") 單例模式 IOC容器啓動會調用方法創建對象放到IOC容器中。以後每次獲取就是直接從容器(理解成從map.get對象)中拿bean
@Scope("prototype")多例模式 IOC容器啓動並不會去調用方法創建對象放在容器中,而是 每次獲取的時候纔會調用方法創建對象

7.基於Java的容器配置

前面的配置文件都是基於applicationContext.xml文件。現在可以通過java實現容器的配置。具體的配置如下。

7.2 實體類
@Component//相當於xml文件中bean標籤
public class User {
    @Value(value = "小米")
    private String name;

    public String getName() {
        return name;
    }

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

}
7.配置文件

UserConfig 類等效於以下Spring XML:
<beans> <bean id="user" class="com.li.pojo.User"/> </beans>

@Bean註釋被用於指示一個方法實例,可以配置,並初始化到由Spring IoC容器進行管理的新對象

@ComponentScan(basePackages = "com.li.pojo") 等效
<beans>
    <context:component-scan base-package="com.li.pojo"/>
</beans>
// @Configuation等價於<Beans></Beans>
//@Bean等價於<Bean></Bean>
@Configuration
@ComponentScan(basePackages = "com.li.pojo")
public class UserConfig {
    @Bean//相當於bean標籤
    //方法名就是id
    //方法返回值就是class屬性
    public User getUser(){
        return new User();//返回的對象
    }
}
7.3測試
public class MyTest {
    @Test
    public void test(){
        //AnnotationConfigApplicationContext(UserConfig.UserConfig) 獲取spring容器
        ApplicationContext context = new AnnotationConfigApplicationContext(UserConfig.class);
        User getUser = context.getBean("getUser", User.class);
        System.out.println(getUser.getName());
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章