Spring框架(一):DI依賴注入思想和IOC控制反轉、配置文件詳解、典型案例分析、容器架構、代理模式

Spring


Spring介紹

  • 官網

  • 百科

    Spring框架是由於軟件開發的複雜性而創建的。Spring使用的是基本的JavaBean來完成以前只可能由EJB完成的事情。然而,Spring的用途不僅僅限於服務器端的開發。從簡單性、可測試性和松耦合性角度而言,絕大部分Java應用都可以從Spring中受益。

  • GitHub/Gitee

Spring優點

  • DI(Dependency Injection)與IOC(Inverse Of Control)
  • AOP(Aspect Oriented Programming)
  • 與所有框架完美集成
  • 使得JDBC/事務等更加方便

Spring容器和IOC與DI思想

Spring容器爲我們管理對象的創建和銷燬全過程

對於像jdbc或是連接池需要經歷一系列的對象創建、使用和銷燬過程的項目,在需求越來越大,對應的對象越來越多,整個過程如果通過我們自己去處理將會十分複雜.

Spring容器通過依賴注入,和自動裝配(過程)實現控制反轉(結果)

類與類之間的依賴關係若通過new構造方法創建對象的方式解決,耦合性太強.Spring獲得創建對象的控制權,程序需要什麼對象,只要去容器中取

  • DI(Dependency Injection)依賴注入就是不通過原始的方法創建對象實例,而是通過自動裝配(底層通過反射實現)依賴容器爲程序注入對象,自動裝配通過配置文件,反射來獲取程序需要的對象類型、實例名
  • IOC(Inverse Of Control)控制反轉:將實例化對象的控制權交給Spring容器

Spring配置


IOCjar包介紹

jar包 概述
Spring-core Spring的核心工具包,spring的其它包都要用到該jar中的包
Spring-beans 包含配置文件,bean的創建等,所有jar包都需要用到
Spring-context 在上面兩個jar完成基礎IOC功能上提供擴展服務,此外還提供許多企業級服務的支持,有郵件服務、任務調度、JNDI定位,EJB集成、遠程訪問、緩存以及多種視圖層框架的支持。
Spring-expression Spring表達式語言,spel表達式,SpEL支持標準數學運算符,關係運算符,邏輯運算符,條件運算符,集合和正則表達式等。
Spring-context-support Spring context的擴展支持,用於MVC方面,比如支持freemarker等模板引擎

Maven引入jar包

<properties>
    <spring.version>5.1.5.RELEASE</spring.version>
</properties>
<dependencies>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-beans -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-support</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-expression</artifactId>
        <version>${spring.version}</version>
    </dependency>
</dependencies>
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.6.0</version>
    <configuration>
        <source>1.8</source> <!-- 源代碼使用jdk1.8支持的特性 -->
        <target>1.8</target> <!-- 使用jvm1.8編譯目標代碼 -->
               <compilerArgs> <!-- 傳遞參數 -->
                    <arg>-parameters</arg>
                    <arg>-Xlint:unchecked</arg>
                    <arg>-Xlint:deprecation </arg>
              </compilerArgs>
    </configuration>
</plugin>
日誌配置
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.26</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.26</version>
</dependency>

在resources文件夾中加入log4j.properties

log4j.rootCategory=DEBUG, stdout 

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=[QC] %p [%t] %C.%M(%L) | %m%n

IOC配置文件

在resources文件夾中加入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"
       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">

  </beans>

Spring案例剖析


配置文件加入
  1. 創建Maven普通項目,添加jar包以及配置文件.

  2. 創建StudentService類,在applicationCotext中配置類信息

    <bean id="studentService" class="com.edu.bean.StudentService"></bean>
    
  3. 創建測試,在鼠標點擊類名按下Alt+Enter選擇Create Test,最後在測試方法註解中去掉org.junit.並導入junit包

    @Test
        public void show() {
            //1、加載容器,通過配置文件加載ApplicationContext,beanFactory(原始)(SpringIOC容器)
            //classpath:會在當前項目resources下面去找(編譯後target下面找)
            //classpath*:查找除了在本項目包括其他項目的jar包的resources文件夾路徑
            ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath*:applicationContext.xml");
            //2、創建對象
            StudentService studentService = (StudentService) context.getBean("studentService");
            //3、調用對象方法
            studentService.show();
            //4、銷燬
            //classPathXmlApplicationContext.close();
            context.registerShutdownHook();//更爲優雅
        }
    

bean標籤屬性詳解

id/name

對象的唯一標識,兩者性質一樣,value也不能相同

Scope

設置對象的實例類型

概述
singleton SpringIoc容器只會創建該Bean的唯一實例,所有的請求和引用都只使用這個實例
prototype 每次請求都創建一個實例
request 在一次Http請求中,容器會返回該Bean的同一個實例,而對於不同的用戶請求,會返回不同的實例。需要注意的是,該作用域僅在基於Web的Spring ApplicationContext情形下有效,以下的session和global Session也是如此
session 同上,唯一的區別是請求的作用域變爲了session
global session 全局的HttpSession
autowire:自動裝配

爲依賴的javabean設置自動裝配的方式,在原對象中必須創建getter,setter(javabean規範),框架底層調用實現

概述
no 即不啓用自動裝配。autowire默認的值。
byName 通過屬性的名字的方式查找JavaBean依賴的對象併爲其注入,使用Seter方法爲其注入。
byType 通過屬性的類型查找JavaBean依賴的對象併爲其注入。使用Seter方法爲其注入
constructor byType一樣,也是通過類型查找依賴對象。與byType的區別在於它不是使用Seter方法注入,而是使用構造子注入
autodetect 在byType和constructor之間自動的選擇注入方式
default 由上級標籤的default-autowire屬性確定

注意:在配置bean時,標籤中autowire屬性的優先級比其上級標籤高,即是說,如果在上級標籤中定義default-autowire屬性爲byName,而在中定義爲byType時,Spring IoC容器會優先使用標籤的配置。

factory-bean/factory-method 工廠方法注入
<bean factory-bean="" factory-method=""></bean>
lazy-init:延遲加載

值爲true時,在Spring容器初始化時該javabean不加載而是等到程序使用該類時加載

默認值爲false,javabean會隨着Spring容器一同被加載.

注意:

  • lazy-init只有在scope爲singleton(單例)時才生效
  • 若lazy-init的javabean被不是lazy-init依賴,則前者被後者第一次主動使用,兩者同時加載
init-method/destroy-method

初始化(構造方法先執行)和銷燬前javabean時調用的方法

關於在spring 容器初始化 bean 和銷燬前所做的操作定義方式有三種:

第一種:通過@PostConstruct 和 @PreDestroy 方法 實現初始化和銷燬bean之前進行的操作

第二種是:通過 在xml中定義init-method 和 destory-method方法

第三種是: 通過bean實現InitializingBean和 DisposableBean接口

Spring容器架構


ApplicationContext

在這裏插入圖片描述

類名 概述
AnnotationConfigApplicationContext 基於註解開發時使用的容器
ClassPathXmlApplicationContext 查找配置文件時默認到classpath路徑下去找,也就是編譯後的classes文件夾
FileSystemXmlApplicationContext 查找配置文件默認到當前項目的文件系統路徑
AnnotationConfigApplicationContext案例分析
//@Configuration標記的方法將該類描述爲配置類,就類似一個xml配置文件,其中多個@Bean標記的方法將被AnnotationConfig(Web)ApplicationContext對象掃描,並用於構造javabean,初始化Spring容器
@Configuration
public class MyConfig {
    //@Bean註解標記的方法返回的bean將被交與容器管理並在容器中以name的value值作爲標識
    @Bean(name = "studentService")
   public StudentService getstudentService()
    {
        return new StudentService();
    }
}
public class AnnoConfigTest {
    @Test
    public void show()
    {
        //1、通過註解構造的配置類獲取容器
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
        //2、獲取對象實例
        StudentService service = context.getBean(StudentService.class);
        service.start();
        //3、銷燬
        context.registerShutdownHook();
    }
}

BeanFactory和ApplicationContext的區別

BeanFactory

是Spring裏面最低層的接口,提供了最簡單的容器的功能,只提供了實例化對象和拿對象的功能;

ApplicationContext

應用上下文,繼承BeanFactory接口,它是Spring的一各更高級的容器,提供了更多的有用的功能;

  1. 國際化(MessageSource)

  2. 訪問資源,如URL和文件(ResourceLoader)

  3. 載入多個(有繼承關係)上下文 ,使得每一個上下文都專注於一個特定的層次,比如應用的web層

  4. 消息發送、響應機制(ApplicationEventPublisher)

  5. AOP(攔截器)

兩者裝載bean的區別

BeanFactory

BeanFactory在啓動的時候不會去實例化Bean,中有從容器中拿Bean的時候纔會去實例化;

ApplicationContext

ApplicationContext在啓動的時候就把所有的Bean全部實例化了。它還可以爲Bean配置lazy-init=true來讓Bean延遲實例化;

選擇

延遲實例化的優點:(BeanFactory

應用啓動的時候佔用資源很少;對資源要求較高的應用,比較有優勢;

不延遲實例化的優點: (ApplicationContext

  1. 所有的Bean在啓動的時候都加載,系統運行的速度快;
  2. 在啓動的時候所有的Bean都加載了,我們就能在系統啓動的時候,儘早的發現系統中的配置問題
  3. 建議web應用,在啓動的時候就把所有的Bean都加載了。(把費時的操作放到系統啓動中完成)

SpringAPI


spring的getBean方法

在這裏插入圖片描述

依賴注入(兩種)

setter注入
<bean class="com.edu.bean.Student">
<!--        int-->
        <property name="age" value="23" ></property>
<!--        String-->
        <property name="name" value="DLz"></property>
<!--        Array-->
        <property name="friends">
            <array>
                <value>zyh</value>
                <value>rit</value>
                <value>laz</value>
            </array>
        </property>
<!--        List-->
        <property name="girlfriends">
            <list>
                <value>zjey</value>
            </list>
        </property>
<!--        Set<Object>-->
        <property name="courses">
            <set>
                <bean class="com.edu.bean.Course">
                    <property name="id" value="1"/>
                    <property name="name" value="語文"/>
                </bean>
                <bean class="com.edu.bean.Course">
                    <property name="id" value="2"/>
                    <property name="name" value="數學"/>
                </bean>
            </set>
        </property>
<!--        Properties-->
        <property name="score">
            <props>
                <prop key="1">88</prop>
                <prop key="2">100</prop>
            </props>
        </property>
    </bean>
構造注入
<bean class="com.edu.bean.Student">
        <constructor-arg index="0" value="Dlz"/>
        <constructor-arg index="1" value="23"/>
        <constructor-arg index="2">
            <list>
                <value>sjw</value>
                <value>jyx</value>
            </list>
        </constructor-arg>
        <constructor-arg index="3">
            <list>
                <value>zzyy</value>
            </list>
        </constructor-arg>
        <constructor-arg index="4">
            <set>
                <bean class="com.edu.bean.Course">
                    <property name="id" value="1"/>
                    <property name="name" value="語文"/>
                </bean>
                <bean class="com.edu.bean.Course">
                    <property name="id" value="2"/>
                    <property name="name" value="數學"/>
                </bean>
            </set>
        </constructor-arg>
        <constructor-arg index="5">
            <props>
                <prop key="1">88</prop>
                <prop key="2">100</prop>
            </props>
        </constructor-arg>
    </bean>

IOC註解

當類標記了註解表示將此類交給IoC容器管理,可以爲註解的value賦值來聲明類的別名。

註解 概述
@Service 一般用於業務層
@Repository 一般用於dao層
@Controller 一般用於控制層
@Configuration 一般用於配置類
@Component 一般類

Configuration和Component的區別

@Configuration 中所有帶 @Bean 註解的方法都會被動態代理,也就是調用會調用代理類來完成,而不是調用我們自己的寫的帶new的方法,因此調用該方法返回的都是同一個實例。而@Component就是直接調用方法,所以每次都在new一個新的對象

注入註解
註解 概述
@Autowired 消除setter/getter方法 作爲標記,告訴spring該屬性依賴spring容器注入默認使用類型注入, 註解屬於spring框架
@Qualifier(value=beanName) 上面的註解使用類型注入,如果想根據bean的名字注入需要配合該註解使用 @Autowired+@Qualifier,註解屬於spring框架
@Resource 註解位於java.annotation.Resource中,爲@Autowired+@Qualifier綜合體
@Primary 爲接口注入時,默認標記注入的實現類,但其他實現類依然會加入到IoC容器
@Value 提取配置文件或者是配置對象的屬性
@Scope 類似於bean標籤中的scope屬性
@Lazy 類似於bean標籤中的lazy-init屬性
@PostConstruct 在構造方法和init方法(如果有的話)之間得到調用,且只會執行一次。
@PreDestory 註解的方法在destory()方法調用後得到執行
<!--引入外部的propertie配置文件-->
<context:property-placeholder location="classpath*:jdbc.properties" file-encoding="UTF-8"></context:property-placeholder>

$與#號區別

  • #只能從配置對象中取數據,支持spel表達式

  • $與#都可以在讀取配置文件爲null的情況下取默認值

    @Value("${jdbc.driver:abc}")
    private String url;
    //前面屬性取到爲null 就取後面的值
    @Value("#{myProperties.username?:'abc'}")
    private String username;
    

引深一點,Spring 容器中的 Bean 是有生命週期的,Spring 允許在 Bean 在初始化完成後以及 Bean 銷燬前執行特定的操作,常用的設定方式有以下三種:

1.通過實現 InitializingBean/DisposableBean 接口來定製初始化之後/銷燬之前的操作方法;

2.通過 元素的 init-method/destroy-method屬性指定初始化之後 /銷燬之前調用的操作方法;

3.在指定方法上加上@PostConstruct 或@PreDestroy註解來制定該方法是在初始化之後還是銷燬之前調用

但他們之前並不等價。即使3個方法都用上了,也有先後順序:

Constructor > @PostConstruct >InitializingBean > init-method

案例
  1. 導入jar包

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>5.1.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>5.1.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-expression</artifactId>
            <version>5.1.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>5.1.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.26</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.26</version>
        </dependency>
    
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-test -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.1.5.RELEASE</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.6.0</version>
                <configuration>
                    <source>1.8</source> <!-- 源代碼使用jdk1.8支持的特性 -->
                    <target>1.8</target> <!-- 使用jvm1.8編譯目標代碼 -->
                    <compilerArgs> <!-- 傳遞參數 -->
                        <arg>-parameters</arg>
                        <arg>-Xlint:unchecked</arg>
                        <arg>-Xlint:deprecation </arg>
                    </compilerArgs>
                </configuration>
            </plugin>
        </plugins>
    </build>
    
  2. 添加日誌配置文件

  3. 添加IOC配置文件並添加配置掃描路徑

        <!--掃描需要交給spring管理的類所在的包路徑-->
    <context:component-scan base-package="cn.cdqf.anno" >
        <!--指定不需要掃描的註解-->
            <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"></context:exclude-filter>
        <!--指定需要掃描的註解-->
            <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"></context:include-filter>
    </context:component-scan>
    
    
  4. 測試類

    @RunWith(SpringJUnit4ClassRunner.class)//讓整個測試代碼在spring容器的環境下運行
    @ContextConfiguration(locations = {"classpath*:application0.xml"})//指定配置文件的位子
    public class StudentServiceImplTest {
        @Autowired//從spring容器中自動注入的對象
        private StudentServiceImpl studentService;
        @Test
        public void show(){
            System.out.println(studentService);
        }
    }
    
IOC概念總結

總結:di是思想,IOC是結果。di思想產生是因爲項目中當某個對象,想持有另外對象的引用,需要自己new,例如controller需要service。顯然這種情況會導致耦合性很強,於是就想能不能不自己new,依賴其他方式注入給我,這是依賴注入思想。spring容器就能完成這種注入,而通過這種注入方式導致的結果就是對象控制權的轉移,這是控制反轉ioc。

當StudentService引用StudentDao,原始方式自己new

StudentDao studentDao = new StudentDao();

耦合性很強,當有一天需要擴展功能從新創建一個StudentDao2需要修改業務層的代碼

StudentDao2 studentDao2 = new StudentDao2();

除了修改這一行,還需要修改裏面的方法定義

首先消除耦合,可以使用面向接口編程,新建一個Dao接口,讓這兩個類都實現dao接口,將所有的方法定義放在接口中,那麼業務層代碼改爲

Dao dao = new StudentDao();

這樣修改的時候只需要修改new StudentDao()這裏的代碼,方法不用修改了

但是還是有耦合,於是DI思想出現,業務層代碼改爲

@Autowired

Private Dao dao;

就解耦合了。

然後通過這種思想達到的效果就是對引用對象控制權由自己new轉移到了容器注入,這種結果就是控制反轉IOC

婚姻介紹所:概念層面理解代理模式

代理模式

核心:讓原有的類中的方法只關注它自己的核心業務,其它公共輔助功能由代理類來完成

靜態代理

手動創建代理對象,只能代理某一類對象

package cn.cdqf.agency;

public interface Singer {

     void sing();
}
==========================
package cn.cdqf.agency;

public class Andy implements Singer{
    @Override
    public void sing()
        {
            System.out.println("鍛鍊歌喉");
            System.out.println("唱歌技巧");
            System.out.println("唱歌");
        }
}
==========================
 package cn.cdqf.agency;

public class Agent implements Singer{

    //指定代理人
    private Singer singer;

    public Agent(Singer singer){
        this.singer = singer;
    }
    public void talk(){
        System.out.println("談價格");
        System.out.println("其它細節");
    }

    public void sing(){
        talk();
        singer.sing();
    }v
}
==========================
package cn.cdqf.agency;

public class Test {
    public static void main(String[] args) {
        Singer singer = new Andy();
        Agent agent = new Agent(singer);
        agent.sing();
    }
}
JDK動態代理

可以代理所有對象

由java動態生成代理對象,被代理對象需要有接口

由程序員完成的是能生成代理對象那個工具類

其實就是通過工具類生成一個與被代理類實現相同接口的一個代理類

  1. 被代理的所有方法都在代理類中增強
  2. 代理類中有被代理類的所有方法
  3. 調用的是JDK動態生成的代理對象,代理對象每個方法都增強,增強的內容就是invoke的內容
  4. Jdk的動態代理,生成代理對象是被代理對象的兄弟(實現了同一個接口)
package cn.cdqf.agency;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 代理類
 * 1.實現當前接口InvocationHandler
 */
public class JDKProxy implements InvocationHandler {
    //被代理對象
    private Object object;

    //直接傳入一個被代理對象 給你返回一個代理對象 Andy  只能唱歌 給你包裝一下 返回一個既可以唱歌也能談業務的代理對象
    public Object getInstance(Object object){
        this.object = object;
        //1.類記載器,這兒可以直接使用被代理的類加載器
        //2.被代理類實現所有接口,使用jdk的動態代理需要要求被代理類實現接口
        //3.能完成代理這個功能的對象,在jdk動態代理中 實現了這個接口InvocationHandler的對象就具有代理功能
        return Proxy.newProxyInstance(object.getClass().getClassLoader(),
                object.getClass().getInterfaces(),this);
    }
    //1.代理對象
    //2.代理類被調用的方法
    //3.代理類被調用方法的參數
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        talk();
        //真正的方法  這個對象應該屬於被代理對象  andy
        Object invoke = method.invoke(object, args);
        System.out.println("完成後續工作");
        return invoke+"abc";
    }
    public void talk(){
        System.out.println("談價格");
        System.out.println("其它細節");
    }
}
package cn.cdqf.agency;

public class Test {
    public static void main(String[] args) {
       /* Singer singer = new Andy();
        Agent agent = new Agent(singer);
        agent.sing();*/
       //任意類:唯一的要求需要實現接口
        Singer singer = new Andy();
        JDKProxy jdkProxy = new JDKProxy();
        Singer instance = (Singer)jdkProxy.getInstance(singer);
        //調用的不是原來的方法 調用的是invoke方法
        String sing = instance.sing();
        System.out.println(sing);
    }
}
CGLIb代理

cglib 使用asm字節碼技術生成代理類

  1. 不需要實現接口
  2. 被代理類不能被final修飾
  3. 生成的代理類是被代理類的子類
package cn.cdqf.proxy;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * 1.不要求實現接口
 * 2.生成的代理類是被代理類的子類
 * 3.被代理類能被繼承,不能被final修飾
 *
 * cglib 使用asm字節碼技術生成代理類
 */
public class CglibTest implements MethodInterceptor {

    private Object object;

    public CglibTest(Object object){
        this.object = object;
    }
    public Object getCglibProxy( ){
        //生成代理類的 功能類
        Enhancer enhancer = new Enhancer();
        //設置代理類的父類(被代理類)
        enhancer.setSuperclass(object.getClass());
        //回調方法:調用被代理類任何一個方法的時候 會調用回調設置的對象的方法
        enhancer.setCallback(this);//回調this對象的intercept方法
        return enhancer.create();
    }

    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("代理前加邏輯");
        //調用原來的方法
        Object invoke = method.invoke(object, objects);
        System.out.println("代理後加邏輯");
        return null;
    }
}

解耦/減少代碼

代理模式是屬於設計模式中的一種,核心目的是在不改變核心邏輯代碼情況增強方法的功能,主要包括靜態代理與動態代理,靜態代理只能代理同一類對象,而動態代理可以代理任意對象。

動態代理包含了,JDk與cglib,jdk使用類加載器動態生成代理類(必須有接口),生成的代理類與被代理類實現同一個接口,增加的功能在invoke方法中實現,cglib是採用asm字節碼技術生成代理對象,要求被代理能被繼承(不能被final修飾),生成的代理對象是被代理對象的子類,增強的功能在intercept方法中寫。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章