热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Spring之AOP入门

SpringAOP入门1、AOP1.1、概念AOP为面向切面编程,采用动态代理实现。1.2、优点采用动态代理的方式,可以增强原有的目标类的方法,我们可以在目标方法执行前后分别做一些

1.2、优点

采用动态代理的方式,可以增强原有的目标类的方法,我们可以在目标方法执行前后分别做一些事情。

对于aop就可以在5种通知里做一些事情,比如说数据库连接的释放,日志的打印,事务的操作。

这种方式,使得不用修改原有程序,就可以增加功能,降低了耦合。

1.3、结构

2、AOP入门案例

2.1、AOP前

AOP采用动态代理实现,故很有必要在看AOP前先看动态代理的实现。动态代理分为两种,对于有接口,有实现类的可以采用jdk动态代理,对于没有接口,只有实现类的,需要采用cglib代理。

2.1.1、依赖包


    
        org.springframework
        spring-core
        3.2.7.RELEASE
    
    
        org.springframework
        spring-context
        3.2.7.RELEASE
    
    
        org.springframework
        spring-beans
        3.2.7.RELEASE
    
    
        org.springframework
        spring-expression
        3.2.7.RELEASE
    
    
        org.springframework
        spring-aop
        3.2.7.RELEASE
    
    
        org.aopalliance
        com.springsource.org.aopalliance
        1.0
    
    
        org.aspectj
        aspectjweaver
        1.6.12
    
    
        commons-logging
        commons-logging
        1.1
    

2.2、jdk动态代理(AOP手动方式)

jdk动态代理中,代理类需要实现目标类的所有接口。

2.2.1、目标类:接口+实现类

接口:

​public interface UserService {
    public void addUser();
    public void updateUser();
    public void deleteUser();
}​

实现类:

public class UserServiceImpl implements UserService {
    public void addUser() {
        System.out.println("addUser");
    }
    public void updateUser() {
        System.out.println("updateUser");
    }
    public void deleteUser() {
        System.out.println("deleteUser");
    }
}

2.2.2切面类:用于保存通知

public class MyAspect {
    public void before() {
        System.out.println("brfore");
    }
    public void after() {
        System.out.println("after");
    }
}

2.2.3、代理类:生成代理对象

public class MyBeanFactory {
    public static UserService createService() {
        // 1、目标类
        final UserService userService = new UserServiceImpl();
        // 2、切面类
        final MyAspect myAspect = new MyAspect();
        // 3、代理对象
        // 参数1、类加载器
                         当前类.class.getClassLoader()
                        目标类对象.getClass().getClassLoader() 
        // 参数2、目标类所实现的所有接口
                        目标类对象.getClass().getInterfaces() 
                        new Class[]{接口.class}
        // 参数3、InvocationHandler处理类,采用匿名内部类实现
        UserService proxyService = (UserService)Proxy.newProxyInstance(MyBeanFactory.class.getClassLoader(),
        userService.getClass().getInterfaces(), new InvocationHandler() {
            //参数1proxy、代理对象
            //参数2method、要执行方法的描述对象
            //参数3arg2、方法的实际参数
            public Object invoke(Object proxy, Method method, Object[] arg2) throws Throwable {
                myAspect.before();
                Object obj = method.invoke(userService, arg2);
                myAspect.after();
                return obj;
            }
        });
        return proxyService;
    }
}

2.2.4、测试类:

@Test
public void fun() {
    UserService userService = MyBeanFactory.createService();
    userService.addUser();
    userService.updateUser();
    userService.deleteUser();
}

2.3、cglib代理方式

cglib代理适用于没有接口只有实现类的情况。cglib是通过让代理类继承目标类,从而增强目标类的方法。

2.3.1、目标类等同于jdk动态代理的接口的实现类

2.3.2、切面类同上

2.3.3、测试类同上

2.3.4、代理类

public class MyBeanFactory {
    public static UserServiceImpl createService() {
    // 1、目标类
    final UserServiceImpl userService = new UserServiceImpl();
    // 2、切面类
    final MyAspect myAspect = new MyAspect();
    // 3、代理类
    Enhancer enhancer = new Enhancer();
    //设置父类
    enhancer.setSuperclass(userService.getClass());
    /* 设置回调函数 , MethodInterceptor接口 等效 jdk中 InvocationHandler接口
     * intercept() 等效 jdk invoke()
     */
    enhancer.setCallback(new MethodInterceptor() {
        //参数1、代理对象
        //参数2、要执行方法的描述对象(当前)
        //参数3、方法的实际参数
        //参数4、代理类方法的代理
        public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
            myAspect.before();
            //通过反射的方式执行要目标类的方法
            Object obj = arg1.invoke(userService, arg2);
            //执行代理类的父类(目标类)
            arg3.invokeSuper(arg0, arg2);
            myAspect.after();
            return obj;
        }
    });
    //创建代理对象,向上转型
    UserServiceImpl proxyService = (UserServiceImpl) enhancer.create();
    return proxyService;
    }
}

2.4、AOP半自动代理

半自动代理采用spring提供的org.springframework.aop.framework.ProxyFactoryBean类(一个代理工厂)生成代理对象

2.4.1、目标类与jdk动态代理相同

2.4.2、切面类:环绕通知(此处与其他不同)

public class MyAspect implements MethodInterceptor 
    public Object invoke(MethodInvocation arg0) throws Throwable {
        System.out.println("before");
        Object obj = arg0.proceed();
        System.out.println("after");
        return obj;
    }
}

2.4.3、配置文件beans.xml



    
    
    
    
    
    
    
        
        
        
        
        
        
        
    

2.4.4、测试类

@Test
public void fun() {
    String xmlPath = "beanfactory/beans.xml";
    ApplicationContext applicatiOnContext= new ClassPathXmlApplicationContext(xmlPath);
    // 获得代理对象
    UserService userService = (UserService) applicationContext.getBean("proxyService");
    userService.addUser();
    userService.updateUser();
    userService.deleteUser();
}

2.4.5、结果

before
addUser
after
before
updateUser
after
before
deleteUser
after

2.5、AOP全自动代理

全自动代理仅需要在beans.xml中配置一下就好,使用

spring会自动判断使用那种代理方式,有接口时采用jdk方式,没有接口时采用cglib方式。

2.5.1、目标类与jdk动态代理相同

2.5.2、测试类与半自动方式相同

2.5.3、切面类

AOP提供了5中通知类型,分别为,前置通知,返回通知,异常通知,最终通知,环绕通知,其中环绕通知最为强大。

除环绕通知,其他可以没有参数。

public class MyAspect {
    //前置通知,在目标类方法执行前执行
    public void before(JoinPoint joinPoint) {
        System.out.println("before");
    }
    //返回通知,在目标类方法执行后执行,可以拿到目标类方法的返回值,如果没有就是null
    public void afterreturning(JoinPoint joinPoint, Object ret) {
        System.out.println("afterreturning" + ret);
    }
    //异常通知,当目标类方法抛出异常时执行,可以拿到异常信息,类似于在catch块里面的方法
    public void throwable(JoinPoint joinPoint, Throwable e) {
        System.out.println("throwable" + " " + e.getMessage());
    }
    //在最后执行
    public void afterafter(JoinPoint joinPoint) {
        System.out.println("afterafter");
    }
    //在方法前后都会执行
    public Object invoke(ProceedingJoinPoint jontPoint) throws Throwable {
        System.out.println("环绕通知-------->前");
        Object obj = jontPoint.proceed();
        System.out.println("环绕通知---------->后");
        return obj;
    }
}

2.5.4、配置文件beans.xml



    
    
    
    
    
    
        
        
        
        
            
            
            
            
            
            
            
            
            
            
        
    

2.5.5、结果

因为环绕通知中有return语句,故执行顺序和配置有关。

2.5.5.1、配置环绕通知时

before
环绕通知-------->前
addUser
afterafter
环绕通知---------->后
afterreturning1
------------------------------------
before
环绕通知-------->前
deleteUser
afterafter
环绕通知---------->后
afterreturningnull
------------------------------------
before
环绕通知-------->前
updateUser
afterafter
throwable / by zero

2.5.5.2、不配置环绕通知

before
addUser
afterreturning1
afterafter
------------------------------------
before
deleteUser
afterreturningnull
afterafter
------------------------------------
before
updateUser
throwable / by zero
afterafter

2.5.5.3、其他

异常通知,只有在产生异常后才会执行,此时,返回通知会被异常挡住不在执行。

返回通知可以拿到目标类方法拿到的返回值,如果没有返回值,就是null。

环绕通知会使得最终通知提前。类似于,return执行前,finally块中的会先执行。

2.5.6、补充

全自动方式也可将切面做成类似于半自动的形式。

切面类:

前置通知:

public class MyAspectBefore implements MethodBeforeAdvice {
    public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable { 
        System.out.println("before"); 
    }
}

环绕通知:

public class MyAspectMethod implements MethodInterceptor {
    public Object invoke(MethodInvocation arg0) throws Throwable {
        System.out.println("环绕前");
        Object obj = arg0.proceed();
        System.out.println("环绕后");
        return obj;
    }
}

配置方式:


    
    
    
    
    

 


推荐阅读
  • 本文介绍了在MFC下利用C++和MFC的特性动态创建窗口的方法,包括继承现有的MFC类并加以改造、插入工具栏和状态栏对象的声明等。同时还提到了窗口销毁的处理方法。本文详细介绍了实现方法并给出了相关注意事项。 ... [详细]
  • 本文由编程笔记#小编为大家整理,主要介绍了logistic回归(线性和非线性)相关的知识,包括线性logistic回归的代码和数据集的分布情况。希望对你有一定的参考价值。 ... [详细]
  • 标题: ... [详细]
  • Spring框架《一》简介
    Spring框架《一》1.Spring概述1.1简介1.2Spring模板二、IOC容器和Bean1.IOC和DI简介2.三种通过类型获取bean3.给bean的属性赋值3.1依赖 ... [详细]
  • 本文介绍了解决java开源项目apache commons email简单使用报错的方法,包括使用正确的JAR包和正确的代码配置,以及相关参数的设置。详细介绍了如何使用apache commons email发送邮件。 ... [详细]
  • 本文详细介绍了在Linux虚拟化部署中进行VLAN配置的方法。首先要确认Linux系统内核是否已经支持VLAN功能,然后配置物理网卡、子网卡和虚拟VLAN网卡的关系。接着介绍了在Linux配置VLAN Trunk的步骤,包括将物理网卡添加到VLAN、检查添加的VLAN虚拟网卡信息以及重启网络服务等。最后,通过验证连通性来确认配置是否成功。 ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • 本文介绍了一个在线急等问题解决方法,即如何统计数据库中某个字段下的所有数据,并将结果显示在文本框里。作者提到了自己是一个菜鸟,希望能够得到帮助。作者使用的是ACCESS数据库,并且给出了一个例子,希望得到的结果是560。作者还提到自己已经尝试了使用"select sum(字段2) from 表名"的语句,得到的结果是650,但不知道如何得到560。希望能够得到解决方案。 ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • 本文介绍了在处理不规则数据时如何使用Python自动提取文本中的时间日期,包括使用dateutil.parser模块统一日期字符串格式和使用datefinder模块提取日期。同时,还介绍了一段使用正则表达式的代码,可以支持中文日期和一些特殊的时间识别,例如'2012年12月12日'、'3小时前'、'在2012/12/13哈哈'等。 ... [详细]
  • 本文介绍了在iOS开发中使用UITextField实现字符限制的方法,包括利用代理方法和使用BNTextField-Limit库的实现策略。通过这些方法,开发者可以方便地限制UITextField的字符个数和输入规则。 ... [详细]
  • 精讲代理设计模式
    代理设计模式为其他对象提供一种代理以控制对这个对象的访问。代理模式实现原理代理模式主要包含三个角色,即抽象主题角色(Subject)、委托类角色(被代理角色ÿ ... [详细]
  • 初识java关于JDK、JRE、JVM 了解一下 ... [详细]
author-avatar
youstar
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有