前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SpringAOP

SpringAOP

作者头像
用户3112896
发布2019-09-26 14:52:50
2900
发布2019-09-26 14:52:50
举报
文章被收录于专栏:安卓圈

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的动态代理

代码语言:javascript
复制
public interface UserDao {
    public void save();
    public void update();
    public void find();
    public void delete();
}
代码语言:javascript
复制
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("删除用户");
    }
}
代码语言:javascript
复制
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);
    }
}

执行

代码语言:javascript
复制
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();
    }
}

输出结果

代码语言:javascript
复制
权限校验的代码====
保存用户
修改用户
查询用户
删除用户

Cglib的动态代理

代码语言:javascript
复制
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("删除用户");
    }
}
代码语言:javascript
复制
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);
    }
}

执行

代码语言:javascript
复制
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();
    }
}

输出结果

代码语言:javascript
复制
权限校验的代码====
保存用户
保存用户
修改用户
查询用户
删除用户

AOP和IOC一样,也有XML和注解两种方式

XML方式:

代码语言:javascript
复制
public interface ProductDao {
    public void save();

    public void update();

    public void find();

    public String delete();
}
代码语言:javascript
复制
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 "二傻";
    }
}
代码语言:javascript
复制
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

代码语言:javascript
复制
<?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>

执行

代码语言:javascript
复制
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();
    }
}

输出结果

代码语言:javascript
复制
权限校验===execution(void com.jinke.aopxml.ProductDao.save())
保存商品
环绕前通知====
修改商品
环绕后通知====
查询商品
最终通知
异常抛出通知===java.lang.ArithmeticException: / by zero

注解的方式

代码语言:javascript
复制
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;
    }
}
代码语言:javascript
复制
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

代码语言:javascript
复制
<?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>

执行

代码语言:javascript
复制
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();
    }
}

输出结果

代码语言:javascript
复制
前置通知
保存订单
前置环绕
修改订单
后置环绕
删除订单
后置增强==三傻
查询订单
最终通知
异常抛出==/ by zero

简单来说,AOP动态代理是为了在不改变源码的前提下,在源码某个方法前,插入执行自己的方法。Android插件化中Hook也是用到的动态代理的思想,如出一辙

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-06-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 安卓圈 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档