SpringAop

1、SpringAop的概念

SpringAop面向切面编程,采用横向抽取机制把分散在各个类当中的重复代码提取出来,然后再编译或运行阶段讲这些抽取出来的代码应用到需要执行的地方。

通过SpringAop可以实现日志记录、性能统计、安全控制、事务处理、异常处理等操作。、

2、SpringAop术语

  • 切面:封装横切系统功能的类
  • 连接点:程序运中的一些时间点,例如方法调用异常抛出等
  • 切入点:切入点实际上就是切面具体要执行的连接点
  • 通知:切入点出具体要执行的代码
  • 引入:允许在现有实现类中添加自定义的方法和属性
  • 目标对象:被通知的对象
  • 代理:通知应用到目标对象后被动态创建的对象
  • 织入:将切面代码插入到具体目标对象,从而生成代理对象的过程。
    • Aop织入分为三种方式:编译期织入、类加载期织入、动态代理织入

3、动态代理

Java当中你的动态代理技术很多,例如JDK、CGLIB、Javaassist、ASM等。

JDK动态代理和CGLIB代理的区别是,JDK动态代理必须借助一个借口才能产生代理类。而CGLIB动态代理不需要,他采用非常底层的字节码技术,对指定目标类生成一个子类,并对子类进行增强。在SpringCore当中已经集成了CGLIB需要的所有jar包。两个代理实现的接口也是不一样的,一个Java反射接口一个是Spring接口,调用的方式也略有差异。

1、JDK动态代理

import com.bbg.aop.aspect.MyAspect;
import com.bbg.aop.dao.TestDao;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class JDKDynamicProxy implements InvocationHandler {

    private TestDao testDao;

    public Object createProxy(TestDao testDao){
        this.testDao = testDao;
        ClassLoader classLoader = JDKDynamicProxy.class.getClassLoader();
        Class[] clazz = testDao.getClass().getInterfaces();
        return Proxy.newProxyInstance(classLoader, clazz, this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        MyAspect myAspect = new MyAspect();

        myAspect.check();
        myAspect.except();

        Object obj = method.invoke(testDao, args);

        myAspect.log();
        myAspect.monitor();
        return obj;
    }
}

2、CGLIB动态代理

import com.bbg.aop.aspect.MyAspect;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CGLIBProxy implements MethodInterceptor {

    public Object createProxy(Object target){
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        MyAspect myAspect = new MyAspect();

        myAspect.check();
        myAspect.except();

        Object obj = methodProxy.invokeSuper(o, objects);

        myAspect.log();
        myAspect.monitor();
        return obj;
    }
}

4、基于代理类的Aop实现

在Spring中默认使用JDK动态代理实现AOP变成。使用org.springframework.aop.framework.ProxyFactoryBean创建代理是SpringAop实现的最基本方式。

ProxyFactoryBean是org.springframework.beans.factory.Factory接口的实现类,FactoryBean负责实例化一个Bean实例,ProxyFactoryBean负责为其他Bean实例创建代理实例。

1、Spring通知类型

  • 环绕通知:在目标方法执行前和执行后实施增强,可应用于日志记录、失误处理等功能
  • 前置通知:在目标方法执行前实施增强,可应用于权限管理等功能
  • 后置返回通知:在目标方法执行成功后实施增强,可应用于关闭流、删除临时文件等功能
  • 后置通知:在目标方法执行后实施增强,不管是否发生异常都会执行该类通知,该类通知可应用于释放资源
  • 异常通知:在方法抛出异常后实施增强,可用于异常处理、记录日志等功能
  • 引入通知:在目标类中添加一些新的方法和属性,可应用于修改目标类

2、ProxyFactoryBean属性

  • target:代理的目标对象
  • proxyInterfaces:代理需要实现的接口
  • interceptorNames:需要织入目标的Advice
  • proxyTargetClass:是否对类代理而不是接口,默认为false使用JDK动态代理。当为true使用CGLIB
  • singleton:返回的代理实例是否为单例,默认为true
  • optimize:当设置为true时强制使用CGLIB代理

3、Maven依赖

<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-context</artifactId>
	<version>${spring.version}</version>
</dependency>
<!-- AOP联盟提供的规范包 -->
<dependency>
    <groupId>aopalliance</groupId>
    <artifactId>aopalliance</artifactId>
    <version>1.0</version>
</dependency>

4、切面实现

注意:这里 MethodInterceptor和CGLIB实现的接口同名但是包不同

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class MySpringAspect implements MethodInterceptor {
    
    public void check(){
        System.out.println("模拟权限控制");
    }

    public void except(){
        System.out.println("模拟异常处理");
    }

    public void log(){
        System.out.println("模拟日志记录");
    }

    public void monitor(){
        System.out.println("模拟性能监控");
    }

    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        check();
        except();
        
        Object object = methodInvocation.proceed();
        
        log();
        monitor();
        return object;
    }
}

5、applicationContext

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:cache="http://www.springframework.org/schema/cache"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
     http://www.springframework.org/schema/aop
     http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
     http://www.springframework.org/schema/context
     http://www.springframework.org/schema/context/spring-context-4.0.xsd
     http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-4.0.xsd">

    <!--目标对象-->
    <bean id="testDao" class="com.bbg.aop.dao.impl.TestDaoImpl"></bean>

    <!--定义切面-->
    <bean id="myAspect" class="com.bbg.aop.aspect.MySpringAspect"></bean>

    <!--使用Spring工厂定义一个代理对象-->
    <bean id="testSpringProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="proxyInterfaces" value="com.bbg.aop.dao.TestDao"/>
        <property name="target" ref="testDao"/>
        <property name="interceptorNames" value="myAspect"/>
        <property name="proxyTargetClass" value="true"/>
    </bean>

</beans>

5、基于XML配置开发AspectJ

AspectJ是一个基于Java语言的AOP框架。从Spring2.0以后引入对他的支持。

使用AspectJ实现AOP有两种方式:基于XML配置开发、基于注解开发

基于XML配置开发AspectJ是指通过XML配置文件定义切面、切入点及通知,所有配置都在<aop:config>元素当中

1、XML配置属性

  • <aop:config>:开发AspectJ的顶层元素,在配置文件的<bean>下可以包含多个该元素
  • <aop:aspect>:配置切面,<aop:config>的子元素,使用ref指定切面
  • <aop:pointcut>:配置切入点,<aop:aspect>的子元素,使用expression指定通知增强的方法
  • <aop:beforet>:配置前置通知,<aop:aspect>的子元素,属性method指定通知方法,属性pointcut-ref指定关联的切入点
  • <aop:after-returning>:配置后置返回通知,<aop:aspect>的子元素,属性method指定通知方法,属性pointcut-ref指定关联的切入点
  • <aop:around>:配置环绕通知,<aop:aspect>的子元素,属性method指定通知方法,属性pointcut-ref指定关联的切入点
  • <aop:after-throwing>:配置异常通知,<aop:aspect>的子元素,属性method指定通知方法,属性pointcut-ref指定关联的切入点
  • <aop:after>:配置后置通知,<aop:aspect>的子元素,属性method指定通知方法,属性pointcut-ref指定关联的切入点
  • <aop:declare-parents>:给通知引入新的额外接口,增强功能

2、Maven依赖

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>${spring.version}</version>
    </dependency>
    <!--AspectJ框架提供的实现-->
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.9.5</version>
    </dependency>

3、切面实现

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;

public class MyAspectXML {

    public void before(JoinPoint joinPoint){
        System.out.println("前置通知");
        System.out.println("target:" + joinPoint.getTarget() + ",method:" + joinPoint.getSignature().getName());
    }

    public void afterReturning(JoinPoint joinPoint){
        System.out.println("后置返回通知");
        System.out.println("target:" + joinPoint.getTarget() + ",method:" + joinPoint.getSignature().getName());
    }

    public Object around(ProceedingJoinPoint  joinPoint) throws Throwable {
        System.out.println("环绕开始");
        Object object = joinPoint.proceed();
        System.out.println("环绕结束");
        return object;
    }

    public void except(Throwable exception){
        System.out.println("异常通知:" + exception.getMessage());
    }

    public void after(){
        System.out.println("后置(最终)通知");
    }

}

4、applicationContext

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:cache="http://www.springframework.org/schema/cache"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
     http://www.springframework.org/schema/aop
     http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
     http://www.springframework.org/schema/context
     http://www.springframework.org/schema/context/spring-context-4.0.xsd
     http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-4.0.xsd">

    <!--目标对象-->
    <bean id="testDao" class="com.bbg.aop.dao.impl.TestDaoImpl"></bean>

    <!--定义切面-->
    <bean id="myAspect" class="com.bbg.aop.aspect.MyAspectXML"></bean>

    <!--配置切面-->
    <aop:config>
        <!--第一个*表示返回类型 com.bbg.aop表示包名 第二个*表示类名 第三个*表示方法名 (..)表示任意参数 注意第一个*之后有一个空格-->
        <!--这里的包名一定要对应需要通知类的全包路径-->
        <aop:pointcut id="mypointcut" expression="execution(* com.bbg.aop.dao.*.*(..))"/>
        <aop:aspect ref="myAspect">
            <aop:before method="before" pointcut-ref="mypointcut"></aop:before>
            <aop:after-returning method="afterReturning" pointcut-ref="mypointcut"></aop:after-returning>
            <aop:around method="around" pointcut-ref="mypointcut"></aop:around>
            <aop:after-throwing method="except" pointcut-ref="mypointcut" throwing="exception"></aop:after-throwing>
            <aop:after method="after" pointcut-ref="mypointcut"></aop:after>
        </aop:aspect>
    </aop:config>

</beans>

5、各个通知执行顺序

前置通知 – 环绕开始 – 切入点方法

发生异常:

Y:最终通知 – 异常通知 – 结束

N:最终通知 – 环绕结束 – 后置返回 – 结束

6、基于注解开发AspectJ

1、注解介绍

  • @Aspect:用于定义个切面
  • @Pointcut:用于定义切入点方法,该方法是一个返回值为void且方法体为空的普通方法
  • @Before:定义前置通知,value属性对应通用切入点,也可以指定具体的切入点
  • @AfterReturning:定义后置返回通知
  • @Around:定义环绕通知
  • @AfterThrowing:定义异常通知,除了以上value属性还有个throwing属性用于访问目标方法抛出的异常,该属性值与异常通知方法中同名的形参一致
  • @After:定义后置通知(最终)

注意:这里的执行顺序就不一定是按照5当中的执行顺序了,有一定的随机性。

AOP使用XML配置情况下,通知的执行顺序由配置顺序决定,在注解情况下由于不存在配置顺序的概念
的概念,参照通知所配置的方法名字符串对应的编码值顺序,可以简单理解为字母排序

2、切面实现

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class MyAspectAnnotaion {

    @Pointcut("execution(* com.bbg.aop.dao.*.*(..))")
    private void myPonintcut(){

    }

    @Before(value = "myPonintcut()")
    public void before(JoinPoint joinPoint){
        System.out.println("前置通知");
        System.out.println("target:" + joinPoint.getTarget() + ",method:" + joinPoint.getSignature().getName());
    }

    @AfterReturning(value = "myPonintcut()")
    public void afterReturning(JoinPoint joinPoint){
        System.out.println("后置返回通知");
        System.out.println("target:" + joinPoint.getTarget() + ",method:" + joinPoint.getSignature().getName());
    }

    @Around(value = "myPonintcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("环绕开始");
        Object object = joinPoint.proceed();
        System.out.println("环绕结束");
        return object;
    }

    @AfterThrowing(value = "myPonintcut()", throwing = "exception")
    public void except(Throwable exception){
        System.out.println("异常通知:" + exception.getMessage());
    }

    @After(value = "myPonintcut()")
    public void after(){
        System.out.println("后置(最终)通知");
    }

}

3、applicationContext

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:cache="http://www.springframework.org/schema/cache"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
     http://www.springframework.org/schema/aop
     http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
     http://www.springframework.org/schema/context
     http://www.springframework.org/schema/context/spring-context-4.0.xsd
     http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache-4.0.xsd">

    <context:component-scan base-package="com.bbg"></context:component-scan>

    <aop:aspectj-autoproxy/>


</beans>

或者可以使用类配置:

//表明此类是spring的配置类
@Configuration
//主键扫描
@ComponentScan("com.itheima")
//启用aop注解驱动
@EnableAspectJAutoProxy
public class SpringConfig {
    
}

4、顺序控制

添加@Order注解

貌似失败了。以后再看吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值