【Spring】---- JDK动态代理

目录

1. 什么是JDK动态代理

2. JDK动态代理简单实例

3. Spring的AOP部分

        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);
        }
    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值