Spring AOP(切面编程)

最近在学习Spring框架,写这篇文章也算是对近段学习的一个总结,本文主要从三种代理模式写起,静态代理、动态代理和Cglib代理,然后到Spring AOP的配置及使用,包括XML方式配置和注解两种实现方式

一、代理模式

代理模式是一种设计模式,简单说即是在不改变源码的情况下,实现对目标对象的功能扩展

功能需求: 现在假设有个 UserDao 接口,有保存动作 save() 方法,UserDaoImpl 实现 UserDao接口 , 但是在执行 save() 方法的时候动态植入两句打印

1. 静态代理

静态代理方式处理的,目标对象必须实现接口,并且代理对象要实现跟目标对象一样的接口

public interface UserDao {
    public void save();
}
public class UserDaoImpl implements UserDao {

    @Override
    public void save() {
        System.out.println("执行保存动作");
    }
}
/**
 * 静态代理类
 * @author liuchao
 */
public class UserProxy implements UserDao {
    
    private UserDao _userDao;
    
    public UserProxy(UserDao userDao) {
        _userDao = userDao;
    }
    
    @Override
    public void save() {
        //静态代理方式实现开始植入
        System.out.println("开启事务");
        
        _userDao.save();
        
        //静态代理方式实现结束植入
        System.out.println("提交事务");
    }
}
import org.junit.Test;
/**
 * 测试类
 * @author liuchao
 */
public class UserTest {
    
    @Test
    public void testSave() {
        
        //1. 创建目标对象
        UserDao userDao = new UserDaoImpl();
        
        //2. 创建代理工厂对象
        UserProxy factory = new UserProxy(userDao);
        
        //3. 执行代理工厂保存方法
        factory.save();
    }
}
//控制台打印结果
开启事务
执行保存动作
提交事务
2. 动态代理

动态代理是JDK自带的,所以也叫做JDK代理,也需要目标对象实现接口,也是只能对目标对象实现接口里面的方法进行拦截。
调用Proxy类的静态方法newProxyInstance即可,该方法会返回代理类对象

static Object newProxyInstance(ClassLoader loader, Class[] interfaces,InvocationHandler h )
接收的三个参数依次为:

  • ClassLoader loader:指定当前目标对象使用类加载器,写法固定
  • Class[] interfaces:目标对象实现的接口的类型,写法固定
  • InvocationHandler h:事件处理接口,需传入一个实现类,一般直接使用匿名内部类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 动态代理工厂类
 * @author liuchao
 */
public class ProxyFactory {

    // 生成动态代理对象
    public static Object newProxyInstance(Object target) {
        // InvocationHandler
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
                new InvocationHandler() {

                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                        System.out.println("开启事务");

                        Object result = method.invoke(target, args);

                        System.out.println("提交事务");

                        return result;
                    }
                });
    }
}
import org.junit.Test;
/**
 * 测试类
 * @author liuchao
 */
public class UserTest {
    
    @Test
    public void testSave() {
        
        UserDao userDao = new UserDaoImpl();
        
        //生成代理对象 (动态创建一个实现该接口的匿名内部类)
        UserDao proxyInstance = (UserDao)ProxyFactory.newProxyInstance(userDao);
        
        //执行保存方法
        proxyInstance.save();
    }
}
//控制台打印
开启事务
执行保存动作
提交事务
3. Cglib代理

第三方框架实现的对基本代理模式的一个拓展,该代理植入不需要目标对象实现接口,内部通过动态生成目标对象子类的方式来实现对目标对象方法拦截

前提条件:

    1. 需要引入Cglib的jar文件,由于Spring的核心包中已经包括了Cglib功能,所以也可以直接引入spring-core-4.0.6.RELEASE.jar
    1. 目标类不能为final
    1. 目标对象的方法如果为finalstatic,那么不会被拦截

所以接下来重新定义的 UserDaoImpl 类不必实现任何接口

public class UserDaoImpl {

    public void save() {
        System.out.println("执行保存动作");
    }
}

代理工厂类必须实现 MethodInterceptor 接口,因为方法拦截是通过实现该接口里面的 intercept (..) 方法。

import java.lang.reflect.Method;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
/**
 * Cglib代理工厂类
 * @author liuchao
 */
public class ProxyFactory implements MethodInterceptor {
    
    private Object target;
    
    public ProxyFactory(Object target) {
        this.target = target;
    }
    
    //实例化代理对象
    public Object getProxyInstance() {
        //1. 工具类
        Enhancer en = new Enhancer();
        //2. 设置父类
        en.setSuperclass(this.target.getClass());
        //3. 设置回调,会调用下面的intercept(..)方法
        en.setCallback(this);
        //4. 创建子类对象
        return en.create();
    }
    
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        
        System.out.println("开启事务");
        
        Object result = method.invoke(this.target, args);
        
        System.out.println("提交事务");
        
        return result;
    }
}
import org.junit.Test;
/**
 * 测试类
 * @author liuchao
 */
public class UserTest {
    
    @Test
    public void testSave() {
        //1. 创建目标对象
        UserDaoImpl userDao = new UserDaoImpl();
        
        //2. 生成代理对象
        UserDaoImpl proxyInstance = (UserDaoImpl) new ProxyFactory(userDao).getProxyInstance();
        
        //3. 执行保存方法
        proxyInstance.save();
        
        System.out.println("代理对象类名: " +  proxyInstance.getClass().getSimpleName());
        System.out.println("代理对象父类名: " + proxyInstance.getClass().getSuperclass().getSimpleName());
    }
}
//控制台打印
开启事务
执行保存动作
提交事务
代理对象类名: UserDaoImpl$$EnhancerByCGLIB$$5467e6b5
代理对象父类名: UserDaoImpl

二、Spring AOP编程

需要导入的jar包

//Spring核心包
    spring-core-4.0.6.RELEASE.jar
    spring-expression-4.0.6.RELEASE.jar
    spring-beans-4.0.6.RELEASE.jar
    commons-logging-1.1.3.jar
         
//后期通过注解方式实现需要
    spring-context-4.0.6.RELEASE.jar 
         
//Aop部分架包
    spring-aop-4.0.6.RELEASE.jar
    aspectjweaver-1.8.7.jar //下载地址: http://central.maven.org/maven2/org/aspectj/aspectjweaver/1.8.7/
    aopalliance-1.0.jar //下载地址: http://central.maven.org/maven2/aopalliance/aopalliance/1.0/

基本概念

    1. Pointcut(切入点):简称切点,定义匹配通知所要织入的一个或多个连接点,一般常用正则表达式定义所匹配的类和方法名称来指定这些切点(切点表达式这里不做过多累述,详情请点击https://www.cnblogs.com/domi22/p/8047929.html。
    1. Aspect(切面):通常是一个类,里面可以定义切入点和通知
    1. JointPoint(连接点):应用执行过程中能够插入切面的一个点,一般是方法的调用
    1. Advice(通知):AOP在特定的切入点上执行的增强处理,有五种,分别为 前置通(Before)后置通知(After)返回通知(After-returning)异常通知(After-throwing)环绕通知(Around)
    1. AOP代理:AOP框架创建的对象,代理就是目标对象的加强。Spring中的AOP代理可以使JDK动态代理,也可以是Cglib代理,前者基于接口,后者基于子类
1. XML方式实现

为了代码清晰省略了 UserDao 和 UserDaoImpl,直接在 UserService 层进行一个简单的数据保存模拟

import org.aspectj.lang.JoinPoint;

/**
 * 自定义一个切面类
 * @author liuchao
 */
public class Aspect {

    public void beforeAction(JoinPoint joinPoint) {
        System.out.println("开启事务");

        System.out.println("开启事务, 连接点 方法名: " + joinPoint.getSignature().getName());

        Object[] args = joinPoint.getArgs();
        if (args != null) {
            for (Object object : args) {
                System.out.println("开启事务, 方法参数: " + object.toString());
            }
        }
    }

    public void afterAction(JoinPoint joinPoint) {
        System.out.println("提交事务");

        System.out.println("提交事务, 连接点 方法名: " + joinPoint.getSignature().getName());

        Object[] args = joinPoint.getArgs();
        if (args != null) {
            for (Object object : args) {
                System.out.println("提交事务, 方法参数: " + object.toString());
            }
        }
    }
}
/**
 * 业务层
 * @author liuchao
 */
public class UserService {
    
    public void save() {
        System.out.println("持久层对象调用保存方法进行数据保存");
    }
}
/**
 * 视图控制器层
 * @author liuchao
 */
public class UserAction {
    //Spring IOC注入, 通过Set方式注入
    private UserService userService;
    public void setUserService(UserService userService) {
        this.userService = userService;
    }
    
    public void saveAction() {
        userService.save();
    }
}

Spring XML文件配置



                
        
        
        
            
        
        
        
        
        
        
        
            
            
            
            
                
                
                
            
        
        

import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
 * 测试类
 * @author liuchao
 */
public class UserTest {
    
    @Test
    public void testSaveAction() {
        @SuppressWarnings("resource")
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("com/e_aop_xml/bean.xml");
        UserAction userAction = (UserAction) context.getBean("userAction");
        userAction.saveAction();
    }
}
//控制台输出
开启事务
开启事务, 连接点 方法名: save
持久层对象调用保存方法进行数据保存
提交事务
提交事务, 连接点 方法名: save
2. 注解方式实现
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

/**
 * 自定义一个切面类
 * @author liuchao
 */
@Component
@org.aspectj.lang.annotation.Aspect
public class Aspect {
    
    @Before("execution(* com.f_aop_anno.UserService.*(..))")
    public void beforeAction(JoinPoint joinPoint) {
        System.out.println("开启事务, 连接点 方法名: " + joinPoint.getSignature().getName());
    }
    
    //自定义一个切点, 然后引用
    @Pointcut("execution(* com.f_aop_anno.UserService.*(..))")
    public void pointCut() {}
    
    @After("pointCut()")
    public void afterAction(JoinPoint joinPoint) {
        System.out.println("提交事务, 连接点 方法名: " + joinPoint.getSignature().getName());
    }
}
import org.springframework.stereotype.Service;
/**
 * 业务层
 * @author liuchao
 */
@Service
public class UserService {
    
    public void save() {
        System.out.println("持久层对象调用保存方法进行数据保存");
    }
}
import javax.annotation.Resource;

import org.springframework.stereotype.Controller;

/**
 * 视图控制器层
 * @author liuchao
 */
@Controller(value="userAction")
public class UserAction {
    
    @Resource
    private UserService userService;
    
    public void saveAction() {
        userService.save();
    }
}

Spring XML文件配置



        
        
        
        
        
        

import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
 * 测试类
 * @author liuchao
 */
public class UserTest {
    
    @Test
    public void testSaveAction() {
        @SuppressWarnings("resource")
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("com/f_aop_anno/bean.xml");
        UserAction userAction = (UserAction) context.getBean("userAction");
        userAction.saveAction();
    }
}
//控制台打印
开启事务, 连接点 方法名: save
持久层对象调用保存方法进行数据保存
提交事务, 连接点 方法名: save

你可能感兴趣的:(Spring AOP(切面编程))