Spring 框架 IOC 與 DI 的總結

一:Spring並天下

1:Spring帝國:

①:Spring崛起

什麼是Spring:

        源於Rod Johnson在其著作《Expert one on one J2EE design and development》中闡述的部分

理念和原型的衍生而來.Spring是一個輕量級的DI/AOP容器的開源框架,致力於構建輕量級的JavaEE應用,簡化應用

開發,本身涵蓋了傳統應用開發還拓展到移動端,大數據領域.

什麼是容器(Container):

        從程序設計角度看就是裝對象的對象,因爲存在放入,拿出等操作,所以容器還是要管理對象的生命週期,如

Tomcat就是Service和JSP的容器.

        Spring提供了JavaEE每一層的解決方案(full stack) .Spring其實就是全棧式框架.

    ![Spring與SpringMVC的框架整合](https://img-blog.csdn.net/20180404200433463?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MDE2MTcwOA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

②:Spring的優勢:

Spring除了不能幫我們寫業務邏輯,其餘的幾乎什麼都能幫助我們簡化開發.

    ①:Spring能幫我們低侵入/低耦合地根據配置文件創建及相關對象之間的依賴關係.

    ②:Spring面向切面編程能幫我們無耦合的實現日誌記錄,性能統計,安全控制等.

    ③:Spring能非常簡單的且強大的聲明式事務管理.

    ④:Spring提供了與第三方數據訪問框架(如:Hibemate,JPA)無縫集成,且自己也提供了一套JDBC模板來方便數據庫訪問.

    ⑤:Spring提供與第三方Web(如Struts1/2,JSF)框架無縫集成,且自己也提供了一套Spring MVC框架來方便Web層搭建.

    ⑥:Spring能方便的與Java Mail,任務調度,緩存框架等技術整合,降低開發難度.

③:Spring帝國

Spring主要產品:

        Spring FrameWork:

        Spring帝國之核心,其他Spring其他產品都是基於Spring框架而來.

        Spring Boot:

        Spring Boot 是由Pivotal團隊提供的全新框架,其設計目的是用來簡化新Spring應用

        的初始搭建以及開發過程.該框架使用了特定的方式來進行配置,從而是開發人員不在需要定義樣板化的配置.

        Spring Cloud:Spring cloud微服務框架,爲開發者提供了在分佈式系統(配置管理,服務發現,熔斷,路由,微代理,

        控制總線,一次性token,全居鎖,leader選舉,分佈式session,集羣狀態)中快速構建的工具,使用Spring Cloud的

        開發者可以快速的啓動服務或構建應用,同時能夠快速和雲平臺資源進行對接.

        Spring Cloud Data Flow: 

        Spring Cloud Data Flow簡化了專注於數據流處理的應用程序的開發和部署.通過

        SpringBoot啓動應用,採用Spring Cloud Stream,Spring Cloud Task完成微服務構建.

        官網的推薦組合:Spring Boot + Spring Cloud + Spring Cloud Data Flow

        ![Spring的其他主要項目版圖](https://img-blog.csdn.net/20180404203728808?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MDE2MTcwOA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

④:Spring基石

    ![Spring基石,瞭解一下就行,框住的是我們需要學習的](https://img-blog.csdn.net/20180404204632103?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MDE2MTcwOA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

⑤:Spring框架包

Spring框架包包含兩大部分:

1:spring-framework-5.X.RELEASE:Spring核心組件(必須).

        docs: Spring開發,幫助文檔.

        libs: Spring 核心組件的,jar包,源代碼,文檔.

        schema: Spring配置文件的schema約束文件.

2:spring-framework-3.0.5.RELEASE-dependencies:

        Spring依賴的第三方組件(根據需要拷貝),包含了各大開源組織提供的依賴jar,比如日誌庫,AOP聯盟庫,連接池庫等.

        建議使用meve來下載jar包,不需要拷貝jar包了.

⑥:STS工具

    Eclipse: STS工具  Spring Tool Suite基於Eclipse的開發工具.

    IDEA:  後面會錄製一套IDEA的視頻.

2:Spring基礎

①:Ioc和DI思想

IoC: Inversion of Control(控制反轉):讀作"翻轉控制",更好理解,不是什麼技術,而是一種設計思想,

        好比於MVC.就是將原本在程序中手動創建對象的控制權,交由Spring框架來管理.

    正控:若調用者需要使用某個對象,其自身就得負責該對象及該對象所依賴對象的創建和組裝.

    反控:調用者只管負責從Spring容器中獲取需要使用的對象,不關心對象的創建過程,也不關心該對象依賴對象的

        創建以及依賴關係的組裝,也就是把創建對象的控制權翻轉給了Spring框架.

DI: Dependency Injection (依賴注入)從字面上分析: IoC:指將對象的創建權,翻轉給了Spring容器:  DI:指Spring

        創建對象的過程中,將對象依賴屬性(常量,對象,集合)通過配置設置值給該對象.

IoC 從字面意義上很難體現出誰來維護對象之間的關係,Martin  Fowler提出一個新的概念--DI,更明確描述了"被注入對象(service對象)

    依賴IoC容器來配置依賴對象(DAO對象)".

②:HelloWord程序

依賴jar:

    spring-beans-版本.RELEASE.jar

    spring-core-版本.RELEASE.jar

    報錯再添加:

    com.springsource.org.apache.commons.logging-1版本.jar
開發jar包

    1:準備jar包

    2:開發HelloWorld程序

    3:在applicationContext.xml中完成配置(如何獲取xsd聲明)

    4:啓動Spring容器

    5:從容器中獲取指定名稱的bean

    6:調用bean的方法
什麼是BeanFactory:

    BeanFactorySpring最古老的接口,表示Spring IoC容器--生產bean對象的工廠,負責配置,創建和管理bean

什麼是Bean:Spring Ioc容器管理的對象稱之爲bean.

Spring Ioc容器如何知道哪些是它管理的對象:

    此時需要配置文件:Spring Ioc容器通過讀取配置文件中的配置元數據,通過元數據對應用中的各個對象進行實例化及裝配.

元數據的配置有三種方式(後講):

    1:XML-based configuration

    2:Annotation-based configuration

    3:Java-bean configuration

Spring IoC管理bean的原理:

    1:通過Resource對象加載配置文件

    2:解析配置文件,得到指定名稱的bean

    3:解析bean元素,id作爲bean的名字,class用於反射得到bean實例:

      注意:此時,bean類必須存在一個無參數構造器(和訪問權限無關);

    4:調用getBean方法的時候,從容器中返回對象實例;

    結論:就是把代碼從JAVA文件中轉移到XML中.

③:getBean方法的三種簽名

    //使用Spring框架之後
    @Test
    public void test2() throws Exception {
        HelloWorld world = null;
        //---------------------------------
        //1:從classpath路徑去尋找資源問價,創建資源對象
        Resource resource = new ClassPathResource("applicationContext.xml");
        //2:根據資源對象,創建Spring IoC容器對象 
        BeanFactory factory = new XmlBeanFactory(resource);
        //3:從Spring IoC容器中獲取指定名稱(helllo world)對象
        //簽名一:Object getBean(String beanName);//根據bean對象在容器中的名稱來取
        //world = (HelloWorld) factory.getBean("HelloWorld");
        //簽名二:<T> T getBean(Class<T> requiredType)//按照指定的類型去尋找bean對象
        //world = factory.getBean(HelloWorld.class);
        //簽名三:<T> T getBean(String name, @Nullable Class<T> requiredType)根據bean的類型+ID名稱去尋找,推薦的
        world = factory.getBean("HelloWorld",HelloWorld.class);
        //---------------------------------
        world.sayHello();
    }

④:Eclipse提示XML語法

XML需要導入schema約束,約束指向網絡路徑:

    方式一:連網後,自動緩存路徑文件到本地,提供提示功能;

    方式二:無連網,需要配置xsd schema文件位置(操作如下圖);

            ①:到解壓spring/schemas/beans/spring-bean.xsd.

            ②:選擇schema location方式.

            ③:複製網絡路徑 http://www.springframework.org/schema/beans/spring-beans.xsd

⑤:Spring基本配置

<bean id="helloWorld" class="cn.wolfcode.hello.HelloWorld"/>

    nameid 以後只需要用id屬性就行,表示的意思一樣,但是name元素可以用空格隔開不同的名字

<import resource=""/>元素

    在開發中,隨着應用規模的增加,系統中<bean>元素配置的數量也會增加,導致applicationContext.xml配置

    文件變得非常臃腫,爲了提高其可讀性,我們可以將一個applicationContext.xml文件分解成多個配置文件,

    然後在applicationContext.xml文件中包含其他配置文件即可.

    語法如下: <import resource="classpath:cn/wolfcode/hello/hello.xml"/>

    使用import元素注意:

        1:默認情況下:從classpath的根路徑尋找.

        2:可以使用前綴來定位文件的基礎位置:

            ①:[classpath:] 後面的文件從classpath路徑開始找(推薦);

            ②:[file]:後面的文件使用文件系統的路徑開始找;

        注意:只有當框架中實現了Resource接口才能夠識別上訴的前綴標識符.

⑥:Spring測試框架

測試依賴:

        Spring-test-版本.RELEASE.jar

        Spring-context-版本.RELEASE.jar

        Spring-aop-版本.RELEASE.jar

        Spring-expression-版本.RELEASE.jar
    //使用Junit4的測試案例
    //運行Spring的jUnit4
    @RunWith(SpringJUnit4ClassRunner.class)
    //上下文配置對象,尋找配置文件的
    //@ContextConfiguration("classpath:cn/wolfcode/spring_test/springtext.xml")
    @ContextConfiguration//默認從測試了路徑找    測試類名-context.xml文件
    public class SpringTestTest {

        //表示自動按照類型從Spring容器中找到bean對象,並設置給該字段
        @Autowired
        private SomeBean bean; 

        @Test
        public void test() throws Exception {
            bean.doWork();
        }
    }
    //使用Junit5方式的測試
    @SpringJUnitConfig
    public class SpringTestTest{

        @Autowired
        private SomeBean bean;

        @Test
        void testName(){
            bean.doWork();
        }
    }

二: 帝國之劍 IoC

1:IoC

①:IOC容器

    1:SpringIoC容器(Contaner):

      BeanFactory:  Spring 最底層的接口,只提供了IoC功能,負責創建,組裝,管理Bean,在應用中,一般不使

                   用BeanFactory,而推薦使用ApplicationContext(應用上下文).

       ApplicationContext:接口繼承了BeanFactory,除此之外還提供AOP集成,國際化處理,事件傳播,統一資源加載等功能.

    2:Bean的創建時機(此時不使用Spring Test)::BeanFactory需要等到獲取某一個bean的時候纔會創建bean--延遲初始化.

        ②:ApplicationContext在啓動Spring容器的時候就會創建所有的bean(Web應用建議).

        bean屬性中的lazy-init="true"也可以延遲初始化,也可以設置到到schema上面的default-lazy-init="true"
    /**
        結論:BeanFactory有延遲初始化的特點,在創建Spring容器的時候,不會立馬去創建容器中管理的Bean對象
            而是要等到從容器中去獲取對象的時候,纔去創建對象.

     */
    //使用BeanFactory
    @Test
    public void testBeanFactory() throws Exception {
        Resource resource = new ClassPathResource("cn/wolfcode/container/ContainerTest-Context.xml");
        BeanFactory factory = new XmlBeanFactory(resource);
        System.out.println("====================");
        Person person = factory.getBean("person",Person.class);
        System.out.println(person);
    }


    /**
    結論:在創建Spring容器的時候,就會把容器中管理的bean馬上初始化,而不會等到獲取bean的時候纔去初始化.

     */
    //使用ApplicationContext
    @Test
    public void test2() throws Exception {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("cn/wolfcode/container/ContainerTest-Context.xml");
        System.out.println("============================");
        Person person = ctx.getBean("person",Person.class);
        System.out.println(person);
    }

②:bean實例化方式:

    ①:構造器實例化(無參數構造器),最標準,使用最多.       (使用比較多)

    ②:靜態工廠方法實例化:解決系統遺留問題

    ③:實例工廠方法實例化:解決系統遺留問題

    ④:實現FactoyrBean接口實例化:實例工廠變種,如集成MyBatis框架使用: (使用比較多)

        org.mybatis.spring.SqlSessionFactoryBean
    <!-- ①:構造器實例化(無參數構造器),最標準,使用最多 -->
    <bean id="cat1" class="cn.wolfcode.createbean._01_constructor.Cat1"/>

    <!-- ②:靜態工廠方法實例化:解決系統遺留問題 -->
    <bean id="cat2" class="cn.wolfcode.createbean._02_static_factory.Cat2Factory" 
    factory-method="createInstance"/>

    <!-- ③:實例工廠方法實例化:解決系統遺留問題 -->   
    <bean id="cat3" class="cn.wolfcode.createbean._03_instance_factory.Cat3Factory"/>
    <bean id="getcat3" factory-bean="cat3" factory-method="createInstance"/>

    <!-- ④:實現FactoryBean接口實例化:實例工廠變種,如集成MyBatis框架使用 -->
    <bean id="dat4" class="cn.wolfcode.createbean._04_factory_bean.Cat4Factory">
        <property name="username" value="你好"></property>
    </bean>

③:bean的作用域:

    在Spring容器中是指其創建的Bean對象相對於其他Bean對象的請求可見範圍

    <bean id="" scope="作用域"/>

    singleton: 單例,在Spring IoC容器中僅存在一個Bean實例 (默認的scope)

    proptotype: 多例,每次從容器中調用Bean時,都返回一個新的實例,即每次調用getBean()時,相當於執行new XxxBean();不會再容器啓動時創建對象

    request: 用於web開發,將Bean放入request範圍,request.setAttribute("xxx")在調用一個request獲得同一個Bean

    session:用於web開發,將Bean放入Session範圍,在同一個 Session獲得同一個Bean

    globalSession:一般用於Porlet應用環境,分佈式系統存在全局session概念(單點登錄),如果不是porlet環境.

    globalSession 等同於Session

    Spring5開始出現:websocket, globalSession作廢.

    在開發中主要使用  scope="singleton"  scope="prototype"

    總結: 對於Struts1中的Action使用request, Struts2中的Action使用prototype類型,其他使用singleton

④:bean初始化和銷燬

    init-method: 定義初始化方法,在構造器執行之後,立馬執行

    destroy-method: 定義銷燬之前的方法,在銷燬執行之前,調用

    scope: 設置爲"prototype" 不是 單例設計模式, 他不知道你啥時候調用完,所以就不關閉資源  考慮資源來之不易,不易輕易釋放

    <bean id="ds" class="cn.wolfcode.lifecycle.MyDataSource" scope="singleton"
    init-method="open" destroy-method="close"/>

    此方法在Spring測試類可用

    如果使用ApplicationContext來創建對象,就得手動關閉資源

    @Test
    public void testName1() throws Exception {
        @Cleanup
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("cn/wolfcode/lifecycle/App-Context.xml");
        MyDataSource ds = ctx.getBean("ds",MyDataSource.class);
        ds.doWork();
    }

    三種關閉資源方法:最後加  ds.close();     @Clearup;      ctx.registerShutdownHook();

⑤:bean的實例化過程(生命週期)

bean的生命週期: bean從出身--消亡直接的整個過程

BeanFactory:         延遲初始化特點

ApplicationContext:  在啓動Spring容器的時候,就會去創建bean對象

    ![bean的生命週期](https://img-blog.csdn.net/2018040512541723?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MDE2MTcwOA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

    ![bean的生命週期2](https://img-blog.csdn.net/20180405125513373?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MDE2MTcwOA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

    ![bean的生命週期3](https://img-blog.csdn.net/20180405132714642?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MDE2MTcwOA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

2: DI

①:通過XML配置裝配

DI:Dependency Injection(依賴注入)

    從字面上分析:IoC:指對象的創建權,反轉給了Spring容器;

DI:Spring創建對象的過程中,將對象依賴屬性(常量,對象,集合)通過配置設值給該對象.

什麼是注入操作?      setter方法    構造器

注入值的類型:

            1):常量值(簡單類型):value元素

            2):對象 :   ref元素

            3):集合 :   對應集合類型元素

通過XML配置裝配(不推薦)

    <bean id="dog" class="" autowire="byType" />  autowire: byName  byType  constructor  no

②:setter注入

    注入常量  注入對象  注入集合  跟上面注入方法一樣
    <!-- 屬性注入:常量類型 -->
    <bean id="employee" class="cn.wolfcode.di_setter.Employee1">
        <property name="name" value="will"></property>
        <property name="age" value="17"></property>
        <property name="salary" value="5000"></property>
    </bean> 
    <!-- 下面這種方法不推薦 -->
    <!-- <bean id="employee" class="cn.wolfcode.di_setter.Employee1" p:name="Lucy" p:age="18" p:salary="1000"/> -->

    <!-- 屬性注入:對象類型 -->
    <bean id="cat1" class="cn.wolfcode.di_setter.Cat1"></bean>
    <bean id="person" class="cn.wolfcode.di_setter.Person1" >
        <property name="c1" ref="cat1"></property>
    </bean>

    <!-- 屬性注入:集合類型 -->
    <bean id="collectionBean1" class="cn.wolfcode.di_setter.CollectionBean1">
        <property name="set">
            <set>
                <value>set1</value>
                <value>set2</value>
            </set>
        </property>
        <property name="list">
            <list>
                <value>list1</value>
                <value>list2</value>
            </list>
        </property>
        <property name="array">
            <array>
                <value>array1</value>
                <value>array2</value>
            </array>
        </property>
        <property name="map">
            <map>
                <entry key="key1" value="value1"/>
                <entry key="key2" value="value2"/>
            </map>
        </property>
        <!-- <property name="prop">
            <props>
                <prop key="p1">v1</prop>
                <prop key="p2">v2</prop>
            </props>
        </property> -->
        <property name="prop">
            <value>
                p1=v1
                p2=v2
                p3=v3
            </value>
        </property>
    </bean>

③:構造器注入

把上面的property換成constructor-arg  即可
<bean id="person2" class="cn.wolfcode.di_constructor.Person2" >
    <constructor-arg name="c2">
            <bean class="cn.wolfcode.di_constructor.Cat2"/>
    </constructor-arg>
</bean>

④:bean元素的繼承

多個bean元素共同配置的抽取,實則是bean配置的拷貝,和Java的繼承不同.

    <!-- 多個bean共同配置的抽取 -->
    <bean id="base" abstract="true">
        <property name="name" value="will"></property>
        <property name="age" value="19"></property>
    </bean>

    <!-- 配置SomeBase1 -->
    <bean id="someBean1" class="cn.wolfcode.bean_tag_inheritance.SomeBean1" parent="base">
        <property name="weight" value="500"></property>
    </bean> 

    <!-- 配置SomeBase2 -->
    <bean id="someBean2" class="cn.wolfcode.bean_tag_inheritance.SomeBean2" parent="base">
        <property name="age" value="99"></property>
        <property name="color" value="業樓"></property>
    </bean>

⑤:配置數據庫連接池

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration
    public class App {

        @Autowired
        private DataSource ds;

        @Test
        public void testName() throws Exception {
          /*ds = new DruidDataSource();
            ds.setDriverClassName("com.mysql.jdbc.Driver");
            ds.setUrl("jdbc:mysql://localhost:3306/springdemo");
            ds.setUsername("root");
            ds.setPassword("111111");
            ds.setInitialSize(2);*/

            @Cleanup
            Connection conn = ds.getConnection();
            @Cleanup
            PreparedStatement ps = conn.prepareStatement("SELECT * FROM student");
            @Cleanup
            ResultSet rs = ps.executeQuery();
            while(rs.next()){
                System.out.print(rs.getLong("id")+",");
                System.out.print(rs.getString("name")+",");
                System.out.println(rs.getInt("age")+",");
            }
        }
    }

⑥:property-placeholder

    <!-- 
            先配置schema屬性
            然後就可以使用context:property-placeholder標籤了
            從classpath:db.properties文件中去加載資源

            system-properties-mode="NEVER"表示不從系統中找屬性,因爲系統中有個也叫
            username的屬性名稱,跟JDBC連接池的屬性名稱相同,有衝突

            最好在properties文件中屬性前面加個JDBC.
     -->
     <!-- 從classpath的跟路徑去加載db.properties文件 -->
        <context:property-placeholder location="classpath:db.properties" system-properties-mode="NEVER"/>


    <!-- 配置一個druid的連接池  -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
        init-method="init" destroy-method="close" >
        <property name="driverClassName" value="${jdbc.driverClassName}"></property>
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
        <property name="initialSize" value="${jdbc.initialSize}"></property>
    </bean>

⑦:模擬用戶註冊的例子

查看我的Eclipse代碼,利用xml配置的方式回顧了IoC跟DI.

domain

@Setter@ToString@Getter
public class User {
    private Long id;
    private String name;
    private int age;
}

dao

public interface IUserDAO {

    void save(User u);
}

impl

public class IUserDAOImpl implements IUserDAO{

    @Setter
    private DataSource ds;

    @SneakyThrows
    public void save(User u){
        System.out.println("保存操作");
        @Cleanup
        Connection conn = ds.getConnection();
        String sql = "INSERT INTO user (name,age) values (?,?)";
        @Cleanup
        PreparedStatement ps = conn.prepareStatement(sql);
        ps.setString(1,u.getName());
        ps.setInt(2, u.getAge());
        ps.executeUpdate();
    }
}

service

public interface IUserService {

    void register(User user);
}

impl

public class UserServiceImpl implements IUserService{

    private IUserDAO dao;

    public void setDao(IUserDAO dao) {
        this.dao = dao;
    }

    @SneakyThrows
    public void register(User user){
        System.out.println("註冊操作");
        dao.save(user);
    }
}

action

//模擬Struts2的Action/SpringMVC的Controller
public class UserAction {

    @Setter
    private IUserService service;

    public String execute() throws Exception{
        System.out.println("註冊請求");
        service.register(new User());
        return "success";
    }
}

app

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class App {

    @Autowired
    private UserAction userAction;

    @Test
    public void testRegister() throws Exception {
        userAction.execute();
    }
}

app-Context.xml

        <!-- 從classpath的跟路徑去加載db.properties文件 -->
        <context:property-placeholder location="classpath:db.properties" system-properties-mode="NEVER"/>
        <!-- 配置一個druid的連接池  -->
        <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" 
        init-method="init" destroy-method="close">
            <property name="driverClassName" value="${jdbc.driverClassName}"></property>
            <property name="url" value="${jdbc.url}"></property>
            <property name="username" value="${jdbc.username}"></property>
            <property name="password" value="${jdbc.password}"></property>
            <property name="initialSize" value="${jdbc.initialSize}"></property>
        </bean>

        <!-- 配置DAO -->
        <bean id="userdao" class="cn.wolfcode.register.dao.impl.IUserDAOImpl">
            <property name="ds" ref="dataSource"></property>
        </bean>

        <!-- 配置Service -->
        <bean id="UserService" class="cn.wolfcode.register.service.impl.UserServiceImpl">
            <property name="dao" ref="userdao"></property>
        </bean>

        <!-- 配置
            如果使用Struts2的Action:此時bean的作用域應該是多例: scope="prototype"
            如果使用SpringMVC的Controller: 此時bean的作用域依然還是使用單例:singleton /或者不寫    
        -->
        <bean id="userAction" class="cn.wolfcode.register.action.UserAction" scope="prototype">
            <property name="service" ref="UserService" ></property>
        </bean>

3:使用註解配置

①:DI註解

Autowired和Qualifier註解

Spring規範提供

    1:可以讓Spring自動的把屬性的對象找出來,並注入到對象.

    2:可以貼在字段或者setter方法上面

    3:可以同時注入多個對象

        @Autowired
        public void setter(OtherBean otherBean,OtherBean other2){}

    4:可以注入一些Spring內置的重要對象,比如BeanFactory,ApplicationContext,ServiceContext等;

    5:默認情況下Autiwrired註解必須要能找到對應的對象,否則報錯.

        通過required=false來避免這個問題:@Autowired(required=false)

    6:第三方程序:Spring3.0之前,需要手動配置Autowired註解的解析程序:

        <context:annotation-config/>在Web開發中必須配置

    7:Autowired註解尋找bean的方法:

        1):首先按照依賴對象的類型找,如果找到,就是用setter或者字段直接注入

        2):如果Spring上下文中找到多個匹配類型,再按照名字去找,如果沒有 匹配報錯;

        3):可以通過使用@Qualifier("name")標籤來規定依賴對象按照bean的id和類型的組合方式去找;

Resource

DI註解:

        Spring官方:  Autowired

        JavaEE規範:  Resource

共同點:  都需要配置DI註解解析器:  <context:annotation-config/>

不同點:    Resource註解必須要能找到對應的對象,否則報錯.

        Resource註解首先按照名字去找,如果找到,就使用setter或者字段注入;

        如果按照名字找不到,再按照類型去找,但如果找到多個匹配類型,報錯;

        Resource可以直接使用name屬性指定bean的名稱(@Resource(name="名字"));但是

            如果指定的name,就只能按照name去找如果找不到,就不再按照類型去找

Value

注入:
        常量類型:   value       Value

        對象類型:   ref         Autowired/Resource

        @Value("${service.port}")
        private int port;

        <!--需要加載的配置文件用 逗號 隔開-->
        <context:property-placeholder location="classpath:db.properties,classpath:server.properties"/>  

②:IoC註解

Component註解和其stereotype註解

    //註解配置:@Component
    @Component("myDataSource")//組件如果不寫value屬性值,此時bean的id默認是類型首字母小寫;
    public class MyDataSource {

    }

    <!-- IoC註解解析器 -->
    <context:component-scan base-package="cn.wolfcode.ioc"/>
bean組件版型:四個組件的功能是相同的,只是用於標註不同類型的組件.

    @Component泛指組件,當組件不好歸類的時候,我們可以使用這個註解進行標註.

    @Repository用於標註數據訪問組件,即DAO組件.

    @Service用於標註業務層組件.

    @Controller用於標註控制層組件(如struts中的Ation,SpringMVCController).

    他們幾個的功能跟component功能其實是一模一樣的.優先使用下面3種註解,當不屬於其他組件再使用component

③:Scope 和 PostConstruc 以及 PreDestroy 註解

@PostConstruct用於貼在初始化方法上

@PreDestroy用於貼在銷燬方法上

    @Component
    @Scope("singleton")
    public class SomeBean {

        public SomeBean(){
            System.out.println("構建SomeBean對象");
        }

        @PostConstruct//構建對象之後
        public void open(){
            System.out.println("初始化方法");
        }

        @PreDestroy//銷燬之前
        public void close(){
            System.out.println("銷燬前掃尾方法");
        }

        public void doWork(){
            System.out.println("工作");
        }
    }

    之後記得寫IoC註解解析器
    <context:component-scan base-package="cn.wolfcode.lifecycle"/>

④:使用註解完成註冊案例

注意:

    使用註解並不能完全取代XML的配置

    比如配置連接池DruiDataSource,我們就不能到這個類中去貼註解.

    JavaConfig + 註解

    @setter註解貼在字段上面僅僅是爲我們生成一個Setter方法,本身不能完成注入操作.

    @Autowired註解貼在字段上面,自動從Spring 容器中去找到匹配的對象,並設置給該字段.

IoCDI其實是一個東西:

    IoC:字面上,更多強調的是Spring幫我們創建對象.

    DI:字面上,Spring不僅幫我們創建對象,還要爲該對象設置依賴的數據.

    最後學會用XML與註解完成註冊案例配置

domain

@Setter@ToString@Getter
public class User {
    private Long id;
    private String name;
    private int age;
}

dao

public interface IUserDAO {

    void save(User u);
}

impl

@Repository
public class IUserDAOImpl implements IUserDAO{

    @Autowired
    private DataSource ds;

    @SneakyThrows
    public void save(User u){
        System.out.println("保存操作");
        @Cleanup
        Connection conn = ds.getConnection();
        String sql = "INSERT INTO user (name,age) values (?,?)";
        @Cleanup
        PreparedStatement ps = conn.prepareStatement(sql);
        ps.setString(1,u.getName());
        ps.setInt(2, u.getAge());
        ps.executeUpdate();
    }
}

service

public interface IUserService {

    void register(User user);
}

impl

@Service
public class UserServiceImpl implements IUserService{

    @Autowired
    private IUserDAO dao;

    @SneakyThrows
    public void register(User user) {
        System.out.println("註冊操作");
        dao.save(user);
    }
}

action

@Component//變現層
public class UserAction {

    @Autowired
    private IUserService service;

    public String execute() throws Exception{
        System.out.println("註冊請求");
        service.register(new User());
        return "success";
    }
}

APP

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class App {

    @Autowired
    private UserAction userAction;

    @Test
    public void testRegister() throws Exception {
        userAction.execute();
    }
}

APP-context.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: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
        " >

        <!-- DI註解解析器 -->
        <context:annotation-config/>
        <!-- IoC註解解析器 -->
        <context:component-scan base-package="cn.wolfcode"/>

        <!-- 從classpath的跟路徑去加載db.properties文件 -->
        <context:property-placeholder location="classpath:db.properties" system-properties-mode="NEVER"/>
        <!-- 配置一個druid的連接池  -->
        <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" 
        init-method="init" destroy-method="close">
            <property name="driverClassName" value="${jdbc.driverClassName}"></property>
            <property name="url" value="${jdbc.url}"></property>
            <property name="username" value="${jdbc.username}"></property>
            <property name="password" value="${jdbc.password}"></property>
            <property name="initialSize" value="${jdbc.initialSize}"></property>
        </bean>
</beans>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章