zoukankan      html  css  js  c++  java
  • 再也不怕aop的原理了

    1 aop是什么

      java的核心思想是面向对象,aop是面向切面编程.是对面向对象的一个补充,简单通俗的理解下aop,假设我们上楼梯,我的目标是三楼,我直接朝我的三楼直接过去,但是我可以在二楼的时候去朋友那里玩下,坐下其他的,然后再去三楼

    这也不会影响我们的目标去三楼,那么去朋友那里玩,相当于增加的生活的乐趣去那里玩.相当于增强了我们的活动.

      那么aop可以做啥呢,可以进行日志记录,可以进行事务管理,可以进行安全控制,可以进行异常处理,可以进行性能统计

    2 理解几个关键字

    切面

     关注点形成的类,就叫切面(类)!

     面向切面编程,就是指对很多功能都有的重复的代码抽取,再在运行的时候网业务方法上动态植入“切面类代码”

    切点

    执行目标对象方法,动态植入切面代码。

    可以通过切入点表达式,指定拦截哪些类的哪些方法; 给指定的类在运行的时候植入切面类代码。

    通知

    在对象上面执行的内容

    3 aop的实现原理:

    aop的底层实现是代理模式加反射.

    反射就不用多讲了,代理模式分为多种,静态代理和动态代理,动态代理面又分为jdk动态代理和cglib动态代理

    代理模式的好处:可以防止对方得到我们真实的方法;

    静态代理的实现方式:

    package com.cxy.cyclicBarrier;
    
    /**
     * Created by Administrator on 2017/4/24.
     */
    public interface IUserDao {
        void save();
    }
    package com.cxy.cyclicBarrier;
    
    /**
     * Created by Administrator on 2019/4/24.
     */
    public class UserDao implements IUserDao {
        @Override
        public void save() {
            System.out.println("已经保存数据");
        }
    }

    代理类:

    package com.cxy.cyclicBarrier;
    
    /**
     * Created by Administrator on 2019/4/24.
     */
    
    
    
    public class UserDaoProxy  implements IUserDao{
        private IUserDao target;
    
        public UserDaoProxy(IUserDao iuserDao) {
            this.target = iuserDao;
        }
    
        public void save() {
            System.out.println("开启事物...");
            target.save();
            System.out.println("关闭事物...");
        }
    
        public static void main(String[] args) {
            UserDao userDao=new UserDao();
            UserDaoProxy userDaoProxy=new UserDaoProxy(userDao );
            userDaoProxy.save();
        }
    
    }

    在最后可以看到执行结果:

    结果分析:

    静态代理:

    由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。

    然后再看jdk动态代理:

    package com.cxy.cyclicBarrier;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    // 每次生成动态代理类对象时,实现了InvocationHandler接口的调用处理器对象
    public class InvocationHandlerImpl implements InvocationHandler {
        private Object target;// 这其实业务实现类对象,用来调用具体的业务方法
        // 通过构造函数传入目标对象
        public InvocationHandlerImpl(Object target) {
            this.target = target;
        }
    
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Object result = null;
            System.out.println("调用开始处理");
            result = method.invoke(target, args);
            System.out.println("调用结束处理");
            return result;
        }
    
        public static void main(String[] args) throws NoSuchMethodException, SecurityException, InstantiationException,
                IllegalAccessException, IllegalArgumentException, InvocationTargetException {
            // 被代理对象
            IUserDao userDao = new UserDao();
            InvocationHandlerImpl invocationHandlerImpl = new InvocationHandlerImpl(userDao);
            ClassLoader loader = userDao.getClass().getClassLoader();
            Class<?>[] interfaces = userDao.getClass().getInterfaces();
            // 主要装载器、一组接口及调用处理动态代理实例
            IUserDao newProxyInstance = (IUserDao) Proxy.newProxyInstance(loader, interfaces, invocationHandlerImpl);
            newProxyInstance.save();
        }
    
    }

    结果分析:

    jdk动态代理:

    1. 通过实现InvocationHandler接口创建自己的调用处理器 IvocationHandler handler = new InvocationHandlerImpl(…);

    2. 通过为Proxy类指定ClassLoader对象和一组interface创建动态代理类Class clazz = Proxy.getProxyClass(classLoader,new Class[]{…});

    3. 通过反射机制获取动态代理类的构造函数,其参数类型是调用处理器接口类型Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});

    4. 通过构造函数创建代理类实例,此时需将调用处理器对象作为参数被传入Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler));

    cglib代理:

    package com.cxy.cyclicBarrier;
    
    import java.lang.reflect.Method;
    
    public class CglibProxy implements MethodInterceptor {
        private Object targetObject;
        // 这里的目标类型为Object,则可以接受任意一种参数作为被代理类,实现了动态代理
        public Object getInstance(Object target) {
            // 设置需要创建子类的类
            this.targetObject = target;
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(target.getClass());
            enhancer.setCallback(this);
            return enhancer.create();
        }
    
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            System.out.println("开启事物");
            Object result = proxy.invoke(targetObject, args);
            System.out.println("关闭事物");
            // 返回代理对象
            return result;
        }
    
    
        public static void main(String[] args) {
            CglibProxy cglibProxy = new CglibProxy();
            UserDao userDao = (UserDao) cglibProxy.getInstance(new UserDao());
            userDao.save();
        }
    }

    执行结果:

    在执行上面代码前需要引入asm包

    原理:利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

    4 区别比较:
    CGLIB动态代理与JDK动态区别

    java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。

    而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

    Spring中。

    1)、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP

    2)、如果目标对象实现了接口,可以强制使用CGLIB实现AOP

    3)、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换

    JDK动态代理只能对实现了接口的类生成代理,而不能针对类 。
    CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法 。
    因为是继承,所以该类或方法最好不要声明成final ,final可以阻止继承和多态。

    5 aop的使用方式:

     1) 注解方式:

    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>  开启事物注解权限
    @Aspect                            指定一个类为切面类        
    @Pointcut("execution(* com.xhz.service.UserService.add(..))")  指定切入点表达式
    @Before("pointCut_()")                前置通知: 目标方法之前执行
    @After("pointCut_()")                后置通知:目标方法之后执行(始终执行)
    @AfterReturning("pointCut_()")         返回后通知: 执行方法结束前执行(异常不执行)
    @AfterThrowing("pointCut_()")            异常通知:  出现异常时候执行
    @Around("pointCut_()")                环绕通知: 环绕目标方法执行
     
    @Component
    @Aspect
    public class AopLog {
     
        // 前置通知
        @Before("execution(* com.cxy.service.UserService.add(..))")
        public void begin() {
            System.out.println("前置通知");
        }
     
        //
        // 后置通知
        @After("execution(* com.cxy.service.UserService.add(..))")
        public void commit() {
            System.out.println("后置通知");
        }
     
        // 运行通知
        @AfterReturning("execution(* com.cxy.service.UserService.add(..))")
        public void returning() {
            System.out.println("运行通知");
        }
     
        // 异常通知
        @AfterThrowing("execution(* com.cxy.service.UserService.add(..))")
        public void afterThrowing() {
            System.out.println("异常通知");
        }
     
        // 环绕通知
        @Around("execution(* com.cxy.service.UserService.add(..))")
        public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
            System.out.println("环绕通知开始");
            proceedingJoinPoint.proceed();
            System.out.println("环绕通知结束");
        }
    }
     

    2)xml方式:

    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
        xmlns:context="http://www.springframework.org/schema/context"
        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/context
            http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop.xsd">
     
        <!-- dao 实例 -->
     
        <bean id="userService" class="com.cxy.service.UserService"></bean>
        <!-- 切面类 -->
        <bean id="aop" class="com.cxy.aop2.AopLog2"></bean>
        <!-- Aop配置 -->
        <aop:config>
            <!-- 定义一个切入点表达式: 拦截哪些方法 -->
            <aop:pointcut expression="execution(* com.cxy.service.UserService.*(..))"
                id="pt" />
            <!-- 切面 -->
            <aop:aspect ref="aop">
                <!-- 环绕通知 -->
                <aop:around method="around" pointcut-ref="pt" />
                <!-- 前置通知: 在目标方法调用前执行 -->
                <aop:before method="begin" pointcut-ref="pt" />
                <!-- 后置通知: -->
                <aop:after method="after" pointcut-ref="pt" />
                <!-- 返回后通知 -->
                <aop:after-returning method="afterReturning"
                    pointcut-ref="pt" />
                <!-- 异常通知 -->
                <aop:after-throwing method="afterThrowing"
                    pointcut-ref="pt" />
            </aop:aspect>
        </aop:config>
     
    </beans>
    public class AopLog2 {
     
        // 前置通知
        public void begin() {
            System.out.println("前置通知");
        }
     
        //
        // 后置通知
        public void commit() {
            System.out.println("后置通知");
        }
     
        // 运行通知
        public void returning() {
            System.out.println("运行通知");
        }
     
        // 异常通知
        public void afterThrowing() {
            System.out.println("异常通知");
        }
     
        // 环绕通知
        public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
            System.out.println("环绕通知开始");
            proceedingJoinPoint.proceed();
            System.out.println("环绕通知结束");
        }
    }
  • 相关阅读:
    关于Winsock编程中IO重叠的概念
    comparator接口与Comparable接口的区别
    String、StringBuffer与StringBuilder之间区别
    Oracle 中 call 和 exec的区别
    谈谈对Spring IOC的理解(转)
    常见的几种RuntimeException
    long(Long)与int(Integer)之间的转换
    ValueStack值栈和ActionContext
    Struts2执行过程解析
    Struts2的经典入门
  • 原文地址:https://www.cnblogs.com/xiufengchen/p/10761036.html
Copyright © 2011-2022 走看看