• spring AOP面向切面编程学习笔记


    一、面向切面编程简介:

    在调用某些类的方法时,要在方法执行前或后进行预处理或后处理;预处理或后处理的操作被封装在另一个类中。如图中,UserService类在执行addUser()或updateUser方法前开启事务,执行完后要提交事务;而几乎所有数据库操作都是如此,那么就可以将事务操作的方法提取出封装到一个类里。然后再利用代理类进行处理(目标类方法增强),返回代理类对象

    二、AOP相关术语

    Target:目标类,需要被增强的类。

    JoinPoint:连接点,目标类上需要被增强的方法。(这些方法可以被增强,也可能不增强)。

    PointCut:切入点,被增强的连接点(已经增强了)。切入点术语特殊的连接点

    Advice:增强/通知,增强的方法。

    weaving:织入,将切入点和通知结合,生成代理类过程。

    Proxy:代理类。

    Aspect:切面,切入点和通知结合形成的面

    三、JDK动态代理模拟AOP

    1、项目结构(接口+目标类+切面类+代理类+测试类)

    2、新建接口UserService

    package hjp.springAOP.jdkProxy;
    
    public interface UserService {
        void addUser();
    
        void updateUser();
    }
    UserService

    3、新建目标类UserServiceImpl,并实现接口UserService

    package hjp.springAOP.jdkProxy;
    
    public class UserServiceImpl implements UserService {
    
        @Override
        public void addUser() {
            // TODO Auto-generated method stub
            System.out.println("jdk add user");
        }
    
        @Override
        public void updateUser() {
            // TODO Auto-generated method stub
            System.out.println("jdk update user");
        }
    
    }
    UserServiceImpl

    4、新建切面类MyAspect

    package hjp.springAOP.jdkProxy;
    
    /**
     * 切面类,用于存放增强
     * 
     * @author JiaPeng
     *
     */
    public class MyAspect {
        public void beafore() {
            System.out.println("before");
        }
    
        public void after() {
            System.out.println("after");
        }
    }
    MyAspect

    5、新建代理类MyFactory

    package hjp.springAOP.jdkProxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    /**
     * 工厂生成代理类,目的:将目标类(切入点)和切面类(通知)结合。
     * 
     * @author JiaPeng
     *
     */
    public class MyFactory {
        public static UserService createService() {
            // 创建目标类
            final UserService userService = new UserServiceImpl();
            // 创建切面类
            final MyAspect myAspect = new MyAspect();
            // 使用jdk动态代理,生成代理类
            // 第一个参数是,当前类下的类加载器,第二个参数是目标类所继承的接口数组,第三个参数是匿名内部类
            // 匿名内部类也就是没有名字的内部类;正因为没有名字,所以匿名内部类只能使用一次,它通常用来简化代码编写;但使用匿名内部类还有个前提条件:必须继承一个父类或实现一个接口
            UserService proxyService = (UserService) Proxy.newProxyInstance(MyFactory.class.getClassLoader(),
                    userService.getClass().getInterfaces(), new InvocationHandler() {
                        // 代理类每一个方法执行时,都将调用处理类的invoke方法
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            // TODO Auto-generated method stub
                            // 执行前通知
                            myAspect.beafore();
                            // 执行目标类的相应方法
                            Object obj = method.invoke(userService, args);
                            // 执行后通知
                            myAspect.after();
                            return obj;
                        }
                    });
            return proxyService;
        }
    }
    代理类

    6、新建测试类

    package hjp.springAOP.jdkProxy;
    
    import org.junit.Test;
    
    public class TestApp {
        @Test
        public void demo1() {
            UserService userService = MyFactory.createService();
            userService.addUser();
            userService.updateUser();
        }
    }
    测试类

    四、CGLIB模拟AOP

    cglib 字节码增强工具,一般框架都使用。只要有类就可以增强

    1、项目结构(cglib的jar包已被spring打包好了,所以添加了spring的主要jar包就可以了)

    2、新建目标类UserServiceImpl,没有接口

    package hjp.springAOP.cglibProxy;
    
    public class UserServiceImpl {
    
        public void addUser() {
            System.out.println("cglib add user");
        }
    
        public void updateUser() {
            System.out.println("cglib update user");
        }
    
    }
    UserServiceImpl

    3、新建切面类

    package hjp.springAOP.cglibProxy;
    
    /**
     * 切面类,用于存放增强
     * 
     * @author JiaPeng
     *
     */
    public class MyAspect {
        public void beafore() {
            System.out.println("before");
        }
    
        public void after() {
            System.out.println("after");
        }
    }
    切面类

    4、新建代理类

    package hjp.springAOP.cglibProxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    import org.eclipse.jdt.internal.compiler.classfmt.FieldInfoWithAnnotation;
    import org.springframework.cglib.proxy.Enhancer;
    import org.springframework.cglib.proxy.MethodInterceptor;
    import org.springframework.cglib.proxy.MethodProxy;
    
    /**
     * 工厂生成代理类,目的:将目标类(切入点)和切面类(通知)结合。
     * 
     * @author JiaPeng
     *
     */
    public class MyFactory {
        public static UserServiceImpl createService() {
            // 创建目标类
            final UserServiceImpl userServiceImpl = new UserServiceImpl();
            // 创建切面类
            final MyAspect myAspect = new MyAspect();
            // 使用cglib创建代理类,其实现方式就是在cglib运行时,动态创建目标类子类(该子类就是代理类,Java动态代理是基于接口或父类的)
            // 创建核心类
            Enhancer enhancer = new Enhancer();
            // 设置父类
            enhancer.setSuperclass(userServiceImpl.getClass());
            // 代理类方法将调用回调函数,等效JDK InvocationHandler
            // 接口Callback,子接口MethodInterceptor对方法进行增强
            enhancer.setCallback(new MethodInterceptor() {
    
                @Override
                // 前三个参数与jdk动态代理invoke相同
                // 第四个参数是方法代理
                public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy)
                        throws Throwable {
                    // TODO Auto-generated method stub
                    // 切面类的前通知
                    myAspect.beafore();
                    // 目标方法
                    //Object object = method.invoke(userServiceImpl, args);
                    Object object = methodProxy.invokeSuper(proxy, args);//执行代理类(子类)父类方法(父类就是目标类),效果和上面的一样
                    // 切面类后通知
                    myAspect.after();
                    return object;
                }
            });
            // 创建代理类
            return (UserServiceImpl) enhancer.create();
        }
    }
    代理类

    5、新建测试类

    package hjp.springAOP.cglibProxy;
    
    import org.junit.Test;
    
    public class TestApp {
        @Test
        public void demo1() {
            UserServiceImpl userService = MyFactory.createService();
            userService.addUser();
            userService.updateUser();
        }
    }
    测试类

     五、利用spring工厂bean,半自动化模拟AOP

    使用spring提供FactoryBean创建代理对象,手动的获取代理对象。生成代理需要应用增强(通知),通知需要确定方法名称。spring规范规定通知类型从而确定方法名称。

    AOP联盟五种通知类型,spring对AOP联盟进行支持。

      前置通知:org.springframework.aop.MethodBeforeAdvice 在目标方法执行前实施增强

      后置通知:org.springframework.aop.AfterReturningAdvice 在目标方法执行后实施增强

      环绕通知:org.aopalliance.intercept.MethodInterceptor 在目标方法执行前后实施增强

      异常抛出通知:org.springframework.aop.ThrowsAdvice 在方法抛出异常后实施增强

      引介通知:org.springframework.aop.Introductioninterceptor 在目标类中添加一些新的方法和属性

    环绕通知可以模拟其他通知类型,如:

    try{

    前置

    //必须手动执行目标方法

    后置

    }catch{

    //异常抛出

    }

    1、项目结构:(新加AOP联盟的jar包和spring对AOP依赖的jar包)

    2、接口UserService及其实现类UserServiceImpl同上面一样

    3、切面类MyAspect不一样了,需要继承接口MethodInterceptor并实现其方法,代码如下:

    package hjp.springAOP.springFactoryBeanProxy;
    
    import org.aopalliance.intercept.MethodInterceptor;
    import org.aopalliance.intercept.MethodInvocation;
    
    /**
     * 切面类,用于存放通知(增强),使用的AOP联盟规范,必须实现接口,从而确定方法名称(即,spring如何执行通知)
     * 
     * @author JiaPeng
     *
     */
    public class MyAspect implements MethodInterceptor {
    
        @Override
        public Object invoke(MethodInvocation methodInvocation) throws Throwable {
            // TODO Auto-generated method stub
            System.out.println("前");
            //环绕通知,必须手动的执行目标方法
            Object obj=methodInvocation.proceed();
            
            System.out.println("后");
            return obj;
        }
        
    }
    切面类

    4、测试类代码如下:

    package hjp.springAOP.springFactoryBeanProxy;
    
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class TestApp {
        @Test
        public void demo1() {
            String xmlPath="hjp/springAOP/springFactoryBeanProxy/beans.xml";
            ApplicationContext applicationContext=new ClassPathXmlApplicationContext(xmlPath);
            UserService userService = (UserService)applicationContext.getBean("proxyServiceId");
            userService.addUser();
            userService.updateUser();
        }
    }
    测试类

    5、新建beans.xml配置文件,里面有注解,不再赘述,注意配置中可以设置是否使用cglib代理

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans 
                                  http://www.springframework.org/schema/beans/spring-beans.xsd">
        <!-- 1、创建目标类 -->
        <bean id="userServiceId" class="hjp.springAOP.springFactoryBeanProxy.UserServiceImpl"></bean>
        <!-- 2、创建切面类 (通知) -->
        <bean id="myAspectId" class="hjp.springAOP.springFactoryBeanProxy.MyAspect"></bean>
        <!-- 3、生成代理,将目标类与切面类结合 ProxyFactoryBean:生成一个特殊代理Bean -->
        <bean id="proxyServiceId" class="org.springframework.aop.framework.ProxyFactoryBean">
            <!-- 确定接口 -->
            <property name="interfaces"
                value="hjp.springAOP.springFactoryBeanProxy.UserService"></property>
            <!-- 确定目标类 -->
            <property name="target" ref="userServiceId"></property>
            <!-- 确定通知,使用切面类名称 -->
            <property name="interceptorNames" value="myAspectId"></property>
            <!-- 强制使用cglib代理,如果不使用cglib可以不写,默认为false -->
            <property name="optimize" value="true"></property>
        </bean>
    </beans>
    配置文件

     六、传统springAOP

    1、项目结构(较上面中的引用,新增了aspectj的jar包)

    2、接口UserService及其实现类UserServiceImpl如上,MyAspect切面类也是如上实现接口MethodInterceptor

    3、beans.xml配置文件,注意要加xsd引用(命名空间xmlns:aop="http://www.springframework.org/schema/aop;引用地址:http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd

    <?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">
        <!-- 1、创建目标类 -->
        <bean id="userServiceId" class="hjp.springAOP.springAOP.UserServiceImpl"></bean>
        <!-- 2、创建切面类 (通知) -->
        <bean id="myAspectId" class="hjp.springAOP.springAOP.MyAspect"></bean>
        <!-- spring 传统AOP开发,使通知引用到目标类切入点上 -->
        <aop:config>
            <!-- pointcut用于声明切入点,确定目标类上哪些方法将被增强 expression用于编写切入点表达式,(aspectj 切入点表达式) 
                固定格式:execution(* hjp.springAOP.springAOP.*.*(..)) 第一个* 表示返回值类型,接着是包名称,第二个* 
                是类名,第三个是方法名,后面括号内点点是参数列表 -->
            <aop:pointcut expression="execution(* hjp.springAOP.springAOP.*.*(..))"
                id="myPointCut" />
            <!-- advice-ref:通知引用;pointcut-ref:切入点引用 -->
            <aop:advisor advice-ref="myAspectId" pointcut-ref="myPointCut" />
        </aop:config>
    </beans>
    配置文件

    4、测试类代码:

    package hjp.springAOP.springAOP;
    
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class TestApp {
        @Test
        public void demo1() {
            String xmlPath="hjp/springAOP/springAOP/beans.xml";
            ApplicationContext applicationContext=new ClassPathXmlApplicationContext(xmlPath);
            UserService userService = (UserService)applicationContext.getBean("userServiceId");
            userService.addUser();
            userService.updateUser();
        }
    }
    测试类
  • 相关阅读:
    一个关于Delphi XML处理单元的BUG
    弹出一个非阻塞对话框
    更新Delphi中SVN客户端版本的方法
    程序只允许运行一个+重复运行程序提前
    Reverse Words in a String
    How to define Servlet filter order of execution using annotations
    Understanding and Using Servlet Filters
    Observer Pattern
    javascript 比较
    SOAP vs REST
  • 原文地址:https://www.cnblogs.com/hujiapeng/p/4792398.html
Copyright © 2020-2023  润新知