AOP学习笔记

Spring-AOP是对AOP框架之一。其他比如还有AspectJ

Aspect-Oriented-Programming(面向切面编程),一种编程思想
切面:Aspect,由切入点额外功能(增强)组成
作用:提供了新的编程角度,不再只是考虑类、对象,而可以考虑切面,切面和目标形成代理,解决项目中额外功能冗余的问题

概念,切面,为什么用aop,好处是什么

1.业务中的问题

业务层存在的问题:两类逻辑=核心业务+额外功能,其中额外功能存在大量的代码冗余,使得项目维护存在极大隐患。

比如下面这样

class UserServiceImpl implements UserService{
	private UserDAO ud;
    public void updateUser(User user){
        System.out.println("事务管理功能");//额外功能 冗余
        ud.update(user); //核心功能
    }
    public void inserUser(User user){
        System.out.println("事务管理功能");//额外功能 冗余
        ud.insertUser(user);//核心功能
    }
    public User queryUser(Integer id){
        System.out.println("事务管理功能");//额外功能 冗余
        ud.queryUser(id);//核心功能
    }
}

2.静态代理

代目标类打理了额外功能
目标类:UserServiceImpl,即,被代理的类
代理类原则:要和原始的业务(target)实现同样的接口,保持功能一致。
代理类组成:额外功能(Advice)+目标(Target)
解决了目标类的冗余问题,但自身却仍然有冗余

class UserServiceProxy implements UserService{//代理类
    UserService us=new UserServiceImpl();
    public void updateUser(User user){
        System.out.println("事务管理功能"); //代理类负责额外功能
        us.updateUser(user); // 目标自己负责核心功能
    }
    public void inserUser(User user){ 
        System.out.println("事务管理功能");//代理类负责额外功能
        us.insertUser(user);// 目标自己负责核心功能
    }
    public User queryUser(Integer id){ 
        System.out.println("事务管理功能");//代理类负责额外功能
        us.queryUser(user);// 目标自己负责核心功能
    }
}
// 创建代理对象,完成业务
UserService userService = new UserServiceProxy();
userService.insertUser(user);

3.动态代理

通过动态字节码结束,在运行时动态生成代理(反射)
则既不用维护代理类,又可以有代码打理额外功能

动态代理实现方案

  • jdk代理(jdk在反射包中提供的一套ap)通过和目标实现相同的接口保证功能一致
  • cglib代理(第三方cglib库中的一套api)通过过继承目标保证功能一致

jdk代理代码如下

		// JDK动态代理
        //1.目标
        UserService userService = new UserServiceImpl();
        //2.额外功能
        InvocationHandler ih = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("额外功能");
                method.invoke(userService,args);//执行核心功能
                //userService.QueryOne();
                return null;
            }
        };
        //3.组装,编制
        UserService proxy= (UserService) Proxy.newProxyInstance(AppTest.class.getClassLoader(), userService.getClass().getInterfaces(), ih);

CGLib代理

// CGLIB动态代理

//目标
final UserService us = new UserServiceImpl();
//额外功能
MethodInterceptor mi = new MethodInterceptor(){
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) 
        throws Throwable {
        System.out.println("###   before invocation");
        Object result = method.invoke(us, objects);
        System.out.println("###   end invocation");
        return result;
    }
}

Enhancer enhancer = new Enhancer();
// 设置需要代理的对象
enhancer.setSuperclass(us.getClass());
// 设置代理人
enhancer.setCallback(mi);
UserServiceImpl proxy = (UserServiceImpl)enhancer.create();

Spring的AOP章节,底层采纳如上两种代理实现,并对动态代理提供了简单的,可操作性强的决绝方案。
当项目中需要使用代理解决问题时,可以采用AOP张杰的内容加以解决
org.springframework.aop.framework.DefaultAopProxyFactory#createAopProxy()中spring做了jdk代理和cglib代理的选择。

AOP 编码流程

首先导入依赖:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-support</artifactId>
    <version>4.3.6.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>4.3.6.RELEASE</version>
</dependency>
<!-- spring-aspects 会传递导入 
     <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.8.9</version>
      <scope>compile</scope>
    </dependency>
 	而aspectweaver 即 AspectJ 的一个库,(可以github搜索“AspectJ”,查看在Github上的镜像)。
	spring-AOP模块,并没有独立提供完整的AOP实现,而是集成了【AspectJ框架(AOP框架)】
	spring-aspects就是负责集成AspectJ。
-->
<!-- AOP联盟,可省略,spring4.3.6中的 aop模块中添加了其中的api( 兼容了AOP联盟 )
<dependency>
    <groupId>aopalliance</groupId>
    <artifactId>aopalliance</artifactId>
    <version>1.0</version>
</dependency>
-->

4.1 准备Target

public class UserServiceImpl implements UserService{
    private UserDAO userDAO;
    // set/get...
    @Override
    public void updateUser(User user) {
        System.out.println("update in service===============");
        userDAO.updateUser(user);
    }
    @Override
    public void insertUser(User user) {
        System.out.println("insert in service===============");
        userDAO.insertUser(user);
    }
}

4.2 准备Advice

这里用的是前置额外功能

public class MyBeforeAdvice implements MethodBeforeAdvice{

    /**
     * @param method 当前执行的方法
     * @param args   当前执行的方法中的参数
     * @param target 目标对象
     * @throws Throwable
     */
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("before~~~");
    }
}

4.3 编织 Weave

所谓编织,即,将Target和Advice组装形成代理
当然组装过程由Spring管理,开发者只需要做出配置,告知Spring需要组装谁即可
先声明目标和额外功能(advice)在编织的配置中加入切入点和目标、额外功能(advice)的ref

<!-- 声明 Target + Advice -->
<!-- 声明 Target -->
<bean id="userService" class="com.zhj.service.UserServiceImpl">
    <!-- 为userDAO属性赋值,值为id=“userDAO”的组件 -->
    <property name="userDAO" ref="userDAO"/>
</bean>
<!-- Advice -->
<bean id="myBefore" class="com.zhj.advice.MyBeforeAdvice"/>

<!-- 编织 配置 -->
<aop:config>
        <!-- 切入点=pointcut
 			execution()表达式:描述切入位置
            组成:修饰符   返回值      包      类  方法名  参数表
                 public Integer com.xx.xxx.AA.xxxXXX(int,String)
            * com.service.UserServiceImpl.*(..):com.service包下UserServiceImpl类中,返回值修饰符任意,方法名任意,
												 参数表任意
            * com.service.UserServiceImpl.queryUser(..):同上,只是方法名不是任意,而是 ”queryUser“
		-->
        <aop:pointcut id="pc" expression="execution(* com.service.UserServiceImpl.queryUser(..))"/>
        <aop:advisor advice-ref="myBefore" pointcut-ref="pc"/>
    </aop:aspect>
</aop:config>

4.4 测试

//获取代理对象,通过目标的Beanid,即可获得,将目标转换为代理对象主要由后处理器后置过程完成
UserService userService = (UserService)context.getBean("userService");
userService.insertUser(new User(...));

4.5 多种Advice

advice有以下几类:
前置额外功能:MethodBeforeAdvice
后置额外功能:AfterReturningAdvice
环绕额外对象:MethodInterceptor
异常额外对象:ThrowAdvice(了解,相当于try…catch中的catch)
最终额外对象:在核心执行之后(即使核心出现了异常,依然执行额外,相当于try…catch…finally中的finally)

4.5.1 前置额外功能

前置额外功能会在目标核心功能执行之前执行

public class MyBeforeAdvice implements MethodBeforeAdvice{
    /**
     * 主体逻辑,在核心功能之前执行
     * @param method  当前执行的方法对象
     * @param args   方法的参数
     * @param target  目标对象
     * @throws Throwable
     */
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("Before04~~~,method:"+method.getName()+" args:"+args.length+" target:"+target);
    }
}

4.5.2 后置额外功能

后置额外功能会在目标核心功能执行之后执行

public class MyAfterAdvice implements AfterReturningAdvice{
    /**
     * 在 核心功能返回之后执行
     * @param returnValue 核心功能返回值
     * @param method  方法对象
     * @param args   方法的参数表
     * @param target  目标对象
     * @throws Throwable
     */
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("After ~~ ret:"+returnValue+" method:"+method.getName());
    }
}

4.5.3 环绕额外功能

环绕额外功能会在目标核心功能执行前后都执行

public class MyMethodInterceptor implements MethodInterceptor {
    /**
     *
     * @param invocation
     * @return
     * @throws Throwable
     */
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("beigin tx~~~");
        Object ret = invocation.proceed();//执行目标的核心功能
        System.out.println("end tx~~~~");
        return ret;//业务结果,向上返回
    }
}

4.5.4 异常额外功能

public class MyThrows implements ThrowsAdvice{
    // 当核心功能中抛出异常时执行,  ex=异常对象
    public void afterThrowing(Exception ex){
        //ex.printStackTrace();
        System.out.println("出现异常啦~~~~:"+ex.getMessage());
    }
}

4.5.5 编织

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

    <!-- 目标 -->
    <bean id="userService" class="com.qianfeng.service.UserServiceImpl"></bean>
    <bean id="userService2" class="com.qianfeng.service.UserServiceImpl2"></bean>
    <!-- 额外功能:Advice -->
    <bean id="before04" class="com.qianfeng.advice.MyBeforeAdvice"></bean>
    <bean id="before05" class="com.qianfeng.advice.MyBeforeAdvice2"></bean>
    <bean id="log04" class="com.qianfeng.advice.MyLogAdvice"></bean>
    <bean id="after04" class="com.qianfeng.advice.MyAfterAdvice"></bean>
    <bean id="mi04" class="com.qianfeng.advice.MyMethodInterceptor"></bean>
    <bean id="myThrows04" class="com.qianfeng.advice.MyThrows"></bean>

    <!-- 编织
         通过目标的信息,额外功能的信息,组件一个新的类:Proxy
     -->
    <aop:config proxy-target-class="false">
        <!-- 切入点:目标中方法
             execution表达式
             execution(修饰符 返回值 包.类.方法名(参数表))
             execution(public Integer com.qianfeng.service.UserServiceImpl.queryUser(Integer,com.qianfeng.pojo.User))
             execution(* com.qianfeng.service.UserServiceImpl.*(..))
         -->
        <aop:pointcut id="pc04" expression="execution(* com.qianfeng.service.UserServiceImpl.*(..))"/>
        <aop:pointcut id="pc05" expression="execution(* com.qianfeng.service.UserServiceImpl.queryOne(..))"/>
        <aop:pointcut id="pc06" expression="execution(* com.qianfeng.service.UserServiceImpl2.*(..))"/>
        <aop:pointcut id="pc07" expression="execution(* com..UserServiceImpl.insertUser(..))"/>
        <aop:pointcut id="pc08" expression="execution(* com..*.*(..))"/>
        <aop:pointcut id="pc09" expression="execution(* com..*.*User(..))"/>
        <aop:pointcut id="pc10" expression="within(com.qianfeng..UserServiceImpl)"/>
        <aop:pointcut id="pc11" expression="within(com.qianfeng..UserServiceImpl) or not args(com.qianfeng.pojo.User)"/>
        <!-- 将某个额外功能,编织到某些切入点中 -->
        <!-- order值越小,切入顺序就越优先 -->
        <aop:advisor advice-ref="before05" pointcut-ref="pc05" order="2"/>
        <aop:advisor advice-ref="before04" pointcut-ref="pc05" order="1"/>
        <!--<aop:advisor advice-ref="before04" pointcut-ref="pc05"/>
        <aop:advisor advice-ref="before04" pointcut-ref="pc06"/>
        <aop:advisor advice-ref="log04" pointcut-ref="pc04"/>
        <aop:advisor advice-ref="after04" pointcut-ref="pc04"/>
        <aop:advisor advice-ref="mi04" pointcut-ref="pc11"/>
        <aop:advisor advice-ref="myThrows04" pointcut-ref="pc08"/>-->
    </aop:config>

    <!--
        class XXProxy implements UserService{
            private UserService target=xxxx;
            @Override
            public Integer insertUser(User user) {
                前置日志额外
                环绕-前
                target.insertUser(id);
                环绕-后
                后置额外
            }

            @Override
            public User queryOne(Integer id) {
                前置日志额外
                前置额外
                target.queryOne(id);
                后置额外
            }
        }
    -->
</beans>

4.6 切入点表达式

4.6.1 execution

1> * com.service.UserServiceImpl.queryUser(..)
    修饰符:任意
    返回值:任意
    包:com.service
    类:UserServiceImpl
    方法:queryUser
    参数表:任意
2> * com.service.UserServiceImpl.*(..)
    修饰符:任意
    返回值:任意
    包:com.service
    类:UserServiceImpl
    方法:所有,任意
    参数表:任意
3> * com..UserServiceImpl.*(..)
    修饰符:任意
    返回值:任意
    包:com包,及其子包
    类:UserServiceImpl
    方法:所有,任意
    参数表:任意
4> * com.service.*.*(..)
    修饰符:任意
    返回值:任意
    包:com.service
    类:所有,任意
    方法:所有,任意
    参数表:任意
5> * *(..)    不建议
    修饰符:任意
    返回值:任意
    包:任意
    类:所有,任意
    方法:所有,任意
    参数表:任意
6> * com.service.UserServiceImpl.query*(..)  【技巧:批量切入】
    修饰符:任意
    返回值:任意
    包:com.service
    类:UserServiceImpl
    方法:所有,任意
    参数表:任意
  *注意:尽量精确,避免不必要的切入

4.6.2 within

描述中所有方法都切入

within(com.service.UserServiceImpl) 类中的所有方法
within(com..UserServiceImpl) com包和com子包下的类中的所有方法

<aop:pointcut id="pc" expression="within(com..UserServiceImpl)"/>

4.6.3 args

描述参数表,符合的方法都切入

args(int,String,com.entity.User) 参数表如此的方法

<aop:pointcut id="pc" expression="args(int,String,com.entity.User)"/>

4.6.4 联用

不同种类的表达式之间,可以使用逻辑运算:

and or not

<aop:pointcut id="pc" expression="execution(* com.zhj.service.UserServiceImpl.*(..)) and args(com.User)"/>
<aop:pointcut id="pc" expression="within(com.service.UserServiceImpl) or args(com.User)"/>
<aop:pointcut id="pc" expression="within(com.service.UserServiceImpl) and not args(com.User)"/>

5. AOP底层细节

5.1 后处理器 BeanPostProcessor

/**
 * 定义bean后处理器
 * 作用:**在bean的创建之后,进行再加工**
 */
public class MyBeanPostProcessor implements BeanPostProcessor{

    /**
     *
     * @param bean  原始的bean对象
     * @param beanName  beanid
     * @return
     * @throws BeansException
     */
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    	//一般后处理器前置过程不作处理
        System.out.println("后处理器 在init之前执行~~~"+bean.getClass());
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    	//后处理器后置过程才是真正处理的地方
        System.out.println("后处理器 在init之后执行~~~"+bean.getClass());
        return bean;// 此处的返回是 getBean() 最终的返回值
    }
}
<!-- 后处理器,将对工厂中所有的bean声明周期进行干预 -->
<bean class="com.qianfeng.beanpostprocessor.MyBeanPostProcessor"></bean>

5.2 Bean的声明周期

构造注入属性 满足依赖初始化返回销毁

完整周期

构造注入属性 满足依赖后处理器前置过程初始化后处理器后制过程返回销毁

bean生命周期部分源码
#前是类名,#后是方法名

AbstractAutowireCapableBeanFactory#doCreateBean(){
    if (instanceWrapper == null) {
        //创建Bean对象
        instanceWrapper = createBeanInstance(beanName, mbd, args);
    }
    ...
   	try {
       	//完成注入
        populateBean(beanName, mbd, instanceWrapper);
        if (exposedObject != null) {
            //执行初始化过程
            exposedObject = initializeBean(beanName, exposedObject, mbd);
        }
    }
}
AbstractAutowireCapableBeanFactory#initializeBean(){
    //....
    if (mbd == null || !mbd.isSynthetic()) {
        // 后处理器的 前置过程
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }

    try {
        // 调用初始化方法
        invokeInitMethods(beanName, wrappedBean, mbd);
    }
    catch (Throwable ex) {
        throw new BeanCreationException(
            (mbd != null ? mbd.getResourceDescription() : null),
            beanName, "Invocation of init method failed", ex);
    }

    if (mbd == null || !mbd.isSynthetic()) {
        // 调用后处理器的 后置过程
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }
}
AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization(){
    //....
    // 遍历所有后处理器,对bean做后处理
    for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
        result = beanProcessor.postProcessAfterInitialization(result, beanName);
        if (result == null) {
            return result;
        }
    }
}

5.3 动态代理构建

构件时刻,在bean的后处理器的后置过程中,会将基于bean定制一个他的代理类(对象)

部分源码

// 工厂启动时,会注册一些 后处理器,其中就包含 AspectJAwareAdvisorAutoProxyCreator
AspectJAwareAdvisorAutoProxyCreator extends AbstractAdvisorAutoProxyCreator{
    ...
}
AbstractAdvisorAutoProxyCreator extends AbstractAutoProxyCreator {
    ...
}
// AbstractAutoProxyCreator是 AspectJAwareAdvisorAutoProxyCreator的父类
// 该父类中定义了 后处理器的功能实现,有父类发起动态代理的定制
AbstractAutoProxyCreator#postProcessAfterInitialization(Object bean, String beanName){
    if (!this.earlyProxyReferences.contains(cacheKey)) {// 开始动态定制代理
        return wrapIfNecessary(bean, beanName, cacheKey);
   	}
}
AbstractAutoProxyCreator#wrapIfNecessary(){
    ....
    Object proxy = createProxy(
					bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
}
AbstractAutoProxyCreator#createProxy(){
    ....
    return proxyFactory.getProxy(getProxyClassLoader());
}
ProxyFactory#getProxy(ClassLoader classLoader) {
    // 最后调用:JdkDynamicAopProxy.getProxy();或 ObjenesisCglibAopProxy.getProxy()
    // 获取代理对象
    return createAopProxy().getProxy(classLoader);
}
ProxyCreatorSupport#createAopProxy() {
    ...
	// DefaultAopProxyFactory.createAopProxy(this)
	// 获取创建代理对象的 对象 ,返回JdkDynamicAopProxy 或 ObjenesisCglibAopProxy
	return getAopProxyFactory().createAopProxy(this);
}
DefaultAopProxyFactory#createAopProxy(){
    // 判断决定使用 jdk代理 还是 cglib代理,
    // 会最终返回 JdkDynamicAopProxy 或 ObjenesisCglibAopProxy
    // JdkDynamicAopProxy 或 ObjenesisCglibAopProxy的 getProxy方法最终返回代理对象
    // 如果需要优化,如果设置了proxy-target-class="true" 如果目标类没有接口
	if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
        Class<?> targetClass = config.getTargetClass();
        if (targetClass == null) {
            throw new AopConfigException("...");
        }
        // 如果目标是接口,如果目标已经是一个代理
        if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
            return new JdkDynamicAopProxy(config);
        }
        return new ObjenesisCglibAopProxy(config);
    }
    else {
        return new JdkDynamicAopProxy(config);
    }
}
JdkDynamicAopProxy#getProxy(ClassLoader classLoader) {
    ...
    return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

5.3 切入顺序

<!-- order值越小,切入顺序就越优先 -->
<aop:advisor advice-ref="before05" pointcut-ref="pc05" order="2"/>
<aop:advisor advice-ref="before04" pointcut-ref="pc05" order="1"/>

5.4 cglib选择

<!-- 强制使用cglib代理 -->
<aop:config proxy-target-class="true">
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值