2024.3.17@spring框架学习笔记

P89  end
P90 JdbcTemplate之回调函数
其它类中的内容与上集相同
测试类中的内容:

@Test
public void testCallback(){
    //如果你想写JDBC代码,可以使用callback回调函数。
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
    JdbcTemplate jdbcTemplate = applicationContext.getBean("jdbcTemplate", JdbcTemplate.class);
    //准备SQL语句
    String sql = "select id, real_name, age from t_user where id = ?";
    //注册回调函数,当execute方法执行的时候,回调函数中的doInPreparedStatement()会被调用。
    User user = jdbcTemplate.execute(sql, new PreparedStatementCallback<User>() {
        @Override
        public User doInPreparedStatement(PreparedStatement ps) throws SQLException, DataAccessException {
            User user = null;
            ps.setInt(1,2);
            ResultSet rs = ps.executeQuery();
            if (rs.next()) {
                int id = rs.getInt("id");
                String realName = rs.getString("real_name");
                int age = rs.getInt("age");
                user = new User(id,realName,age);
            }

            return user;
        }
    });
    System.out.println(user);

}
P90  end
P91  JdbcTemplate之整合德鲁伊连接池
P91  start :
使用德鲁伊连接池需要引入德鲁伊连接池的依赖。(毕竟别人写的阿里巴巴开发的)
<!--引入德鲁伊连接池 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.8</version>
</dependency>

spring配置文件spring.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--配置自己写的数据源 -->
    <!--当然,也可以集成其他人或者其他组织开发的数据源,例如:c3p0  dbcp druid -->
<!--    <bean id="ds" class="com.powernode.spring6.bean.MyDataSource">-->
<!--        <property name="driver" value="com.mysql.cj.jdbc.Driver"/>-->
<!--        <property name="url" value="jdbc:mysql://localhost:3306/spring6"/>-->
<!--        <property name="username" value="root"/>-->
<!--        <property name="password" value="123456"/>-->
<!--    </bean>-->


    <!--引入德鲁伊连接池 -->
    <bean id="ds" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/spring6"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>


    <!--配置JdbcTemplate -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="ds"/>
    </bean>

</beans>
测试类与上集相同

P91  end
P92 GoF代理模式之对代理模式的深入理解
P92 start  :
- 代理模式的作用:
- 1)当一个对象需要受到保护时,可以考虑使用代理模式去完成某个行为
- 2)需要给某个对象的功能进行增强时,可以考虑找一个代理进行增强
- 3)A 对象和 B 对象无法直接进行交互时,也可以使用代理模式来解决
什么时候使用代理:
1,在程序中,对象A和对象B无法直接交互时
2,在程序中,功能需要增强时。
3,在程序中,目标需要被保护时
4,在程序中,目标不但受到保护,并且代码也得到了复用
代理模式是GoF23种设计模式之一。属于结构型设计模式。
代理模式中的角色:
- 代理类(代理主题)
- 目标类(真实主题)
- 代理类和目标类的公共接口(抽象主题):客户端在使用代理类时就像在使用目标类,不被客户端所察觉,所以代理类和目标类要有共同的行为,也就是实现共同的接口。
代理模式在代码实现上,包括两种形式
- 静态代理
- 动态代理
OrderService接口中的内容:
package com.powernode.proxy.service;

/**
 * 订单业务接口
 */
public interface OrderService {
    /**
     * 生成订单
     */
    void generate() throws InterruptedException;

    /**
     * 修改订单信息
     */
    void modify();
    /**
     * 查看订单详情
     */
    void detail();
}


OrderServiceImpl类中的内容:
package com.powernode.proxy.service;

public class OrderServiceImpl implements OrderService{

    /**
     * 项目经理提出一个新的需求:要统计所有业务接口中每一个业务方法的耗时。
     * 解决方案一:硬编码,在每一个业务接口中的每一个业务方法中直接添加统计耗时的程序。
     * 这种方案的缺点:
     *         缺点一:违背OCP开闭原则。
     *         缺点二:代码没有得到复用。(相同的代码写了很多遍)
     * 解决方案二:编写业务类的子类,让子类继承业务类,对每个业务方法进行重写。
     *          缺点一:虽然解决了OCP开闭原则。但是这种方式会导致耦合度很高,因为采用了继承关系。
     *              继承关系是一种耦合度非常高的关系,不建议使用。
     *          缺点二:代码没有得到复用。(相同的代码写了很多遍)
     * 解决方案三:代理模式。
     */

//    @Override
//    public void generate(){
//        long begin = System.currentTimeMillis();
//        //模拟生成订单的耗时
//        try{
//            Thread.sleep(1234);
//        }catch(InterruptedException e){
//            e.printStackTrace();
//        }
//
//        System.out.println("订单已生成.");
//        long end = System.currentTimeMillis();
//        System.out.println("耗时" + (end - begin) + "耗时");
//    }
//
//    @Override
//    public void modify() {
//        long begin = System.currentTimeMillis();
//        //模拟修改订单的耗时
//        try{
//            Thread.sleep(456);
//        }catch(InterruptedException e){
//            e.printStackTrace();
//        }
//        System.out.println("订单已修改.");
//        long end = System.currentTimeMillis();
//        System.out.println("耗时" + (end - begin) + "耗时");
//    }
//
//    @Override
//    public void detail() {
//        long begin = System.currentTimeMillis();
//        //模拟查询订单的耗时
//        try{
//            Thread.sleep(111);
//        }catch(InterruptedException e){
//            e.printStackTrace();
//        }
//        System.out.println("请查看订单详情.");
//        long end = System.currentTimeMillis();
//        System.out.println("耗时" + (end - begin) + "耗时");
//    }


    @Override
    public void generate(){

        //模拟生成订单的耗时
        try{
            Thread.sleep(1234);
        }catch(InterruptedException e){
            e.printStackTrace();
        }

        System.out.println("订单已生成.");

    }

    @Override
    public void modify() {

        //模拟修改订单的耗时
        try{
            Thread.sleep(456);
        }catch(InterruptedException e){
            e.printStackTrace();
        }
        System.out.println("订单已修改.");

    }

    @Override
    public void detail() {

        //模拟查询订单的耗时
        try{
            Thread.sleep(111);
        }catch(InterruptedException e){
            e.printStackTrace();
        }
        System.out.println("请查看订单详情.");
    }


}


OrderServiceImplSub类中的 内容:
package com.powernode.proxy.service;

public class OrderServiceImplSub extends OrderServiceImpl{
    @Override
    public void generate() {
        long begin = System.currentTimeMillis();
        super.generate();
        long end = System.currentTimeMillis();
        System.out.println("耗时" + (end - begin) + "毫秒");
    }

    @Override
    public void modify() {
        long begin = System.currentTimeMillis();
        super.modify();
        long end = System.currentTimeMillis();
        System.out.println("耗时" + (end - begin) + "毫秒");
    }

    @Override
    public void detail() {
        long begin = System.currentTimeMillis();
        super.detail();
        long end = System.currentTimeMillis();
        System.out.println("耗时" + (end - begin) + "毫秒");
    }
}


测试类中的内容:
package com.powernode.proxy.client;

import com.powernode.proxy.service.OrderService;
import com.powernode.proxy.service.OrderServiceImpl;
import com.powernode.proxy.service.OrderServiceImplSub;

public class Test {
    public static void main(String[] args) throws InterruptedException {
//        OrderService orderService = new OrderServiceImpl();
//        orderService.generate();
//        orderService.detail();
//        orderService.modify();


        OrderService orderService = new OrderServiceImplSub();
        orderService.generate();
        orderService.detail();
        orderService.modify();


    }
}


P93 END
P94 GoF代理模式之静态代理代码实现。
类和类之间的关系:包括6种关系。其中两个关系是:
泛化关系
关联关系
泛化关系:继承 is a(A是B)
关联关系: has a (A有B)
相比来说:泛化关系的耦合度高于关联关系。优先选择使用关联关系。
(我们要面向接口编程)
字节码生成技术是个啥东东???
OrderService接口中的内容:
package com.powernode.proxy.service;

/**
 * 订单业务接口
 */
public interface OrderService {//代理对象和目标对象的公共接口
    /**
     * 生成订单
     */
    void generate();

    /**
     * 修改订单信息
     */
    void modify();
    /**
     * 查看订单详情
     */
    void detail();
}


OrderServiceImpl类中的内容:
package com.powernode.proxy.service;

public class OrderServiceImpl implements OrderService{//目标对象

    /**
     * 项目经理提出一个新的需求:要统计所有业务接口中每一个业务方法的耗时。
     * 解决方案一:硬编码,在每一个业务接口中的每一个业务方法中直接添加统计耗时的程序。
     * 这种方案的缺点:
     *         缺点一:违背OCP开闭原则。
     *         缺点二:代码没有得到复用。(相同的代码写了很多遍)
     * 解决方案二:编写业务类的子类,让子类继承业务类,对每个业务方法进行重写。
     *          缺点一:虽然解决了OCP开闭原则。但是这种方式会导致耦合度很高,因为采用了继承关系。
     *              继承关系是一种耦合度非常高的关系,不建议使用。
     *          缺点二:代码没有得到复用。(相同的代码写了很多遍)
     * 解决方案三:代理模式。
     *          优点1:解决了OCP问题。
     *          有点2:采用代理模式的has a,可以降低耦合度。
     *
     * 目前我们使用的是静态代理,这个静态代理的缺点是什么?
     *          类爆炸。假设系统中有1000个接口,那么每个接口都需要代理类,
     *              这样类会急剧膨胀。不好维护。
     *          怎么解决类爆炸问题?
     *              可以使用动态代理来解决这个问题。
     *          动态代理还是代理模式,只不过添加了字节码生成技术,可以在内存中为我们
     *              动态的生成一个class字节码,这个字节码就是代理类。
     *         在内存中动态的生成字节码代理类的技术。叫做:动态代理。
     */

//    @Override
//    public void generate(){
//        long begin = System.currentTimeMillis();
//        //模拟生成订单的耗时
//        try{
//            Thread.sleep(1234);
//        }catch(InterruptedException e){
//            e.printStackTrace();
//        }
//
//        System.out.println("订单已生成.");
//        long end = System.currentTimeMillis();
//        System.out.println("耗时" + (end - begin) + "耗时");
//    }
//
//    @Override
//    public void modify() {
//        long begin = System.currentTimeMillis();
//        //模拟修改订单的耗时
//        try{
//            Thread.sleep(456);
//        }catch(InterruptedException e){
//            e.printStackTrace();
//        }
//        System.out.println("订单已修改.");
//        long end = System.currentTimeMillis();
//        System.out.println("耗时" + (end - begin) + "耗时");
//    }
//
//    @Override
//    public void detail() {
//        long begin = System.currentTimeMillis();
//        //模拟查询订单的耗时
//        try{
//            Thread.sleep(111);
//        }catch(InterruptedException e){
//            e.printStackTrace();
//        }
//        System.out.println("请查看订单详情.");
//        long end = System.currentTimeMillis();
//        System.out.println("耗时" + (end - begin) + "耗时");
//    }


    @Override
    public void generate(){//目标方法

        //模拟生成订单的耗时
        try{
            Thread.sleep(1234);
        }catch(InterruptedException e){
            e.printStackTrace();
        }

        System.out.println("订单已生成.");

    }

    @Override
    public void modify() {//目标方法

        //模拟修改订单的耗时
        try{
            Thread.sleep(456);
        }catch(InterruptedException e){
            e.printStackTrace();
        }
        System.out.println("订单已修改.");

    }

    @Override
    public void detail() {//目标方法

        //模拟查询订单的耗时
        try{
            Thread.sleep(111);
        }catch(InterruptedException e){
            e.printStackTrace();
        }
        System.out.println("请查看订单详情.");
    }


}


OrderServiceProxy类中的 内容:
package com.powernode.proxy.service;
//代理对象(代理对象和目标对象要具有相同的行为,就要实现同一个或者同一些接口)
//客户端在使用代理对象的时候就像在使用目标对象一样。
public class OrderServiceProxy implements OrderService{

    //将目标对象作为代理对象的一个属性。这种关系叫做关联关系。比继承关系的耦合度低。
    //代理对象中含有目标对象的引用。关联关系. has a
    //注意:这里要写一个公共接口类型。因为公共接口耦合度低。
    private OrderService target;//这就是目标对象.目标对象一定是实现了OrderService接口的。

    //创建代理对象的时候,传一个目标对象给代理对象。
    public OrderServiceProxy(OrderService target) {
        this.target = target;
    }

    @Override
    public void generate(){//代理方法
        //增强
        long begin = System.currentTimeMillis();
        //调用目标对象的目标方法
        target.generate();
        long end = System.currentTimeMillis();
        System.out.println("耗时"+(end - begin)+"毫秒");
    }

    @Override
    public void modify() {//代理方法
        //增强
        long begin = System.currentTimeMillis();
        //调用目标对象的目标方法
        target.modify();
        long end = System.currentTimeMillis();
        System.out.println("耗时"+(end - begin)+"毫秒");

    }

    @Override
    public void detail() {//代理方法
        //增强
        long begin = System.currentTimeMillis();
        //调用目标对象的目标方法
        target.detail();
        long end = System.currentTimeMillis();
        System.out.println("耗时"+(end - begin)+"毫秒");

    }
}


测试类中的 内容:
package com.powernode.proxy.client;

import com.powernode.proxy.service.OrderService;
import com.powernode.proxy.service.OrderServiceImpl;
import com.powernode.proxy.service.OrderServiceImplSub;
import com.powernode.proxy.service.OrderServiceProxy;

public class Test {
    public static void main(String[] args) throws InterruptedException {
//        OrderService orderService = new OrderServiceImpl();
//        orderService.generate();
//        orderService.detail();
//        orderService.modify();


//        OrderService orderService = new OrderServiceImplSub();
//        orderService.generate();
//        orderService.detail();
//        orderService.modify();


        //创建目标对象
        OrderService target = new OrderServiceImpl();
        //创建代理对象
        OrderService proxy = new OrderServiceProxy(target);
        //调用代理对象的代理方法
        proxy.generate();
        proxy.modify();
        proxy.detail();

    }
}


P94  end
P95  GoF代理模式之静态代理代码实现   start  :
14.3 动态代理
在程序运行阶段,在内存中动态生成代理类,被称为动态代理,目标是为了减少代理类的数量。解决代码复用的问题。
在内存当中动态生成类的技术常见的包括:
- JDK动态代理技术:只能代理接口
- CGLIB动态代理技术:CGLIB(Code Generation Library)是一个开源项目。是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展java类与实现java接口。它既可以代理接口,又可以代理类,底层是通过继承的方式实现的。性能比JDK动态代理要好。(底层有一个小而快的字节码处理框架ASM.)
- Javassist动态代理技术:Javassist是一个开源的分析,编辑和创建java字节码的类库。是由东京工业大学的数学和计算机科学系的千叶滋所创建的。它已加入了开源代码JBoss应用服务器项目,通过使用javassist对字节码操作为JBoss实现动态"AOP"框架。
P95 end
P96 GoF代理模式之JDK动态代理的两个参数  start :
14.3.1JDK动态代理
orderService类中的的内容:
package com.powernode.proxy.service;

/**
 * 订单业务接口
 */
public interface OrderService {//代理对象和目标对象的公共接口
    /**
     * 生成订单
     */
    void generate();

    /**
     * 修改订单信息
     */
    void modify();
    /**
     * 查看订单详情
     */
    void detail();
}


OrderServiceImpl类中的内容:
package com.powernode.proxy.service;

public class OrderServiceImpl implements OrderService{//目标对象

    /**
     * 项目经理提出一个新的需求:要统计所有业务接口中每一个业务方法的耗时。
     * 解决方案一:硬编码,在每一个业务接口中的每一个业务方法中直接添加统计耗时的程序。
     * 这种方案的缺点:
     *         缺点一:违背OCP开闭原则。
     *         缺点二:代码没有得到复用。(相同的代码写了很多遍)
     * 解决方案二:编写业务类的子类,让子类继承业务类,对每个业务方法进行重写。
     *          缺点一:虽然解决了OCP开闭原则。但是这种方式会导致耦合度很高,因为采用了继承关系。
     *              继承关系是一种耦合度非常高的关系,不建议使用。
     *          缺点二:代码没有得到复用。(相同的代码写了很多遍)
     * 解决方案三:代理模式。
     *          优点1:解决了OCP问题。
     *          有点2:采用代理模式的has a,可以降低耦合度。
     *
     * 目前我们使用的是静态代理,这个静态代理的缺点是什么?
     *          类爆炸。假设系统中有1000个接口,那么每个接口都需要代理类,
     *              这样类会急剧膨胀。不好维护。
     *          怎么解决类爆炸问题?
     *              可以使用动态代理来解决这个问题。
     *          动态代理还是代理模式,只不过添加了字节码生成技术,可以在内存中为我们
     *              动态的生成一个class字节码,这个字节码就是代理类。
     *         在内存中动态的生成字节码代理类的技术。叫做:动态代理。
     */


    @Override
    public void generate(){//目标方法

        //模拟生成订单的耗时
        try{
            Thread.sleep(1234);
        }catch(InterruptedException e){
            e.printStackTrace();
        }

        System.out.println("订单已生成.");

    }

    @Override
    public void modify() {//目标方法

        //模拟修改订单的耗时
        try{
            Thread.sleep(456);
        }catch(InterruptedException e){
            e.printStackTrace();
        }
        System.out.println("订单已修改.");

    }

    @Override
    public void detail() {//目标方法

        //模拟查询订单的耗时
        try{
            Thread.sleep(111);
        }catch(InterruptedException e){
            e.printStackTrace();
        }
        System.out.println("请查看订单详情.");
    }


}

测试类Client中的内容:
package com.powernode.proxy.client;

import com.powernode.proxy.service.OrderService;
import com.powernode.proxy.service.OrderServiceImpl;

import java.lang.reflect.Proxy;

public class Client {
    //客户端程序
    public static void main(String[] args) {

        //创建目标对象
        OrderService target = new OrderServiceImpl();
        //创建代理对象
        //三个参数:类加载器,代理类要实现的接口,调用处理器
        /*
        1.newProxyInstance 翻译为:新建代理对象
            也就是说,通过调用这个方法可以创建代理对象。
            本质上,这个Proxy.newProxyInstance()方法的执行,做了两件事:
                第一件事:再内存中动态的生成了一个代理类的字节码class。
                第二件事:new对象了。通过内存中生成的代理类这个代码,实例化了代理对象。
        2.关于newProxyInstance()方法的三个重要的参数,每一个什么含义,有什么用?
            第一个参数:ClassLoader loader
                类加载器。这个类加载器有什么用呢?
                    再内存当中生成的字节码也是class文件,要执行也得先加载到内存当中。
                    加载类就需要类加载器,所以这里需要指定类加载器。
                    并且JDK要求,目标类的类加载器必须和代理类的类加载器使用同一个。

            第二个参数:Class<?>[] interfaces
            第三个参数:InvocationHandler h

         */
        Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),)
        //调用代理对象的代理方法

    }
}


P96 END
P97 GoF代理模式之JDK动态代理的调用处理器 start:
其他文件中的内容与上集相同
TimerInvocationHandler类中的内容:
package com.powernode.proxy.service;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * 专门负责计时的一个调用处理器对象。
 * 在这个调用处理器当中编写计时相关的增强代码。
 * 这个调用处理器只需要写一个就行了。
 */
public class TimerInvocationHandler implements InvocationHandler {


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        return null;
    }
}


Client客户端程序中的 内容:
package com.powernode.proxy.client;

import com.powernode.proxy.service.OrderService;
import com.powernode.proxy.service.OrderServiceImpl;
import com.powernode.proxy.service.TimerInvocationHandler;

import java.lang.reflect.Proxy;

public class Client {
    //客户端程序
    public static void main(String[] args) {

        //创建目标对象
        OrderService target = new OrderServiceImpl();
        //创建代理对象
        //三个参数:类加载器,代理类要实现的接口,调用处理器
        /*
        1.newProxyInstance 翻译为:新建代理对象
            也就是说,通过调用这个方法可以创建代理对象。
            本质上,这个Proxy.newProxyInstance()方法的执行,做了两件事:
                第一件事:再内存中动态的生成了一个代理类的字节码class。
                第二件事:new对象了。通过内存中生成的代理类这个代码,实例化了代理对象。
        2.关于newProxyInstance()方法的三个重要的参数,每一个什么含义,有什么用?
            第一个参数:ClassLoader loader
                类加载器。这个类加载器有什么用呢?
                    再内存当中生成的字节码也是class文件,要执行也得先加载到内存当中。
                    加载类就需要类加载器,所以这里需要指定类加载器。
                    并且JDK要求,目标类的类加载器必须和代理类的类加载器使用同一个。

            第二个参数:Class<?>[] interfaces
                代理类和目标类要实现同一个接口或同一些接口。
                在内存中生成代理类的时候,这个代理类是需要你告诉它实现哪些接口的。

            第三个参数:InvocationHandler h
                    InvocationHandler 被翻译为:调用处理器。是一个接口。
                    在调用处理器接口中编写的就是:增强代码。
                    因为具体要增强什么代码,JDK动态代理技术它是猜不到的。没有那么神。
                    既然是接口,就要写接口的实现类。

                    可能会有疑问?
                        自己还要动手写调用处理器接口的实现类,这不会类爆炸吗?不会。
                        因为这种调用处理器写一次就好。

         */
        Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
                new TimerInvocationHandler());
        //调用代理对象的代理方法

    }
}


P97 end
P98 GoF代理模式之JDK动态代理invoke方法
在java语言中,接口有个特点,当我们的一个类实现了一个接口的时候,就 要求我们必须实现接口里的方法。
本集    其他文件内容和上集相同
TimerInvocationHandler类中的内容:
package com.powernode.proxy.service;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * 专门负责计时的一个调用处理器对象。
 * 在这个调用处理器当中编写计时相关的增强代码。
 * 这个调用处理器只需要写一个就行了。
 */
public class TimerInvocationHandler implements InvocationHandler {


    /*
    1。为什么强行要求你必须实现InvocationHandler接口?
        因为一个类实现接口就必须实现接口中的方法。
        以下这个方法必须是invoke(),因为JDK在低层调用invoke()方法的程序已经提前写好了。
        注意:invoke方法不是我们程序员负责调用的,是JDK负责调用的。
    2.invoke方法什么时候被调用呢?
        当代理对象调用代理方法的时候,注册在InvocationHandler调用处理器当中的invoke()方法被调用。


     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //这个接口的目的就是为了让你有地方写增强代码。
        System.out.println("invoke.....");
        return null;
    }
}


客户端Client类中的内容:
package com.powernode.proxy.client;

import com.powernode.proxy.service.OrderService;
import com.powernode.proxy.service.OrderServiceImpl;
import com.powernode.proxy.service.TimerInvocationHandler;

import java.lang.reflect.Proxy;

public class Client {
    //客户端程序
    public static void main(String[] args) {

        //创建目标对象
        OrderService target = new OrderServiceImpl();
        //创建代理对象
        //三个参数:类加载器,代理类要实现的接口,调用处理器
        /*
        1.newProxyInstance 翻译为:新建代理对象
            也就是说,通过调用这个方法可以创建代理对象。
            本质上,这个Proxy.newProxyInstance()方法的执行,做了两件事:
                第一件事:再内存中动态的生成了一个代理类的字节码class。
                第二件事:new对象了。通过内存中生成的代理类这个代码,实例化了代理对象。
        2.关于newProxyInstance()方法的三个重要的参数,每一个什么含义,有什么用?
            第一个参数:ClassLoader loader
                类加载器。这个类加载器有什么用呢?
                    再内存当中生成的字节码也是class文件,要执行也得先加载到内存当中。
                    加载类就需要类加载器,所以这里需要指定类加载器。
                    并且JDK要求,目标类的类加载器必须和代理类的类加载器使用同一个。

            第二个参数:Class<?>[] interfaces
                代理类和目标类要实现同一个接口或同一些接口。
                在内存中生成代理类的时候,这个代理类是需要你告诉它实现哪些接口的。

            第三个参数:InvocationHandler h
                    InvocationHandler 被翻译为:调用处理器。是一个接口。
                    在调用处理器接口中编写的就是:增强代码。
                    因为具体要增强什么代码,JDK动态代理技术它是猜不到的。没有那么神。
                    既然是接口,就要写接口的实现类。

                    可能会有疑问?
                        自己还要动手写调用处理器接口的实现类,这不会类爆炸吗?不会。
                        因为这种调用处理器写一次就好。

                注意:代理对象和目标对象实现的接口一样,所以可以向下转型。
         */
        OrderService proxyObj = (OrderService)Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
                new TimerInvocationHandler());
        //调用代理对象的代理方法
        //注意:调用代理对象的代理方法的时候,如果你要做增强的话,目标对象的目标方法得保证执行。
        proxyObj.generate();
        proxyObj.modify();
        proxyObj.detail();

    }
}


P98 END
P99  GoF代理模式之invoke方法的三个参数  start:
其它文件内容与上集文件内容相同
TimerInvocationHandler类中的内容:
package com.powernode.proxy.service;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * 专门负责计时的一个调用处理器对象。
 * 在这个调用处理器当中编写计时相关的增强代码。
 * 这个调用处理器只需要写一个就行了。
 */
public class TimerInvocationHandler implements InvocationHandler {

    //目标对象
    private Object target;

    public TimerInvocationHandler(Object target) {
        //赋值给成员变量
        this.target=target;
    }

    /*
        1。为什么强行要求你必须实现InvocationHandler接口?
            因为一个类实现接口就必须实现接口中的方法。
            以下这个方法必须是invoke(),因为JDK在低层调用invoke()方法的程序已经提前写好了。
            注意:invoke方法不是我们程序员负责调用的,是JDK负责调用的。
        2.invoke方法什么时候被调用呢?
            当代理对象调用代理方法的时候,注册在InvocationHandler调用处理器当中的invoke()方法被调用。
        3.invoke方法的三个参数:
            invoke方法是JDK负责调用的,所以JDK调用这个方法的时候会自动给我们传过来这三个参数。
            我们可以在invoke方法的大括号中直接使用。
            第一个参数:Object proxy  代理对象的引用,这个参数使用较少
            第二个参数:Method method   目标对象上的目标方法。(要执行的目标方法就是它。)
            第三个参数: Object[] args    目标方法上的实参。
            invoke方法执行过程中,使用method来调用目标对象的目标方法。
         */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //这个接口的目的就是为了让你有地方写增强代码。
        System.out.println("增强1");
        //  调用目标对象上的目标方法
        // 方法四要素:哪个对象,哪个方法,传什么参数,返回什么值
        Object retValue = method.invoke(target, args);
        System.out.println("增强2");
        return null;
    }
}


Client客户端程序中的 内容:
package com.powernode.proxy.client;

import com.powernode.proxy.service.OrderService;
import com.powernode.proxy.service.OrderServiceImpl;
import com.powernode.proxy.service.TimerInvocationHandler;

import java.lang.reflect.Proxy;

public class Client {
    //客户端程序
    public static void main(String[] args) {

        //创建目标对象
        OrderService target = new OrderServiceImpl();
        //创建代理对象
        //三个参数:类加载器,代理类要实现的接口,调用处理器
        /*
        1.newProxyInstance 翻译为:新建代理对象
            也就是说,通过调用这个方法可以创建代理对象。
            本质上,这个Proxy.newProxyInstance()方法的执行,做了两件事:
                第一件事:再内存中动态的生成了一个代理类的字节码class。
                第二件事:new对象了。通过内存中生成的代理类这个代码,实例化了代理对象。
        2.关于newProxyInstance()方法的三个重要的参数,每一个什么含义,有什么用?
            第一个参数:ClassLoader loader
                类加载器。这个类加载器有什么用呢?
                    再内存当中生成的字节码也是class文件,要执行也得先加载到内存当中。
                    加载类就需要类加载器,所以这里需要指定类加载器。
                    并且JDK要求,目标类的类加载器必须和代理类的类加载器使用同一个。

            第二个参数:Class<?>[] interfaces
                代理类和目标类要实现同一个接口或同一些接口。
                在内存中生成代理类的时候,这个代理类是需要你告诉它实现哪些接口的。

            第三个参数:InvocationHandler h
                    InvocationHandler 被翻译为:调用处理器。是一个接口。
                    在调用处理器接口中编写的就是:增强代码。
                    因为具体要增强什么代码,JDK动态代理技术它是猜不到的。没有那么神。
                    既然是接口,就要写接口的实现类。

                    可能会有疑问?
                        自己还要动手写调用处理器接口的实现类,这不会类爆炸吗?不会。
                        因为这种调用处理器写一次就好。

                注意:代理对象和目标对象实现的接口一样,所以可以向下转型。
         */
        OrderService proxyObj = (OrderService)Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
                new TimerInvocationHandler(target));
        //调用代理对象的代理方法
        //注意:调用代理对象的代理方法的时候,如果你要做增强的话,目标对象的目标方法得保证执行。
        proxyObj.generate();
//        proxyObj.modify();
//        proxyObj.detail();

    }
}

运行结果:
增强1
订单已生成.
增强2

P99 END
P100 GoF代理模式之invoke方法的返回值  start:

OrderServiceImpl类中的内容:
package com.powernode.proxy.service;

public class OrderServiceImpl implements OrderService{//目标对象

    @Override
    public String getName() {
        System.out.println("getName()方法执行了");
        return "张三";
    }

    /**
     * 项目经理提出一个新的需求:要统计所有业务接口中每一个业务方法的耗时。
     * 解决方案一:硬编码,在每一个业务接口中的每一个业务方法中直接添加统计耗时的程序。
     * 这种方案的缺点:
     *         缺点一:违背OCP开闭原则。
     *         缺点二:代码没有得到复用。(相同的代码写了很多遍)
     * 解决方案二:编写业务类的子类,让子类继承业务类,对每个业务方法进行重写。
     *          缺点一:虽然解决了OCP开闭原则。但是这种方式会导致耦合度很高,因为采用了继承关系。
     *              继承关系是一种耦合度非常高的关系,不建议使用。
     *          缺点二:代码没有得到复用。(相同的代码写了很多遍)
     * 解决方案三:代理模式。
     *          优点1:解决了OCP问题。
     *          有点2:采用代理模式的has a,可以降低耦合度。
     *
     * 目前我们使用的是静态代理,这个静态代理的缺点是什么?
     *          类爆炸。假设系统中有1000个接口,那么每个接口都需要代理类,
     *              这样类会急剧膨胀。不好维护。
     *          怎么解决类爆炸问题?
     *              可以使用动态代理来解决这个问题。
     *          动态代理还是代理模式,只不过添加了字节码生成技术,可以在内存中为我们
     *              动态的生成一个class字节码,这个字节码就是代理类。
     *         在内存中动态的生成字节码代理类的技术。叫做:动态代理。
     */


    @Override
    public void generate(){//目标方法

        //模拟生成订单的耗时
        try{
            Thread.sleep(1234);
        }catch(InterruptedException e){
            e.printStackTrace();
        }

        System.out.println("订单已生成.");

    }

    @Override
    public void modify() {//目标方法

        //模拟修改订单的耗时
        try{
            Thread.sleep(456);
        }catch(InterruptedException e){
            e.printStackTrace();
        }
        System.out.println("订单已修改.");

    }

    @Override
    public void detail() {//目标方法

        //模拟查询订单的耗时
        try{
            Thread.sleep(111);
        }catch(InterruptedException e){
            e.printStackTrace();
        }
        System.out.println("请查看订单详情.");
    }


}


OrderService接口中的内容:
package com.powernode.proxy.service;

/**
 * 订单业务接口
 */
public interface OrderService {//代理对象和目标对象的公共接口

    String getName();

    /**
     * 生成订单
     */
    void generate();

    /**
     * 修改订单信息
     */
    void modify();
    /**
     * 查看订单详情
     */
    void detail();
}

TimerInvocationHandler类中的内容:
package com.powernode.proxy.service;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * 专门负责计时的一个调用处理器对象。
 * 在这个调用处理器当中编写计时相关的增强代码。
 * 这个调用处理器只需要写一个就行了。
 */
public class TimerInvocationHandler implements InvocationHandler {

    //目标对象
    private Object target;

    public TimerInvocationHandler(Object target) {
        //赋值给成员变量
        this.target=target;
    }

    /*
        1。为什么强行要求你必须实现InvocationHandler接口?
            因为一个类实现接口就必须实现接口中的方法。
            以下这个方法必须是invoke(),因为JDK在低层调用invoke()方法的程序已经提前写好了。
            注意:invoke方法不是我们程序员负责调用的,是JDK负责调用的。
        2.invoke方法什么时候被调用呢?
            当代理对象调用代理方法的时候,注册在InvocationHandler调用处理器当中的invoke()方法被调用。
        3.invoke方法的三个参数:
            invoke方法是JDK负责调用的,所以JDK调用这个方法的时候会自动给我们传过来这三个参数。
            我们可以在invoke方法的大括号中直接使用。
            第一个参数:Object proxy  代理对象的引用,这个参数使用较少
            第二个参数:Method method   目标对象上的目标方法。(要执行的目标方法就是它。)
            第三个参数: Object[] args    目标方法上的实参。
            invoke方法执行过程中,使用method来调用目标对象的目标方法。
         */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //这个接口的目的就是为了让你有地方写增强代码。
//        System.out.println("增强1");
        long begin = System.currentTimeMillis();

        //  调用目标对象上的目标方法
        // 方法四要素:哪个对象,哪个方法,传什么参数,返回什么值
        Object retValue = method.invoke(target, args);
//        System.out.println("增强2");
        long end = System.currentTimeMillis();
        System.out.println("耗时"+(end - begin)+"毫秒");

        //注意这个invoke方法的返回值,如果代理对象调用代理方法之后,需要返回结果的话
        //invoke方法必须将目标对象的目标方法执行结果继续返回。
        return retValue;
    }
}

Client客户端类中的内容:
package com.powernode.proxy.client;

import com.powernode.proxy.service.OrderService;
import com.powernode.proxy.service.OrderServiceImpl;
import com.powernode.proxy.service.TimerInvocationHandler;

import java.lang.reflect.Proxy;

public class Client {
    //客户端程序
    public static void main(String[] args) {

        //创建目标对象
        OrderService target = new OrderServiceImpl();
        //创建代理对象
        //三个参数:类加载器,代理类要实现的接口,调用处理器
        /*
        1.newProxyInstance 翻译为:新建代理对象
            也就是说,通过调用这个方法可以创建代理对象。
            本质上,这个Proxy.newProxyInstance()方法的执行,做了两件事:
                第一件事:再内存中动态的生成了一个代理类的字节码class。
                第二件事:new对象了。通过内存中生成的代理类这个代码,实例化了代理对象。
        2.关于newProxyInstance()方法的三个重要的参数,每一个什么含义,有什么用?
            第一个参数:ClassLoader loader
                类加载器。这个类加载器有什么用呢?
                    再内存当中生成的字节码也是class文件,要执行也得先加载到内存当中。
                    加载类就需要类加载器,所以这里需要指定类加载器。
                    并且JDK要求,目标类的类加载器必须和代理类的类加载器使用同一个。

            第二个参数:Class<?>[] interfaces
                代理类和目标类要实现同一个接口或同一些接口。
                在内存中生成代理类的时候,这个代理类是需要你告诉它实现哪些接口的。

            第三个参数:InvocationHandler h
                    InvocationHandler 被翻译为:调用处理器。是一个接口。
                    在调用处理器接口中编写的就是:增强代码。
                    因为具体要增强什么代码,JDK动态代理技术它是猜不到的。没有那么神。
                    既然是接口,就要写接口的实现类。

                    可能会有疑问?
                        自己还要动手写调用处理器接口的实现类,这不会类爆炸吗?不会。
                        因为这种调用处理器写一次就好。

                注意:代理对象和目标对象实现的接口一样,所以可以向下转型。
         */
        OrderService proxyObj = (OrderService)Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
                new TimerInvocationHandler(target));
        //调用代理对象的代理方法
        //注意:调用代理对象的代理方法的时候,如果你要做增强的话,目标对象的目标方法得保证执行。
        proxyObj.generate();
        proxyObj.modify();
        proxyObj.detail();
        String name = proxyObj.getName();
        System.out.println(name);
    }
}

运行结果:
订单已生成.
耗时1235毫秒
订单已修改.
耗时458毫秒
请查看订单详情.
耗时113毫秒
getName()方法执行了
耗时0毫秒
张三

P100  end
P101 GoF代理模式之JDK动态代理工具类封装
P101  start :
Time:4h
视频网址:
https://www.bilibili.com/video/BV1Ft4y1g7Fb?p=100&vd_source=ff898217e3612fcbdefecfdc49657ab1


 

  • 7
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值