JDK动态代理在Spring AOP中的实现

动态代理机制

  • 通过实现 InvocationHandler 接口创建自己的调用处理器
  • 通过为 Proxy 类指定 ClassLoader 对象和一组 interface 来创建动态代理类
  • 通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型
  • 通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入

AOP思想

  • OOP中引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。
  • AOP技术利用一种称为“横切”的技术,解剖封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,这样就能减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。
  • AOP把软件系统分为两个部分:核心关注点和横切关注点,业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。

动态代理源码实现 AopProxyFactory

public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {

	@Override
	public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
		if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
			Class<?> targetClass = config.getTargetClass();
			if (targetClass == null) {
				throw new AopConfigException("TargetSource cannot determine target class: " +
						"Either an interface or a target is required for proxy creation.");
			}
			if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
				// 表示在有接口实现的时候采用JDK动态代理
                return new JdkDynamicAopProxy(config);
			}
            // 在没有接口实现的时候采用Cglib动态代理
			return new ObjenesisCglibAopProxy(config);
		}
		else {
			return new JdkDynamicAopProxy(config);
		}
	}

Spring中AOP的实现

程序自上而下执行,与主业务逻辑的关系不大的横切性问题,aop面向切面编程,就是将主业务与横切代码分离,做到解耦。

常见实现有:

  • 统一日志处理
  • 统一异常处理
  • Spring事务管理

代码实现

定义接口

/**
 * @ClassName: BuyService
 * @Description: 购买基础接口
 * @Author: 尚先生
 * @CreateDate: 2019/6/17 14:52
 * @Version: 1.0
 */
public interface BuyService {

    String buyPhone(BuyService buyService);

    String buyComputer(BuyService buyService);

}

定义实现类

/**
 * @ClassName: BusiServiceImpl
 * @Description: 购买实现类
 * @Author: 尚先生
 * @CreateDate: 2019/6/17 14:53
 * @Version: 1.0
 */
@Service
public class BuyServiceImpl implements BuyService {

    @Intercept("buyPhone")
    @Override
    public String buyPhone(BuyService buyService) {
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("==========当前类描述 " + buyService.getClass().getName() + "=============" + " buyPhone");
        this.buyComputer(this);
        return "buy phone";
    }

    @Intercept("buyComputer")
    @Override
    public String buyComputer(BuyService buyService) {
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("==========当前类描述 " + buyService.getClass().getName() + "=============" + " buyComputer");
        return "buy computer";
    }
}

自定义拦截注解

/**
 * @ClassName: Intercept
 * @Description: 自定义拦截器注解
 * @Author: 尚先生
 * @CreateDate: 2019/6/17 16:28
 * @Version: 1.0
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Intercept {

    String value() default "";
}

自定义拦截器实现

/**
 * @ClassName: Interceptor
 * @Description: 拦截器实现类
 * @Author: 尚先生
 * @CreateDate: 2019/6/17 15:12
 * @Version: 1.0
 */
@Component
@Aspect
public class Interceptor {
    @Pointcut(value = "@annotation(com.learn.demo.java.proxy.Intercept)")
    public void buySomething() {
        System.out.println("===========自定义切入点===============");
    }
    @Around("buySomething()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        try {
            //通过获取 Intercept 注解
            Method proxyMethod = ((MethodSignature) point.getSignature()).getMethod();
            Method targetMethod = point.getTarget().getClass().getMethod(proxyMethod.getName(), proxyMethod.getParameterTypes());
            Intercept intercept = targetMethod.getAnnotation(Intercept.class);
            String methodName = targetMethod.getName();
            //处理注解逻辑
            String value = intercept.value();
            System.err.println("=========== " + methodName + " 获取前置拦截信息 ===========" + value);
            return point.proceed();
        } catch (Throwable e) {
            System.out.println("执行异常"+ e.getMessage());
        }finally {
            System.err.println("=========== " + " 后置处理结果返回 ===========");
        }
        return "执行异常,请查看详细日志信息";
    }
}

自定义拦截器配置类

/**
 * @ClassName: AspectJConfig
 * @Description: 开启Spring对AspectJ的支持
 * @Author: 尚先生
 * @CreateDate: 2019/6/17 18:39
 * @Version: 1.0
 */
@Configuration
@ComponentScan("com.learn.demo.java.proxy")
@EnableAspectJAutoProxy
public class AspectJConfiguration {
}

启动引导类

/**
 * @ClassName: Bootstrap
 * @Description: 启动测试类
 * @Author: 尚先生
 * @CreateDate: 2019/6/17 14:58
 * @Version: 1.0
 */
public class Bootstrap {
    public static void main(String[] args) {
        // spring 采用的 jdk 动态代理
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();

        context.scan("com.learn.demo.java.proxy");

        context.register(Interceptor.class);

        context.refresh();

        BuyService bean = context.getBean("buyServiceImpl", BuyService.class);

        String phone = bean.buyPhone(bean);

        System.err.println("=========Bootstrap.class============== " + phone);

        // 输出代理对象 class 文件
        createProxyClassFile();
    }

    /**
     * 生成代理文件
     */
    private static void createProxyClassFile() {
        String name = "ProxyBuyService";
        byte[] data = ProxyGenerator.generateProxyClass(name,
                new Class[] {BuyService.class});
        FileOutputStream out = null;
        try {
            out = new FileOutputStream("D://" + name + ".class");
            out.write(data);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (null != out)
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
        }
    }
}

执行结果

=========== buyPhone 获取前置拦截信息 ===========buyPhone
==========当前类描述 com.sun.proxy.$Proxy22============= buyPhone
===========  后置处理结果返回 ===========
=========Bootstrap.class============== buy phone
==========当前类描述 com.learn.demo.java.proxy.BuyServiceImpl============= buyComputer

流程分析

  • 从执行结果中可以明确看到在buyPhone()中执行的对象com.sun.proxy.$Proxy22,而后执行this.buyComputer(this);执行的对象变为com.learn.demo.java.proxy.BuyServiceImpl,所以在面向切面编程时只会拦截buyPhone()方法。
  • 主要是生成的代理对象跟被代理对象不是同一个,所以后者调用类方法就像是直接调用,不会走切面。