博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring(十七):Spring AOP(一):简介
阅读量:6514 次
发布时间:2019-06-24

本文共 9846 字,大约阅读时间需要 32 分钟。

背景:

需求:

给一个计算器计算函数执行前后添加日志。

实现:

1)直接在函数中修改代码;

IArithmeticCalculator.java接口类

package com.dx.spring.beans.aop;public interface IArithmeticCalculator {    // Addition, subtraction, multiplication, and division    int add(int i, int j);    int sub(int i, int j);    int multi(int i, int j);    int div(int i, int j);}

实现类中添加日志:

package com.dx.spring.beans.aop;public class ArithmeticCalculatorImpl implements IArithmeticCalculator {    @Override    public int add(int i, int j) {        System.out.println("method add before");        int result = i + j;        System.out.println("method add after");        return result;    }    @Override    public int sub(int i, int j) {        System.out.println("method sub before");        int result = i - j;        System.out.println("method sub after");        return result;    }    @Override    public int multi(int i, int j) {        System.out.println("method multi before");        int result = i * j;        System.out.println("method multi after");        return result;    }    @Override    public int div(int i, int j) {        System.out.println("method div before");        int result = i / j;        System.out.println("method div after");        return result;    }}
View Code

2)Java动态代理,实现参考《》;

ArithmeticCalculatorImpl.Java实现:

package com.dx.spring.beans.aop;public class ArithmeticCalculatorImpl implements IArithmeticCalculator {    @Override    public int add(int i, int j) {        int result = i + j;        return result;    }    @Override    public int sub(int i, int j) {        int result = i - j;        return result;    }    @Override    public int multi(int i, int j) {        int result = i * j;        return result;    }    @Override    public int div(int i, int j) {        int result = i / j;        return result;    }}
View Code

ProxyFactory.java动态代理工厂:

package com.dx.spring.beans.aop;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class ProxyFactory {    private Object target=null;    public ProxyFactory(Object target) {        this.target = target;    }         /**     * 给目标对象生成代理对象     */    public Object getProxyInstance() {        ClassLoader classLoader = target.getClass().getClassLoader();        Class
[] interfaces = target.getClass().getInterfaces(); InvocationHandler invocationHandler = new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { before(method); // 执行目标对象方法 Object returnValue = method.invoke(target, args); after(method); return returnValue; } }; return Proxy.newProxyInstance(classLoader, interfaces, invocationHandler); } private void before(Method method) { System.out.println("before " + method); } private void after(Method method) { System.out.println("after " + method); } }
View Code

调用测试Client.java:

package com.dx.spring.beans.aop;public class Client {    public static void main(String[] args) {        IArithmeticCalculator arithmeticCalculator = new ArithmeticCalculatorImpl();        // int result=arithmeticCalculator.add(1, 2);        // System.out.println(result);        IArithmeticCalculator arithmeticCalculatorProxyFactory = (IArithmeticCalculator) new ProxyFactory(arithmeticCalculator).getProxyInstance();        int result = arithmeticCalculatorProxyFactory.add(1, 2);        System.out.println(result);    }}

3)Spring AOP实现。

下边会,这里暂时不举例。

AOP:

AOP简介:

1)AOP(Aspect-Oriented Programming,面向切面编程):是一种新的方法论,是对传统OOP(Object-Oriented Programming,面向对象编程)的补充。

2)AOP的主要编程对象是切面(aspect),而切面模块化横切关注点。

3)在应用AOP编程时,任然需要定义公共功能,但可以明确的定义这个功能在哪里,以什么方式应用,并且不必修改受影响的类。这样一来横切关注点就被模块化到特殊的对象(切面)里。

4)AOP的好处:

---- 每个事物逻辑位于一个位置,代码不分散,便于维护和升级;

---- 业务模块更简洁,只包含核心业务代码。

AOP术语:

1)切面(aspect):横切关注点(跨越应用程序多个模块的功能)被模块化的特殊对象;

2)通知(advice):切面必须要完成的工作;

3)目标(target):被通知的对象;

4)连接点(Joinpoint):程序执行的某个特定位置:如类某个方法调用前、调用后、方法抛出异常后等。

连接点由两个信息确定:方法表示的程序执行点、相对点表示的方位。

例如:ArithmethicCalculator#add()方法执行前的连接点执行点为ArithmethicCalculator#add();方位为该方法执行前的位置。

5)切点(pointcut):每个类都拥有多个连接点:例如ArithmethicCalculator的所有方法实际上都是连接点,即连接点是程序类中客观存在的事务。

AOP通过切点位置到特定的连接点。类比:连接点相当于数据库中的记录,切点相当于查询条件。切点和连接点不是一对一的关系,一个切点匹配多个连接点,切点通过org.springframework.aop.Pointcut接口进行描述,它使用类和方法作为连接点的查询条件。

Spring AOP

1)AspectJ:java社区里最完整最流行的AOP框架。在Spring2.0以上版本中,可以使用AspectJ注解基于XML配置的AOP。

备注:先学习基于AspectJ注解的方式,接着再学习基于XML配置。

2) 在Spring中启动AspectJ注解支持:

  1. 要在Spring应用中使用AspectJ注解,必须在ClassPath下包含AspectJ类库:aopalliance.jar、aspectj.wearver.jar和spring-aspects.jar;
  2. 将aop schema添加到<beans>根元素中;
  3. 要在Spring IOC容器中启用AspectJ注解支持,只要在Bean配置文件中定义一个空的xml元素<aop:aspectj-auotproxy>;
  4. 当Spring IOC容器侦测到Bean配置文件中的<aop:aspectj-autoproxy>元素是,会自动为与aspectj切面匹配的bean创建代理。

Spring AOP实现需求:

第一步:导入spring aop依赖包&额外依赖包(aopalliance.jar、asm.jar、aspectjrt.jar、aspectjweaver.jar);

Aop额外依赖包:aopalliance.jar、asm.jar、aspectjrt.jar、aspectjweaver.jar下载:链接:https://pan.baidu.com/s/1SjYtrfrUwPIZXikr4guXkg 密码:lowr

第二步:添加spring-aop.xml

第三步:添加Spring组件

IArithmeticCalculator.java组件接口

package com.dx.spring.beans.aop;/** * Description:Addition, subtraction, multiplication, and division */public interface IArithmeticCalculator {    int add(int i, int j);    int sub(int i, int j);    int multi(int i, int j);    int div(int i, int j);}
View Code

ArithmeticCalculatorImpl.java组件

package com.dx.spring.beans.aop;import org.springframework.stereotype.Component;@Component("arithmeticCalculator")public class ArithmeticCalculatorImpl implements IArithmeticCalculator {    @Override    public int add(int i, int j) {        int result = i + j;        return result;    }    @Override    public int sub(int i, int j) {        int result = i - j;        return result;    }    @Override    public int multi(int i, int j) {        int result = i * j;        return result;    }    @Override    public int div(int i, int j) {        int result = i / j;        return result;    }}
View Code

第四步:给Spring组件添加LoggingAspect切面类:

LoggingAspect.java切面类

package com.dx.spring.beans.aop;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.springframework.stereotype.Component;//把这个类声明为一个切面:需要把该类放入到IOC容器中、再声明为一个切面。@Aspect@Componentpublic class LoggingAspect {        // 声明该方法为一个前置通知:在目标方法开始之前执行    @Before("execution(public int com.dx.spring.beans.aop.IArithmeticCalculator.add(int, int))")    public void beforeMethod() {        System.out.println("before ");    }}

第五步:添加测试类Client.java&测试

package com.dx.spring.beans.aop;import org.springframework.context.ApplicationContext;import org.springframework.context.support.ClassPathXmlApplicationContext;public class Client {    public static void main(String[] args) {        // 1:创建Spring的IOC容器;        ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-aop.xml");        // 2.从IOC容器中获取Bean的实例        IArithmeticCalculator arithmeticCalculator = (IArithmeticCalculator) ctx.getBean("arithmeticCalculator");        // 3.使用Bean        int result = arithmeticCalculator.add(1, 3);        System.out.println(result);    }}

测试结果:

before

4

Spring AOP合并切入点表达式:

示例:

上边的LoggingAspect.java中的配置仅仅适用于接口IArithm*eticCalculator.java的add方法,如果想通过一个配置适用IArithmeticCalculator.java接口的四个方法,可以使用“*”占位符混匹方式。

第一步:修改LoggingAspect.java接口

package com.dx.spring.beans.aop;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.springframework.stereotype.Component;//把这个类声明为一个切面:需要把该类放入到IOC容器中、再声明为一个切面。@Aspect@Componentpublic class LoggingAspect {        // 声明该方法为一个前置通知:在目标方法开始之前执行    @Before("execution(public int com.dx.spring.beans.aop.IArithmeticCalculator.*(int, int))")    public void beforeMethod() {        System.out.println("before ");    }}

第二步:修改Client.java&测试

// 1:创建Spring的IOC容器;        ApplicationContext ctx = new ClassPathXmlApplicationContext("spring-aop.xml");        // 2.从IOC容器中获取Bean的实例        IArithmeticCalculator arithmeticCalculator = (IArithmeticCalculator) ctx.getBean("arithmeticCalculator");        // 3.使用Bean        int result = arithmeticCalculator.add(1, 3);        System.out.println(result);                result = arithmeticCalculator.add(4, 2);        System.out.println(result);

测试结果:

before

4
before
6

利用方法签名编写AspectJ切入点表达式:

最经典的切入点表达式是根据方法的签名来匹配各种方法:

  • --- execution(* com.dx.spring.beans.aop.IArithmeticCalculator.*.(..)):匹配IArithmeticCalculator中声明的所有方。第一个"*"代表任意修饰符及任意返回值;第二个"*"代表任意方法;其中".."匹配任意数量的参数。若目标类与接口与该切面在同一个包中,可以省略报名。
  • --- execution(public * IArithmeticCalculator.*.(..)):匹配IArithmeticCalculator接口的所有公有方法。
  • --- execution(public double IArithmeticCalculator.*.(..)):匹配IArithmeticCalculator接口中返回double类型数据的方法。
  • --- execution(public double IArithmeticCalculator.*.(double,..)):匹配IArithmeticCalculator接口中返回double类型数据且第一个参数参数为double类型的(".."匹配任意数量任意类型的参数)方法。
  • --- execution(public double IArithmeticCalculator.*.(double,..)):匹配IArithmeticCalculator接口中返回double类型数据且有两个类型为double类型的参数的方法。

Spring AOP让通知访问当前连接点的细节:

通知方法支持接收参数,其中就允许接收JoinPoint,通过JointPoint参数对象,可以获取函数名称,和参数。

第一步:修改LoggingAspect.java类:

package com.dx.spring.beans.aop;import java.util.List;import java.util.Arrays;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.springframework.stereotype.Component;//把这个类声明为一个切面:需要把该类放入到IOC容器中、再声明为一个切面。@Aspect@Componentpublic class LoggingAspect {    // 声明该方法为一个前置通知:在目标方法开始之前执行    @Before("execution(public int com.dx.spring.beans.aop.IArithmeticCalculator.*(int, int))")    public void beforeMethod(JoinPoint joinpoint) {        String methodName = joinpoint.getSignature().getName();        List args = Arrays.asList(joinpoint.getArgs());        System.out.println("before method " + methodName + " with " + args);    }}

第二步:测试打印结果

before method add with [1, 3]

4
before method add with [4, 2]
6

 

转载地址:http://ebofo.baihongyu.com/

你可能感兴趣的文章
如何缓解影子云服务安全风险?
查看>>
Bossies 2016:最佳开源大数据工具
查看>>
银行卡信息安全事件频发 互联网站成数据泄露"重灾区"
查看>>
云服务器 ECS 使用OpenAPI管理ECS:使用OpenAPI弹性创建ECS实例
查看>>
象云2.0产品发布暨国产操作系统首次入驻公有云
查看>>
一个完美DCIM应该具备的功能与价值
查看>>
《SEO的艺术(原书第2版)》——1.5 人们如何搜索
查看>>
经验贴 | 电梯监控的布线技巧
查看>>
唐山联通与丰南区政府签署“智慧城市”战略合作协议
查看>>
研究显示:广告拦截应用正在破坏互联网
查看>>
优云·小课堂 第八期:运维自动化的魅力
查看>>
稳定+性能+价格,阿里云发力ECS企业级产品
查看>>
写个软件来防止服务器网站CPU百分百
查看>>
智能城市里,“公共电话亭”的存在意味着什么?
查看>>
JVM分代垃圾回收策略的基础概念
查看>>
《交互式程序设计 第2版》一3.5 捕获简单用户交互行为
查看>>
安装操作系统需要注意的事项
查看>>
5G技术的5大猜想
查看>>
MongoDB 3.0(1):CentOS7 安装MongoDB 3.0服务
查看>>
别随便安装 Pokemon GO被曝藏恶意后门
查看>>