Spring AOP详解

Spring AOP

1.概述
*1. 作用: 利用aop可以对业务逻辑的各个部分进行隔离, 从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性, 同时提高了开发效率.
*2. 主要功能: 
    日志记录, 性能统计, 安全控制, 事务处理, 异常处理...
*3. 主要目的:
    将日志记录, 性能统计...等代码从业务逻辑代码中划分出来,通过对这些行为的分离. 我们将他们独立到非业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码.
2.AOP和OOP的区别
*** aop和oop的区别?
 OOP(面向对象编程)针对业务处理过程的实体及其属性和行为进行抽象封装,以获得更加清晰高效的逻辑单元划分.
 AOP则是针对业务处理过程中的切面进行提取, 它所面对的是处理过程中的某个步骤或阶段, 以获得逻辑过程中各部分之间低耦合的隔离效果.
 这两种思想有着本质的区别, OOP面向名词领域, AOP面向动词领域.
3.相关术语
举例:
// 使用jdk动态代理完成对UserDao功能增强, 提供Proxy
Proxy.newProxyInstance(UserDao类加载器, userDao实现的接口数组[], new InvocationHandler(){
    Proxy object = public Object invoke(Object proxy, Method method, Object[] args){
        //1. 可以通过参数method判断执行的方法们           //定义切入点 pointcut
        //2. 增强功能代码.                            // advice通知
        // 1 + 2 = 切面 aspect.
    }
})
1. 目标对象 (target)
    指的是需要被增强的对象, 由于spring aop 是通过动态代理模式实现, 从而这个对象永远是被代理对象.
    * 指的是一个具体的类.
2. 连接点 (join point)
    所谓连接点就是目标对象中哪些被拦截的点, 这些点指的就是方法. 
    *** 需要被增强的方法
3. 切入点 (pointcut)
    表示一组连接点. 切入点就是指我们要对哪些连接点进行拦截的定义.
4. 通知 (advice)
    通知是指拦截到连接点之后所要做的事情就是通知. 简单说就是增强.
    通知分为前置通知, 后置通知, 异常通知, 最终通知, 环绕通知.
5. 切面 (aspect)
    切入点 + 通知 = 切面;
6. 代理Proxy
    一个类被aop织入增强后, 就产生一个结果代理类.
3.1 JDK动态代理: 只能为接口的实现类做动态代理.
*注: 在运行时, 在JVM内部动态生成class字节码对象(class对象)
/**
 * 创建一个使用JDK的proxy完成动态代理的工具
 */
public class JDKProxyFactory implements InvocationHandler {
    // 需要增强的对象的引用
    private Object target;
    // 构造方法
    public JDKProxyFactory(Object object) {
        this.target = object;
    }
    // 创建代理对象
    public Object createProxy(){
        // 使用proxy完成代理对象的创建, 三个参数
        Object proxy = Proxy.newProxyInstance(
                // 参数一: 目标对象的类加载器
                target.getClass().getClassLoader(),
                // 参数二: 实现的接口
                target.getClass().getInterfaces(),
                // 参数三: 一个实现InvocationHandler接口的对象,
                // 因为该类实现了InvocationHandler, 直接使用this
                this
        );
        return proxy;
    }
    /**
     *
     * @param proxy 代理对象, 一般不使用
     * @param method 调用的方法的method对象
     * @param args 调用的方法的参数
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("方法增强了...");
        return method.invoke(target, args);
    }
}
public class test {
    @Test
    public void test(){
        //1. 创建目标对象
        IUserService iUserService = new IUserServiceImpl();
        //2. 通过动态代理来增强
        JDKProxyFactory jdkProxyFactory = new JDKProxyFactory(iUserService);
        //3. 调用方法获取代理对象
        IUserService proxy = (IUserService) jdkProxyFactory.createProxy();
        proxy.login();
    }
}
3.2 CGLIB动态代理: 不仅可以为实现接口的类做代理, 也可以为没有实现接口的类做代理.
*注: CGLIB底层是通过使用一个小而快的字节码处理框架ASM, 来转换字节码并生成新的类.
public class CglibProxyFactory implements MethodInterceptor {
    // 目标对象的引用
    private Object target;
    // 构造方法传递目标对象
    public CglibProxyFactory(Object target) {
        this.target = target;
    }
    //创建代理对象
    public Object createProxy(){
        //1. 创建Enhancer对象
        Enhancer enhancer = new Enhancer();
        //2. 传递目标对象的Class, 其实是在内存中创建了一个当前目标类的子类对象.
        enhancer.setSuperclass(target.getClass());
        //3. 设置回调操作(相当于InvocationHandler)
        enhancer.setCallback(this);
        return enhancer.create();
    }
    // 相当于invoke
    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        System.out.println("功能增强");
        return method.invoke(target , args);
        //return methodProxy.invoke(proxy, args);   等价
    }
}
@Test
    public void cglib(){
        //1. 创建目标对象
        IUserService iUserService = new IUserServiceImpl();
        //2. 创建代理对象
        CglibProxyFactory cglibProxyFactory = new CglibProxyFactory(iUserService);
        //3. 调用方法获取代理对象
        IUserService proxy = (IUserService) cglibProxyFactory.createProxy();
        proxy.login();
    }
4.Spring使用那种动态代理机制?
* 如果目标对象, 有接口, 优先使用 jdk动态代理;
* 如果目标对象, 没有接口, 使用 Cglid动态代理;
5.Spring AOP编程
5.1.1 Spring的传统AOP编程(了解)
1. Spring的传统AOP编程(了解)
    1) 传统的spring aop支持五种形式的增强:
    * 前置通知
    * 后置通知
    * 环绕通知
    * 异常抛出通知
    * 引介通知
    2) 流程:
    * 第一步: 编写目标 target;
    * 第二步: 增强 advice

// 编写target
public interface IOrderService {
    public void addOrder();
    public void updateOrder();
}
// 增强
public class OrderHelper implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation mi) throws Throwable {
        System.out.println("环绕前....");
        Object value = mi.proceed();
        System.out.println("环绕后....");
        return value;
    }
}
<?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:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 目标target -->
    <bean id="orderService" class="cn.orange.aop.OrderServiceImpl"></bean>
    <!-- 通知advice -->
    <bean id="orderServiceAdvice" class="cn.orange.aop.OrderHelper"></bean>
    <!-- 定义切点 pointcut -->
    <bean id="orderServicePointCut" class="org.springframework.aop.support.JdkRegexpMethodPointcut">
        <property name="pattern" value=".*Order"></property>
    </bean>
    <!-- 切面aspect=pointcut+advice -->
    <bean id="orderServiceAspect" class="org.springframework.aop.support.DefaultPointcutAdvisor">
        <property name="advice" ref="orderServiceAdvice"/>
        <property name="pointcut" ref="orderServicePointCut"/>      
    </bean> 
    <!-- 代理 proxy -->
    <bean id="orderServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="target" ref="orderService"/>
        <property name="interceptorNames" value="orderServiceAspect"/>
        <property name="proxyInterfaces" value="cn.itheima.aop.IOrderService"/>
    </bean>
</beans> 
5.1.2传统基于aspectJ切点AOP开发
<?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:context="http://www.springframework.org/schema/context"
    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/context http://www.springframework.org/schema/context/spring-context.xsd
         http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- 目标target -->
    <bean id="orderService" class="cn.itheima.aop.OrderServiceImpl"></bean>
    <!-- 通知advice -->
    <bean id="orderServiceAdvice" class="cn.itheima.aop.OrderHelper"></bean>

    <!-- 使用aop标签来完成切面与切点声明 -->
    <aop:config>
        <!-- 定义切点 -->       切点表达式
        <aop:pointcut expression="execution(* cn.orange.aop.IOrderService.*(..))"
            id="orderServicePointCut" />
        <!-- 定义切面 -->
        <aop:advisor advice-ref="orderServiceAdvice" pointcut-ref="orderServicePointCut" />
        <!-- <aop:aspect></aop:aspect> aspectj框架它定义切面使用的 -->
    </aop:config>
</beans> 
5.2.1 Spring整合aspectj框架实现的AOP编程. (目前的主流)
aspectj有六种通知, 在其基础上添加了 最终通知after.
开发步骤:
* 1. 定义目标 target
* 2. 创建通知 advice, 相较于传统的aop开发(需要实现接口), aspectj只需定义增强,通过配置文件完成增强
* 3. 在配置文件中配置信息
<?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:context="http://www.springframework.org/schema/context"
    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/context http://www.springframework.org/schema/context/spring-context.xsd
         http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- target -->
    <bean id="userService" class="cn.itheima.aspectj.UserServiceImpl"/>
    <!-- advice-->
    <bean id="userServiceAdvice" class="cn.itheima.aspectj.UserServiceHelper"/>

    <!-- 使用aop:config来声明  使用aop:aspect来配置切面 -->
    <aop:config proxy-target-class="true">
        <aop:aspect ref="userServiceAdvice">
            <aop:pointcut expression="execution(* *.del(..))" id="delPointCut"/>
            <!-- 前置通知 -->
            <aop:before method="before" pointcut-ref="delPointCut"/>
            <aop:before method="before1" pointcut-ref="delPointCut"/>
            <aop:after-returning method="afterReturning" pointcut-ref="delPointCut" returning="val"/>
            <aop:around method="around"  pointcut-ref="delPointCut"/>
            <aop:after-throwing method="afterThrowing" pointcut-ref="delPointCut" throwing="ex"/>
            <aop:after method="after" pointcut-ref="delPointCut"/>
        </aop:aspect>
    </aop:config>
</beans> 
// 环绕通知, 方法参数的应用
// 通知类
public class UserServiceHelper(){
    // 环绕通知
    public Object around(ProceedingJoinPoint pjp){
        System.out.println("环绕前.");
        Object value = pjp.proceed();
        System.out.println("环绕后."); 
        return value;
    }
}
5.2.2 通知上的参数详解
//advice 通知
public class UserServiceHelper {

    // 前置通知     joinPoint
    public void before(JoinPoint jp) {
        // cn.oranges.aspectj.IUserService
        System.out.println("拦截的目标类:" + jp.getSignature().getDeclaringTypeName());
        // 方法的名称
        System.out.println("拦截的方法名称:" + jp.getSignature().getName());
        System.out.println("前置通知");
    }

    // 后置通知
    public void afterReturning(JoinPoint jp, Object val) {
        System.out.println("目标方法返回值:" + val);
        System.out.println("后置通知");
    }
6.代理方式的选择: proxy-target-class=”true”选择使用Cglib代理
<aop:config proxy-target-class="true">
    <aop:aspect ref="userServiceAdvice">
        <aop:pointcut expression="execution(* *.del(..))" id="delPointCut"/>
        <aop:before method="before" pointcut-ref="delPointCut"/>
        <aop:after-returning method="afterReturning" pointcut-ref="delPointCut" returning="val"/>
        </aop:aspect>
</aop:config>
7.重点: 注解开发
<?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:context="http://www.springframework.org/schema/context"
    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/context http://www.springframework.org/schema/context/spring-context.xsd
         http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- 开启注解扫描 -->
    <context:component-scan base-package="cn.oranges" />
    <!-- 开启aspectj注解自动代理, 并且使用cglib代理 -->
    <aop:aspectj-autoproxy proxy-target-class="true"/>
</beans>
//通知
// 各种通知上可以写切点表达式, 也可以直接写切点
@Component
@Aspect // 声明当前的bean就是一个切面
public class CustomerServiceHelper {

    @Pointcut("execution(* *.s*(..))")
    private void mypointcut(){}

    @Pointcut("execution(* *.update(..))")
    private void mypointcut1(){}

    // 前置通知
    @Before("mypointcut()||mypointcut1()")
    public void before() {
        System.out.println("前置通知...");
    }

    // 后置通知
    @AfterReturning(value = "execution(* *.update(..))", returning = "value")
    public void afterReturning(JoinPoint jp, Object value) {
        System.out.println("后置通知,目标方法的返回是" + value);
    }

    // 环绕通知
    @Around("mypointcut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("环绕前...");
        Object value = pjp.proceed();
        System.out.println("环绕后");
        return value;
    }

    // 异常抛出通知
    @AfterThrowing(value = "mypointcut()", throwing = "ex")
    public void afterThrowing(JoinPoint jp, Throwable ex) {
        System.out.println("异常抛出通知:" + ex);
    }

    // 最终通知
    @After("mypointcut()")
    public void after() {
        System.out.println("最终通知");
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
完整版:https://download.csdn.net/download/qq_27595745/89522468 【课程大纲】 1-1 什么是java 1-2 认识java语言 1-3 java平台的体系结构 1-4 java SE环境安装和配置 2-1 java程序简介 2-2 计算机中的程序 2-3 java程序 2-4 java类库组织结构和文档 2-5 java虚拟机简介 2-6 java的垃圾回收器 2-7 java上机练习 3-1 java语言基础入门 3-2 数据的分类 3-3 标识符、关键字和常量 3-4 运算符 3-5 表达式 3-6 顺序结构和选择结构 3-7 循环语句 3-8 跳转语句 3-9 MyEclipse工具介绍 3-10 java基础知识章节练习 4-1 一维数组 4-2 数组应用 4-3 多维数组 4-4 排序算法 4-5 增强for循环 4-6 数组和排序算法章节练习 5-0 抽象和封装 5-1 面向过程的设计思想 5-2 面向对象的设计思想 5-3 抽象 5-4 封装 5-5 属性 5-6 方法的定义 5-7 this关键字 5-8 javaBean 5-9 包 package 5-10 抽象和封装章节练习 6-0 继承和多态 6-1 继承 6-2 object类 6-3 多态 6-4 访问修饰符 6-5 static修饰符 6-6 final修饰符 6-7 abstract修饰符 6-8 接口 6-9 继承和多态 章节练习 7-1 面向对象的分析与设计简介 7-2 对象模型建立 7-3 类之间的关系 7-4 软件的可维护与复用设计原则 7-5 面向对象的设计与分析 章节练习 8-1 内部类与包装器 8-2 对象包装器 8-3 装箱和拆箱 8-4 练习题 9-1 常用类介绍 9-2 StringBuffer和String Builder类 9-3 Rintime类的使用 9-4 日期类简介 9-5 java程序国际化的实现 9-6 Random类和Math类 9-7 枚举 9-8 练习题 10-1 java异常处理 10-2 认识异常 10-3 使用try和catch捕获异常 10-4 使用throw和throws引发异常 10-5 finally关键字 10-6 getMessage和printStackTrace方法 10-7 异常分类 10-8 自定义异常类 10-9 练习题 11-1 Java集合框架和泛型机制 11-2 Collection接口 11-3 Set接口实现类 11-4 List接口实现类 11-5 Map接口 11-6 Collections类 11-7 泛型概述 11-8 练习题 12-1 多线程 12-2 线程的生命周期 12-3 线程的调度和优先级 12-4 线程的同步 12-5 集合类的同步问题 12-6 用Timer类调度任务 12-7 练习题 13-1 Java IO 13-2 Java IO原理 13-3 流类的结构 13-4 文件流 13-5 缓冲流 13-6 转换流 13-7 数据流 13-8 打印流 13-9 对象流 13-10 随机存取文件流 13-11 zip文件流 13-12 练习题 14-1 图形用户界面设计 14-2 事件处理机制 14-3 AWT常用组件 14-4 swing简介 14-5 可视化开发swing组件 14-6 声音的播放和处理 14-7 2D图形的绘制 14-8 练习题 15-1 反射 15-2 使用Java反射机制 15-3 反射与动态代理 15-4 练习题 16-1 Java标注 16-2 JDK内置的基本标注类型 16-3 自定义标注类型 16-4 对标注进行标注 16-5 利用反射获取标注信息 16-6 练习题 17-1 顶目实战1-单机版五子棋游戏 17-2 总体设计 17-3 代码实现 17-4 程序的运行与发布 17-5 手动生成可执行JAR文件 17-6 练习题 18-1 Java数据库编程 18-2 JDBC类和接口 18-3 JDBC操作SQL 18-4 JDBC基本示例 18-5 JDBC应用示例 18-6 练习题 19-1 。。。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值