目录
1. 什么是JDK动态代理
2. JDK动态代理简单实例
3. Spring的AOP部分
一、什么是JDK动态代理?
1. JDK动态代理的实现是在运行时,根据一组接口定义,使用Proxy、InvocationHandler等工具类去生成一个代理类和代理类实例。二、JDK动态代理简单实例
public class DynamicProxyTest {
interface IHello{
void sayHello();
}
// Hello为目标对象,也称为被代理类对象
static class Hello implements IHello{
@Override
public void sayHello() {
System.out.println("hello world");
}
}
// 为了演示方法,吧InvocationHandler实现类写成静态内部类,上面也是.
static class InvocationHandlerImpl implements InvocationHandler{
private Object obj;
// 构造此函数的目的是为了接收被代理类对象->Hello
public InvocationHandlerImpl(Object obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 对被代理类进行功能扩展
System.out.println("welcome");
// 执行被代理类本就执行的方法
return method.invoke(obj, args);
}
}
public static void main(String[] args) {
Hello hello = new Hello();
// InvocationHandler是由代理实例的调用处理程序实现的接口
// 每个代理实例都有一个关联的调用处理程序。当在代理实例上调用方法时,方法调用将被编码并分派到其调用处理程序的invoke方法。
InvocationHandler invocationHandler = new InvocationHandlerImpl(hello);
/**
* 第一个参数ClassLoader loader:被代理类(Hello)的类加载器
* 第二个参数Class<?>[] interfaces:被代理类(Hello)实现的接口
* 第三个参数InvocationHandler h:InvocationHandler对象,拓展写在这里面
* 该方法返回一个实现了IHello的接口,并且代理了new Hello()实例行为的对象
* 该方法最后生成了"$Proxy().class"文件,将该文件反编译
* 可知 public final class $Proxy() extends Proxy implements DynamicProxyTest.IHello
*/
IHello iHello = (IHello) Proxy.newProxyInstance(Hello.class.getClassLoader(), Hello.class.getInterfaces(), invocationHandler);
iHello.sayHello();
}
}
三、Spring的AOP部分
1. 概念
Aspect Oriented Programming:面向切面编程
2. 解决什么问题
软件有个概念:关注点分离,Aop就是关注点分离这种思想的技术实现,不同的问题交给专门的部分来解决,每一部分只处理自己的部分,这样就可以将与业务逻辑代码无关的而又通用的功能提取出来[事务、日志、异常处理、缓存],实现了高内聚、低耦合,便于项目的后期维护
3. 底层原理
主要是由JDK动态代理或者CGLIB代理实现的,这里先讲下JDK动态代理,以后再补充。
4.代码
4.1 导入依赖jar包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.0.0.RELEASE</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
4.2 配置beans.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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--开启扫描包-->
<context:component-scan base-package="cn.whc.March_30"/>
<!--开启基于注解的AOP支持-->
<aop:aspectj-autoproxy/>
</beans>
4.3 接口类、实现类、切面(Aspect)
public interface Caculator {
int add(int i, int j);
}
// 被代理类
@Component
public class CaculatorImpl implements Caculator {
public int add(int i, int j) {
int result = i + j;
System.out.println("目标add方法执行了");
return result;
}
}
// @Aspect:加入切面
@Component
@Aspect
public class LogAspect {
// 前置通知 切入点表达式
@Before("execution(public int cn.whc.March_30.CaculatorImpl.add(int, int))")
public void beforeLog(){
System.out.println("在目标方法执行之前执行");
}
}
4.4 测试
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
Caculator caculator = context.getBean(Caculator.class);
caculator.add(1, 2);
}
4.5 输出结果
注意:在获取容器中的对象时,应区别以下情况,加了@Aspect注解后会有所不同
1)Caculator caculator = context.getBean(Caculator.class); //对
2)Caculator caculator = context.getBean(CaculatorImpl.class); //错
3)CaculatorImpl caculator = context.getBean(CaculatorImpl.class); //错
4)Object proxy = context.getBean("caculatorImpl"); // 对
原因
(1)对:因为代理类属于接口类型,它实现了接口类
(2)和(3)错:找不到对应的类
(4)对:是因为获取代理类,代理类对象在容器中的id或者name值是被代理类类名首字母小写
System.out.println(proxy);// cn.whc.March_30.CaculatorImpl@2a798d51
System.out.println(proxy.getClass()); // class com.sun.proxy.$Proxy8
打印结果可以看出,得到的类还是代理类,你会疑惑为什么proxy打印结果是CaculatorImpl的toString方法,那是因为从容器中获取的是代理类,我们可以从代理类 $ Proxy().class 的Class文件,反编译得到$Proxy()中的toString方法
public final String toString() {
// 所以我们得到的是被代理类的toString方法
try {
return (String)this.h.invoke((Object)this, m2, null);
}
catch (Error | RuntimeException v0) {
throw v0;
}
catch (Throwable var1_1) {
throw new UndeclaredThrowableException(var1_1);
}
}