Spring AOP

代理

通过代理类,为原始类增加额外的功能。
好处:利于原始类(目标类)的维护

静态代理

为每一个原始类,编写一个代理类,二者实现相同接口

public class UserServiceProxy implements UserService {
    UserService userService=new UserServiceImpl();
    @Override
    public void login() {
        System.out.println("============before===========");
        userService.login();;
        System.out.println("============after===========");
    }
}

缺点:1)静态类文件太多,不利于管理。2)额外功能扩展性差。

AOP

面向切面编程:本质就是spring的动态代理开发,通过代理类增加额外功能。
以切面为基本单位的程序开发,通过切面间的彼此协同,相互调用,完成程序的构建。
切面=切入点+额外功能。

spring动态代理底层

动态代理:动态创建代理类。使用了动态字节码技术。
底层有两种实现方式JDK 和CGLIB

spring 默认使用jdk方式
springboot 默认使用cglib方式。

也可以自己设置。

<!--基于注解AOP-->
<aop:aspectj-autoproxy proxy-target-class="true"/>
<!--传统AOP-->
<aop:config proxy-target-class="true"/>

JDK

通过接口(实现与原始类相同的接口)才能创建代理类。

public class Main {
    public static void main(String[] args) {
        final UserService userService=new UserServiceImpl();
        InvocationHandler invocationHandler=new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("=======================logging========================");
                return method.invoke(userService,args);
            }
        };
        UserService userServiceProxy=(UserService) Proxy.newProxyInstance(Main.class.getClassLoader(), userService.getClass().getInterfaces(),invocationHandler);
        userServiceProxy.login();

    }
}

CGLIB

通过继承原始类创建的代理类

public static void main(String[] args) {
       UserServiceImpl userServiceImpl=new UserServiceImpl();
       Enhancer enhancer=new Enhancer();
       enhancer.setClassLoader(Main.class.getClassLoader());
       enhancer.setSuperclass(userServiceImpl.getClass());
       enhancer.setCallback(new MethodInterceptor() {
           @Override
           public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
               System.out.println("================before================");
               method.invoke(userServiceImpl,objects);
               System.out.println("================end================");
               return null;
           }
       });
       UserServiceImpl userService=(UserServiceImpl) enhancer.create();
       userService.login();
   }
类加载的作用

1)把字节码文件加载到JVM中
2)创建类的Class对象。

如何获取类加载器

每一个.class文件 自动分配与之对应的ClassLoader

动态字节码技术 直接在jvm中生成代理对象字节码。
需要ClassLoader创建代理类的class对象,因为动态字节码没有对应.class文件,所以JVM不为其分配ClassLoader ,所以需要借用一个。

spring工厂如何加工原始对象

实现BeanPostProcessor

public class BeanPostProcessorImpl implements BeanPostProcessor {
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return Proxy.newProxyInstance(BeanPostProcessorImpl.class.getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("================logging before method=================");
                Object o= method.invoke(bean,args);
                return  o;
            }
        });
    }
}
 <bean id="BeanPostProcessorImpl" class="proxy.BeanPostProcessorImpl"></bean>

Spring 动态代理(Spring AOP)

实现MethodBeforeAdvice

在原始方法前增加额外功能

public class  Before implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
       System.out.println("在原始方法前增强");
    }
}

配置文件

<bean id="userService" class="proxy.UserServiceImpl"></bean>
    <bean id="before" class="proxy.Before"></bean>
    <aop:config>
        <aop:pointcut id="pc" expression="execution(* *(..))"/>
        <aop:advisor advice-ref="before" pointcut-ref="pc"/>
    </aop:config>

方法拦截器(实现MethodInterceptor)

在原始方法前、后、前后、异常是添加额外功能

public class MyMethodInterceptor implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        System.out.println("在原始方法前添加额外功能");
        try{
            methodInvocation.proceed();
        }catch (Exception e){
            System.out.println("在原始方法异常时添加额外功能");
        }
        System.out.println("在原始方法后添加额外功能");
        return null;
    }
}

配置方法同上

基于注解的AOP(只是使用了aspectJ ,实质还是Spring AOP)

@Aspect
public class BaseAspectJ {
    @Pointcut("execution(* *(..))")
    public void pointcut(){}

    @Around(value="pointcut()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("===================loging==============");
        return proceedingJoinPoint.proceed();
    }
}
 <bean id="baseAspectJ" class="proxy.BaseAspectJ"></bean>
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

spring aop 和 aspectJ 对比

1、Spring aop只增强bean对象,aspectJ可增强任何java对象。

public class UserServiceImpl implements UserService , ApplicationContextAware {
    private ApplicationContext applicationContext;
    @Override
    public void login()  {
        System.out.println("登录");
        ((UserService)this.applicationContext.getBean("userService")).subLogin();
       //直接调用this.subLogin(), subLogin()不增强。 因为this不是代理对象,需要getBean获取代理。
    }
    public void subLogin(){
        System.out.println("子登录");
    }
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext=applicationContext;
    }
}

如果你正在维护一个现有的应用(该应用并没有使用Spring框架),AspectJ就将是一个自然的选择了

2、织入时机
1)Aspectj是静态织入:即编译期间织入。编译出来的class文件,字节码就已经被织入了
2)Spring Aop采用的动态织入,即编译后织入或运行时织入
编译后织入:指织入过程只在第一次调用时执行。
运行时织入指根据代码动态运行的中间状态来决定如何操作,每次调用Target的时候都执行。

Spring只支持运行时织入。如果你有多个团队分别开发多个使用Spring编写的模块(导致生成多个jar文件,例如每个模块一个jar文件),并且其中一个团队想要在整个项目中的所有Spring bean(例如,包括已经被其他团队打包了的jar文件)上应用日志通知(在这里日志只是用于加入横切关注点的举例),那么通过配置该团队自己的Spring配置文件就可以轻松做到这一点。之所以可以这样做,就是因为Spring使用的是运行时织入。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值