zoukankan      html  css  js  c++  java
  • 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也是用到的动态代理的思想,如出一辙

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

  • 相关阅读:
    Lucene.Net 2.3.1开发介绍 —— 二、分词(一)
    控制‘控制台应用程序’的关闭操作
    详解for循环(各种用法)
    敏捷软件开发
    Sql Server的一些知识点
    在SharePoint 2010 中配置Remote Blob Storage FILESTREAM Provider
    使用LotusScript操作Lotus Notes RTF域
    JOpt Simple 4.5 发布,命令行解析器
    John the Ripper 1.8.0 发布,密码破解工具
    PacketFence ZEN 4.0.1 发布,网络接入控制
  • 原文地址:https://www.cnblogs.com/anni-qianqian/p/11015893.html
Copyright © 2011-2022 走看看