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);
        
        //以上都是不改变源码的情况下对原方法的增强
        
    }
}

运行


基于子类的动态代理

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);
    }
}

结果


应用:自定义连接池

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<Connection> pool = Collections.synchronizedList(new ArrayList<Connection>());//把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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,695评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,569评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,130评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,648评论 1 297
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,655评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,268评论 1 309
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,835评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,740评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,286评论 1 318
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,375评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,505评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,185评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,873评论 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,357评论 0 24
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,466评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,921评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,515评论 2 359

推荐阅读更多精彩内容