SpringAOP---ProxyFactoryBean

在面試中咱們常常被問到spring的AOP,今天我也說一下AOP.java

AOP爲Aspect Oriented Programming的縮寫,意爲:面向切面編程,經過預編譯方式和運行期動態代理實現程序功能的統一維護的一種技術。AOP是OOP的延續,是軟件開發中的一個熱點,也是Spring框架中的一個重要內容,是函數式編程的一種衍生範型。利用AOP能夠對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度下降,提升程序的可重用性,同時提升了開發的效率。 AOP底層採用兩種動態代理模式實現: 1.JDK的動態代理node

2. CGLIB動態代理
複製代碼

當被代理的對象沒有實現接口時候,使用的是CGLIB代理,當被代理的對象實現接口時候使用的是JDK代理,固然也能夠強制使用CGLIB代理。 spring有支持3中類型實現AOP 須要實現接口,手工建立代理,最原始的實現方式python

使用XML配置。aop命名空間nginx

使用註解(@AspectJ)web

咱們今天主要是講解最原始的方式,手工建立代理,實現AOP.這種當時幾乎不用了,主要是讓你們理解AOP的做用以及原理,學習以前咱們先搞清楚幾個概念. 1.切面(Aspect) 交叉業務邏輯,例如事務、安全、日誌等,稱爲切面。面試

2.目標對象(Target) 業務類的對象,稱爲目標對象。redis

3.織入(Weaving) 將切面插入到目標對象的目標方法的過程,稱爲織入。spring

4.鏈接點(JoinPoint) 目標對象中能夠被切面織入的方法。docker

5.切入點(Pointcut) 目標對象中真正被切面織入的方法。切入點必定是鏈接點,但鏈接點不必定是切入點。被標記爲final的方法是不能做用鏈接點與切入點的。typescript

6.通知(Advice) 通知是切面的一種,能夠完成簡單的織入功能。通知能夠定義切面織入的時間點,切入點定義了切面織入的位置。

7.顧問(Advisor) 顧問是切面的一種,可以將通知以更爲複雜的方式織入到目標對象中,是將通知包裝爲 更復雜的切面的裝配器。

上面的通知(Advice)和顧問(Advisor)是兩種織入的方式,他們兩也是有區別的,通知是給目標對象的全部方法都會織入切面,而Advisor是有選擇的給目標對象的方法織入切面。咱們接下來就看一下,如何基於手動建立代理方式而且使用Advisor織入來演示Spring AOP,先看一下他的關係圖

咱們按照上面的圖實現AOP 實現classFilter 和MethodMatcher package com.jiepi.spring.util;

import com.jiepi.spring.serviceImp.Person; import org.springframework.aop.ClassFilter;

public class MyClassFilter implements ClassFilter {

/*
 *  1.一個接口下會有多個實現類
 *  2.判斷當前實現類是否是咱們織入方式關心的目標類
 *  BaseService接口咱們如今只想管理Person.
 *  參數:就是當前被攔截類:可能Person,可能Gog
 * */
@Override
public boolean matches(Class<?> clazz) {
    if (clazz == Person.class) {
        return true;
    }
    return false;
}
複製代碼

}

package com.jiepi.spring.util;

import org.springframework.aop.MethodMatcher;

import java.lang.reflect.Method;

public class MyMethodMatcher implements MethodMatcher {

/*
 *  被監控接口好比(BaseService),沒有重載方法
 *  每個方法名稱都是以惟一
 *  此時能夠採用 static檢測方式,只根據方法名稱判斷
 * 參數:method: 接口中某一個方法
 *     targetClass: 接口中一個實現類
 *
 *  業務:只想爲Person類中play方法提供織入
 */
@Override
public boolean matches(Method method, Class<?> targetClass) {
    String methodName = method.getName();
    if ("play".equals(methodName)) {
        return true;
    }
    return false;
}

@Override
public boolean isRuntime() {
    return false;
}

@Override
public boolean matches(Method method, Class<?> targetClass, Object... args) {
    return false;
}
複製代碼

} 2.實現切入點PointCut實現獲取那個對象的那個方法要進行織入切面

package com.jiepi.spring.util;

import org.springframework.aop.ClassFilter; import org.springframework.aop.MethodMatcher; import org.springframework.aop.Pointcut;

public class MyPointCut implements Pointcut {

private ClassFilter classFilter;

private MethodMatcher methodMatcher;

public void setClassFilter(ClassFilter classFilter) {

    this.classFilter = classFilter;
}

public void setMethodMatcher(MethodMatcher methodMatcher) {

    this.methodMatcher = methodMatcher;
}

@Override
public ClassFilter getClassFilter() {
    return this.classFilter;
}

@Override
public MethodMatcher getMethodMatcher() {
    return this.methodMatcher;
}
複製代碼

} 3.實現 切面 advice

package com.jiepi.spring.advice;

import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;

public class MyAfterAdvice implements AfterReturningAdvice {

//切面:次要業務
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
    System.out.println("玩完以後我要給潔癖洗澡");
}
複製代碼

} 4.實現PointcutAdvisor實現綁定切入點和切面

package com.jiepi.spring.util;

import org.aopalliance.aop.Advice; import org.springframework.aop.Pointcut; import org.springframework.aop.PointcutAdvisor;

public class MyPointCutAdvisor implements PointcutAdvisor {

private Pointcut pointcut;//當前攔截對象和對象調用主要業務方法 person對象.play()

private  Advice advice; //次要業務以及次要業務與主要業務執行順序

public void setPointcut(Pointcut pointcut) {
    this.pointcut = pointcut;
}

public void setAdvice(Advice advice) {
    this.advice = advice;
}

@Override
public Pointcut getPointcut() {

    return this.pointcut;
}

@Override
public Advice getAdvice() {

    return this.advice;
}

@Override
public boolean isPerInstance() {
    return false;
}
複製代碼

} 5.目標對象

package com.jiepi.spring.service;

public interface BaseService {

void eat();

void play();
複製代碼

}

package com.jiepi.spring.serviceImp;

import com.jiepi.spring.service.BaseService;

public class Person implements BaseService {

@Override
public void eat() {
    System.out.println("潔癖主人吃漢堡包");
}

@Override
public void play() {
    System.out.println("潔癖主人玩電腦");
}
複製代碼

} 6.配置依賴注入,實現手動建立代理對象

<!-- 註冊被監控實現類 -->
<bean id="person" class="com.jiepi.spring.serviceImp.Person"/>
<bean id="dog" class="com.jiepi.spring.serviceImp.Dog"/>

<!-- 註冊通知實現類 -->
<bean id="after" class="com.jiepi.spring.advice.MyAfterAdvice"/>
<!-- 註冊類型過濾器 -->
<bean id="classFilter" class="com.jiepi.spring.util.MyClassFilter"/>
<!-- 註冊方法匹配器 -->
<bean id="methodMatcher" class="com.jiepi.spring.util.MyMethodMatcher"/>
<!-- 註冊切入點 -->
<bean id="pointCut" class="com.jiepi.spring.util.MyPointCut">
    <property name="classFilter" ref="classFilter"/>
    <property name="methodMatcher" ref="methodMatcher"/>
</bean>
<!-- 註冊顧問 -->
<bean id="myAdvisor" class="com.jiepi.spring.util.MyPointCutAdvisor">
    <property name="advice" ref="after"/>
    <property name="pointcut" ref="pointCut"/>
</bean>
<!-- 註冊代理對象工廠 -->
<!--
                      此時生成代理對象,只會負責person.play方法監控
                      與Advice不一樣,不會對BaseService全部的方法進行監控
 -->
<bean id="personFactory" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="target" ref="person"/>
    <property name="interceptorNames" value="myAdvisor"/>
</bean>
複製代碼
7.測試結果

package com.jiepi.spring;

import com.jiepi.spring.service.BaseService; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestMain {

@Test
public void test() {
    ApplicationContext factory = new ClassPathXmlApplicationContext("spring_config.xml");
    BaseService service = (BaseService) factory.getBean("personFactory");
    service.play();
    System.out.println("===========下面是沒有進行代理的方法================");
    service.eat();
}
複製代碼

}

運行結果: 潔癖主人玩電腦 玩完以後我要給潔癖洗澡 ===========下面是沒有進行代理的方法================ 潔癖主人吃漢堡包 能夠看到咱們的結果,僅僅對person目標對象中的play使用了AOP,織入了其餘業務。 這個例子僅僅是讓你們理解SpringAOP.並不建議使用,由於寫起來太繁瑣了,咱們大部分工做用的都是AspectJ.其餘方式的使用有機會再和你們分享.

但願對你們有所幫助,也但願你們持續關注轉載。關注公衆號獲取相關資料請回復:typescript,springcloud,springboot,nodejs,nginx,mq,javaweb,java併發實戰,java併發高級進階,實戰java併發,極客時間dubbo,kafka,java面試題,ES,zookeeper,java入門到精通,區塊鏈,java優質視頻,大數據,kotlin,瞬間之美,HTML與CSS,深刻體驗java開發,web開發CSS系列,javaweb開發詳解,springmvc,java併發編程,spring源碼,python,go,redis,docker,即獲取相關資料 ​

相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息