spring初学入门(4)——AOP的了解和使用(bai)

1.什么是AOP?

AOP就是面向切面编程
通俗点说AOP的解决的主要问题就是将共同重复的代码就行抽离(日志,事务,鉴权等)。
什么又是切面?切面就是一个类,类里面包含类某一类具体业务(日志,事务,鉴权),用这个类 去拦截/增强 你需要监控的方法或者业务
AOP作用:

  • 1.将共同/重复代码抽取 提高开发i效率
    -> 2.调高代码课维护性

在这里插入图片描述

AOP名词理解:
切入点:就是切面需要拦截增强的 类的内部的方法
连节点:每一个类里 普通的方法都是 连接点
切面:就是封装某一种业务抽离(日志,事务。鉴权)
增强/通知:就是切面中的方法 对目标对象中的方法进行增强/拦截/通知
织入:是一个过程/动作,就是将切面切向要拦截那些类的方法的过程
目标对象:就是被代理的对象

2.AOP的实现

两种实现方式

  • 基于自身的ProxyFactoryBean 实现aop功能
  • 借助于aspectj第三方aop框架实现
    (aspectj 也是一个优秀的aop框架)
3.AOP 实现的原理

aop实现就是通过动态代理模式实现

动态代理:有两种

  • jdk 动态代理 只能代理 目标对象有实现的接口
  • cglib 可以代理类 代理接口
4.基于ProxyFactoryBean实现Aop
1. ProxyFactoryBean

ProxyFactoryBean是FactoryBean接口的实现类,FactoryBean负责实例化一个Bean,而ProxyFactoryBean负责为其他Bean创建代理实例。在Spring中,使用ProxyFactoryBean是创建AOP代理的基本方式。
在这里插入图片描述
ProxyTargetClass (是否强制使用CGLIB来实现代理)

​ (true : 强制使用CGLIB来实现代理)

​ (false : 不强制使用CGLIB来实现代理,首选JDK来实现代理)(默认值)

isOptimize (是否对生成代理策略进行优化)

​ (true : 进行优化,如果有接口就代理接口(使用JDK动态代理),没有接口代理类(CGLIB代理))

​ (false : 不进行优化) (默认值)

注FactoryBean和 BeanFactory非常相似,但是两者很不同,这里给出了区别
.

FactoryBean:作用是创建bean,容器加载时所有的对象都是由FactoryBean的子类创建

BeanFactory:是spring的核心容器之一,顶层接口,作用主要是让用户获取bean,判断bean 是否单例

2.实现

在Pom文件中l引入依赖


    <properties>
        <!--在当前pom 或者父类pom 中声明属性  -->
        <spirng.version>5.0.16.RELEASE</spirng.version>
    </properties>


    <dependencies>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spirng.version}</version>
        </dependency>

        <!-- 导入spring aop -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${spirng.version}</version>
        </dependency>

    </dependencies>

1.创建切面MyAspect


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

public class MyAspect implements MethodInterceptor {

    public Object invoke(MethodInvocation methodInvocation) throws Throwable {

        // 记录日志
       log( methodInvocation.getMethod().getName());

       Object result = methodInvocation.proceed();

        return result;
    }

    public void log(String log){
        System.out.println("日志调用方法:"+log);
    }
}

2.创建配置文件,在xml文件中进行配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="studentDao" class="com.wgz.spring.dao.impl.StudentDaoImpl"></bean>

    <bean id="myAspect" class="com.wgz.spring.proxyfactory.MyAspect">

    </bean>
    <bean id="studentDaoProxy" class="org.springframework.aop.framework.ProxyFactoryBean">

         <!--
         当实现接口时,指定对应得接口  可以选择不写,
         如果代理只有类,不能写否者报错
          <property name="proxyInterfaces" value="com.wgz.spring.dao.StudentDaoIm">
            </property>-->
            <!--
                指定目标对象
            -->
            <property name="target" ref="studentDao"></property>

            <!--
                指定切面
             -->
            <property name="interceptorNames" value="myAspect"></property>
            <!--
                true   cglib 代理
                false  jdbc 动态代理
            -->
            <property name="proxyTargetClass" value="false"></property>
    </bean>

</beans>

3.测试

public class ProxyFactoryTest {

    public static void main(String[] args) {

        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("proxyfactory/proxyfactory.xml");

        StudentDaoImpl studentDao = (StudentDaoImpl) applicationContext.getBean("studentDaoProxy");

       Student student = studentDao.findStudentById(10);
        System.out.println("student:"+student);
    }

}

jdk和CGLIBl两种代理的区别?

1.jdk 只能代理类实现的接口的类,而没有实现接口类必须使用CGLIB
2.从性能来说:
CGLIB:创建慢,执行块(CGLIB 要生成对应的代理的代码,在创建代理对象)
JDK:创建快,执行慢(反射本身执行就比正常代码的慢)

5.基于AspectJ开发的实现

AspectJ是一个基于Java语言的AOP框架,它提供了强大的AOP功能。Spring 2.0以后,Spring AOP引入了对AspectJ的支持,并允许直接使用AspectJ进行编程,而Spring自身的AOP API也尽量与AspectJ保持一致。新版本的Spring框架,也建议使用AspectJ来开发AOP。

使用AspectJ实现AOP有两种方式:

  • 基于XML的声明式AspectJ
  • 基于注解的声明式AspectJ
1spring通知的类型

Spring按照通知在目标类方法的连接点位置,可以分为5种类型,具体如下:

  • org.springframework.aop.MethodBeforeAdvice(前置通知)
    在目标方法执行前实施增强,可以应用于权限管理等功能。
  • org.springframework.aop.AfterReturningAdvice(后置通知)
    在目标方法执行后实施增强,可以应用于关闭流、上传文件、删除
    临时文件等功能。
  • org.aopalliance.intercept.MethodInterceptor(环绕通知)
    在目标方法执行前后实施增强,可以应用于日志、事务管理等功能。
  • org.springframework.aop.ThrowsAdvice(异常抛出通知)
    在方法抛出异常后实施增强,可以应用于处理异常记录日志等功能。
  • org.springframework.aop.IntroductionInterceptor(引介通知)
    在目标类中添加一些新的方法和属性,可以应用于修改老版本程序。
2.基于XML的声明式AspectJ

基于XML的声明式AspectJ是指通过XML文件来定义切面、切入点及通知,所有的切面、切入点和通知都必须定义在aop:config元素内。

配置切面

在Spring的配置文件中,配置切面使用的是aop:aspect元素,该元素会将一个已定义好的Spring Bean转换成切面Bean,所以要在配置文件中先定义一个普通的Spring Bean。

      <!--配置aop 切面-->
              <aop:aspect ref="myAspect">
                  <!--绑定切点-->
  				...
      		</aop:config>

在这里插入图片描述

配置切入点

当aop:pointcut元素作为aop:config元素的子元素定义时,表示该切入点是全局切入点,它可被多个切面所共享;当aop:pointcut元素作为aop:aspect元素的子元素时,表示该切入点只对当前切面有效。

<!-*.*表示impl下的所有类->
    <aop:pointcut id="myPointCut" expression="execution(* com.yyf.spring.dao.impl.*.*(..))"/>

切入点表达式execution(* com.yyf.spring.dao.impl..(…))

表达式是表示我们我们当前切面需要拦截那写方法
写法:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?
name-pattern(param-pattern) throws-pattern?)
execution(权限修饰符? 返回值类型 方法类的全路径名?方法名(参数名)异常类型? )
?部分表示可以省略不写

配置通知
使用aop:aspect的子元素可以配置5种常用通知,这5个子元素不支持使用子元素,但在使用时可以指定一些属性,其常用属性及其描述如下:

在这里插入图片描述
所需要的依赖

>
>        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.6.12</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.6.12</version>
        </dependency>

1.创建切面

public class MyAspect {

    /**
     * 前置通知   注意JoinPoint 中 P 大写引入的是org.aspectj.lang.JoinPoint;
     * @param joinPoint
     */
    public void myBefore(JoinPoint joinPoint){

        System.out.println("前置通知");
        System.out.println("目标类:"+joinPoint.getTarget());
        System.out.println("目标方法:"+joinPoint.getSignature().getName());

    }


    /**
     * 后置通知
     * @param joinPoint
     */
    public void myAfterReturning(JoinPoint joinPoint,Object result){
        System.out.println("后置通知");
        System.out.println("目标方法:"+joinPoint.getSignature().getName());
        System.out.println("result:"+result);
    }

    /**
     * 环绕通知
     * @param proceedingJoinPoint
     * @return
     * @throws Throwable
     */
    public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {

        System.out.println("环绕通知:执行方法前");
        Object result = proceedingJoinPoint.proceed();
        System.out.println("result:"+result);
        System.out.println("环绕通知:执行方法后");

        return result;
    }

    /**
     * 异常通知
     * @param joinPoint
     * @param e
     */
    public void myAfterThrowing(JoinPoint joinPoint,Throwable e){

        System.out.println("异常通知出错了:"+e.getMessage());
    }


    public void myAfter(JoinPoint joinPoint){
        System.out.println("最终通知"+ joinPoint.getSignature().getName());
    }

}

2.配置文件

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

    <bean id="studentDao"  class="com.yyf.spring.dao.impl.StudentDaoImpl"></bean>
    <bean id="myAspect" class="com.yyf.spring.aspect.xml.MyAspect"></bean>

    <!-- 
        配置aop
    -->
    <aop:config>
            <!--配置aop 切面-->
            <aop:aspect ref="myAspect">
                <!--绑定切点-->
                <aop:pointcut id="myPointCut" expression="execution(* com.yyf.spring.dao.impl.*.*(..))"/>
                <!-- 配置各种通知-->
                <aop:before method="myBefore" pointcut-ref="myPointCut"></aop:before>
                <aop:around method="myAround" pointcut-ref="myPointCut"></aop:around>
                <aop:after-returning method="myAfterReturning" pointcut-ref="myPointCut" returning="result"></aop:after-returning>
                <aop:-throwing method="myAfterThrowing" pointcut-ref="myPointCut" throwing="e"></aop:after-throwing>
                <aop:after method="myAfter" pointcut-ref="myPointCut"></aop:after>
            </aop:aspect>
    </aop:config>
3.基于注解的AspectJ(主要方式)

AspectJ框架为AOP的实现提供了一套注解,用以取代Spring配置文件中为实现AOP功能所配置的臃肿代码。AspectJ的注解及其描述如下所示:
在这里插入图片描述
示例

1.声明切面

@Component
@Aspect
public class MyAspect {


    @Pointcut("execution(* com.wgz.spring.dao.impl.*.*(..))")
    private void myPointCut(){}

    /**
     * 前置通知   注意JoinPoint 中 P 大写引入的是org.aspectj.lang.JoinPoint;
     * @param joinPoint
     */
    @Before("myPointCut()")
    public void myBefore(JoinPoint joinPoint){

        System.out.println("前置通知");
        System.out.println("目标类:"+joinPoint.getTarget());
        System.out.println("目标方法:"+joinPoint.getSignature().getName());

    }


    /**
     * 后置通知
     * @param joinPoint
     */
    @AfterReturning(value = "myPointCut()",returning = "result")
    public void myAfterReturning(JoinPoint joinPoint,Object result){
        System.out.println("后置通知");
        System.out.println("目标方法:"+joinPoint.getSignature().getName());
        System.out.println("result:"+result);
    }

    /**
     * 环绕通知
     * @param proceedingJoinPoint
     * @return
     * @throws Throwable
     */
    @Around(value = "myPointCut()")
    public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {

        System.out.println("环绕通知:执行方法前");
        Object result = proceedingJoinPoint.proceed();
        System.out.println("result:"+result);
        System.out.println("环绕通知:执行方法后");

        return result;
    }

    /**
     * 异常通知
     * @param joinPoint
     * @param e
     */
    @AfterThrowing(value = "myPointCut()",throwing = "e")
    public void myAfterThrowing(JoinPoint joinPoint,Throwable e){

        System.out.println("异常通知出错了:"+e.getMessage());
    }

    @After("myPointCut()")
    public void myAfter(JoinPoint joinPoint){
        System.out.println("最终通知"+ joinPoint.getSignature().getName());
    }

}

2.设置配置文件开启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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- 开启注解扫描器-->
    <context:component-scan base-package="com.wgz.spring"></context:component-scan>
    <!-- 开启aop asjpectj 功能-->
    <aop:aspectj-autoproxy/>
</beans>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring AOPSpring框架中的一个重要模块,它提供了面向切面编程(AOP)的支持。AOP是一种编程思想,它可以在不改变原有代码的情况下,通过在程序运行时动态地将代码“织入”到现有代码中,从而实现对原有代码的增强。 Spring AOP提供了基于注解的AOP实现,使得开发者可以通过注解的方式来定义切面、切点和通知等相关内容,从而简化了AOP使用。 下面是一个基于注解的AOP实现的例子: 1. 定义切面类 ```java @Aspect @Component public class LogAspect { @Pointcut("@annotation(Log)") public void logPointcut() {} @Before("logPointcut()") public void beforeLog(JoinPoint joinPoint) { // 前置通知 System.out.println("执行方法:" + joinPoint.getSignature().getName()); } @AfterReturning("logPointcut()") public void afterLog(JoinPoint joinPoint) { // 后置通知 System.out.println("方法执行完成:" + joinPoint.getSignature().getName()); } @AfterThrowing(pointcut = "logPointcut()", throwing = "ex") public void afterThrowingLog(JoinPoint joinPoint, Exception ex) { // 异常通知 System.out.println("方法执行异常:" + joinPoint.getSignature().getName() + ",异常信息:" + ex.getMessage()); } } ``` 2. 定义业务逻辑类 ```java @Service public class UserService { @Log public void addUser(User user) { // 添加用户 System.out.println("添加用户:" + user.getName()); } @Log public void deleteUser(String userId) { // 删除用户 System.out.println("删除用户:" + userId); throw new RuntimeException("删除用户异常"); } } ``` 3. 在配置文件中开启AOP ```xml <aop:aspectj-autoproxy/> <context:component-scan base-package="com.example"/> ``` 在这个例子中,我们定义了一个切面类LogAspect,其中通过@Aspect注解定义了一个切面,通过@Pointcut注解定义了一个切点,通过@Before、@AfterReturning和@AfterThrowing注解分别定义了前置通知、后置通知和异常通知。 在业务逻辑类中,我们通过@Log注解标注了需要增强的方法。 最后,在配置文件中,我们通过<aop:aspectj-autoproxy/>开启了AOP功能,并通过<context:component-scan>扫描了指定包下的所有组件。 这样,当我们调用UserService中的方法时,就会触发LogAspect中定义的通知,从而实现对原有代码的增强。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值