3.1 动态代理

基于接口的动态代理

演员拿钱就演,经纪公司IActor.java会有对艺人签约的标准,演员继承了经纪公司接口就可以上路了,当剧组,Client.java要邀请演员时,经纪公司会在不询问演员的情况下,对事情进行操作,可以克扣钱,也可以多要钱,即在不改变源码的情况下对方法进行了增强,就是动态代理

Actor.java

package com.itheima.proxy;

/*
 * 一个演员
 * */

public class Actor implements IActor{
    
    /*
     * 基本的演出
     * */
    public void basicAct(float money) {
        System.out.println("拿到钱,开始基本的表演" + money);
    }
    
    /*
     * 危险的表演
     * */
    public void dangerAct(float money) {
        System.out.println("拿到钱,开始危险的表演" + money);
    }
    
}

IActor.java

package com.itheima.proxy;

/*
 * 经济公司对签约艺人的标准
 * */
public interface IActor {

    void basicAct(float money);
    
    void dangerAct(float money);
}

Client.java

package com.itheima.proxy;

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

/*
 * 模拟一个剧组
 * */
public class Client {

    public static void main(String[] args) {
        final Actor actor = new Actor();
//      actor.basicAct(100f);
//      actor.dangerAct(500f);
        
        /*
         * 动态代理:
         作用就是不改变源码的基础上,对已有方法增强,是AOP思想的实现技术
         分类:
            1. 基于接口的动态代理
                要求:被代理类最少实现一个接口
                提供者:JDK官方
                涉及的类:Proxy
                创建代理对象的方法:newProxyInstance(ClassLoader, Class[], InvocationHandler)
                    参数的含义:
                              ClassLoader: 类加载器,和被代理对象使用相同的类加载器,一般都是固定写法
                                  Class[]:字节码数组,被代理类实现的接口,(要求代理对象和被代理对象具有相同的行为),一般都是固定写法
                        InvocationHandler:是一个接口,就是用于我们提供增强代码的,我们一般都是提供一个该接口的实现类,可以是匿名内部类
                                                 含义:如何代理,此处的代码只能是谁用谁提供
                    设计模式:策略模式
                        使用要求:数据已经有了,目标明确,达成目标的过程就是策略
                        在dbutils中的ResultSetHandler就是策略模式的具体应用
                        
         * */
        IActor proxyActor = (IActor)Proxy.newProxyInstance(actor.getClass().getClassLoader(), actor.getClass().getInterfaces(), new InvocationHandler() {
            /*
             * 执行被代理对象的任何方法都会经过该方法,该方法有拦截的功能
             方法的参数:
                Object proxy:代理对象的引用,不一定每次都用
                Method method:当前执行的方法
                Object[] args:当前执行的方法所需的参数
             返回值:
                当前执行方法的返回值
             * */
            
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object rtValue = null;
                
                //取出执行方法中的参数,给的钱
                Float money = (Float)args[0];
                //判断当前执行的是什么方法
                if("basicAct".equals(method.getName())) {
                    //基本演出
                    if(money>10000) {
                        //执行方法,开始基础表演
                        rtValue = method.invoke(actor, money);
                    }
                }
                if("dangerAct".equals(method.getName())) {
                    //危险演出
                    if(money>50000) {
                        //执行方法,可以修改传入的参数
                        rtValue = method.invoke(actor, money/2);
                    }
                }
                
                return rtValue;
            }
        });
        
        proxyActor.basicAct(20000);
        proxyActor.dangerAct(50000);
        
        //以上都是不改变源码的情况下对原方法的增强
        
    }
}

运行


3.1 动态代理_第1张图片

基于子类的动态代理

Actor.java

package com.itheima.cglib;

/*
 * 一个演员
 * */

public class Actor{
    
    /*
     * 基本的演出
     * */
    public void basicAct(float money) {
        System.out.println("拿到钱,开始基本的表演" + money);
    }
    
    /*
     * 危险的表演
     * */
    public void dangerAct(float money) {
        System.out.println("拿到钱,开始危险的表演" + money);
    }
    
}

Client.java

package com.itheima.cglib;

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

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

/*
 * 模拟一个剧组
 * */
public class Client {

    public static void main(String[] args) {
        final Actor actor = new Actor();
        
        /*
         基于子类的动态代理
         
            要求:被代理类不能是最终类,不能用final修饰
            提供者:第三方CGLib
            涉及的类:Enhancer
            创建代理对象的方法:create(Class,Callback);
            参数含义:
                Class:被代理对象的字节码
                Callback:如何代理,和invocationHandler的作用是一样的,也是一个接口,我们一般使用该接口的子接口MethodInterceptor
                    在使用时,也是创建该接口的实现类(匿名内部类)
                    
         * */
        Actor cglibactor = (Actor)Enhancer.create(actor.getClass(), new MethodInterceptor() {
            /*
             * 执行被代理对象的任何方法,都会经过该方法,它和基于接口动态代理的invoke方法的作用是一摸一样的
             方法的参数
                前三个参数和invoke方法的参数含义和作用都一样
                MethodProxy methodProxy:当前执行方法的代理对象,一般不用
             * */
            @Override
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object rtValue = null;
                
                //取出执行方法中的参数,给的钱
                Float money = (Float)args[0];
                //判断当前执行的是什么方法
                if("basicAct".equals(method.getName())) {
                    //基本演出
                    if(money>10000) {
                        //执行方法,开始基础表演
                        rtValue = method.invoke(actor, money);
                    }
                }
                if("dangerAct".equals(method.getName())) {
                    //危险演出
                    if(money>50000) {
                        //执行方法,可以修改传入的参数
                        rtValue = method.invoke(actor, money/2);
                    }
                }
                
                return rtValue;
            }
        });
        cglibactor.basicAct(50000);
        cglibactor.dangerAct(100000);
    }
}

结果


3.1 动态代理_第2张图片

应用:自定义连接池

dbcp就是使用的动态代理方法
装饰者模式也可以实现,c3p0就是这样实现的,用包装类实现close方法
MyDataSource.java

package com.itheima.dataSource;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import com.itheima.utils.JDBCUtil;

/**
 * 自定义连接池
 * @author zhy
 *
 */
public class MyDataSource {
    //定义一个池,用于存放连接
    private static List pool = Collections.synchronizedList(new ArrayList());//把ArrayList转成线程安全的
    
    //使用静态代码块给池中加入连接
    static{
        for(int i=0;i<10;i++){
            Connection conn = JDBCUtil.getConnection();
            pool.add(conn);
        }
    }
    
    /**
     * 获取一个连接
     * @return
     */
    public static Connection getConnection(){
        final Connection conn =  pool.remove(0);
        //创建代理对象
        Connection proxyConn = (Connection) Proxy.newProxyInstance(conn.getClass().getClassLoader(),
                conn.getClass().getInterfaces(), 
                new InvocationHandler() {
                    
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        Object rtValue = null;
                        //1.判断当前方法是不是close方法
                        if("close".equals(method.getName())){
                            //不能直接关闭
                            pool.add(conn);//还回池中
                        }else{
                            rtValue = method.invoke(conn, args);
                        }
                        return rtValue;
                    }
                });
        return proxyConn;
    }
    
    
    /**
     * 获取池中的连接数
     * @return
     */
    public static int getPoolSize(){
        return pool.size();
    }
}

DataSourceTest.java

package com.itheima.test;

import java.sql.Connection;
import java.sql.SQLException;

import com.itheima.dataSource.MyDataSource;

public class DataSourceTest {

    public static void main(String[] args) throws Exception {
        int size = MyDataSource.getPoolSize();
        System.out.println("使用连接之前"+size);
        
        for(int i=0;i<10;i++){
            Connection conn = MyDataSource.getConnection();
            System.out.println(conn);
            conn.close();//此时不能关闭,必须把连接放回池里
        }
        
        int size1 = MyDataSource.getPoolSize();
        System.out.println("使用连接之后"+size1);
        
        for(int i=0;i<10;i++){
            Connection conn = MyDataSource.getConnection();
            System.out.println(conn);
            conn.close();//此时不能关闭,必须把连接放回池里
        }
    }

}

JDBCUtil.java

package com.itheima.utils;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ResourceBundle;

/**
 * 数据库操作相关的工具类
 * @author zhy
 *
 */
public class JDBCUtil {
    
    //使用ResourceBundle读取资源文件
    private static ResourceBundle bundle = ResourceBundle.getBundle("dbconfig");
    
    private static String driver;
    private static String url;
    private static String user;
    private static String password;
    
    //使用静态代码块进行赋值
    static{
        driver = bundle.getString("DRIVER");
        url = bundle.getString("URL");
        user = bundle.getString("USER");
        password = bundle.getString("PASSWORD");
    }
    
    /**
     * 获取连接
     * @return
     */
    public static Connection getConnection(){
        Connection conn = null;
        try {
            Class.forName(driver);
            conn = DriverManager.getConnection(url, user, password);
            return conn;
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
    
    /**
     * 释放资源
     * @param rs
     * @param st
     * @param conn
     */
    public static void release(ResultSet rs,Statement st,Connection conn){
        if(rs != null){
            try{
                rs.close();
            }catch(Exception e){
                e.printStackTrace();
            }
        }
        if(st != null){
            try{
                st.close();
            }catch(Exception e){
                e.printStackTrace();
            }
        }
        if(conn != null){
            try{
                conn.close();
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    }
}

dbconfig.properties

#\u6570\u636E\u5E93\u8FDE\u63A5\u7684\u914D\u7F6E

#\u6570\u636E\u5E93\u9A71\u52A8
DRIVER=com.mysql.jdbc.Driver

#\u8FDE\u63A5\u5B57\u7B26\u4E32
URL=jdbc:mysql://localhost:3306/crm_struts2

#\u6570\u636E\u5E93\u7528\u6237\u540D
USER=root

#\u6570\u636E\u5E93\u5BC6\u7801
PASSWORD=suntong
3.1 动态代理_第3张图片

5.0.8和5.1.7有区别。5.0.8的connection是类,而5.1.7是接口,用的是他的实现类,继承的已经不是java.sql.Connection了

你可能感兴趣的:(3.1 动态代理)