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()方法。
-
主要是生成的代理对象跟被代理对象不是同一个,所以后者调用类方法就像是直接调用,不会走切面。
-
代码已经在GitHub中更新,更多详情可关注dwyanewede。更多JDK动态代理,可参见上篇文章:JDK动态代理实现原理
更多优秀文章
JDK动态代理实现原理
https://blog.csdn.net/shang_xs/article/details/92772437
java界的小学生
https://blog.csdn.net/shang_xs