Spring的AOP

AOP面向切面編程
Spring是基於Aspectj的AOP開發java

 

AOP的底層原理就是動態代理spring

 

動態代理分兩種
JDK動態代理:只能對實現了接口的類產生代理
Cglib動態代理:第三方代理技術,對沒有實現接口的類產生代理對象,生成子類對象,能夠動態添加類的屬性和方法express

Spring會根據是否有接口自動選擇相應的代理編程

 

術語:
鏈接點:能夠被攔截的點
切入點:真正被攔截的點
通知:加強方法
引介:類的加強
目標:被加強的對象
織入:將加強應用到目標的過程
代理:織入加強後產生的對象
切面:切入點和通知的組合微信

 

通知類型:ide

前置通知:
目標方法執行以前進行操做,能夠得到切入點信息
後置通知:
目標方法執行以後進行操做,能夠得到方法的返回值
環繞通知:
目標方法執行以前和以後進行操做,能夠阻止目標方法的執行
異常拋出通知:
程序出現異常時進行操做,能夠得到拋出的異常信息
最終通知:
不管代碼知否有異常,老是會執行this

 

切入點表達式語法
[訪問修飾符] 方法返回值 包名.類名.方法名(參數)
public void com.jinke.spring.CustomerDao.save(..)
* *.*.*.*Dao.save(..)
* com.jinke.spring.CustomerDao+.save(..)
* com.jinke.spring..*.*(..)spa

 

先介紹下兩種動態代理插件

JDK的動態代理代理

public interface UserDao { public void save(); public void update(); public void find(); public void delete(); }
public class UserDaoImpl implements UserDao { @Override public void save() { System.out.println("保存用戶"); } @Override public void update() { System.out.println("修改用戶"); } @Override public void find() { System.out.println("查詢用戶"); } @Override public void delete() { System.out.println("刪除用戶"); } }
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class JdkProxy implements InvocationHandler { private UserDao userDao; public JdkProxy(UserDao userDao) { this.userDao = userDao; } public UserDao createProxy() { UserDao userDaoProxy = (UserDao) Proxy.newProxyInstance(userDao.getClass().getClassLoader(), userDao.getClass().getInterfaces(), this); return userDaoProxy; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //判斷方法名是否是save
        if ("save".equals(method.getName())) { //加強
            System.out.println("權限校驗的代碼===="); return method.invoke(userDao, args); } return method.invoke(userDao, args); } }

執行

import org.junit.Test; public class Demo { @Test public void demo() { UserDao userDao = new UserDaoImpl(); UserDao proxy = new JdkProxy(userDao).createProxy(); proxy.save(); proxy.update(); proxy.find(); proxy.delete(); } }

輸出結果

權限校驗的代碼==== 保存用戶 修改用戶 查詢用戶 刪除用戶

 

Cglib的動態代理

public class CustomerDao { public void save() { System.out.println("保存用戶"); } public void update() { System.out.println("修改用戶"); } public void find() { System.out.println("查詢用戶"); } public void delete() { System.out.println("刪除用戶"); } }
import org.springframework.cglib.proxy.Enhancer; import org.springframework.cglib.proxy.MethodInterceptor; import org.springframework.cglib.proxy.MethodProxy; import java.lang.reflect.Method; public class CglibProxy implements MethodInterceptor { private CustomerDao customerDao; public CglibProxy(CustomerDao customerDao) { this.customerDao = customerDao; } public CustomerDao createProxy() { Enhancer enhancer = new Enhancer(); //設置父類
 enhancer.setSuperclass(customerDao.getClass()); //設置回調(相似於InvocationeHnadler對象)
        enhancer.setCallback(this); //建立代理對象
        CustomerDao proxy = (CustomerDao) enhancer.create(); return proxy; } @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { if ("save".equals(method.getName())) { //加強
            System.out.println("權限校驗的代碼===="); methodProxy.invokeSuper(proxy, args); } return methodProxy.invokeSuper(proxy, args); } }

執行

import org.junit.Test; public class Demo { @Test public void demo() { CustomerDao customerDao = new CustomerDao(); CustomerDao proxy = new CglibProxy(customerDao).createProxy(); proxy.save(); proxy.update(); proxy.find(); proxy.delete(); } }

輸出結果

權限校驗的代碼==== 保存用戶 保存用戶 修改用戶 查詢用戶 刪除用戶

 

AOP和IOC同樣,也有XML和註解兩種方式

XML方式:

public interface ProductDao { public void save(); public void update(); public void find(); public String delete(); }
public class ProductDaoImpl implements ProductDao { @Override public void save() { System.out.println("保存商品"); } @Override public void update() { System.out.println("修改商品"); } @Override public void find() { System.out.println("查詢商品"); int i = 1 / 0; } @Override public String delete() { System.out.println("刪除商品"); return "二傻"; } }
import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; /** * 切面類 */
public class MyAspectXML { public void checkPri(JoinPoint joinPoint) { System.out.println("權限校驗===" + joinPoint); } public void writeLog(Object result) { System.out.println("日誌記錄===" + result); } public Object around(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("環繞前通知===="); Object obj = joinPoint.proceed(); System.out.println("環繞後通知===="); return obj; } public void afterThrowing(Throwable ex) { System.out.println("異常拋出通知===" + ex); } public void after() { System.out.println("最終通知"); } }

配置文件ApplicationComtext4.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:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--配置目標對象:被加強的對象-->
    <bean id="productDao" class="com.jinke.aopxml.ProductDaoImpl"/>
    <!--將切面類交給Spring管理-->
    <bean id="myAspect" class="com.jinke.aopxml.MyAspectXML"/>

    <!--經過AOP的配置完成對目標類產生代理-->
    <aop:config>
        <!--表達式配置哪些類的那些方法須要進行加強-->
        <aop:pointcut id="pointcut1" expression="execution(* com.jinke.aopxml.ProductDaoImpl.save(..))"/>
        <aop:pointcut id="pointcut2" expression="execution(* com.jinke.aopxml.ProductDaoImpl.delete(..))"/>
        <aop:pointcut id="pointcut3" expression="execution(* com.jinke.aopxml.ProductDaoImpl.update(..))"/>
        <aop:pointcut id="pointcut4" expression="execution(* com.jinke.aopxml.ProductDaoImpl.find(..))"/>

        <!--配置切面-->
        <aop:aspect ref="myAspect">
            <!--前置通知-->
            <aop:before method="checkPri" pointcut-ref="pointcut1"/>
            <!--後置通知-->
            <aop:after-returning method="writeLog" pointcut-ref="pointcut2" returning="result"/>
            <!--環繞通知-->
            <aop:around method="around" pointcut-ref="pointcut3"/>
            <!--異常拋出通知-->
            <aop:after-throwing method="afterThrowing" pointcut-ref="pointcut4" throwing="ex"/>
            <!--最終通知-->
            <aop:after method="after" pointcut-ref="pointcut4"/>
        </aop:aspect>
    </aop:config>
</beans>

執行

import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import javax.annotation.Resource; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:ApplicationContext4.xml") public class SpringDemo { @Resource(name = "productDao") private ProductDao productDao; @Test public void demo() { productDao.save(); productDao.update(); productDao.find(); productDao.delete(); } }

輸出結果

權限校驗===execution(void com.jinke.aopxml.ProductDao.save()) 保存商品 環繞前通知==== 修改商品 環繞後通知==== 查詢商品 最終通知 異常拋出通知===java.lang.ArithmeticException: / by zero

 

註解的方式

public class OrderDao { public void save() { System.out.println("保存訂單"); } public void update() { System.out.println("修改訂單"); } public String delete() { System.out.println("刪除訂單"); return "三傻"; } public void find() { System.out.println("查詢訂單"); int i = 1 / 0; } }
import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; @Aspect public class MyAspectAnno { @Before(value = "MyAspectAnno.pointcutSave()") public void before() { System.out.println("前置通知"); } @AfterReturning(value = "MyAspectAnno.pointcutDelete()", returning = "result") public void afterReturn(Object result) { System.out.println("後置加強==" + result); } @Around(value = "MyAspectAnno.pointcutUpdate()") public void around(ProceedingJoinPoint joinPoint) throws Throwable { System.out.println("前置環繞"); joinPoint.proceed(); System.out.println("後置環繞"); } @AfterThrowing(value = "MyAspectAnno.pointcutFind()", throwing = "ex") public void afterThrowing(Throwable ex) { System.out.println("異常拋出==" + ex.getMessage()); } @After(value = "MyAspectAnno.pointcutFind()") public void after() { System.out.println("最終通知"); } /** * 切入點註解 */ @Pointcut(value = "execution(* com.jinke.aopanno.OrderDao.find())") private void pointcutFind() { } @Pointcut(value = "execution(* com.jinke.aopanno.OrderDao.save())") private void pointcutSave() { } @Pointcut(value = "execution(* com.jinke.aopanno.OrderDao.update())") private void pointcutUpdate() { } @Pointcut(value = "execution(* com.jinke.aopanno.OrderDao.delete())") private void pointcutDelete() { } }

配置文件ApplicationContext5.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:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--在配置文件中開啓註解AOP的開發-->
    <aop:aspectj-autoproxy/>

    <bean id="orderDao" class="com.jinke.aopanno.OrderDao"/>
    <bean id="myAspect" class="com.jinke.aopanno.MyAspectAnno"/>
</beans>

執行

import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import javax.annotation.Resource; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:ApplicationContext5.xml") public class SpringDemo { @Resource private OrderDao orderDao; @Test public void demo() { orderDao.save(); orderDao.update(); orderDao.delete(); orderDao.find(); } }

輸出結果

前置通知 保存訂單 前置環繞 修改訂單 後置環繞 刪除訂單 後置加強==三傻 查詢訂單 最終通知 異常拋出==/ by zero

簡單來講,AOP動態代理是爲了在不改變源碼的前提下,在源碼某個方法前,插入執行本身的方法。Android插件化中Hook也是用到的動態代理的思想,一模一樣

歡迎關注個人微信公衆號:安卓圈

相關文章
相關標籤/搜索