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

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

  • 相关阅读:
    电脑麦克风不能用
    MathType中带上下标字符不对其
    Visio显示不完整
    程序员美食-煎豆腐
    Visio画好的图在word中只显示一部分
    Visio中旋转文本框与箭头平行
    Tikhonov regularization和岭回归
    linux下使用sftp
    git cherry-pick. 如何把已经提交的commit, 从一个分支放到另一个分支
    python 操作消息队列
  • 原文地址:https://www.cnblogs.com/anni-qianqian/p/11015893.html
Copyright © 2020-2023  润新知