mysql:day5-详解多线程状态下的事务(连接池、动态代理技术)

本文探讨了多线程环境下共享数据库连接的问题,并提出了三种解决方案:限制个数的多例加锁、自定义Connection类以及使用动态代理技术。通过实例代码展示了如何有效地管理多线程中的数据库连接。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题:

如下面代码:
cn.hncu.demo.TxMultiThreadDemo

package cn.hncu.demo;

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

import cn.hncu.pool.ConnsFactory;
import cn.hncu.util.ConnFactory;

/*
 * 通过本例的演示说明:
 * 如果多个线程获取的是同一个con对象,那么一个线程的事务提交,会把其它线程正在执行的语句也提交了。
 * 反之,如果一个线程进行事务回滚,那么会把其它线程的语句也回滚了。
 * 还有,一个线程把con关闭,那么其它线程执行提交或回滚时就会出异常。
 * -----总之,多线程共享同一个连接,会相互影响。
 */
public class TxMultiThreadDemo {
    public static void main(String[] args) {
        Connection con = null;
        try {
            con=ConnFactory.getCon();
            con.setAutoCommit(false);
            new OneThread().start();
            Statement st = con.createStatement();
            String sql ="insert into student values('p00a','测试一',23)";
            st.execute(sql);
            sql ="insert into student values('p0a0','测试二',29)";
            st.execute(sql);
            System.out.println("主线程准备提交");
            con.commit();
            System.out.println("主线程已经提交");
        } catch (Exception e) {
            try {
                con.rollback();
                System.out.println("主线程回滚");
            } catch (SQLException e1) {
                throw new RuntimeException("主线程事务回滚失败!", e1);
            }
        }finally{
            try {
                if(con!=null){
                    con.setAutoCommit(true);
                    con.close();
                }
            } catch (Exception e) {
                throw new RuntimeException("主线程事务关闭失败!", e);
            }
        }
    }
}
class OneThread extends Thread{

    @Override
    public void run() {
        Connection con = null;
        try {
            con=ConnFactory.getCon();
            con.setAutoCommit(false);
            Statement st = con.createStatement();
            String sql ="insert into student values('p00b','测试一#',21)";
            st.execute(sql);
            sql ="insert into student values('p0b0','测试二#',39)";
            st.execute(sql);
            System.out.println("次线程准备提交");
            con.commit();
            System.out.println("次线程已经提交");
        } catch (Exception e) {
            try {
                con.rollback();
                System.out.println("次线程回滚");
            } catch (SQLException e1) {
                throw new RuntimeException("次线程事务回滚失败!", e1);
            }
        }finally{
            try {
                if(con!=null){
                    con.setAutoCommit(true);
                    con.close();
                }
            } catch (Exception e) {
                throw new RuntimeException("次线程事务关闭失败!", e);
            }
        }
    }

}

cn.hncu.util.ConnFactory

package cn.hncu.util;

import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.Properties;

public class ConnFactory {
    private static Connection con ;
    static{
        //采用配置文件的方式连接数据库
        try {
            Properties p = new Properties();
            p.load(ConnFactory.class.getClassLoader().getResourceAsStream("jdbc.properties"));
            String driver =p.getProperty("driver");
            String url = p.getProperty("url");
            String userName=p.getProperty("userName");
            String password = p.getProperty("password");
            //1加载连接器
            Class.forName(driver);
            //2建立连接
            con = DriverManager.getConnection(url, userName, password);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static Connection getCon(){
        return con;
    }
//  public static void main(String[] args) {
//      getCon();
//      System.out.println(con);
//  }
}

如果多个线程获取的是同一个con对象,那么一个线程的事务提交,会把其它线程正在执行的语句也提交了。
反之,如果一个线程进行事务回滚,那么会把其它线程的语句也回滚了。
还有,一个线程把con关闭,那么其它线程执行提交或回滚时就会出异常。
—–总之,多线程共享同一个连接,会相互影响。
由此引出了线程池

解决方法:

解决方法1:

限制个数的多例+锁拿connection对象+把关流改成还回去一个connection对象
cn.hncu.pool.ConnsFactory

package cn.hncu.pool;

import java.sql.Connection;
import java.sql.DriverManager;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import cn.hncu.util.ConnFactory;

public class ConnsFactory {
    private static List<Connection> pool = new ArrayList<Connection>();
    private static final int NUM=3;
    static{
        //采用配置文件的方式连接数据库
        try {
            Properties p = new Properties();
            p.load(ConnFactory.class.getClassLoader().getResourceAsStream("jdbc.properties"));
            String driver =p.getProperty("driver");
            String url = p.getProperty("url");
            String userName=p.getProperty("userName");
            String password = p.getProperty("password");
            //1加载连接器
            Class.forName(driver);
            for (int i = 0; i < NUM; i++) {
                //2建立连接
                Connection con = DriverManager.getConnection(url, userName,password);
                pool.add(con);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static synchronized Connection  getCon() throws InterruptedException{
        if(pool.size()<=0){
            Thread.sleep(100);
            getCon();
        }
        return pool.remove(0);
    }
    public static void backCon(Connection con){
        System.out.println("还回来一个连接...");
        pool.add(con);
    }
}

cn.hncu.pool.TestPool

package cn.hncu.pool;

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


public class TestPool {
    public static void main(String[] args) {
        Connection con = null;
        try {
            con= ConnsFactory.getCon();
            con.setAutoCommit(false);
            new OneThread(1).start();
            new OneThread(2).start();
            new OneThread(3).start();
            new OneThread(4).start();
            new OneThread(5).start();
            Statement st = con.createStatement();
            String sql ="insert into student values('p00a','测试一',23)";
            st.execute(sql);
            sql ="insert into student values('p0a0','测试二',29)";
            st.execute(sql);

            System.out.println("主线程准备提交");
            con.commit();
            System.out.println("主线程已经提交");
        } catch (Exception e) {
            try {
                con.rollback();
                System.out.println("主线程回滚");
            } catch (SQLException e1) {
                throw new RuntimeException("主线程事务回滚失败!", e1);
            }
        }finally{
            try {
                if(con!=null){
                    con.setAutoCommit(true);
                    ConnsFactory.backCon(con);//将关流改成还连接对象,如果一定要用关闭连接,
                    //那么我们将close()函数的功能修改成还连接对象,以后学动态代理、切面技术会学到
                }
            } catch (Exception e) {
                throw new RuntimeException("主线程事务关闭失败!", e);
            }
        }
    }
}

class OneThread extends Thread{
    private int num;
    public OneThread(int num) {
        this.num = num;
    }
    @Override
    public void run() {
        Connection con = null;
        try {
            con= ConnsFactory.getCon();
            con.setAutoCommit(false);
            Statement st = con.createStatement();
            String sql ="insert into student values('p0"+num+"1','测试一"+num+"',25)";
            st.execute(sql);
            sql ="insert into student values('p"+num+"01','测试二"+num+"',26)";
            st.execute(sql);

            System.out.println("线程"+num+"准备提交");
            con.commit();
            System.out.println("线程"+num+"已经提交");
        } catch (Exception e) {
            try {
                con.rollback();
                System.out.println("线程"+num+"回滚");
            } catch (SQLException e1) {
                throw new RuntimeException("第"+num+"个事务回滚失败!", e1);
            }
        }finally{
            try {
                if(con!=null){
                    con.setAutoCommit(true);
                    ConnsFactory.backCon(con);
                }
            } catch (Exception e) {
                throw new RuntimeException("第"+num+"个事务关闭失败!", e);
            }
        }
    }

}

上面这种方法索然是能解决多线程下的事务处理,但是把我们习惯的con.close()方法变成了ConnsFactory.backCon(con)这样不符合我们开发的习惯。
由此我们要学习动态代理技术。

解决方法2

可以用继承Connection 然后覆盖也可以用实现Connection接口,然后实现它的接口,使用装饰模式封装一个Connection对象我们自己做connection的colse()方法自己想要做的方法自己写,不做的就用封装的Connection对象去做。Connection对象用构造方法传进来去封装。
缺点是:代码太长了,为了一个自己写的方法实现Connection接口的所有抽象方法,我的天。当然这里只是为了过度。
cn.hncu.pool.day2.v1.ConnsUtils2

package cn.hncu.pool.day2.v1;

import java.sql.Array;
import java.sql.Blob;
import java.sql.CallableStatement;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.NClob;
import java.sql.PreparedStatement;
import java.sql.SQLClientInfoException;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.SQLXML;
import java.sql.Savepoint;
import java.sql.Statement;
import java.sql.Struct;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executor;

import cn.hncu.util.ConnFactory;

public class ConnsUtils2 {
    private static List<Connection> pool = new ArrayList<Connection>();
    private static final int NUM=3;
    static{
        //采用配置文件的方式连接数据库
        try {
            Properties p = new Properties();
            p.load(ConnFactory.class.getClassLoader().getResourceAsStream("jdbc.properties"));
            String driver =p.getProperty("driver");
            String url = p.getProperty("url");
            String userName=p.getProperty("userName");
            String password = p.getProperty("password");
            //1加载连接器
            Class.forName(driver);
            for (int i = 0; i < NUM; i++) {
                //2建立连接
                Connection con = DriverManager.getConnection(url, userName,password);
                MyConnection conn = new MyConnection(con);//加强版的conn
                pool.add(conn);//将加强版的conn放入连接池
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static synchronized Connection  getCon() throws InterruptedException{
        if(pool.size()<=0){
            Thread.sleep(100);
            return getCon();
        }
        return pool.remove(0);
    }
    static class MyConnection implements Connection{
        private Connection con = null;
        public MyConnection(Connection con) {
            this.con=con;
        }

        @Override
        public <T> T unwrap(Class<T> iface) throws SQLException {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public boolean isWrapperFor(Class<?> iface) throws SQLException {
            // TODO Auto-generated method stub
            return false;
        }

        @Override
        public Statement createStatement() throws SQLException {
            return con.createStatement();
        }

        @Override
        public PreparedStatement prepareStatement(String sql)
                throws SQLException {
            return con.prepareStatement(sql);
        }

        @Override
        public CallableStatement prepareCall(String sql) throws SQLException {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public String nativeSQL(String sql) throws SQLException {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public void setAutoCommit(boolean autoCommit) throws SQLException {
            con.setAutoCommit(autoCommit);
        }

        @Override
        public boolean getAutoCommit() throws SQLException {
            return con.getAutoCommit();
        }

        @Override
        public void commit() throws SQLException {
            con.commit();
        }

        @Override
        public void rollback() throws SQLException {
            con.rollback();
        }

        @Override
        public void close() throws SQLException {
            pool.add(con);
        }

        @Override
        public boolean isClosed() throws SQLException {
            // TODO Auto-generated method stub
            return false;
        }

        @Override
        public DatabaseMetaData getMetaData() throws SQLException {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public void setReadOnly(boolean readOnly) throws SQLException {
            // TODO Auto-generated method stub

        }

        @Override
        public boolean isReadOnly() throws SQLException {
            // TODO Auto-generated method stub
            return false;
        }

        @Override
        public void setCatalog(String catalog) throws SQLException {
            // TODO Auto-generated method stub

        }

        @Override
        public String getCatalog() throws SQLException {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public void setTransactionIsolation(int level) throws SQLException {
            // TODO Auto-generated method stub

        }

        @Override
        public int getTransactionIsolation() throws SQLException {
            // TODO Auto-generated method stub
            return 0;
        }

        @Override
        public SQLWarning getWarnings() throws SQLException {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public void clearWarnings() throws SQLException {
            // TODO Auto-generated method stub

        }

        @Override
        public Statement createStatement(int resultSetType,
                int resultSetConcurrency) throws SQLException {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public PreparedStatement prepareStatement(String sql,
                int resultSetType, int resultSetConcurrency)
                throws SQLException {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public CallableStatement prepareCall(String sql, int resultSetType,
                int resultSetConcurrency) throws SQLException {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public Map<String, Class<?>> getTypeMap() throws SQLException {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
            // TODO Auto-generated method stub

        }

        @Override
        public void setHoldability(int holdability) throws SQLException {
            // TODO Auto-generated method stub

        }

        @Override
        public int getHoldability() throws SQLException {
            // TODO Auto-generated method stub
            return 0;
        }

        @Override
        public Savepoint setSavepoint() throws SQLException {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public Savepoint setSavepoint(String name) throws SQLException {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public void rollback(Savepoint savepoint) throws SQLException {
            // TODO Auto-generated method stub

        }

        @Override
        public void releaseSavepoint(Savepoint savepoint) throws SQLException {
            // TODO Auto-generated method stub

        }

        @Override
        public Statement createStatement(int resultSetType,
                int resultSetConcurrency, int resultSetHoldability)
                throws SQLException {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public PreparedStatement prepareStatement(String sql,
                int resultSetType, int resultSetConcurrency,
                int resultSetHoldability) throws SQLException {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public CallableStatement prepareCall(String sql, int resultSetType,
                int resultSetConcurrency, int resultSetHoldability)
                throws SQLException {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public PreparedStatement prepareStatement(String sql,
                int autoGeneratedKeys) throws SQLException {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public PreparedStatement prepareStatement(String sql,
                int[] columnIndexes) throws SQLException {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public PreparedStatement prepareStatement(String sql,
                String[] columnNames) throws SQLException {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public Clob createClob() throws SQLException {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public Blob createBlob() throws SQLException {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public NClob createNClob() throws SQLException {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public SQLXML createSQLXML() throws SQLException {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public boolean isValid(int timeout) throws SQLException {
            // TODO Auto-generated method stub
            return false;
        }

        @Override
        public void setClientInfo(String name, String value)
                throws SQLClientInfoException {
            // TODO Auto-generated method stub

        }

        @Override
        public void setClientInfo(Properties properties)
                throws SQLClientInfoException {
            // TODO Auto-generated method stub

        }

        @Override
        public String getClientInfo(String name) throws SQLException {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public Properties getClientInfo() throws SQLException {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public Array createArrayOf(String typeName, Object[] elements)
                throws SQLException {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public Struct createStruct(String typeName, Object[] attributes)
                throws SQLException {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public void setSchema(String schema) throws SQLException {
            // TODO Auto-generated method stub

        }

        @Override
        public String getSchema() throws SQLException {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public void abort(Executor executor) throws SQLException {
            // TODO Auto-generated method stub

        }

        @Override
        public void setNetworkTimeout(Executor executor, int milliseconds)
                throws SQLException {
            // TODO Auto-generated method stub

        }

        @Override
        public int getNetworkTimeout() throws SQLException {
            // TODO Auto-generated method stub
            return 0;
        }

    }

}

cn.hncu.pool.day2.v1.TestPool

package cn.hncu.pool.day2.v1;

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


public class TestPool {
    public static void main(String[] args) {
        Connection con = null;
        try {
            con= ConnsUtils2.getCon();
            con.setAutoCommit(false);
            new OneThread(1).start();
            new OneThread(2).start();
            new OneThread(3).start();
            new OneThread(4).start();
            new OneThread(5).start();
            Statement st = con.createStatement();
            String sql ="insert into student values('p00a','测试一',23)";
            st.execute(sql);
            sql ="insert into student values('p0a0','测试二',29)";
            st.execute(sql);

            System.out.println("主线程准备提交");
            con.commit();
            System.out.println("主线程已经提交");
        } catch (Exception e) {
            try {
                con.rollback();
                System.out.println("主线程回滚");
            } catch (SQLException e1) {
                throw new RuntimeException("主线程事务回滚失败!", e1);
            }
        }finally{
            try {
                if(con!=null){
                    con.setAutoCommit(true);
                    con.close();
                }
            } catch (Exception e) {
                throw new RuntimeException("主线程事务关闭失败!", e);
            }
        }
    }
}

class OneThread extends Thread{
    private int num;
    public OneThread(int num) {
        this.num = num;
    }
    @Override
    public void run() {
        Connection con = null;
        try {
            con= ConnsUtils2.getCon();
            con.setAutoCommit(false);
            Statement st = con.createStatement();
            String sql ="insert into student values('p0"+num+"1','测试一"+num+"',25)";
            st.execute(sql);
            sql ="insert into student values('p"+num+"01','测试二"+num+"',26)";
            st.execute(sql);

            System.out.println("线程"+num+"准备提交");
            con.commit();
            System.out.println("线程"+num+"已经提交");
        } catch (Exception e) {
            try {
                con.rollback();
                System.out.println("线程"+num+"回滚");
            } catch (SQLException e1) {
                throw new RuntimeException("第"+num+"个事务回滚失败!", e1);
            }
        }finally{
            try {
                if(con!=null){
                    con.setAutoCommit(true);
                    con.close();
                }
            } catch (Exception e) {
                throw new RuntimeException("第"+num+"个事务关闭失败!", e);
            }
        }
    }

}

怎么样 是不是很长,其实你可以不用看这段代码。根本没什么卵用,就是改了一个close方法。让它实现把传进来的connection对象加到pool-连接池去。这样让别人调用时可以实现方法1中把connection对象还回来切程序员在编写代码的时候还是按照以前的习惯调用close方法。

解决方法三 真正的动态代理

cn.hncu.pool.day2.v2.ConnsUtils3

package cn.hncu.pool.day2.v2;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Array;
import java.sql.Blob;
import java.sql.CallableStatement;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.NClob;
import java.sql.PreparedStatement;
import java.sql.SQLClientInfoException;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.SQLXML;
import java.sql.Savepoint;
import java.sql.Statement;
import java.sql.Struct;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executor;

import cn.hncu.util.ConnFactory;

public class ConnsUtils3 {
    private static List<Connection> pool = new ArrayList<Connection>();
    private static final int NUM=3;
    static{
        //采用配置文件的方式连接数据库
        try {
            Properties p = new Properties();
            p.load(ConnFactory.class.getClassLoader().getResourceAsStream("jdbc.properties"));
            String driver =p.getProperty("driver");
            String url = p.getProperty("url");
            String userName=p.getProperty("userName");
            String password = p.getProperty("password");
            //1加载连接器
            Class.forName(driver);
            for (int i = 0; i < NUM; i++) {
                //2建立连接
                final Connection con = DriverManager.getConnection(url, userName,password);
                Connection conn = (Connection)Proxy.newProxyInstance(
                        ConnsUtils3.class.getClassLoader(), 
                        new Class[]{Connection.class}, 
                        new InvocationHandler() {
                            @Override
                            public Object invoke(Object proxy, Method method, Object[] args)
                                    throws Throwable {
                                if(method.getName().equalsIgnoreCase("close")){
                                    pool.add(con);
                                    return null;
                                }else{
                                    return method.invoke(con, args);
                                }
                            }
                        }
                );
                pool.add(conn);//将代理后的的conn放入连接池
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static synchronized Connection  getCon() throws InterruptedException{
        if(pool.size()<=0){
            Thread.sleep(100);
            return getCon();
        }
        return pool.remove(0);
    }


}

cn.hncu.pool.day2.v2.TestPool

package cn.hncu.pool.day2.v2;

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


public class TestPool {
    public static void main(String[] args) {
        Connection con = null;
        try {
            con= ConnsUtils3.getCon();
            con.setAutoCommit(false);
            new OneThread(1).start();
            new OneThread(2).start();
            new OneThread(3).start();
            new OneThread(4).start();
            new OneThread(5).start();
            Statement st = con.createStatement();
            String sql ="insert into student values('p00a','测试一',23)";
            st.execute(sql);
            sql ="insert into student values('p0a0','测试二',29)";
            st.execute(sql);

            System.out.println("主线程准备提交");
            con.commit();
            System.out.println("主线程已经提交");
        } catch (Exception e) {
            try {
                con.rollback();
                System.out.println("主线程回滚");
            } catch (SQLException e1) {
                throw new RuntimeException("主线程事务回滚失败!", e1);
            }
        }finally{
            try {
                if(con!=null){
                    con.setAutoCommit(true);
                    con.close();
                }
            } catch (Exception e) {
                throw new RuntimeException("主线程事务关闭失败!", e);
            }
        }
    }
}

class OneThread extends Thread{
    private int num;
    public OneThread(int num) {
        this.num = num;
    }
    @Override
    public void run() {
        Connection con = null;
        try {
            con= ConnsUtils3.getCon();
            con.setAutoCommit(false);
            Statement st = con.createStatement();
            String sql ="insert into student values('p0"+num+"1','测试一"+num+"',25)";
            st.execute(sql);
            sql ="insert into student values('p"+num+"01','测试二"+num+"',26)";
            st.execute(sql);

            System.out.println("线程"+num+"准备提交");
            con.commit();
            System.out.println("线程"+num+"已经提交");
        } catch (Exception e) {
            try {
                con.rollback();
                System.out.println("线程"+num+"回滚");
            } catch (SQLException e1) {
                throw new RuntimeException("第"+num+"个事务回滚失败!", e1);
            }
        }finally{
            try {
                if(con!=null){
                    con.setAutoCommit(true);
                    con.close();
                }
            } catch (Exception e) {
                throw new RuntimeException("第"+num+"个事务关闭失败!", e);
            }
        }
    }

}

怎么样,是不是比起方法二 简洁多了。我也是这样认为的,哈哈。其实就是用动态代理技术,将connection对象的close方法拦截住,然后把拦截到的connection对象加到pool连接池中 至于其他的方法都放行。

让我们来学一下动态代理

动态代理要有接口,具体的代理对象。
动态代理其实很简单,就是java.lang.reflect包里的一个Proxy类的newProxyInstance静态方法,调用一下就好了。返回的就是被代理后的对象。

newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 

解释一下参数的意思:
第一个参数:是类加载的空间,可以用当前类或者系统的加载类
第二个参数:我们要代理对象实现的接口,可以是多个,所以是一个数组
第三个参数:和按钮监听一样,就是实现这个InvocationHandler 的类都行
然后实现InvocationHandler 的就要实现里面的抽象方法invoke方法:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 

再来解释一下这几个参数的意思
第一个参数:被代理的对象
第二个参数:学过类反射的都应该知道,这是利用类反射所拿到的方法
第三个参数:方法所需的参数。
然后在这个invoke方法里面,你就可以做你想做的事。 拦截方法,输入输出呀什么的,想干嘛就干嘛 很强大,很java。放行就是调用方法的invoke方法就执行这个方法了。

下面我们给几个简单一点例子理解一下动态代理
1、房东-中介-房客
IRenter接口

package cn.hncu.pool.day2.proxy.v1;

public interface IRenter {
    public void rent(int i);
}

Renter

package cn.hncu.pool.day2.proxy.v1;

public class Renter implements IRenter{

    @Override
    public void rent(int i) {
        System.out.println("给你"+i+"个房间,交费500元");
    }

    @Override
    public String toString() {
        System.out.println("你好:我是房东,请爱惜房间,不要乱涂乱画");
        return "你好:我是房东,请爱惜房间,不要乱涂乱画";
    }


}

Client

package cn.hncu.pool.day2.proxy.v1;

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 IRenter rent = new Renter();//接口是是IRenter 被代理的对象是rent
        Object obj = Proxy.newProxyInstance(
                Client.class.getClassLoader(), 
                new Class[]{IRenter.class}, 
                new InvocationHandler() {
            //动态代理最主要的方法
            @Override//proxy是被代理的对象,method是用类加载的方法,args是参数
            public Object invoke(Object proxy, Method method, Object[] args)
                    throws Throwable {
                System.out.println("我是中介,我收取了一定的中介费用");
                Object oo =method.invoke(rent, args);//在代理里面invoke就是放行这个方法,这里是什么都没有防范就都放行了
                System.out.println("如果还有什么需要请来找我们,我们是最好的");
                return oo;

            }
        });

        //返回的obj就是被代理之后的对象
        IRenter rr = (IRenter)obj;
        rr.rent(5);
        rr.toString(); //只要是rr里面的方法 都会执行在动态代理中输入的两条语句
    }
}

下面让我们试一下在拦截一下然后做点什么吧
Client2

package cn.hncu.pool.day2.proxy.v1;

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

public class Client2 {
    public static void main(String[] args) {
        final IRenter rent = new Renter();//接口是是IRenter 被代理的对象是rent
        Object obj = Proxy.newProxyInstance(
                Client2.class.getClassLoader(), 
                new Class[]{IRenter.class}, 
                new InvocationHandler() {
            //动态代理最主要的方法
            @Override//proxy是被代理的对象,method是用类加载的方法,args是参数
            public Object invoke(Object proxy, Method method, Object[] args)
                    throws Throwable {
                Object oo;//在代理里面invoke就是放行这个方法,这里是什么都没有防范就都放行了
                if (method.getName().equals("rent")) {
                    System.out.println("我是中介,我收取了一定的中介费用");
                    oo = method.invoke(rent, args);
                    System.out.println("如果还有什么需要请来找我们,我们是最好的");
                }else{
                    oo = method.invoke(rent, args); //这里不执行两条语句 直接放行
                }
                return oo;

            }
        });

        //返回的obj就是被代理之后的对象
        IRenter rr = (IRenter)obj;
        rr.rent(5);
        rr.toString(); //这种方法的代理就是只有是rent方法才会执行两条输出语句,而其他的就直接放行 原样输出
    }
}

2、代理java API中的接口
幻读 :在执行方法的时候告诉你执行了什么方法
ProxyDemo2

package cn.hncu.pool.day2.proxy.v2;

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

/*
 * 代理java API中的接口
 * 
 * */
public class ProxyDemo2 {
    /*
     * 幻读 :在执行方法的时候告诉你执行了什么方法
     * */
    public static void main(String[] args) {
        Object obj = Proxy.newProxyInstance(
                ProxyDemo2.class.getClassLoader(),
                new Class[]{List.class},
                new A()
        );
        List list = (List) obj;
        list.add(1);
        list.add("你好");
        list.remove(0);
        list.get(0);

    }
    static class A implements InvocationHandler{
        private  List list= new ArrayList();
        @Override
        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable {
            System.out.println("你现在执行的是"+method.getName()+"方法");
            Object oo = method.invoke(list, args);
            return oo;
        }

    }
}

3、做一个动态代理的模板
ProxyUtil

package cn.hncu.pool.day2.proxy.v3.utils;

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

public class ProxyUtil implements InvocationHandler {
    private Object srcObj = null;

    private ProxyUtil(Object srcObj) {
        this.srcObj = srcObj;
    }
    public static Object getProxy(Object srcObj){
        Object obj = Proxy.newProxyInstance(
                ProxyUtil.class.getClassLoader(), 
                srcObj.getClass().getInterfaces(), 
                new ProxyUtil(srcObj));
        return obj;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println("你现在执行的方法是:"+method.getName()+"方法,正在被工具类处理。。。");
        return method.invoke(srcObj, args);
    }


}

IAnimal接口

package cn.hncu.pool.day2.proxy.v3.domain;

public interface IAnimal {
    public void run();
}

IPerson接口

package cn.hncu.pool.day2.proxy.v3.domain;

public interface IPerson {
    public void sayHi();
}

Person

package cn.hncu.pool.day2.proxy.v3.domain;

public class Person  implements IPerson{
    private String name;

    public Person(String name) {
        this.name = name;
    }

    @Override
    public void sayHi() {
        System.out.println("你好啊,"+name+",祝你七夕节有情人终成眷属!");
    }

}

Dog

package cn.hncu.pool.day2.proxy.v3.domain;

public class Dog implements IAnimal{
    private String name;

    public Dog(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        System.out.println("Dog:"+name+",正在吃骨头,很开心");
    }

}

Clinet

package cn.hncu.pool.day2.proxy.v3;

import cn.hncu.pool.day2.proxy.v3.domain.Dog;
import cn.hncu.pool.day2.proxy.v3.domain.IAnimal;
import cn.hncu.pool.day2.proxy.v3.domain.IPerson;
import cn.hncu.pool.day2.proxy.v3.domain.Person;
import cn.hncu.pool.day2.proxy.v3.utils.ProxyUtil;

public class Client {
    public static void main(String[] args) {
        IPerson person = new Person("曹思琪");
        IPerson person2 =(IPerson)ProxyUtil.getProxy(person);//被代理之后的person
        person2.sayHi();


        IAnimal dog = new Dog("EDG-Nice");
        IAnimal dog2 = (IAnimal)ProxyUtil.getProxy(dog);
        dog2.run();
    }
}   

好了动态代理的学习到此为止。QWQ~~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值