Spring Aop&proxy到底是什么关系

Spring Aop&proxy到底是什么关系

proxy代理模式,是一种设计模式,具体内容可参见 https://www.cnblogs.com/cenyu/p/6289209.html
Spring Aop,是利用了代理模式在运行期间对植入了增强的代码

  1. 如果Spring bean没有用到aop(即没有aop会作用到当前bean)时,这个时候的bean就是一个普通的bean(利用反射机制创建的) 可以使用
    org.springframework.aop.support.AopUtils.isAopProxy(proxy),来判断是不是代理增强的bean
  2. 可以使用 org.springframework.aop.support.AopUtils.isJdkDynamicProxy(proxy)
    判断是不是JDK动态代理生成的bean
  3. 可以使用
    org.springframework.aop.support.AopUtils.isCglibProxy(proxy)
    判断是不是CGLIB动态代理生成的bean

Aop使用方式1(配置一个包路径作为切入点)

配置一个包路径作为切入点,也就是说被切面的类自己毫无感知

示例代码

package com.subject.spring.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LogAspect {

    public LogAspect() {
        System.out.println("构造器LogAspect加载..." +this);
    }

    @Pointcut("execution(public * com.subject.spring.*.*(..))")
    public void pointcut() {
    }

    //这里的"pointcut()"需要与上面的方法名 pointcut() 保持一致
    @Around("pointcut()")
    public Object aroundMethod(JoinPoint joinPoint) throws Throwable {
        try {
            System.out.println("start。。。");
            return ((ProceedingJoinPoint) joinPoint).proceed();
        } catch (Throwable e) {
            System.out.println("Throwable。。。");
            throw e;
        } finally {
            System.out.println("end。。。");
        }
    }
}

Aop使用方式2(配置一个注解作为切入点)

配置一个注解作为切入点,被切面的类需要在相应的方法中引入

@Aspect
public class ApiMonitorProcess implements BeanPostProcessor {
    private static final Logger LOGGER = LoggerFactory.getLogger(ApiMonitorProcess.class);


    @Pointcut("@annotation(com.jd.o2o.commons.monitor.ApiMonitor)")
    public void pointcut() {
    }

    @Around("pointcut()")
    public Object aroundProcess(ProceedingJoinPoint pjp) throws Throwable {

            Object apiEntity = pjp.getTarget();
            Method method = ((MethodSignature)pjp.getSignature()).getMethod();
            String interfaceName = apiEntity.getClass().getName();
            String apiName = interfaceName + "." + method.getName();
            Object apiRtn = null;
            Throwable e = null;
            long startTime = System.nanoTime();
            long killTime = 0L;
            String killTimeStr = null;

            try {
                apiRtn = pjp.proceed(pjp.getArgs());
            } catch (Throwable var17) {
                e = var17;
            } finally {
                killTime = System.nanoTime() - startTime;
                killTimeStr = numberFormat.format(killTime);
            }

            this.log(interfaceName, apiName, method, pjp.getArgs(), apiRtn, killTimeStr, e);
            if(e != null) {
                throw e;
            } else {
                return apiRtn;
            }
        }
    }


@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface ApiMonitor {
    int mLog() default 0;

    String mKey() default "";

    int mPeriod() default 1;

    MonitorTag[] mTags() default {MonitorTag.Log};
}

使用方式

@ApiMonitor
public String say(String a){
    System.out.println("say a=" + a);
    return a+a;
}

spring aop的完整示例

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

    <!-- 强制使用cglib代理-->
    <aop:aspectj-autoproxy expose-proxy="true"/>

    <context:component-scan base-package="com.subject.spring"  use-default-filters="true">
    </context:component-scan>

    <!--<bean class="com.subject.spring.aop.LogAspect" />-->
</beans>

LogAspect类

package com.subject.spring.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LogAspect {

    public LogAspect() {
        System.out.println("构造器LogAspect加载..." +this);
    }

    @Pointcut("execution(public * com.subject.spring.*.*(..))")
    public void pointcut() {
    }

    //这里的"pointcut()"需要与上面的方法名 pointcut() 保持一致
    @Around("pointcut()")
    public Object aroundMethod(JoinPoint joinPoint) throws Throwable {
        try {
            System.out.println("start。。。");
            return ((ProceedingJoinPoint) joinPoint).proceed();
        } catch (Throwable e) {
            System.out.println("Throwable。。。");
            throw e;
        } finally {
            System.out.println("end。。。");
        }
    }
}

具体被代理的类OneBean

package com.subject.spring;

import org.springframework.stereotype.Service;

@Service
public class OneBean {

    public OneBean() {
        System.out.println("构造器OneBean加载..." +this);
    }


    public String say(String a){
        System.out.println("say a=" + a);
        return a+a;
    }

    public String say2(String a){
        System.out.println("say2 a=" + a);
        return a+a;
    }
}

测试工具类

package com.subject.spring;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.AopProxy;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.aop.support.AopUtils;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringDemo {
    private static final Logger logger = LoggerFactory.getLogger(SpringDemo.class);
    private static  ClassPathXmlApplicationContext classPathXmlApplicationContext;
    public static void main(String[] args){
        logger.info("aaa");
        String paths[] = {"spring-config.xml"};
        classPathXmlApplicationContext=   new ClassPathXmlApplicationContext(paths);
        OneBean bean=(OneBean)classPathXmlApplicationContext.getBean("oneBean");

//        AopProxyUtils.
        boolean a=AopUtils.isAopProxy(bean);
        System.out.println("a=" + a);
        boolean b=AopUtils.isJdkDynamicProxy(bean);
        System.out.println("b=" + b);
        boolean c=AopUtils.isCglibProxy(bean);
        System.out.println("c=" + c);

        bean.say("abcd");
        bean.say2("hhhh");
    }
}

测试结果

构造器LogAspect加载...com.subject.spring.aop.LogAspect@1b59d510
构造器OneBean加载...com.subject.spring.OneBean@535c97e4
构造器OneBean加载...com.subject.spring.OneBean$$EnhancerByCGLIB$$935c4a54@2c8c7d6
是否是代理bean=true
是否是JDK代理bean=false
是否是Cglib代理bean=true
start。。。
say a=abcd
end。。。
start。。。
say2 a=hhhh
end。。。

变数1:如果将上面OneBean类的say2方法加上final修饰后的执行结果呢

即:

public final String say2(String a){
    System.out.println("say2 a=" + a);
    return a+a;
}

测试结果

构造器LogAspect加载...com.subject.spring.aop.LogAspect@3582c132
构造器OneBean加载...com.subject.spring.OneBean@5f0f0625
构造器OneBean加载...com.subject.spring.OneBean$$EnhancerByCGLIB$$fe9e14e2@6482d603
是否是代理bean=true
是否是JDK代理bean=false
是否是Cglib代理bean=true
start。。。
say a=abcd
end。。。
say2 a=hhhh  

变数1结论:被关键字final修饰的方法进行代理时直接忽略代理逻辑,直接执行原始类的方法

变数2:如果将上面OneBean类加上final修饰后的执行结果呢

即:
@Service
public final class OneBean {

public OneBean() {
    System.out.println("构造器OneBean加载..." +this);
}

测试结果

构造器LogAspect加载...com.subject.spring.aop.LogAspect@26b31b77
构造器OneBean加载...com.subject.spring.OneBean@63f8247d
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'oneBean' defined in file [E:\github\javastudy\study-service\target\classes\com\subject\spring\OneBean.class]: Initialization of bean failed; nested exception is org.springframework.aop.framework.AopConfigException: Could not generate CGLIB subclass of class [class com.subject.spring.OneBean]: Common causes of this problem include using a final class or a non-visible class; nested exception is java.lang.IllegalArgumentException: Cannot subclass final class class com.subject.spring.OneBean
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:529)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:458)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:295)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:292)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:628)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:932)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:479)
    at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139)
    at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:93)
    at com.subject.spring.SpringDemo.main(SpringDemo.java:16)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)

变数2结论:被关键字final修饰的类进行代理时直接抛出异常,程序停止运行

变数3:前面的测试结果(是否是Cglib代理bean=true)表明都是基于cglib的,那么如果不在spring-config.xml配置文件中强制指定要求cglib,即

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

    <!-- 强制使用cglib代理-->
    <!--<aop:aspectj-autoproxy expose-proxy="true"/>-->

    <context:component-scan base-package="com.subject.spring"  use-default-filters="true">
    </context:component-scan>

</beans>

测试结果

构造器LogAspect加载...com.subject.spring.aop.LogAspect@1b192059
构造器OneBean加载...com.subject.spring.OneBean@31a3ca10
是否是代理bean=false
是否是JDK代理bean=false
是否是Cglib代理bean=false
say a=abcd
say2 a=hhhh

变数3结论:如果不强制指定cglib,而且当被代理的bean(OneBean)没有实现接口时,这个bean最终是没有经过动态代理的bean,也就是没有被增加代码的bean

猜你喜欢

转载自blog.csdn.net/hl_java/article/details/79445060