zoukankan      html  css  js  c++  java
  • 使用静态工厂方法创建连接池(关于代理和包装的使用)

    创建Connection的过程是非常耗时的,为了保证Conection可以重用。应该将Connection进行池管理。

    使用静态工厂方法管理一个唯一的连接:

    package cn.itcast.utils;
    import java.sql.Connection;
    import java.sql.Driver;
    import java.sql.DriverManager;
    import java.util.Enumeration;
    /**
     * 用静态工厂方法管理一个唯一的可重用的连接
     */
    public class ConnUtils {
        private static Connection con;
        //在静态代码块中创建与数据库的连接
        static{
            try{
                Class.forName("com.mysql.jdbc.Driver");
                String url = "jdbc:mysql:///db909?characterEncoding=UTf8";
                con = DriverManager.getConnection(url,"root","1234");
            }catch(Exception e){
                throw new RuntimeException(e.getMessage(),e);
            }
        }
        //使用一个静态方法-静态工厂方法,返回connection实例
        public static Connection getCon(){
            return con;
        }
    }

    连接池的实现

    为什么要有连接池?

    1:维护多个连接。必须要保证每一个线程获取到的是不同的connection对象。

    2:提供一个方法可以回收连接。

    以下是最基本的实现,但是实际都不这么用的代码段:

    package cn.itcast.utils;
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.util.ArrayList;
    import java.util.List;
    public class ConnUtils2 {
        //声明一个容器,放所有声明的连接Connection
        private static List<Connection> pool = new ArrayList<Connection>();
        static{
            try{
                Class.forName("com.mysql.jdbc.Driver");
                String url = "jdbc:mysql:///db909?characterEncoding=UTf8";
                for(int i=0;i<3;i++){
                    //创建三个连接
                    Connection con = DriverManager.getConnection(url,"root","1234");
                    //将这个三个连接放到pool中去
                    pool.add(con);
                }
                System.err.println("连接是:"+pool);
            }catch(Exception e){
                throw new RuntimeException(e.getMessage(),e);
            }
        }
        public static Connection getCon(){
            synchronized (pool) {
                Connection con = pool.remove(0);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.err.println("还有:"+pool.size());
                return con;
            }
        }
        //手工的还连接
        public static void back(Connection con){
            System.err.println("还连接:"+con);
            pool.add(con);
        }
    }

    6、在代码中调用close时也可以还连接,对close方法进行修改。

    动态代理,作用:

    1:对某个方法增强。

    2:在不污染源类的情况下,修改原类的行为。

    代理类,与被代理类,两个不同的实体。

    要求:所有被代理的类,都必须要拥有一个接口。本质上是对方法进行修改,但其实它是通过反射执行的某个方法。

    动态代理的核心类:

    Proxy – 用于创建给定接口的子类,在内存中动态的创建。$Proxy0.此类只使用一次。

    InovocationHandler – 执行句柄。在执行时可以获取被代理类的所有反射。用户的每一次调用都会被这个句柄拦截到。

    package cn.itcast.demo;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class ProxyDemo2 {
        public static void main(String[] args) throws Exception {
            final GDG o = new GDG(4);
            System.err.println("1:声明被代理的对象"+o+","+o.getClass());
            Object proxyedObj = 
                    Proxy.newProxyInstance(
                            ProxyDemo2.class.getClassLoader(),
                            new Class[]{IGDG.class},
                            new InvocationHandler() {
                                public Object invoke(Object proxy, Method method, Object[] args)
                                        throws Throwable {
                                    System.err.println("5:正在调用的方法是:"+method.getName());//talk
                                    Object returnValue = method.invoke(o,args);
                                    System.err.println("6:通过反射调用目标(被代理类)类的方法成功,返回值是:"+returnValue);
                                    return "你不好";
                                }
                            }
                        );
            
            System.err.println("2:代理类创建完成:"+proxyedObj.getClass());
            System.err.println("3:强转成IGDG接口");
            IGDG o2 = (IGDG) proxyedObj;
            System.err.println("4:调用代理类的talk方法");
            String back = o2.talk();
            System.err.println("7:方法都调用完成	"+back);
        }
    }
    package cn.itcast.demo;
    public class GDG implements IGDG {
        int nu = 0;
        public GDG(int nm) {
            nu=nm;
        }
        public String talk(){
            System.err.println("5.1:开始讲了....."+nu);
            return "你好:"+nu;
        }
    }
    package cn.itcast.demo;
    
    public interface IGDG {
        String talk();
    }

    代理的任务

    1:在内存中创建某个接口的子类。

    2:拦截所有在代理上执行的方法。( 除了getClass方法。)

    用动态代理书写连接池

    设计:

       代理的目标:原生的connection。

       代理的目的:修改close方法,让close方法不可以关闭连接,且主动收回连接。

    通过动态代理,和线程通讯:

       1:对Connection进行代理。

    2:在获取Connection时,通过同步,如果没有连接时,就让线程进入等待池。

    3:修改close方法,且在还了连接以后唤醒正在等待的线程。

    package cn.itcast.utils;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.util.ArrayList;
    import java.util.List;
    public class ConnUtils4 {
        //第一步:声明连接池维护所有的连接
        private static List<Connection> pool = new ArrayList<Connection>();
        //第二步:静态代码块中创建多个连接
        static{
            try{
                Class.forName("com.mysql.jdbc.Driver");
                String url = "jdbc:mysql:///db909?characterEncoding=UTF8";
                for(int i=0;i<3;i++){
                    final Connection con = DriverManager.getConnection(url,"root","1234");//com.mysql.jdbc.Jdbc4Connection@
                    //对con对象进行动态代理生成Connectioin接口的子类,同时也是con的代理类。
                    Object proxyedCon = 
                            Proxy.newProxyInstance(
                                    ConnUtils4.class.getClassLoader(),
                                    new Class[]{Connection.class},
                                    //声明执行句柄,只对close方法设置拦截
                                    new InvocationHandler() {
                                        public Object invoke(Object proxy, Method method, Object[] args)
                                                throws Throwable {
                                            if(method.getName().equals("close")){
                                                System.err.println("有人想关闭连接,不能关,还连接");
                                                //将proxy再加到pool中,这个proxy就是proxyedCon
                                                synchronized (pool) {//将代理对象,再重新加回到pool中来,这个proxy参数,就是this.
                                                    pool.add((Connection) proxy);
                                                    pool.notify();
                                                }
                                                return null;
                                            }else{
                                                System.err.println("放行"+method.getName());
                                                return method.invoke(con, args);//通过反射执行con即被代理对象的方法。
                                            }
                                        }
                                    });
                    //一定要将代理对象添加到池中去。
                    pool.add((Connection) proxyedCon);
                }
            }catch(Exception e){
                throw new RuntimeException(e.getMessage(),e);
            }
        }
        /**
         * 提供一个静态工厂方法返回一个连接
         */
        public static Connection getCon(){
            synchronized (pool) {
                if(pool.size()==0){
                    try {
                        pool.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    return getCon();
                }
                Connection con = pool.remove(0);//返回一个代理的connection对象
                   System.err.println("还有几个:"+pool.size());
                return con;
            }
        }
    }

    优化版:

    通过类加载器读取一个资源文件:

    SomeClass.class.getResource(xxx) – 获取与SomeClass字节码同一个目录下的xxx文件。

    SomeClass.class.getClassLoader().getResource(“xxxx”); - 获取classpath根下上的xxx文件。

    1:将url,driver,name,pwd写到一个配置文件中去。-properties

    2:通过LinkedList来维护一个池。

    package cn.itcast.utils;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.net.HttpURLConnection;
    import java.net.URL;
    import java.net.URLDecoder;
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.util.ArrayList;
    import java.util.LinkedList;
    import java.util.List;
    import java.util.Properties;
    public class ConnUtils3 {
        private static LinkedList<Connection> pool = new LinkedList<Connection>();
        static{
            try {
                //声明资源器类 - 
                Properties prop = new Properties();
                //获取这个文件的路径
                URL url = ConnUtils3.class.getClassLoader().getResource("jdbc.properties");
                String path = url.getPath();
                //为了防止有中文或是空格
                path = URLDecoder.decode(path,"UTf-8");
                File file = new File(path);
                //加载jdbc.properties这个文件
                prop.load(new FileInputStream(file));
                //获取信息
                String driver = prop.getProperty("driver");
                Class.forName(driver);
                String jdbcurl = prop.getProperty("url");
                String nm = prop.getProperty("name");
                String pwd = prop.getProperty("pwd");
                //创建三个原生的连接,都将它们代理
                String poolSize = prop.getProperty("poolSize");
                int size = Integer.parseInt(poolSize);
                for(int i=0;i<size;i++){
                    final Connection con = DriverManager.getConnection(jdbcurl,nm,pwd);
                    //对con进行动态代理
                    Object proxyedObj = 
                            Proxy.newProxyInstance(ConnUtils3.class.getClassLoader(),
                                        new Class[]{Connection.class},
                                        new InvocationHandler() {
                                            public Object invoke(Object proxy, Method method, Object[] args)
                                                    throws Throwable {
                                                //是否是close方法
                                                if(method.getName().equals("close")){
                                                    synchronized (pool) {
                                                        pool.addLast((Connection) proxy);
                                                        pool.notify();
                                                    }
                                                    return null;//如果调用的是close则不会调用被代理类的方法。
                                                }
                                                return method.invoke(con, args);
                                            }
                                        });
                    //将代理对象放到pool中
                    pool.add((Connection) proxyedObj);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        
        public static Connection getConn(){
            synchronized (pool) {
                if(pool.size()==0){
                    try {
                        pool.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    return getConn();
                }else{
                    Connection con = pool.removeFirst();
                    System.err.println("还有几个:"+pool.size());
                    return con;
                }
            }
        }
        
        
    }

    再说连接池的作用

    1:维护多个连接。

    在初始化时创建List,将多个连接放到List中去.

    2:可以在close时回收连接

    对Connection进行动态代理,

    对close方法进行拦截。

    标准连接池的实现

    1:实现dataSource接口。

    2:声明一个集合类用于管理多个连接。

    3:必须要拥有一种能力,回收连接。

    4:必须要实现一个方法,getConnection以获取一个连接。

    5:实现DataSource接口的类,一般不能拥有static池对象。List.

    6:在一个程序中,要求只拥有一个DataSource实例就可以了。

    简单实现:

        // 声明一个连接池
        private LinkedList<Connection> pool = new LinkedList<Connection>();
    
        // 在初始化这个DataSourc的子类时在构造方法设置多个连接
        public DBpool() {
            try {
                Class.forName("com.mysql.jdbc.Driver");
                String url = "jdbc:mysql://127.0.0.1:3306/contacts?useUnicode=true&characterEncoding=UTF8";
                for (int i = 0; i < 3; i++) {
                    Connection con = DriverManager.getConnection(url,"root","admin");
                    pool.add(con);
                }
            } catch (Exception e) {
                throw new RuntimeException(e.getMessage(), e);
            }
        }
    
        @Override
        public Connection getConnection() throws SQLException {
            return pool.removeFirst();
        }

    通过代理进行改造:

    package javaee.datasource;
    
    import java.io.PrintWriter;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.SQLException;
    import java.sql.SQLFeatureNotSupportedException;
    import java.util.LinkedList;
    import java.util.logging.Logger;
    
    import javax.sql.DataSource;
    
    public class DBpool implements DataSource {
        // 声明一个连接池
        private LinkedList<Connection> pool = new LinkedList<Connection>();
    
        // 在初始化这个DataSourc的子类时在构造方法设置多个连接
        public DBpool() {
            try {
                Class.forName("com.mysql.jdbc.Driver");
                String url = "jdbc:mysql://127.0.0.1:3306/contacts?useUnicode=true&characterEncoding=UTF8";
                for (int i = 0; i < 3; i++) {
                    final Connection con = DriverManager.getConnection(url, "root",
                            "admin");
    
                    Object proxyedConn = Proxy.newProxyInstance(
                            DBpool.class.getClassLoader(),
                            new Class[] { Connection.class },
                            new InvocationHandler() {
    
                                @Override
                                public Object invoke(Object proxyedConnection,
                                        Method method, Object[] args)
                                        throws Throwable {
                                    if (method.getName().equals("close")) {
                                        synchronized (pool) {
                                            pool.addLast((Connection) proxyedConnection);
                                            pool.notify();
                                        }
                                        return null;
                                    }
    
                                    // 目标返回值
                                    Object returnValue = method.invoke(con, args);
    
                                    return returnValue;
                                }
                            });
                    pool.add((Connection) proxyedConn);
                }
            } catch (Exception e) {
                throw new RuntimeException(e.getMessage(), e);
            }
        }
    
        @Override
        public Connection getConnection() throws SQLException {
            synchronized (pool) {
                if (pool.size() == 0) {
                    try {
                        pool.wait();
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    return getConnection();
                }
            }
            Connection con = pool.removeFirst();
            System.err.println("size:" + pool.size());
            return con;
    
        }
                    //...
    }

    要求你自己实现一个类MyInputStream读取文件,且不能抛出异常(认识包装)

    package javaee.wrapper;
    
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    
    public class TestDemo {
        public static void main(String[] args) throws Exception {
            InputStream is=new MyinputStream("d:/a.txt");
            byte[] bt=new byte[1024];
            int len=0;
            while((len=is.read(bt))!=-1){
                String s=new String(bt,0,len);
                System.err.println(s);
            }
            is.close();
        }
    }
    package javaee.wrapper;
    
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.io.InputStream;
    
    class MyinputStream extends InputStream {
        private InputStream is;
    
        public MyinputStream(String path) {
            try {
                is = new FileInputStream(path);
            } catch (FileNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    
        public int read(byte[] b) {
            int i = -1;
            try {
                i = is.read(b);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return i;
        }
    
        public void close() {
            try {
                is.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    
        @Override
        public int read() throws IOException {
            // TODO Auto-generated method stub
            return 0;
        }
    }

    以下通过包装实现对close方法的修改,以回收连接

    1:实现Connection接口,拥有一个Connection的成员。

    2:修改close方法。

    3:其他的方法都调用成员变量的connection。

    实现的部分代码:

    public class DBpool_wrapper implements DataSource {
        // 声明一个连接池
        private LinkedList<Connection> pool = new LinkedList<Connection>();
    
        // 在初始化这个DataSourc的子类时在构造方法设置多个连接
        public DBpool_wrapper() {
            try {
                Class.forName("com.mysql.jdbc.Driver");
                String url = "jdbc:mysql://127.0.0.1:3306/contacts?useUnicode=true&characterEncoding=UTF8";
                for (int i = 0; i < 3; i++) {
                    Connection con = DriverManager.getConnection(url, "root",
                            "admin");
                    MyConn conn = new MyConn(con);
                    pool.add(conn);
                }
            } catch (Exception e) {
                throw new RuntimeException(e.getMessage(), e);
            }
        }
    
        public Connection getConnection() {
            synchronized (pool) {
                if (pool.size() == 0) {
                    try {
                        pool.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    return getConnection();
                }
                Connection con = pool.removeFirst();
                System.err.println("size:" + pool.size());
                return con;
            }
        }
    
        public class MyConn implements Connection {
            private Connection conn;
    
            // 通过构造接收MySql的connection的对象JDBC4Connection@8888
            public MyConn(Connection con) {
                this.conn = con;
            }
    
            public void close() throws SQLException {
                synchronized (pool) {
                    System.err.println("有人还连接:" + this);
                    pool.add(this);
                    pool.notify();
                }
            }
        }
    }

    用包装处理get方式的乱码:

    package javaee.utils;
    
    import java.io.IOException;
    import java.io.UnsupportedEncodingException;
    import java.lang.reflect.Method;
    import java.util.Iterator;
    import java.util.Map;
    
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletRequestWrapper;
    import javax.servlet.http.HttpServletResponse;
    
    import org.apache.taglibs.standard.lang.jstl.ValueSuffix;
    
    public abstract class BaseServlet extends HttpServlet {
    
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp)
                throws ServletException, IOException {
            doPost(req, resp);
        }
    
        @Override
        protected void doPost(HttpServletRequest req, HttpServletResponse resp)
                throws ServletException, IOException {
            req.setCharacterEncoding("UTF-8");
            // getparamters
            String methodName = req.getParameter("cmd");
            // 通过反射调用子类的方法
            try {
                Method method = this.getClass().getMethod(methodName,
                        HttpServletRequest.class, HttpServletResponse.class);
                MyServletRequest myreq = new MyServletRequest(req);
                method.invoke(this, myreq, resp);
            } catch (Exception e) {
                // TODO Auto-generated catch block
                throw new RuntimeException(e.getMessage(), e);
            }
        }
    
    }
    
    class MyServletRequest extends HttpServletRequestWrapper {
        HttpServletRequest req;
    
        public MyServletRequest(HttpServletRequest request) {
            super(request);
            this.req = request;
        }
    
        public String getParameter(String name) {
            // TODO Auto-generated method stub
            String value = req.getParameter(name);
            if (req.getMethod().equals("GET")) {
                System.err.println("Decode");
                try {
                    value = new String(value.getBytes("ISO-8859-1"), "UTF-8");
                } catch (UnsupportedEncodingException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            return value;
        }
    
        public String[] getParameterValues(String name) {
            // TODO Auto-generated method stub
            String[] value = req.getParameterValues(name);
            if (req.getMethod().equals("GET")) {
                System.err.println("Decode[]");
                try {
                    for (int i = 0; i < value.length; i++) {
                        value[i] = new String(value[i].getBytes("ISO-8859-1"),
                                req.getCharacterEncoding());
    
                    }
                } catch (UnsupportedEncodingException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            return value;
        }
    
        public Map<String, String[]> getParameterMap() {
            Map<String, String[]> map = req.getParameterMap();
            if (req.getMethod().equals("GET")) {
                System.err.println("Decode_Map");
                Iterator<String[]> it = map.values().iterator();
                while (it.hasNext()) {
                    String[] ss = it.next();
                    for (int i = 0; i < ss.length; i++) {
                        try {
                            ss[i] = new String(ss[i].getBytes("ISO-8859-1"),
                                    req.getCharacterEncoding());
                        } catch (UnsupportedEncodingException e) {
                            e.printStackTrace();
                        }
                    }
                }
                
            }
            return map;
        }
    }

    7、小总结

    1:代理或是包装都是对某个类的方法进行增强。

    代理:必须要根据给定的接口,在内存中创建这个接口的子类。$Proxy0。

    包装:不需要接口,但声明声明一个类,变成被包装类的子类,同时拥有一个被包装类的成员。

    2:代理基本代码:

    Object proxyedObj = 
        Proxy.newProxyInstance(ClassLoader,
                               class[]{被代理的类的接口数组.class},
                               new InvocationHandler(){//执行句柄
                                      public Object invode(Object 代理,Method 方法反射,object[] args){
                                              Reutrn method.invode(被代理类,args);
                                 }
                               }

    3:包装:

    如果一个类是某个类的包装类,则:

    A extends B{

    Privet B b;

    }

    4:什么情况下,使用包装,什么情况下使用代理

    如果官方(SUN)提供了包装类适配器,则应该优先使用包装。如HttpServletRequest,它的包装类就是HtpServletRequestWraper.

    如果官方没有提供包装类的适配器,则可以使用动态代理。如Connection。

    5:我们已经学习过的类中哪些是包装模式:

    GenericServlet- 包装模式。

    public abstract class GenericServlet

    implements Servlet, ServletConfig, java.io.Serializable

    private transient ServletConfig config;

    IO

    New BufferedReader(New FileReader(new FileInpjutStreamRreader(new FileInputSteam()));

  • 相关阅读:
    关于SQL Server将一列的多行内容拼接成一行的问题讨论
    Win7 disk.sys无法加载的问题
    mvel2.0语法指南
    让网页不被缓存的解决办法
    让html页面不缓存js的实现方法
    mysql中间件研究(tddl atlas cobar sharding-jdbc)
    jdk环境变量设置理解
    the import cannot be resolved
    Oracle解除表锁定问题
    windows系统中软件开发常用的软件
  • 原文地址:https://www.cnblogs.com/sunhan/p/3542131.html
Copyright © 2011-2022 走看看