基于XML实现AOP

本文介绍了Spring中的面向切面编程(AOP),包括通过JDK和CGLib动态代理实现AOP的方式。JDK动态代理适用于有接口的情况,而CGLib则能在无接口的情况下进行代理。AOP关键术语包括连接点、切入点和通知,通过XML配置可以方便地实现AOP增强。文章还讨论了AOP的使用场景,如日志、权限设置和事务管理,并提供了具体的代码示例。

2. 应用XML编程实现AOP

面向切面编程(Aspect Oriented Programming),其目的是为了拦截某些类下的某些方法,参数、返回值内容,在此基础上,通过增强的形式,使用代理设计模式显示AOP编程。

2.1 AOP代理实现形式
2.1.1 通过JDK动态代理实现

动态代理是指:代理类对象是在运行时,由JVM根据反射机制动生成的。动态代理不需要定义代理类的java源文件
动态代理其实就是JDK运行期间,动态创建class字节码文件加载到JVM

2.1.1.1 优点

1.不用创建类文件
2.当修改了接口中的方法,不会影响代理类
3.不用给不同的目标随时创建代理

2.1.1.2 术语概念

1.目标类:实现了功能接口的实现类对象
2.代理类:作为调用类和目标类之间的桥接
3.调用类:需要调用目标类方法来完成值的获取
4.OCP原则:程序设计的一个考虑,类设计的时候尽量避免方法中代码的二次修改,但是欢迎类设计者扩展一个类的功能(方法)
5.耦合度:一个类过于依赖另一个类,就会产生耦合性

package com.csi.proxy;
import com.csi.service.UserService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class MyProxy implements InvocationHandler {
    private UserService userService ;
    public MyProxy(UserService userService) {
        this.userService = userService ;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object o = null ;
        if("list".equals(method.getName())) {
            System.out.println("前置通知");
            o = method.invoke(userService,args) ;
            System.out.println("后置通知");
        }
        return o;
    }
}
package com.csi.proxy;
import com.csi.service.UserService;
import com.csi.service.impl.UserServiceImpl;
import java.lang.reflect.Proxy;

public class TestMyProxy {
    public static void main(String[] args) {
        UserService userService = new UserServiceImpl() ;
        MyProxy myProxy = new MyProxy(userService) ;
        UserService userServiceProxy = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(),
                                                                            userService.getClass().getInterfaces(), myProxy);
        userServiceProxy.list();
    }
}

JDK的动态代理,是要求必须拥有接口的。Spring能够对任何的一个对象都进行代理设计,此时如果某个对象没有对应的接口,只是单单的利用JDK动态代理,则无法创建对应的代理对象。Spring AOP就会采用CGLib生成。

2.1.2 通过CGLib动态代理实现

CGLib是通过继承目标类实现的一种代理方式,因此不需要提前准备目标对应的接口。在Spring生成代理对象时,实际上是生成代理目标的一个子类。

public class SampleClass {
    public void test(){
        System.out.println("hello world");
    }
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        //告诉代理类,他的父类是谁
        enhancer.setSuperclass(SampleClass.class);
        //通过setCallback方法,拦截对应的调用方法进行增强
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object obj, Method method, Object[] args,MethodProxy proxy) throws Throwable {
                System.out.println("before method run...");
                //返回值
                Object result = proxy.invokeSuper(obj, args);
                System.out.println("after method run...");
                return result;
            }
        });
        SampleClass sample = (SampleClass) enhancer.create();
        sample.test();
    }
}

2.1.3 AOP术语

在这里插入图片描述

  • 连接点(JoinPoint): 正在执行的方法,例如:update()、delete()、select()、等都是连接点。
  • 切入点(PointCut):进行功能增强的方法,例如:update()、delete()方法,select()方法沒有被增强所以不是切入点,但是是连接点。
    • 在Spring AOP中,一个切入点只能描述一个具体的方法,也可以匹配多个方法
      • 一个具体的方法:com.csi.dao包下的Dao接口中的无参无返回值的save方法
      • 匹配多个方法:所有的save方法,所有的get开头的方法,所有以Dao接口中的任意方法,所有带有一个参数的方法
  • 通知(Advice):在切入点前后执行的操作,也就是增强的共性功能
    • 在Spring AOP中,功能最终以方法的形式呈现
  • 通知类:通知方法所在的类叫做通知类
  • 切面(Aspect):描述通知与切入点的对应关系,也就是那些通知方法对应那哪些切入点方法。
2.1.4 通过编程形式基于XML实现AOP
  • 引入aspectjweaver环境

    <dependency>
    	<groupId>org.aspectj</groupId>
    	<artifactId>aspectjweaver</artifactId>
    	<version>1.9.9.1</version>
    </dependency>
    
  • 修改核心配置文件

    <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">
    
  • 编写增强通知类

    public class LoggerService {
        /**
    	* 前置通知
    	* @param joinPoint 连接点
    	*/
        public void beforeRecord(JoinPoint joinPoint) {
            /**
    	* joinPoint.getTarget():获取到目标的类
    	* joinPoint.getSignature().getName():获取到方法
    	* joinPoint.getArgs():获取到参数
    	*/
            System.out.println("前置通知:" + joinPoint.getTarget() + "的" +
                               joinPoint.getSignature().getName() + ",参数是" +
                               Arrays.toString(joinPoint.getArgs()));
        }
        /**
    	* 后置通知
    	* @param joinPoint 连接点
    	* @param result 返回值结果
    	*/
        public void afterReturning(JoinPoint joinPoint, Object result) {
            System.out.println("后置通知:" + joinPoint.getTarget() + "的" +
                               joinPoint.getSignature().getName() + ",返回值是:" + result);
        }
    }
    
  • 在核心配置文件中增加aop的配置

    <bean id="loggerService" class="com.csi.aop.LoggerService" />
    <bean id="xxx" class="符合与pointcut规则的实现类" />
    <aop:config>
        <aop:pointcut id="pointcut" expression="execution(* com.csi.service..*.*(..))" />
        <aop:aspect ref="loggerService">
            <aop:before method="xxxxx" pointcut-ref="pointcut" />
            <aop:after-returning method="xxxx" pointcut-ref="pointcut" returning="方法参数" />
        </aop:aspect>
    </aop:config>
    
  • 测试

    ApplicationContext ctx = new ClasspathXMLApplicationContext("applicationXXX.xml") ;
    Object obj = ctx.getBean("id") ;
    obj.xxx() ;
    
  • 结果

    前置通知:com.csi.service.impl.PersonServiceImpl@1dde4cb2的list,参数是[]调用了list方法
    

    后置通知:com.csi.service.impl.PersonServiceImpl@1dde4cb2的list,返回值是:[com.csi.domain.UserInfo@72057ecf,
    com.csi.domain.UserInfo@1afd44cb]

从实验的结果中,发现AOP灵活度非常高,当需要或不需要前置或后置通知时,只需要进行配置即可。

2.1.5 使用场景

在业界公认的包含以下场景

  • 日志
    会降低日志输出灵活度,没有办法进行个性化设置
  • 权限设置
    如果下沉到了service层,那么基本上没有太大意义
  • 事务管理
  • 由于事务管理基本上是一成不变的,所以是最适合的使用场景
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值