Spring的AOP

AOP面向切面编程
Spring是基于Aspectj的AOP开发

 

AOP的底层原理就是动态代理

 

动态代理分两种
JDK动态代理:只能对实现了接口的类产生代理
Cglib动态代理:第三方代理技术,对没有实现接口的类产生代理对象,生成子类对象,可以动态添加类的属性和方法

Spring会根据是否有接口自动选择相应的代理

 

术语:
连接点:可以被拦截的点
切入点:真正被拦截的点
通知:增强方法
引介:类的增强
目标:被增强的对象
织入:将增强应用到目标的过程
代理:织入增强后产生的对象
切面:切入点和通知的组合

 

通知类型:

前置通知:
目标方法执行之前进行操作,可以获得切入点信息
后置通知:
目标方法执行之后进行操作,可以获得方法的返回值
环绕通知:
目标方法执行之前和之后进行操作,可以阻止目标方法的执行
异常抛出通知:
程序出现异常时进行操作,可以获得抛出的异常信息
最终通知:
无论代码知否有异常,总是会执行

 

切入点表达式语法
[访问修饰符] 方法返回值 包名.类名.方法名(参数)
public void com.jinke.spring.CustomerDao.save(..)
* *.*.*.*Dao.save(..)
* com.jinke.spring.CustomerDao+.save(..)
* com.jinke.spring..*.*(..)

 

先介绍下两种动态代理

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也是用到的动态代理的思想,如出一辙

欢迎关注我的微信公众号:安卓圈

posted @ 2019-06-13 12:54  嘉禾世兴  阅读(360)  评论(0编辑  收藏  举报