spring-3-AOP

自定義註解類
@Retention(RetentionPolicy.RUNTIME),編譯是保留運行時註解
1、定義註解類
package anno;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

//此註解依次可以在類 屬性 方法進行
@Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD})

//運行時可以查看註解信息
@Retention(RetentionPolicy.RUNTIME)
public @interface Entity {
    //註解的參數
    public String value();
}

  

2、定義dao(模擬映射)

package dao;

import anno.Entity;

@Entity("entity")
public class CityEntity {
    private String id;
    private String name;

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public String getId() {
        return id;
    }
}

  

3、創建公共生成sql工具類
package util;

import anno.Entity;
import dao.CityEntity;

public class UtilSql {
    public static String builSqlStr(Object object){

        Class clazz = object.getClass( );

        // 先判斷是否存在註解
        if(clazz.isAnnotationPresent(Entity.class)){

            //判斷是否是自定義Entity註解
            Entity entity = (Entity) clazz.getAnnotation(Entity.class);

            //獲取相關注解傳的值
            String tableName = entity.value();
            CityEntity cityEntity = (CityEntity) object;
            String sql = "select * from "+tableName+" where id ='"+cityEntity.getId()+"'  and name = '"+cityEntity.getName()+"'";
            return sql;
        }
       return "沒有獲取到相關sql";
    }
}

  

4、測試

package test;

import dao.CityEntity;
import util.UtilSql;

public class Test {
    public static void main(String args[]){
        CityEntity cityEntity = new CityEntity();
        cityEntity.setId("1");
        cityEntity.setName("wjw");
        String sql = UtilSql.builSqlStr(cityEntity);
        System.out.println(sql);
    }
}

  

5、測試結果

 

6、先關項目截圖

 

 
Aop是什麼

  與OOP對比,AOP是處理一些橫切性問題,這些橫切性問題不會影響到主邏輯實現的,但是會散落到代碼的各個部分,難以維護。AOP就是把這些問題和主業務邏輯分開,達到與主業務邏輯解耦的目的。

 

aop應用場景

  1、日誌記錄

  2、權限校驗

  3、效率檢查

  4、事務管理

  5、.......

 

  • Aspect:切面,跨越多個類別的關注點的模塊化。事務管理是企業Java應用程序中橫切關注點的一個很好的例子。在Spring AOP中,方面是通過使用常規類(基於模式的方法)或使用註釋的常規類來實現的 @Aspect註釋(@AspectJ樣式註釋的

    •  
      一定要給spring去管理  抽象  aspectj->類   xml->label
       
  • Join point:連接點,目標對象植入邏輯類中的的點(方法地方),目標對象就是代理對象的原對象。程序執行期間的一個點,例如執行方法或處理異常。在Spring AOP中,連接點始終表示方法執行。

  • Advice:建議,特定連接點的某個方面採取的操作。不同類型的建議包括“周圍”,“之前”和“之後”建議。(建議類型將在後面討論。)許多AOP框架(包括Spring)將建議建模爲攔截器並在連接點周圍維護一系列攔截器。

    •   
      Before 連接點執行之前,但是無法阻止連接點的正常執行,除非該段執行拋出異常
      After  連接點正常執行之後,執行過程中正常執行返回退出,非異常退出
      After throwing  執行拋出異常的時候
      After (finally)  無論連接點是正常退出還是異常退出,都會執行
      Around advice: 圍繞連接點執行,例如方法調用。這是最有用的切面方式。around通知可以在方法調用之前和之後執行自定義行爲。它還負責選擇是繼續加入點還是通過返回自己的返回值或拋出異常來快速建議的方法執行。
  • Pointcut:切入點,就是連接點的集合,再主邏輯中的某一分支需要連接N(不定數)個方法,我們可以使用N個連接點或者直接使用切入點執行N個方法匹配連接點的謂詞。建議與切入點表達式相關聯,並在切入點匹配的任何連接點處運行(例如,執行具有特定名稱的方法)。由切入點表達式匹配的連接點的概念是AOP的核心,Spring默認使用AspectJ切入點表達式語言。

  • Introduction:引入(簡介),代表類型聲明其他方法或字段。Spring AOP允許您向任何建議的對象引入新接口(以及相應的實現)。例如,您可以使用簡介使bean實現 IsModified接口,以簡化緩存。(介紹被稱爲AspectJ社區中的類型間聲明。)

  • Target Object:目標對象,被代理對象的原對象。由一個或多個方面建議的對象。也稱爲“建議對象”。由於Spring AOP是使用運行時代理實現的,因此該對象始終是代理對象。

  • AOP proxy:AOP代理,由AOP框架創建的對象,用於實現方面契約(建議方法執行等)。在Spring Framework中,AOP代理是JDK動態代理或CGLIB代理。

  • Weaving:編織,將方面與其他應用程序類型或對象鏈接以創建建議對象。這可以在編譯時(例如,使用AspectJ編譯器),加載時間或在運行時完成。與其他純Java AOP框架一樣,Spring AOP在運行時執行編織

 

各種連接點joinPoint的意義:
   1、execution
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)
這裏問號表示當前項可以有也可以沒有,其中各項的語義如下
modifiers-pattern:方法的可見性,如public,protected;
ret-type-pattern:方法的返回值類型,如int,void等;
declaring-type-pattern:方法所在類的全路徑名,如com.spring.Aspect;
name-pattern:方法名類型,如buisinessService();
param-pattern:方法的參數類型,如java.lang.String;
throws-pattern:方法拋出的異常類型,如java.lang.Exception;
example:
@Pointcut("execution(* com.chenss.dao.*.*(..))")//匹配com.chenss.dao包下的任意接口和類的任意方法
@Pointcut("execution(public * com.chenss.dao.*.*(..))")//匹配com.chenss.dao包下的任意接口和類的public方法
@Pointcut("execution(public * com.chenss.dao.*.*())")//匹配com.chenss.dao包下的任意接口和類的public 無方法參數的方法
@Pointcut("execution(* com.chenss.dao.*.*(java.lang.String, ..))")//匹配com.chenss.dao包下的任意接口和類的第一個參數爲String類型的方法
@Pointcut("execution(* com.chenss.dao.*.*(java.lang.String))")//匹配com.chenss.dao包下的任意接口和類的只有一個參數,且參數爲String類型的方法
@Pointcut("execution(* com.chenss.dao.*.*(java.lang.String))")//匹配com.chenss.dao包下的任意接口和類的只有一個參數,且參數爲String類型的方法
@Pointcut("execution(public * *(..))")//匹配任意的public方法
@Pointcut("execution(* te*(..))")//匹配任意的以te開頭的方法
@Pointcut("execution(* com.chenss.dao.IndexDao.*(..))")//匹配com.chenss.dao.IndexDao接口中任意的方法
@Pointcut("execution(* com.chenss.dao..*.*(..))")//匹配com.chenss.dao包及其子包中任意的方法
https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#aop-pointcuts-example

2、within
™表達式的最小粒度爲類
// within與execution相比,粒度更大,僅能實現到包和接口、類級別。而execution可以精確到方法的返回值,參數個數、修飾符、參數類型等
@Pointcut("within(com.chenss.dao.*)")//匹配com.chenss.dao包中的任意方法
@Pointcut("within(com.chenss.dao..*)")//匹配com.chenss.dao包及其子包中的任意方法

3、args
args表達式的作用是匹配指定參數類型和指定參數數量的方法,與包名和類名無關
/**
 * args同execution不同的地方在於:
 * args匹配的是運行時傳遞給方法的參數類型
 * execution(* *(java.io.Serializable))匹配的是方法在聲明時指定的方法參數類型。
 */
@Pointcut("args(java.io.Serializable)")//匹配運行時傳遞的參數類型爲指定類型的、且參數個數和順序匹配
@Pointcut("@args(com.chenss.anno.Chenss)")//接受一個參數,並且傳遞的參數的運行時類型具有@Classifie

   4、this JDK代理時,指向接口和代理類proxy,cglib代理時 指向接口和子類(不使用proxy)

   5、target 指向接口和子類

/**
 * 此處需要注意的是,如果配置設置proxyTargetClass=false,或默認爲false,則是用JDK代理,否則使用的是CGLIB代理
 * JDK代理的實現方式是基於接口實現,代理類繼承Proxy,實現接口。
 * 而CGLIB繼承被代理的類來實現。
 * 所以使用target會保證目標不變,關聯對象不會受到這個設置的影響。
 * 但是使用this對象時,會根據該選項的設置,判斷是否能找到對象。
 */
@Pointcut("target(com.chenss.dao.IndexDaoImpl)")//目標對象,也就是被代理的對象。限制目標對象爲com.chenss.dao.IndexDaoImpl類
@Pointcut("this(com.chenss.dao.IndexDaoImpl)")//當前對象,也就是代理對象,代理對象時通過代理目標對象的方式獲取新的對象,與原值並非一個
@Pointcut("@target(com.chenss.anno.Chenss)")//具有@Chenss的目標對象中的任意方法
@Pointcut("@within(com.chenss.anno.Chenss)")//等同於@targ
proxy模式裏面有兩個重要的術語
proxy Class
target Class
CGLIB和JDK有區別    JDK是基於接口   cglib是基於繼承所有this可以在cglib作用

 



根據官網寫的簡單切入點測試

1、javaConfig
package com.appconfig;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

//聲明這是一個javaconfig
@Configuration
//掃描com包下的所有類
@ComponentScan("com")
//允許使用AdpectJ自動代理
@EnableAspectJAutoProxy

//javaConfig
public class Appconfig {
}

  

2、模擬持久層用的查詢

package com.dao;
import org.springframework.stereotype.Component;


@Component
public class Wjw_Dao {

    public void query(){
        System.out.println("我是query");
    }
}

  

3、切入點類

package com.appconfig;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

//需要由容器管理
@Component

//說明這是一個切面類
@Aspect
public class AspectjConfig {

    //連接點對哪些方法進行連接
    @Pointcut("execution(* com.dao.*.*(..))")
    public void pointcut(){

    }

    //每次調用query之前需要處理的事情
    @Before("pointcut()")
    public void befor(){
        System.out.println("我是切面再邏輯之前打印");
    }
}

  

4、測試

import com.dao.Wjw_Dao;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Test {
    public static void main(String args[]){
        AnnotationConfigApplicationContext acc = new AnnotationConfigApplicationContext(Appconfig.class);
        Wjw_Dao wd = acc.getBean(Wjw_Dao.class);
        wd.query();
    }
}

  

5、截圖

 

相關擴展點:自動代理(spring默認使用的是標準JDK中的proxy)

  如果使用EnableAspectAutoProxy註解需要注意的是,當一個接口的實現類需要使用切入點或者連接點的時候需要設置默認參數爲true

  如果不設置則會出現找不到對應的代理對象類型(很奇怪的一個問題)

  @EnableAspectAutoProxy(proxyTargetClass=true) 

  爲什麼要將默認參數設置爲true?

  因爲此時生成的對象是proxy和Dao,唯獨不是class對象,設置完後將使代理後的對象爲目標對象

  我們可以生成相關代理對象的字節碼的文件進行查看。

  

public static void main (String args []){
Class<?>[] interfaces = new Class[]{Dao.class};
byte bytes [] = ProxyGenerator.generateProxyClass("wjwTest",interfaces);
File file = new File("D:\\test.class");
try{
FileOutputStream fw = new FileOutputStream(file);
fw.write(bytes);
fw.flush();
fw.close();
System.out.println("已完成");
}catch(IOException e){
System.out.println("exception");
e.getMessage();
}

  

擴展二,爲什麼javaJDK動態代理只能接口,而不能使用繼承?

 

 

 

 


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