Spring AOP 入门

根据老师上课内容,自己对学习的整理

SpringAOP

在软件业,AOP为Aspect Oriented Progamming的缩写,意为:

面向切面编程,通过预编译和运行期间动态代理实现程序功能的统一维护的一种技术

AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合性降低,提高程序的可重用性,同时提高了开发的效率

AOP主要作用:在不修改原有代码的条件下对方法进行扩展

难点:

切面:类似汉堡包,上面是事务,中间是业务方法

预编译:预编译是做些代码文本的替换工作。是整个编译过程的最先做的工作【百度百科】

                类似static,静态资源会被优先加载

动态代理:通过第三方代理,JDK代理或者CGLib代理

IOC-DI/AOP之间的区别

1.IOC-DI利用第三方容器将对象统一管理,利用依赖注入为属性赋值,解决了对象与对象之间的耦合性的问题

进阶:SpringMVC框架(对象)/Mybatis框架(对象)类似的框架都可以交给Spring容器管理。Spring可以以统一的方法管理其他第三方的框架,使得调用浑然一体,降低耦合,解决框架与框架之间的耦合问题

2.AOP名称为面向切面编程,使得业务逻辑各部分之间的耦合度降低

动态代理

动态代理的作用:使用代理机制可以有效降低代码耦合性,将公共的代码/重复的代码,写在代理机制中,通过代理机制调用方法,使得真实的业务被调用。通过调用机制,可以有效的降低业务之间的耦合

               代理机制                                Service

                事务开始

用户---> 目标方法调用   --->                业务方法

                事务提交

动态代理分类

代理特点:使用代理对象和是使用目标方法一样(高仿)

JDK代理

1.jdk动态代理是JDK源码提供的,无需导入额外的jar包

2.JDK代理对象要求实现和被代理相同的接口 必须有接口

3.JDK代理创建速度快,运行时稍慢

public class JDKProxy{
    /**
     * 参数说明:
     * ClassLoader loader,  类加载器:将class加载到java运行机制中
     * Class<?>[] interfaces, 被代理者的接口数组  java可以多实现
     * InvocationHandler h  将代理对象扩展的内容写到处理器中
     * @param target
     * @return
     */
    public static Object getProxy(Object target){
    //1.获取目标对象的类加载器
    ClassLoader loader = target.getClass().getClassLoader();
    //2.获取接口数组,因为java可以多实现
    Class<?>[] interfaces = target.getClass().getInterfaces();
    //3.获取处理器对象
    InvocationHandler h = getInvocationHandler(target);
    //创建代理对象
    return Proxy.newProxyInstance(loader,interfaces,h);
    }
    
    //获取InvocationHandler对象
    public static InvocationHandler getInvocationHandler(Object target){
        //当代理对象执行业务操作时,通过InvocationHandler进行业务的扩展
        return new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws         Throwable {
                System.out.println("事务开始");
                //调用 让目标方法执行 target:目标对象!!!! 其它都是固定写法!!!
                Object result = method.invoke(target, args);
                System.out.println("事务提交");
                return result;
            }
        };
    }
}

测试类

public static void main(String[] args) {
        ApplicationContext context =
            new AnnotationConfigApplicationContext(SpringConfig.class);
        //1.获取目标对象  userServiceImpl 接口UserSesrvice 实现类对象
        UserService target = context.getBean(UserService.class);
        //2.获取代理对象  实现了UserService接口
        UserService proxy = (UserService) JDKProxy.getProxy(target);
        proxy.addUser();
    }

CGLib代理

1.cglib动态代理需要导入额外的包才能使用。

2.cglib要求被代理者可以没有接口,但是cglib代理对象是目标对象的子类(继承)

3.cglib创建对象时较慢,但是运行期速度快。

public class CglibProxy {

    //因为Cglib代理需要继承被代理者对象,所以需要设置父级
    public static Object getObject(Object target){
        //创建增强器对象
        Enhancer enhancer = new Enhancer();
        //设置父级
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(getMethodInterceptor());
        return enhancer.create();
    }

    public static MethodInterceptor getMethodInterceptor(){

        return new MethodInterceptor() {
            @Override
            public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("数据库事务开始");
                //执行父类的方法
                Object proxy = methodProxy.invokeSuper(obj,objects);
                System.out.println("数据库事务提交");
                return proxy;
            }
        };
    }

}

测试类

@Test
    void testCGB(){
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        UserService userService = context.getBean(UserService.class);
        //使用代理对象 调用方法
        UserService proxy = (UserService) CglibProxy.getObject(userService);
        proxy.addUser();
    }

AOP        重点,面试会问

AOP底层实现就是JDK代理和CGLib代理

Spring中的AOP也是利用代理对象在不修改源码的情况下,对方法进行扩展

首先需要导入Jar包,在pom文件中

		<!--引入AOPjar包文件-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

编辑切面类

注解:

@Aspect标识当前类是一个切面

@Pointcut标识切入点表达式【建议通过高级的封装提取,不然需要再每一个通知上面都需要写回很麻烦】

切入点表达式

        *号是通配符,表示任意

        bean标签

                bean("对象的ID")只能匹配固定的类/对象一个        粗粒度

@Pointcut("bean(userServiceImpl)")

        within表达式

                within(包名.类名)可以匹配多个类        粗粒度

//单个
@Pointcut("within(com.xb.demo1.service.UserServiceImpl)")
//多个
@Pointcut("within(com.xb.demo1.service.*)")

        execution表达式        细粒度

                @Pointcut("execution(* com.*..*(..))")

                匹配com包下任意类任意方法任意参数 任意返回值的方法

                execution(返回值类型 包名.类名.方法名(参数列表))

        @annotation

                @Pointcut("annotation(路径.注解)")

                使用注解拦截

-----------------------------------常用于检查程序运行状态--------------------------------

使用方法例如:@Before("pointCut()")【pointCut()为公共的切入点表达式】

@Before前置通知       

在执行方法前执行

@AfterReturning后置通知 【常用于记录返回值结果】

在执行方法后执行

@AfterReturning(value="pointCut()",returinng="result")就可以在方法中使用返回值了

@After最终通知                基本不用

不管目标方法执行是否正确,都要执行(类似try-catch中的finally)

@AfterThrowing        异常通知【用于知道异常属性】

目标方法执行时,抛出异常

@AfterThrowing(value="pointCut()",throwing="exection")

public void afterThrowing(Exception exception){

exception.printStackTrace;

sout...

}

--------------------------------------------------------------------------------------------------

@Around        环绕通知(类似包括前置通知后置通知的整合)

环绕通知必须提供参数,并且必须位于通知的第一位

前四大通知类型不能控制目标方法的运行,一般记录程序的运行状态

在目标方法执行前后都要运行,只有环绕通知才可以控制目标方法运行,使用最多的通知方法

@EnableAspectJAutoProxy让AOP生效,写在配置类中

可以添加属性(proxyTargetClass=true)让代理强制使用CGLib代理,不建议使用默认为jdk代理,因为不是所有类都有接口。

@Order(value=数字)

给切面类排序,数字越小,越先执行

关于JoinPoint和ProceedingJoinPoint区别

说明:
1.ProceedingJoinPoint只适用于环绕通知,因为只有环绕通知,才能控制目标方法的运行.
2.JoinPoint 适用于其它的四大通知类型,可以用来记录运行的数据.
3. ProceedingJoinPoint 中有特殊的方法proceed();
4. 如果使用"JoinPoint" 则必须位于参数的第一位

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值