Spring复习 day(3)AOP及相关注解

一、动态代理

1.1 动态代理

  • invoke中参数是具体的被代理对象
public class test {
    public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList<>();
        List list = (List) Proxy.newProxyInstance(arrayList.getClass().getClassLoader(), arrayList.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                if (method.getName().equals("add")) {
                    System.out.println("向数组中添加了元素");
                    return method.invoke(arrayList, args);
                }
                return method.invoke(proxy, args);
            }
        });
        list.add(1);
    }
}

1.2 cglib代理

  • 要求:被代理的类不能是最终类
public class test {
    public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList<>();
        ArrayList arrayList1 = (ArrayList) Enhancer.create(arrayList.getClass(), new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                if (method.getName().equals("add")) {
                    System.out.println("使用了增加的方法");
                    return method.invoke(arrayList, objects);
                }
                return method.invoke(arrayList, objects);
            }
        });
        arrayList1.add(1);
    }
}

1.3 动态代理的好处

  • 可以把一些重复代码写进动态代理中,提高编程的效率。

1.4 Aop作用

  • 程序运行期间,不修改源码,对已有代码进行增强。

减少重复代码
提高开发效率
维护方便

1.5 aop实现方式

  • 使用动态代理技术

二、spring中的aop

2.1 aop相关术语

  • joinpoint(连接点)
    指方法
  • pointcut(切入点)
    指对哪些连接点进行拦截,一般指被增强的连接点。
  • 通知
    拦截到连接点之后要做的事情

类型:前置通知,后置通知,环绕通知,异常通知,返回通知。
catch里面一般是异常通知,finally里面一般是最终通知

  • weaving(织入)
    增强应用到目标对象创建新的代理对象的过程。

  • 切面
    切入点和通知的结合

2.2 spring框架的作用

spring框架监控切入点的执行,一旦监控到切入点执行,就会使用动态代理,创建目标对象的代理对象,根据通知的类型,在代理对象的对应位置,将通知织入,完成代码的逻辑运行。

2.3 切入点表达式

访问修饰符 返回值 包名.包名…类名.方法名(参数列表)

2.4 基于xml方式配置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:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- an HTTP Session-scoped bean exposed as a proxy -->
    <!-- 被增强的类-->
       <bean id="stuservice" class="com.example.demo.Service.stuService"></bean>
    <!-- 增强所在的类-->
       <bean id="countlog" class="com.example.demo.Logger.CountLog"></bean>
       <aop:config>
           <aop:aspect id="logService" ref="countlog">
               <!--配置通知的类型, 并且建立通知方法和切入点的关联-->
               <aop:before method="log" pointcut="execution(public int com.example.demo.Service.stuService.getCount())"></aop:before>
           </aop:aspect>
       </aop:config>
</beans>

2.5 测试aop

  • 需要倒入相关的依赖
    这个包的主要作用是解析切入点表达式
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.5</version>
</dependency>

  • 测试
class stuServiceTest {

    @Test
    void getCount() {
        //1.获取容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //2.获取对象
        stuService stuservice = (stuService) ac.getBean("student");
        //3.执行方法
        stuservice.getCount();
    }
    
}
  • 结果
在取款前打印日志
获取剩余的存款

2.6 切入点表达式

  • 全通配写法
* *..*.*()

可以用一个*号匹配一个包名,用…表示所有的包

  • 可以写参数类型
    基本数据类型直接写类型名称
    引用数据类型写包名.类型名

2.7 其他通知类型测试

  • bean.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"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- an HTTP Session-scoped bean exposed as a proxy -->
    <!-- 被增强的类-->
    <bean id="student" class="com.example.demo.Service.stuService"></bean>
    <!-- 增强所在的类-->
       <bean id="countLog" class="com.example.demo.Logger.CountLog"></bean>
       <aop:config>
           <aop:aspect id="logService" ref="countLog">
               <!--配置通知的类型, 并且建立通知方法和切入点的关联-->
               <aop:before method="beforeAdvice" pointcut="execution(public * com.example.demo.Service.stuService.getCount())"></aop:before>
               <aop:after method="afterAdvice" pointcut="execution(public * com.example.demo.Service.stuService.getCount())"></aop:after>
               <aop:after-returning method="returnAdvice" pointcut="execution(public int com.example.demo.Service.stuService.getCount())"></aop:after-returning>
               <aop:around method="SurroundAdvice" pointcut="execution(public * com.example.demo.Service.stuService.getCount())"></aop:around>
               <aop:after-throwing method="WrongAdvice" pointcut="execution(public * com.example.demo.Service.stuService.getCount())"></aop:after-throwing>
           </aop:aspect>
       </aop:config>
</beans>
  • 结果
前置通知
环绕通知
后置通知
null

2.8 报错

org.springframework.aop.AopInvocationException: Null return value from advice does not match primiti

将基本数据类型改成引用数据类型

2.9 通知总结

  • 前置通知在切入点执行前执行
  • 后置通知在切入点执行后执行
  • 最终通知是无论切入点是否正常执行都会执行
  • 异常通知是发生异常后执行

2.10 通知执行顺序

befrore–>around–>after–>after-throwing/after-return

2.11 配置切入点表达式

<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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- an HTTP Session-scoped bean exposed as a proxy -->
    <!-- 被增强的类-->
    <bean id="student" class="com.example.demo.Service.stuService"></bean>
    <!-- 增强所在的类-->
       <bean id="countLog" class="com.example.demo.Logger.CountLog"></bean>
       <aop:config>
           <aop:aspect id="logService" ref="countLog">
               <!--配置通知的类型, 并且建立通知方法和切入点的关联-->
               <aop:before method="beforeAdvice" pointcut-ref="express1"></aop:before>
               <aop:after method="afterAdvice" pointcut-ref="express1"></aop:after>
               <aop:after-returning method="returnAdvice" pointcut-ref="express1"></aop:after-returning>
               <aop:after-throwing method="WrongAdvice" pointcut-ref="express1"></aop:after-throwing>
               <aop:around method="SurroundAdvice" pointcut-ref="express1"></aop:around>
               <aop:pointcut id="express1" expression="execution(public * com.example.demo.Service.stuService.getCount())"/>
           </aop:aspect>
       </aop:config>
</beans>

2.12 环绕通知

public Integer SurroundAdvice(ProceedingJoinPoint joinPoint){
        Object[] args = joinPoint.getArgs();
        Integer proceed = 0;
        try {
            System.out.println("around start");
            proceed = (Integer)joinPoint.proceed(args);
            System.out.println("around after");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }finally {
            return proceed;
        }
    }
  • 测试最终结果
before
around start
around after
after-return
after

三、注解方式实现aop

3.1 配置文件的修改

在配置文件中加入允许注解相关配置

<?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:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 开启注解需要扫描的包-->
    <context:component-scan base-package="com.example.demo"></context:component-scan>
    <!-- 开启支持aop相关的注解-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

3.2 切面类

package com.example.demo.Logger;

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

@Component("logger")
@Aspect
public class CountLog {
    @Pointcut("execution(public * com.example.demo.Service.stuService.getCount())")
    public void p1(){}
    @Before("p1()")
    public void beforeAdvice(){
        System.out.println("before");
    }
    @After("p1()")
    public void afterAdvice(){
        System.out.println("after");
    }
    @AfterThrowing("p1()")
    public void WrongAdvice(){
        System.out.println("wrong");
    }
    /*@Around("p1()")
    public Integer SurroundAdvice(ProceedingJoinPoint joinPoint){
        Object[] args = joinPoint.getArgs();
        Integer proceed = 0;
        try {
            System.out.println("around start");
            proceed = (Integer)joinPoint.proceed(args);
            System.out.println("around after");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }finally {
            return proceed;
        }
    }*/
    @AfterReturning("p1()")
    public void returnAdvice(){
        System.out.println("after-return");
    }
}

3.3 结果

before
after
after-return
800


3.4 不使用xml配置的方式

  • 配置类
package com.example.demo.Service.Configuration;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@ComponentScan(basePackages = "com.example.demo")
@EnableAspectJAutoProxy
public class aopCofig {
}

  • 测试类
class stuServiceTest {

    @Test
    void getCount() {
        //1.获取容器
        ApplicationContext ac = new AnnotationConfigApplicationContext("com.example.demo.Service.Configuration");
        //2.获取对象
        stuService stuservice = (stuService) ac.getBean("student");
        //3.执行方法
        System.out.println(stuservice.getCount());
    }

}

3.5 各种通知类型模拟

 @Around("p1()")
    public Integer SurroundAdvice(ProceedingJoinPoint joinPoint){
        Object[] args = joinPoint.getArgs();
        Integer proceed = 0;
        try {
            System.out.println("前置通知");
            proceed = (Integer)joinPoint.proceed(args);
            System.out.println("后置通知");
            System.out.println("around after");
        } catch (Throwable throwable) {
            System.out.println("异常通知");
            throwable.printStackTrace();
        }finally {
            System.out.println("after通知");
            return proceed;
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值