Spring的AOP

Posted anni-qianqian

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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也是用到的动态代理的思想,如出一辙

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

技术图片

以上是关于Spring的AOP的主要内容,如果未能解决你的问题,请参考以下文章

JAVA之AOP

Spring AOP

Spring源码高级笔记之——Spring AOP应用

2018.12.24 Spring中的aop演示

Spring的AOP面向切面编程

Spring框架 AOP