Giter Club home page Giter Club logo

java-web-'s Introduction

学习笔记库

The note of Java Web

个人学习用的一些小知识的笔记。


目录

java-web-'s People

Contributors

zuiliushang avatar

Stargazers

李森林 avatar fengzhao avatar  avatar Jermic avatar dreamxuwj avatar yinchunfeng avatar  avatar genglong avatar

Watchers

James Cloos avatar  avatar

Forkers

gl5

java-web-'s Issues

模拟转账(Dbutils框架)

首先我们会想到改写Dao 层来开启事务,从而实现转账。

public void transfer(int sourceid,int targetid,float money) throws SQLException{    
        Connection conn = null;
        try{
            conn = JdbcUtils.getConnection();
            conn.setAutoCommit(false);
            QueryRunner runner = new QueryRunner();//不要给连接池

            String sql1 = "update account set money=money-100 where id=1";
            runner.update(conn,sql1);

            String sql2 = "update account set money=money+100 where id=2";
            runner.update(conn,sql2);

            conn.commit();
        }catch(Exception e){
            if(conn!=null){
                conn.rollback();
            }
        }finally {
            conn.close();
        }
  • 但是,这样在Dao 层我们就会出现了 service 层的代码,违反了三层架构模式的**。所以我们仅仅改写Dao为:
    public void update(Account account) throws SQLException{

        QueryRunner qr = new QueryRunner();//不传递连接池
        String sql = "update account set name=?,money=? where id=?";
        Object params[] = {account.getName(),account.getMoney(),account.getId()};
        qr.update(conn,sql, params);
    }

    public Account find(int id) throws SQLException{
        QueryRunner qr = new QueryRunner();
        String sql = "select * from account where id=?";
        Account account = qr.query(conn,sql, new BeanHandler<>(Account.class),id );
        return account;
    }
  • 然后改写我们的业务层 service:
    public void transfer(int sourceId,int targetId,float money) throws SQLException{
        Connection conn = null;

        try{
            conn = JdbcUtils.getConnection();
            conn.setAutoCommit(false);
            AccountDao dao = new AccountDao(conn);
            Account source = dao.find(sourceId);
            Account target = dao.find(targetId);

            source.setMoney(source.getMoney()-money);
            target.setMoney(target.getMoney()+money);
            //这个要同一事务,也就是2次update方法共享同一连接
            //只能在service 自己管理连接
            dao.update(source);

            //int i=1/0;

            dao.update(target);
            conn.commit();
        }catch(Exception e){

            if (conn!=null) {
                conn.rollback();
            }
            e.printStackTrace();
        }finally {

            if(conn!=null){
                conn.close();
            }
        }

    }
  • 但是这样的代码并不优雅,所以我们会用到 ThreadLocal 类来管理连接:

ThreadLocal 类:一个容器,向这个容器存储的对象,在当前线程内都可以取得出来。

  • 简单的入门示例:
    public static void main(String[] args) {

        Thread currentThread = Thread.currentThread();
        System.out.println(currentThread);
        ThreadLocal t = new ThreadLocal<>();
        t.set("aaa");//把某个对象绑定到当前线程上  map(currentThread,"aaa")
        String value = (String) t.get();
        System.out.println(value);
    }
  • 首先改写 JdbcUtils 工具类

增加一个线程容器

    private static ThreadLocal<Connection> threadLocal = new ThreadLocal<>();

改写方法

    public static void startTransaction(){
        try {
            Connection conn = threadLocal.get();
            if(conn==null){
                conn = getConnection();
                threadLocal.set(conn);//把conn绑定到当前线程
            }
            conn.setAutoCommit(false);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static void rollback(){
        try {
            Connection conn = threadLocal.get();
            if (conn!=null) {
                conn.rollback();
            }   
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static void commit(){
        try {
            Connection conn = threadLocal.get();
            if (conn!=null) {
                conn.commit();
            }   
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static void release(){
        try {
            Connection conn = threadLocal.get();
            if (conn!=null) {
                conn.close();
                threadLocal.remove();//接触当前线程上绑定的connection
            }       
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    public static Connection getConnection() throws SQLException{
        Connection conn = threadLocal.get();
        if (conn==null) {
            conn = getDataSource().getConnection();
            threadLocal.set(conn);
        }
        return conn;
    }

改写Service 层:

    public void transfer(int sourceId,int targetId,float money) throws SQLException{
        Connection conn = null;

        try{
            JdbcUtils.startTransaction();

            AccountDao dao = new AccountDao();
            Account source = dao.find(sourceId);
            Account target = dao.find(targetId);

            source.setMoney(source.getMoney()-money);
            target.setMoney(target.getMoney()+money);
            //这个要同一事务,也就是2次update方法共享同一连接
            //只能在service 自己管理连接
            dao.update(source);

            //int i=1/0;

            dao.update(target);
            JdbcUtils.commit();
        }catch(Exception e){

            if (conn!=null) {
                JdbcUtils.rollback();
            }
            e.printStackTrace();
        }finally {
            JdbcUtils.release();
        }

    }

改写 Dao 层:

    public void update(Account account) throws SQLException{

        QueryRunner qr = new QueryRunner();
        String sql = "update account set name=?,money=? where id=?";
        Object params[] = {account.getName(),account.getMoney(),account.getId()};
        qr.update(JdbcUtils.getConnection(),sql, params);
    }

    public Account find(int id) throws SQLException{
        QueryRunner qr = new QueryRunner();
        String sql = "select * from account where id=?";
        Account account = 
qr.query(JdbcUtils.getConnection(),sql, new BeanHandler<>(Account.class),id );
        return account;
    }

DTD学习笔记

DTD语法细节

在DTD文档中使用ELEMENT声明一个XML元素

元素类型可以是元素内容、或者类型

如为元素的类型,则直接书写。

DTD规范还定义了如下几种类型
EMPTY:用于定义空元素,例如


(没有子元素)
ANY:表示元素内容为任意类型。 (元素里面可以乱写子元素)

","是决定属性的顺序 如(书名,作者,售价)
"|" 多个任意选一个 只能出现一个
元素出现没有顺序(书名|作者|售价)*

+:一次或者多次
?:0次或者一次
*:0次或者多次

ATTLIST设置属性

例子

对应XML
<商品 类别="服装" 颜色="黄色">…</商品>
<商品 类别="服装">…</商品>

设置说明:

REQUIRED:必须设置该属性

IMPLIED:可以设置也可以不设置

FIXED:说明属性的取值固定为一个值,在XML文件中不能为

该属性设置其他值,但需要为该属性提供这个值
网站职务 CDATA #FIXED "页面作者"
(在XML中 不设置属性值则使用默认值!)
爱好 CDATA "上网"

常用属性类型
CDATA:表示普通文本字符串
ENUMERATED:表示某一系列 品种 (肌XC|XX) XX
ID(编号不能以数字开头)
ENTITY实体
(实体用于为一段内容穿件一个别名,以后再XML文档中就可以使用别名来引用这段内容)

JDBC的学习(编写自己的JDBC框架)

JDBC的学习(编写自己的JDBC框架)

元数据 — DataBaseMetaData

元数据:数据库、表、列的定义信息。

Connection.getDatabaseMetaData()

DataBaseMetaData 对象

  • getURL():返回一个 String 类对象,代表数据库的URL。
  • getUserName():返回连接当前数据库管理系统的用户名。
  • getDatabaseProductName():返回数据库的产品名称。
  • getDatabaseProductVersion():返回数据库的版本号。
  • getDriverName():返回驱动程序的版本号。
  • isReadOnly():返回一个 boolean 值,指示数据库是否只允许读操作。

元数据 — ParameterMetaData

PreparedStatement.getParameterMetaData()

  • 获得代表 PreparedStatement 元数据的 ParameterMetaData 对象。
  • Select * from user where name=? and password=?

ParameterMetaData 对象

  • getParameterCount(): 获得指定参数的个数。
  • getParameterType(int param):获得指定参数的 sql 类型。

元数据 — ResultSetMetaData

ResultSet.getMetaData()

  • 获得代表 ResultSet 对象元数据的 ResultSetMetaData 对象。

ResultSetMetaData 对象:

  • getColumnCount():返回 resultset 对象的列数。
  • getColumnName(int column) :获得指定列的名称。
  • getColumnTypeName(int column):获得指定列的类型。

例子:数据库查询功能

public static void update(String sql,Object params[]) throws SQLException{
        Connection conn = null;
        PreparedStatement st = null;
        ResultSet rs = null;

        try {

            conn=JdbcUtils_C3P0.getConnection();
            st = conn.prepareStatement(sql);
            for(int i=0;i<params.length;i++){
                st.setObject(i+1, params[i]);
            }
            st.executeUpdate();

        } finally {
            JdbcUtils_C3P0.release(conn, rs, st);
        }
    }

    public static Object query(String sql,Object params[],ResultSetHandler rsh) throws SQLException{
        Connection conn = null;
        PreparedStatement st = null;
        ResultSet rs = null;

        try {

            conn=JdbcUtils_C3P0.getConnection();
            st = conn.prepareStatement(sql);
            for(int i=0;i<params.length;i++){
                st.setObject(i+1, params[i]);
            }
            rs=st.executeQuery();
            return rsh.handler(rs);

        } finally {
            release(conn, rs, st);
        }
    }

}

interface ResultSetHandler{
    public Object handler(ResultSet rs);
}

class BeanHandler implements ResultSetHandler{

    private Class clazz;
    public BeanHandler(Class clazz) {
        this.clazz = clazz;
    }
    @Override
    public Object handler(ResultSet rs) {
        try {
            if (!rs.next()) {
                return null;
            }
            Object bean = clazz.newInstance();

            ResultSetMetaData metaData = rs.getMetaData();
            int columnCount = metaData.getColumnCount();//得到结果集中有几列数据
            for(int i=0;i<columnCount;i++){
                String coulmnName = metaData.getColumnName(i+1);//得到每列的列名
                Object coulmnData = rs.getObject(i+1);//得到列数据

                Field f = clazz.getDeclaredField(coulmnName);//反射出类上列名对应的属性
                f.setAccessible(true);
                f.set(bean,coulmnData);
            }
            return bean;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }

}

class BeanListHandler implements ResultSetHandler{

    private Class clazz;
    public BeanListHandler(Class clazz) {
        this.clazz = clazz;
    }


    @Override
    public Object handler(ResultSet rs) {
        try {
            List list = new ArrayList();
            while(rs.next()){
                Object bean = clazz.newInstance();

                ResultSetMetaData metaData = rs.getMetaData();
                int count = metaData.getColumnCount();
                for(int i=0;i<count;i++){
                    String name = metaData.getColumnName(i+1);
                    Object value = rs.getObject(name);

                    Field f = bean.getClass().getDeclaredField(name);
                    f.setAccessible(true);
                    f.set(bean, value);
                }
                list.add(bean);
            }
            return list.size()>0?list:null;

        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }


}

HTTP请求的一些细节

HTTP请求

Accept:text/html,image/* 浏览器告诉服务器所支持的数据类型
Accept-Charset:ISO-889-1 字符集
Accept-Encoding:gzip,compress 压缩格式
Accept-Language:en-us,zh-cn 语言环境
Host:www.zuiliushang.com:8080 想访问哪台主机
If-Modified-Since:Tue,11 Jul 2016 18:XX:XX GMT 缓存数据时间
Referer:http://www.zuiliushang.com/index.jsp 我是从哪个页面来的(防盗链)
User-Agent:Mozilla/4.0(compatible;MSIE 5.5;Windows NT 5.0) 客户机环境
Cookie
Connection:close/Keep-Alive 通知服务器请求完之后断开或者保持连接
Date:Tue,11 Jul 2016 18:23:51 GMT 请求时间

请求重定向和请求转发的区别

RequestDispatcher.forward方法只能将请求转发给同一个WEB应用中的组件;而HttpServletResponse.sendRedirect方法还可以重定向到同一个站点上的其他应用程序中的资源,甚至是使用绝对URL重定向到其他站点的资源。

如果传递给HttpServletResponse.sendRedirect方法的相对URL以"/"开头,他是相对于整个WEB站点的根目录;如果创建RequestDispatcher对象时指定的相对URL以"/"开头,它是相对于当前WEB应用程序的根目录。

调用HttpServletResponse.sendRedirect方法重定向的访问过程结束后,浏览器地址栏中显示的URL会发生改变,由初始的URL地址变成重定向的目标URL;而调用RequestDispatcher,forward 方法的请求转发过程结束后,浏览器地址栏保持初始的URL地址不变。

HttpServletResponse.sendRedirect 方法对浏览器的请求直接作出响应,响应的结果就是告诉浏览器去重新发出对另外一个URL的访问请求;

RequestDispatcher.forward方法在服务器端内部将请求转发给另外一个资源,浏览器只知道发出了请求并得到了相应结果,并不知道在服务器程序内部发生了转发行为。

RequestDispatcher.forward方法的调用者与被调用者之间共享相同的request对象和response对象,它们属于同一个访问请求和响应过程;而HttpServletResponse.sendRedirect方法调用者与被调用者使用个字的request对象和response对象,它们属于两个独立的访问请求和响应过程。

使用JDBC处理大数据

使用JDBC处理大数据

在实际开发中,程序需要把大文本或二进制数据保存到数据库。

基本概念:大数据也称之为 LOB (Large Objects),LOB 又分为:

  • clob 和 blob
    clob 用于存储大文本。Text
    blob 用于存储二进制数据,例如图像、声音、二进制文等。

对MySQL 而言只有 blob , 而没有clob, mysql 储存大文本采用的是 Text, Text 和 blob 分别又分为:

  • TINYTEXT TEXT MEDIUMTEXT LONGTEXT
  • TINYBLOB BLOB MEDIUMBLOB LONGBLOB

对于 MySQL 中的 Text 类型,可调用如下方法设置:

PreparedStatement.setCharacterStream(index,reader,length);
//注意length 长度须设置,并且设置为 int 型。

对 MySQL 中的Text 类型,可调用如下方法获取:

reader = resultSet.getCharacterStream(i);
reader = resultSet.getClob(i).getCharacterStream();
string s = resultSet.getString(i);

使用 JDBC 进行批处理

业务场景:当需要向数据库发送一批 SQL 语句执行时,应避免向数据库一条条的发送执行,而应采用 JDBC 的批处理机制,以提升执行效率。

实现批处理有两种方式,第一种方式:

  • Statement.addBatch(sql) list

执行批处理 SQL 语句

  • executeBatch()方法:执行批处理命令。
  • clearBatch()方法:清除批处理命令。

采用 Statement.addBatch(sql) 方式实现批处理:

  • 优点:可以向数据库发送多条不同的 SQL 语句。
  • 缺点:

    SQL语句没有预编译

    当向数据库发送多条语句相同,但仅参数不同的 SQL 语句时,需要重复写上很多条SQL 语句。例如:

Insert into user(name,password) values('aa','123');
Insert into user(name,password) values('bb','123');
Insert into user(name,password) values('cc','123');
Insert into user(name,password) values('dd','123');

实现批处理的第二种方式:

  • PreparedStatement.addBatch()

例如:

String sql="insert into testbatch(id,name) values(?,?)";
PrepareStatement st = conn.prepareStatement(sql);
for(int i = 0 ;i<100000000;i++)
{
    st.setInt(1,i);
    st.setString(2,"aa"+i);
    st.addBatch();

    if(i%1000==0)
    {
        st.executeBatch();
        st.clearBatch;
    }
}
st.executeBatch();

获得数据库自动生成的主键

示例:

            conn = JdbcUtils.getConnection();
            String sql = "insert into test(name) values(?)";
            st = conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
            st.setString(1, "asda");
            st.executeUpdate();
            rs = st.getGeneratedKeys();
            if (rs.next()) {
                System.out.println(rs.getInt(1));
            }

JDBC 调用存储过程

编写存储过程

  • 得到 CallableStatement ,并调用存储过程:
    CallableStatement cStmt = conn.prepareCall("{call demoSp(?,?)}");
  • 设置参数,注册返回值,得到输出:
    cStmt.registerOutParameter(2,Type.VARCHAR);
    cStmt.setString(1,"abcdefg");
    cStmt.execute();
    System.out.println(cStmt.getString(2));

生成服务器证书(加密技术)

命令:

keytool -genkey -alias tomcat -keyalg RSA

在server.xml文件中配置加密连接器,
并制定加密连接器从哪个密钥库中拿出数字证书
server.xml
<Connector port = "8443"protocol="HTTP"
浏览器访问 https:...:8443

JDBC的学习(数据库连接池篇)

使用数据库连接池优化程序性能

应用程序直接获取链接的缺点。

qq 20160221142334

  • 缺点:用户每次请求都需要向数据库获得链接,而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长。假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大的浪费数据库的资源,并且极易造成数据库服务器内存溢出、死机。
  • 使用连接池:

qq 20160221142334

编写数据库连接池

  • 编写连接池需实现 java.sql.DataSource 接口。DataSource 接口中定义了两个重载的 getConnection 方法:
  • Connection getConnection()
  • Connection getConnection(String username,String password )
  • 实现 DataSource 接口,并实现连接池功能的步骤:
  • 在 DataSource 构造函数中批量创建与数据库的连接,并把创建的连接加入 LinkedList 对象中。
  • 实现 getConnection 方法, 让 getConnection 方法每次调用时,从 LinkedList 中取一个 Connection 返回给用户。
  • 当用户使用完 Connection , 调用 Connection.close() 方法时,Collection 对象应保证将自己返回到 LinkedList 中,而不要把 conn 还给数据库。
  • Collection保证将自己返回给LinkedList 中是此处编程的难点!!
//使用集合保存 连接
    private static LinkedList<Connection> list = new LinkedList<Connection>();


    static{

        try{
        InputStream inputStream =
 JdbcPool.class.getClassLoader().getResourceAsStream("db.properties");
        Properties properties = new Properties();
        properties.load(inputStream);

        String driver = properties.getProperty("driver");
        String url = properties.getProperty("url");
        String user = properties.getProperty("user");
        String password = properties.getProperty("password");

        Class.forName(driver);

        for(int i = 0;i<10;i++){
            Connection conn = DriverManager.getConnection(url, user, password);
            list.add(conn);
        }

        }catch(Exception e){
            throw new ExceptionInInitializerError(e);
        }

    }

/*
     *方法1.写一个子类,覆盖 close 方法
     *方法2.写一个connection的包装类,增强 close 方法
     *方法3.用动态代理,返回一个代理对象出去,拦截close方法的调用,对close进行增强 
     */
    @Override
    public Connection getConnection() throws SQLException {

        if(list.size()>0){

            final Connection conn = list.removeFirst();
            return (Connection)Proxy.newProxyInstance(JdbcPool.class.getClassLoader(), 
conn.getClass().getInterfaces(),new InvocationHandler() {

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

                    if(!method.getName().equals("close")){  
                        return method.invoke(conn, args);
                    }else {
                        return list.add(conn);
                    }
                }
            });

        }else
        {
            throw new RuntimeException("对不起,数据库忙碌");
        }
    }

包装设计模式:

/*
用包装设计模式对某个对象进行增强
1.写一个类,实现与被增强对象(mysql的connection)相同的接口。
2.定义一个变量,指向被增强对象。
3.定义一个构造函数方法,接收被增强对象。
4.覆盖想增强的方法。
5.对于不想增强的方法,直接调用被增强对象的方法。
 */

class MyConnection implements Connection{

    private Connection conn;
    private List pool;
    public MyConnection(Connection conn,List pool){
        this.conn = conn;
        this.pool = pool;
    }


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


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

数据库连接:

    private static JdbcPool pool = new JdbcPool();

    public static Connection getConnection() throws SQLException{

        return pool.getConnection();
    }

开源数据库连接池

  • 现在很多 WEB 服务器 (Weblogic,WebSphere,Tomcat)都提供了 DataSource 的实现,即连接池的实现。通常我们把 DataSource 的实现,按其英文含义称之为数据源,数据源中都包含了数据库连接池的实现。
  • 也有一些开源组织提供了数据源的独立实现:
    DBCP 数据库连接池
    C3P0 数据库连接池
  • 实际应用时不需要编写连接数据库代码,直接从数据源获得数据库的连接。程序猿编程时也应尽量使用这些数据源的实现,以提升程序的数据库访问性能。

DBCP 数据源

  • DBCP 是 Apache 软件基金组织下的开源连接池实现,使用 DBCP 数据源,应用程序应在系统中增加如下两个 jar 文件:
  • Commons-dbcp.jap:连接池的实现
  • commons-pool.jar:连接池实现的依赖库
  • Tomcat 的连接池正是采用该连接池来实现的。该数据库连接池既可以与应用服务器整合使用,也可由应用程序独立使用。

DBCP配置文件dbcpconfig.properties

#连接设置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbc
username=root
password=zsdegywzxh

#<!-- 初始化连接 -->
initialSize=10

#最大链接数量
maxActive=50

#<!-- 最大空闲连接 -->
maxIdle=20

#<!-- 最小空闲连接 -->
minIdle=5

#<!-- 超时等待时间 以毫秒为单位 60000毫秒/1000等于60秒 -->
maxWait=60000

#JDBC驱动建立连接时附带的连接属性属性格式必须为:[属性名=property;]
#注意:"user"与"password"两个属性会被明确地传递,因此这里不需要包含他们。
connectionProperties=useUnicode=true;characterEncoding=UTF8

#指定由连接池所创建的连接自动提交(auto-commit)状态。
defaultAutoCommit=true

#driver default 指定由连接池所创建的链接的只读(read-only)状态。
#如果没有设置该值,则"setReadOnly"方法将不被调用。(某些驱动并不支持只读模式,如:Informix)
defaultReadOnly=

#driver default 指定由连接池所创建的连接事务级别(TransactionIsolation)
#通过这个池创建连接的默认事务策略,设置值为下列中的某一个: (参考 javadoc),NONE,
READ_COMMITTED,READ_UNCOMMITTED,REPEATABLE_READ,SERIALIZABLE
defaultTransactionIsolation=READ_UNCOMMITTED

示例代码:

private static DataSource ds = null;

    static{
        try{
        InputStream in = 
JdbcUtils_DBCP.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
        Properties properties = new Properties();
        properties.load(in);

        BasicDataSourceFactory factory = new BasicDataSourceFactory();
        ds = factory.createDataSource(properties);
        System.out.println(ds+"aaaa");
        }catch(Exception e){
            throw new ExceptionInInitializerError(e);
        }

    }

    public static Connection getConnection() throws SQLException{

        return ds.getConnection();
    }

C3P0数据源

  • 使用 C3P0数据源,应用程序应在系统中增加如下两个 jar 文件:
  • c3p0:连接池的实现
  • mchange-commons-java:连接池实现的依赖库

示例

    private static ComboPooledDataSource ds = null;

    static{
        try{
            ds = new ComboPooledDataSource();
            ds.setDriverClass("com.mysql.jdbc.Driver");
            ds.setJdbcUrl("jdbc:mysql://localhost:3306/day16");
            ds.setUser("root");
            ds.setPassword("sdfaf");

            ds.setInitialPoolSize(10);
            ds.setMaxPoolSize(20);
            ds.setMinPoolSize(5);
        }catch(Exception e){
            throw new ExceptionInInitializerError(e);
        }

    }

配置 Tomcat 数据源

查看 Tomcat 文档,示例代码:

<Context>
    <Resource name="jdbc/datasource" auto="Container"
type="javax.sql.DataSource" username="root" password="root"
driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost:3306/jdbc" 
maxActive="8" maxIdle="4"/>
</Context>
    Context initCtx = new InitialContext();
    Context envCtx = (Context) initCtx.lookup("java:comp/env");
    dataSource = (DataSource)envCtx.lookup("jdbc/datasource");
  • 特别提醒:此种配置下,驱动jar 文件必须防止在tomcat 的 lib 下

例子:

try{
            Context initCtx = new InitialContext();//初始化jndi
            Context envCtx = (Context) initCtx.lookup("java:comp/env");//得到jndi容器
            DataSource ds = (DataSource)
              envCtx.lookup("jdbc/EmployeeDB");//从容器中检索连接池

            Connection conn = ds.getConnection();

        conn.close();
        }catch(Exception e){
            e.printStackTrace();
        }
<Context>
  <Resource name="jdbc/EmployeeDB"
            auth="Container"
            type="javax.sql.DataSource"
            username="root"
            password="zsdegywzxh"
            driverClassName="com.mysql.jdbc.Driver"
            url="jdbc:mysql://localhost:3306/day16"
            maxTotal="8"
            maxIdle="4"/>
</Context>

JAVA-ConvertUtils.register注册转换器用法

OrzzzZz.. 通向技术宅之路艰难啊

在我们使用

   BeanUtils.copyProperties(目标对象, 原对象);

由于copyProperties只支持基本的类型装换,所以当我们要转换像Date这种类型的时候
我们就需要注册一个转换器

    ConvertUtils.register(new DateLocaleConverter(), Date.class);
    //需要转成Date类型的数据都要通过DateLocaleConverter这个转换器的处理。
    BeanUtils.copyProperties(目标对象, 原对象);

EL表达式中的11个隐式对象

EL表达式中的11个隐式对象

  • pageContext

    对应于JSP中的pageContext对象 是pageContext对象!

  • pageScope

    代表page域中用于保存属性的Map对象

  • requestScope

    代表request域中用于保存属性的Map对象

  • sessionScope

    代表session域中用于保存属性的Map对象

  • applicationScope

    代表application域中用于保存属性的Map对象

  • param

    表示一个保存了所有请求参数的Map集合

   <!-- 此表达式经常用在数据回显上 -->
<form action="${pageContext.request.contextPath}/servlet/ServletDemo1" method="post">
    <input type="text" name="username" value="${param.username}" />
    <input type="submit" value="注册" />
</form>
  • paramValues

    表示了一个保存了所有请求参数的Map对象,它对于某个请求参数,返回的是一个String[]
    用于可能username==xsh&username=xsh1

  • header

    表示一个保存了所有http请求头字段的Map对象。如果头里面有“-”,例如Accept-Encoding,
    则要header["Accept-Encoding"]。

  • headerValues

    表示一个保存了所有http请求头字段的Map对象。返回的是String[],但是如果头里面有“-”,例如Accept-Encoding,
    则要headerValues["Accept-Encoding"]。

  • cookie

    表示一个保存了所有cookie的Map对象

${cookie.JSESSIONID.value}
  • initParam

    表示一个保存了所有web应用初始化参数的map对象
    在web.xml文件中的对象

<context-param>
    <param-name>name</param-name>
    <param-value>xsh</param-value>
</context-param>

Filter 高级开发 篇

Filter 高级开发

由于开发人员在 filter 中可以得到代表用户请求和响应的 request、response 对象,因此在编程中可以使用 Decorator(装饰器) 模式对 request、response 对象进行包装,再把包装对象传给目标资源,从而实现一些特殊需求。

案例1:实现解决全站乱码转换器

//为解决全站乱码
public class CharacterEncodingFilter implements Filter{

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // TODO Auto-generated method stub

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;
        //解决post的乱码
        req.setCharacterEncoding("utf-8");
        resp.setContentType("text/html;charset=utf-8");
        resp.setCharacterEncoding("utf-8");
        System.out.println("aaa");
        MyCharacterEncodingRequest requestWrapper = new MyCharacterEncodingRequest(req);
        chain.doFilter(requestWrapper, resp);
    }

    @Override
    public void destroy() {
        // TODO Auto-generated method stub

    }

}
/*
1.实现与被增强对象相同的接口    
2.定义一个变量记住被增强对象    
3.定义一个构造器,接收被增强对象
4.覆盖需要增强方法
5.对于不想增强的方法,直接调用被增强对象(目标对象)的方法
 */
class MyCharacterEncodingRequest extends HttpServletRequestWrapper{



    private HttpServletRequest request;

    public MyCharacterEncodingRequest(HttpServletRequest request) {
        super(request);
        this.request = request;
    }


    public String getParameter(String name) {

        try{
        String value = this.request.getParameter(name);
        if (value==null) {
            return null;
        }
        if (!this.request.getMethod().equalsIgnoreCase("get")) {
            return value;
        }

        value = new String(value.getBytes("ISO8859-1"),this.request.getCharacterEncoding());

        return value;
        }catch(Exception e){
            throw new RuntimeException(e);
        }
    }



}

案例2:实现解决过滤脏话问题

public class DirtyFilter implements Filter{


    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // TODO Auto-generated method stub

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;

        DirtyRequest dirtyRequest = new DirtyRequest(req);
        System.out.println("sss");
        chain.doFilter(dirtyRequest, resp);

    }

    @Override
    public void destroy() {
        // TODO Auto-generated method stub

    }

}

class DirtyRequest extends HttpServletRequestWrapper{

    private List<String> dirtyWords =  Arrays.asList("傻B","操蛋","畜生");
    private HttpServletRequest request;

    public DirtyRequest(HttpServletRequest request) {
        super(request);
        this.request = request;
    }


    @Override
    public String getParameter(String name) {



        String value = this.request.getParameter(name);
        if(value==null){
            return null;
        }
        for(String dirtyWord:dirtyWords){
            if (value.contains(dirtyWord)) {
                value = value.replace(dirtyWord, "*****");
            }
        }

        return value;

    }


}

案例3:实现html转义输出

public class HtmlFilter implements Filter{

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // TODO Auto-generated method stub

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;

        MyHtmlFilter filter = new MyHtmlFilter(req);
        chain.doFilter(filter, resp);
    }

    @Override
    public void destroy() {
        // TODO Auto-generated method stub

    }

}

class MyHtmlFilter extends HttpServletRequestWrapper{

    private HttpServletRequest request;
    public MyHtmlFilter(HttpServletRequest request) {
        super(request);
        this.request = request;
    }
    /* (non-Javadoc)
     * @see javax.servlet.ServletRequestWrapper#getParameter(java.lang.String)
     */
    @Override
    public String getParameter(String name) {

        String value = this.request.getParameter(name);
        if (value==null) {
            return null;
        }

        return filter(value);
    }

    public String filter(String message) {

        if (message == null)
            return (null);

        char content[] = new char[message.length()];
        message.getChars(0, message.length(), content, 0);
        StringBuilder result = new StringBuilder(content.length + 50);
        for (int i = 0; i < content.length; i++) {
            switch (content[i]) {
            case '<':
                result.append("&lt;");
                break;
            case '>':
                result.append("&gt;");
                break;
            case '&':
                result.append("&amp;");
                break;
            case '"':
                result.append("&quot;");
                break;
            default:
                result.append(content[i]);
            }
        }
        return (result.toString());

    }


}

jsp:setProperty标签以及Date设置小问题

jsp:setProperty标签工作时,它会自动把字符串转成八种基本类型
但是对于复杂类型无法自动进行装换。

<jsp:setProperty name="person" property="birthday" value="<%=new Date() %>" />

JNDI技术简介

JNDI技术简介

  • JNDI(Java Naming and Directory Interface ),Java 命名和目录接口,它对应于 J2SE 中的javax.naming包。
  • 这套 API 的主要作用在于:它可以把 Java 对象放在一个容器中(JNDI 容器),并为容器中的 Java 对象取一个名称,以后程序想获得 Java 对象,只需要通过名称检索即可。
  • 其核心 API 为 Context, 它代表 JNDI 容器,其 lookup 方法为检索容器中对应名称的对象。

JDBC的学习(多表查询)

一对多:

  • 假设有员工表employee 和公司表 department。
    当我们要添加 department 对象时:
    public void add(Department department) throws SQLException{
        QueryRunner qr = new QueryRunner(JdbcUtils);

        //…………………………………… 省略用事务来………………………………………………、

        //1.取出department 对象的基本信息存在 department 类
        String sql = "insert into department(id,name) values(?,?)";
        Object params[] = {department.getId(),department.getName};
        qr.update(sql,params);
        //2.取出 department 对象中所有的员工信息,存在员工表
        sql = "insert into employee(id,name,salary,department_id) values(?,?,?,?)";

        Set<Employee> set = department.getEmployees();
        Object params2[][] = new Object[set.size()][];

        int index = 0;
        for(Employee e:set){
            params2[index++] = new Object()
{e.getId(),e.getName(),e.getSalary(),department.getId()};
        }
        qr.batch(sql, params2);
    }

Hibernate 的 session.add(department) 原理。

多对多:

  • 假设有老师表teacher 和学生表 student。
    我们需要再创建一个表 teacher_student 说明老师和学生的关系。
public void add(Teacher teacher) throws SQLException{
        QueryRunner qr = new QueryRunner(JdbcUtils.getDataSource());

        //…………………………………… 省略用事务来………………………………………………、

        //1.取出老师 的基本信息
        String sql = "insert into department(id,name,salary) values(?,?,?)";
        Object params[] = {teacher.getId(),teacher.getName(),teacher.getSalary()};
        qr.update(sql,params);
        //2.取出 d老师下面的学生信息
        sql = "insert into student(id,name) values(?,?)";
        Set<Student> set = teacher.getEmployees();
        Object params2[][] = new Object[set.size()][];

        int index = 0;
        for(Student s:set){
            params2[index++] = new Object(){s.getId(),s.getName()};

            //3.在中间表说明关系
            String sql3 = "insert into teacher_student(teacher_id,student_id) values(?,?)";
            params=new Object[]{teacher.getId,s.getId};
            qr.update(sql3, params);
        }
        qr.batch(sql, params2);
    }

JAVA-数据指纹(md5)与任意二进制明文字符(base64)生成随机数(单态设计模式)

class TokenProccessor{
    /**
     * 单态设计模式(保证类的对象在内存中只有一个)
     * 1。把类的构造函数私有
     * 2.自己创建一个类的对象
     * 3.对外提供一个公共的方法,返回类的对象
     * 
     */

    private TokenProccessor(){}

    private static final TokenProccessor instance = new TokenProccessor();

    public static TokenProccessor getInstance(){
        return instance;
    }

    public String makeToken(){  //checkException

        //
        String token = (System.currentTimeMillis()+new Random().nextInt(999999999))+"";


        //数据指纹  128位长       16个字节  md5
        try {
            MessageDigest md = MessageDigest.getInstance("md5");
            byte[] md5 = md.digest(token.getBytes());

            //base64编码--任意二进制编码明文字符
            BASE64Encoder encoder = new BASE64Encoder();
            return encoder.encode(md5);

        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }

    }
}

JAVA单态设计模式

单态设计模式(保证类的对象在内存中只有一个)

1.把类的构造函数私有
2.自己创建一个类的对象
3.对外提供一个公共的方法,返回类的对象

class TokenProccessor{
    /**
     * 单态设计模式(保证类的对象在内存中只有一个)
     * 1。把类的构造函数私有
     * 2.自己创建一个类的对象
     * 3.对外提供一个公共的方法,返回类的对象
     * 
     */

    private TokenProccessor(){}

    private static final TokenProccessor instance = new TokenProccessor();

    public static TokenProccessor getInstance(){
        return instance;
    }


    }
}

实例 DaoFactory

public class DaoFactory {
    private UserDao userDao = null;

    private DaoFactory(){
        try {
            InputStream inputStream = DaoFactory.class.getClassLoader().getResourceAsStream("daoconfig.properties");
            Properties properties = new Properties();
            properties.load(inputStream);
            String daoClassName = properties.getProperty("userdao");
            userDao =(UserDao) Class.forName(daoClassName).newInstance();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    };
    private static final DaoFactory instance = new DaoFactory();

    public static DaoFactory getInstance(){
        return instance;
    }

    public UserDao createUserDao(){
        return userDao;
    }
}

调用时

UserDao dao = DaoFactory.getInstance().createUserDao();

JAVA-防止表单的重复提交

防止表单的重复提交

所用知识

单态设计模式

md5与base64

  • FormServlet.java
public class FormServlet extends HttpServlet {


    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException {

        /*String username = req.getParameter("username");

        try {
            Thread.sleep(1000*3);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        System.out.println("向数据库插入!!!!"+username);*/

        String token = TokenProccessor.getInstance().makeToken();
        req.getSession().setAttribute("token", token);//在服务器端保存随机数

        resp.setHeader("Content-type", "text/html;charset=utf-8");
        PrintWriter writer = resp.getWriter();
        writer.println("<form action='/Day07/form/DoFormServlet' method='post'>");
        writer.write("<input type='hidden' name='token' value='"+token+"' >");
        writer.println("用户名:<input type='text' name='username'>");
        writer.println("<input type='submit' value='提交'>"); 
        writer.println("</form>");

    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException {
        doGet(req, resp);
    }
}

class TokenProccessor{
    /**
     * 单态设计模式(保证类的对象在内存中只有一个)
     * 1。把类的构造函数私有
     * 2.自己创建一个类的对象
     * 3.对外提供一个公共的方法,返回类的对象
     * 
     */

    private TokenProccessor(){}

    private static final TokenProccessor instance = new TokenProccessor();

    public static TokenProccessor getInstance(){
        return instance;
    }

    public String makeToken(){  //checkException

        //
        String token = (System.currentTimeMillis()+new Random().nextInt(999999999))+"";


        //数据指纹  128位长       16个字节  md5
        try {
            MessageDigest md = MessageDigest.getInstance("md5");
            byte[] md5 = md.digest(token.getBytes());

            //base64编码--任意二进制编码明文字符
            BASE64Encoder encoder = new BASE64Encoder();
            return encoder.encode(md5);

        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }

    }
}
  • DoFormServlet.java
public class DoFormServlet extends HttpServlet{

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException {

        resp.setHeader("Content-type", "utf-8");
        PrintWriter writer = resp.getWriter();
        boolean b = isToken(req);//判断用户是否提交
        if(b==true){
            System.out.println("对不起,请不要重复提交!");
            return ;
        }

        req.getSession().removeAttribute("token");

        System.out.println("处理提交!");
    }

    private boolean isToken(HttpServletRequest req) {
        String client_token = req.getParameter("token");
        if(client_token==null){//客户端没有带过来
            return true;
        }

        String server_token = (String) req.getSession().getAttribute("token");
        if(server_token==null){//服务器处理完了 已经没有了
            return true;
        }

        if (!server_token.equals(client_token)) {//客户端带过来的是否等于服务器端的
            return true;
        }

        return false;
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException {

        doGet(req, resp);
    }

}

JAVA检验日期是否合法的小技巧

酱酱酱酱
第一种肯定是恶心的正则表达式啦 QAQ
一般验证表单首先是要将表单弄成一个bean对象
然后将数据都存入进去

  this.birthday.matches("(19|20)\d{2}-(1[0-2]|0?[1-9])-(0?[1-9]|[1-2][0-9]|3[0-1])");

恶心吧QAQ

其实有一种简单一点的

             if (this.birthday != null ){

            try {
                DateLocaleConverter dlc = new DateLocaleConverter();
                dlc.convert(this.birthday);
            } catch (Exception e) {

                isOk = false ;
                errors.put("birthday", "非法的生日格式");
            }

        }

记住一定要

     DateLocaleConverter dlc = new DateLocaleConverter();

因为如果是用

   SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

1999-20-55会判断为合法的哦 QAQ

JSTL常用标签库(核心标签库篇)

c:out

  • jsp <c:out value="${data}" escapeXml="true" default="对不起,值取不到"></c:out>

c:set 向web存数据,向map或者bean存数据

  • jsp <c:set var="data" value="xxx" scope="page" />
  • jsp <c:set property="data" value="xxx" target="${map}" />
  • jsp <c:set property="data" value="xxx" target="${person}" />

c:remove移除

c:catch

c:if

c:choose

c:foreach

c:param

  • 在JSP页面进行URL的相关操作时,经常要在URL地址后面附加一些参数。<c:param>标签可以嵌套在<c:import>,<c:url>,<c:redirect>标签内,为这些标签所使用的URL地址附加参数。
  • <c:param>标签在为一个URL地址附加参数时,将自动对参数值进行URL编码,例如,如果传递的参数值为"**",则将其转换为"%d6%d0%b9%fa"后再附加到URL地址后面,这也就是使用<c:param>标签的最大好处
  • 示例:jsp <c:param name="name" value="value" />

c:url

  • <c:url>标签用于在JSP页面中构造一个URL地址,其主要目的是在实现URL重写。URL重写就是将会话标识号以参数形式附加在URL地址后面。
属性名 是否支持EL 属性类型 属性描述
value true String 指定要构造的URL
var false String 指定将构造出的URL结果保存在Web域中的属性名称。
scope false String 指定将构造出的URL结果保存到哪个Web域中。
  • 示例:jsp <a href="<c:url value='/servlet/ServletDemo'/>">URL重构</a>
  • 一般会这样写:
<c:url value='/servlet/ServletDemo' var="servletDemo" />
<a href="${servletDemo}">URL重构</a>

<<<<<< 配合<c:param>使用

  • 实例
   <c:url value='/servlet/ServletDemo' var="servletDemo">
         <c:param name="name" value="醉流觞" />
         <c:param name="password" value="醉流觞" />
         <c:param name="email" value="醉流觞" />
   </c:url>

<<<<<<

c:redirect

session与浏览器服务

服务器是如何做到一个session为一个浏览器的多次请求而服务

服务器创建session出来后,会把session的id号,以cookie的形式回写给客户机,这样。只要客户机的浏览器不关,再去访问服务器时,都会带着session的id号去,服务器发现客户机带session id过来了,就会使用内存中与之对应的session为之服务

Filter的学习(数据压缩篇)

Filter的学习(数据压缩篇)

  • 在输出数据的时候,为了减少用户的响应时间,一般都把数据进行压缩后输出。
    例子:
protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException {

        String message = "aaaaaa";

        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        GZIPOutputStream gout = new GZIPOutputStream(bout);
        gout.write(message.getBytes());
        gout.close();
        //拿到压缩数据
        byte gzip[] = bout.toByteArray();
        //告诉浏览器要解压
        resp.setHeader("content-encoding", "gzip");
        resp.setContentLength(gzip.length);
        resp.getOutputStream().write(gzip);

    }

过滤器实现(部分):

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {

        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;

        BufferResponse myResponse = new BufferResponse(resp);

        chain.doFilter(req, myResponse);

        byte out[] = myResponse.getBuffer();

        ByteArrayOutputStream bout = new ByteArrayOutputStream();
        GZIPOutputStream gout = new GZIPOutputStream(bout);
        gout.write(out);
        gout.close();

        byte gzip[] = bout.toByteArray();
        resp.setHeader("content-encoding", "gzip");
        resp.setContentLength(gzip.length);
        resp.getOutputStream().write(gzip);

    }

    @Override
    public void destroy() {
        // TODO Auto-generated method stub

    }

}


class BufferResponse extends HttpServletResponseWrapper{
    private PrintWriter pw;
    private ByteArrayOutputStream bout = new ByteArrayOutputStream();
    private HttpServletResponse response;
    public BufferResponse(HttpServletResponse response) {
        super(response);
        this.response = response;
    }

    @Override
    public ServletOutputStream getOutputStream() 
            throws IOException {

        return new MyServletOutputStream(bout);
    }

    public byte[] getBuffer(){

        try {
            if (pw!=null) {
                pw.close();
            }
            if(bout!=null){
                bout.flush();//刷新 确保数据真正进入了数组
                return bout.toByteArray();
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return null;

    }

    @Override
    public PrintWriter getWriter() 
            throws IOException {
        pw = new PrintWriter(new OutputStreamWriter(bout, 
this.response.getCharacterEncoding()));
        return pw;
    }



}

class MyServletOutputStream extends ServletOutputStream{


    private ByteArrayOutputStream bout;
    public MyServletOutputStream(ByteArrayOutputStream bout) {
        this.bout = bout;
    }

    @Override
    public void write(int b) throws IOException {
        this.bout.write(b);

    }

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

    @Override
    public void setWriteListener(WriteListener listener) {
        // TODO Auto-generated method stub

    }

}
 <filter-mapping>
    <filter-name>GzipFilter</filter-name>
    <url-pattern>*.jsp</url-pattern>
    <dispatcher>FORWARD</dispatcher>
    <dispatcher>REQUEST</dispatcher>
  </filter-mapping>
    <filter-mapping>
    <filter-name>GzipFilter</filter-name>
    <url-pattern>*.js</url-pattern>
  </filter-mapping>
    <filter-mapping>
    <filter-name>GzipFilter</filter-name>
    <url-pattern>*.css</url-pattern>
  </filter-mapping>
    <filter-mapping>
    <filter-name>GzipFilter</filter-name>
    <url-pattern>*.html</url-pattern>
  </filter-mapping>

Apache-Dbutils框架的学习

Apache-Dbutils框架的学习

API:

  • org.apache.commons.dbutils.QueryRunner
  • org.apache.commons.dbutils.ResultSetHandler
  • 工具类:
    org.apache.commons.dbutils.DbUtils

JAVA-什么是域(pageContext request session servletContext)

  • pageContext(page域)

  • request(request域)

    如果客户向服务器发出请求,产生的数据,用户看完就没用了,像这样的数据就存入request域中. (看新闻)

  • session(session域)

    如果客户向服务器发出请求,产生的数据,用户看完了等会还有用,像这样的数据就存入session域中. (购物)

  • servletContext(application域)

    如果客户向服务器发出请求,产生的数据,用户用完了,还要给其他用户用,像这样的数据就存入application域中. (聊天)

JSP如何自定义标签(进阶篇二)开发带属性的标签

JSP如何自定义标签(进阶篇二)开发带属性的标签

  • 1.在标签处理器中编写每个属性对应的setter方法
  • 2.在TLD文件中描述标签的属性
public class SimpleTagDemo5 extends SimpleTagSupport {

    private int count;

    @Override
    public void doTag() throws JspException, IOException {

        for(int i=0;i<count;i++)
        {
            this.getJspBody().invoke(null);
        }

    }

    public void setCount(int count) {
        this.count = count;
    }

}
  • 注意:
    true表达依赖
    true
    这个属性true时 说明count="${aaa}"可以是成立
    如果为false时 只能是count="10"
       <tag>
        <description>Outputs Hello, World</description>
        <name>SimpleTagDemo5</name>
        <tag-class>com.zuiliushang.simpletag.SimpleTagDemo5</tag-class>
        <body-content>scriptless</body-content>
        <attribute>
            <description>控制循环</description>
            <name>count</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
    </tag>
<zuiliushang:SimpleTagDemo5 count="10">
aaaawqe
</zuiliushang:SimpleTagDemo5>

防止SQL语句的注入

防止SQL语句的注入

statement 存在 sql 注入攻击问题,例如登陆用户名采用 ‘or 1=1 or username=’

对于防范SQL 注入,可以采用 PrepareStatement 取代 Statement

String sql ="select from users where username=? and password=?";
PreparedStatement st = conn.prepareStatement(sql);
st.setString(1,username);
st.setString(2,password);
  • PreparedStatement 是Statement 的子类
  • PreparedStatement 能防止 SQL 语句的注入问题
  • PreparedStatement 能对它自身代表的SQL语句进行预编译,以减轻服务器的压力。

JAVA开发-文件上传下载

JAVA开发-文件上传下载

话不多说,张腿就来个例子:

  • 首先写一个表单提交:记住 enctype="multipart/form-data" 一定要设置
<form action="${pageContext.request.contextPath }/servlet/UploadServlet" 
enctype="multipart/form-data" method="post">
        上传用户:<input type="text" name="username" /><br />
        上传文件:<input type="file" name="file1" /><br />
        上传文件:<input type="file" name="file2" /><br />
        <input type="submit" value="提交">
    </form>
  • UploadServlet处理
        //注意当表单的提交类型为multipart/form-data时候,servlet就不能用传统的方式获取表单属性
        String username = req.getParameter("username");
        System.out.println(username);//输出结果为null

        InputStream inputStream = req.getInputStream();
        int len = 0;
        byte buffer[] = new byte[1024];
        while((len=inputStream.read(buffer))>0)
        {
            System.out.println(new String(buffer, 0, len));
        }

但是我们没有办法来截取数据。

这个时候就要介绍一个jar包。

EL表达式调用JAVA方法

使用EL表达式调用JAVA方法

EL表达式语法允许开发人员开发自定义函数,以调用JAVA类的方法。

  • 示例: <<< ${prefix:method(params)}
  • 在EL表达式中调用的只是 JAVA类的静态方法。
  • 这个JAVA类的静态方法需要在TLD文件中描述,才可以被EL表达式调用。
  • EL自定义函数用于扩展EL表达式的功能,可以让EL表达式完成普通Java程序代码所能完成的功能。

EL Function 开发步骤

一般来说,EL自定义函数开发与应用包括以下三个步骤:

  • 编写一个JAVA类的静态方法。
  • 编写标签库描述符(tld)文件,在tld文件中描述自定义函数。
  • 在JSP页面中导入和使用自定义函数。

示例:开发对html标签进行转义的el function

·1.编写一个JAVA类的静态方法。

public class HtmlFilter {

    public static String filter(String message) {

        if (message == null)
            return (null);

        char content[] = new char[message.length()];
        message.getChars(0, message.length(), content, 0);
        StringBuilder result = new StringBuilder(content.length + 50);
        for (int i = 0; i < content.length; i++) {
            switch (content[i]) {
            case '<':
                result.append("&lt;");
                break;
            case '>':
                result.append("&gt;");
                break;
            case '&':
                result.append("&amp;");
                break;
            case '"':
                result.append("&quot;");
                break;
            default:
                result.append(content[i]);
            }
        }
        return (result.toString());
    }
}

2.TLD文件中描述

 <function>
        <description>Converts the string to all caps</description>
        <name>filter</name>
        <function-class>com.zuiliushang.HtmlFilter</function-class>
        <function-signature>java.lang.String filter( java.lang.String )</function-signature>
    </function>

3.JSP页面中导入和使用自定义函数。

${fn:filter("<a href=''>点我</a>") }

JSP如何自定义标签(开发篇)

1.防盗链标签

public class RefererTag extends SimpleTagSupport {

    private String site;
    private String page;

    @Override
    public void doTag() throws JspException, IOException {
        PageContext pageContext = (PageContext) this.getJspContext();
        HttpServletRequest request = (HttpServletRequest) pageContext.getRequest();
        String referer = request.getHeader("referer");
        System.out.println(referer);
        if(referer==null || !referer.startsWith(site)){

            HttpServletResponse response = (HttpServletResponse) pageContext.getResponse();
            String webroot = request.getContextPath();

            if(page.startsWith(webroot)){
                System.out.println(page);
                response.sendRedirect(page);
            }else
            {
                System.out.println(webroot+page);
                response.sendRedirect(webroot+page);
            }
            throw new SkipPageException();
        }
    }
    public void setSite(String site) {
        this.site = site;
    }

    public void setPage(String page) {
        this.page = page;
    }
}
<zuiliushang:referer site="http://localhost:8080/" page="/index.jsp"/>
<tag>
        <description>Outputs Hello, World</description>
        <name>referer</name>
        <tag-class>com.zuiliushang.web.tag.RefererTag</tag-class>
        <body-content>scriptless</body-content>

        <attribute>
            <description>地址</description>
            <name>site</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
        <attribute>
            <description>地址</description>
            <name>page</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
    </tag>

JDBC的学习(事务篇)

事务

事务的概念

  • 事务指逻辑上的一组操作,组成这组操作的各个单元,要不全部成功,要不全部不成功。
  • 例如:A——B 转账,对应于如下两条 sql 语句。
    update from account set money=money+100 where name='b';
    update from account set money=money-100 where name='a';

数据库开启事务命令。

  • start transaction 开启事务
  • Rollback 回滚事务
  • Commit 提交事务
    Start transaction
    ……
    ……
    commit

使用事务

当 Jdbc 程序向数据库获得一个 Connection 对象时,默认情况下这个 Connection 对象会自动向数据库提交在它上面发送的 SQL 语句。若想关闭这种默认提交方式,让多条 SQL 在一个事务中执行,可使用下列语句:

  • Connection.setAutoCommit(false); start transaction
  • Connection.rollback(); rollback
  • Connection.commit(); commit
public static void main(String[] args){

        Connection conn = null;
        PreparedStatement st = null;
        ResultSet rs = null;
        Savepoint sp = null;
        try {
            conn = JdbcUtils.getConnection();
            conn.setAutoCommit(false);

            String sql1 = "update account set money=money-100 where name='aa'";
            String sql2 = "update account set money=money+100 where name='bb'";

            st=conn.prepareStatement(sql1);
            st.executeUpdate();

            sp = conn.setSavepoint();
            int x= 1/0;
            st=conn.prepareStatement(sql2);
            st.executeUpdate();

            conn.commit();
        } catch (SQLException e) {
            try {
                conn.rollback(sp);
                conn.commit();
            } catch (SQLException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
            e.printStackTrace();
        }finally {
            JdbcUtils.release(conn, rs, st);
        }

    }

事务的特性(ACID)

  • 原子性(Atomicity)
    原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
  • 一致性(Consistency)
    事务必须使数据库从一个一致性状态变换到另外一个一致性状态。
  • 隔离性(Isolation)
    事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
  • 持久性(Durability)
    持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发送故障也不应该对其有任何影响。

事务的隔离级别

  • 多个线程开启各自事务操作数据库中数据时,数据库系统要负责隔离操作,以保证各个线程在获取数据时的准确性。
  • 如果不考虑隔离性,可能会引发如下问题:

脏读:

  • 指一个事务读取了另外一个事务未提交的数据。
    这是非常危险的,加入 A 向 B 转账100元,对应 sql 语句如下:
    1.update account set money=money+100 while name='b';
    2.update account set money=money-100 while name='a';

当第1条 sql 执行完,第2条还没执行(A 未提交时),如果此时B 查询自己的账户,就会发现自己多了100元钱。如果A 等 B 走后再回滚,B 就会损失100元。

不可重复读:

  • 在一个事务内读取表中的某一行数据,多次读取结果不用。
  • 和脏读的区别是,脏读是读取前一事务未提交的脏数据,不可重复读是重新读取了前一事务已提交的数据。

虚读(幻读)

  • 是指在一个事务内读取到了别的事务插入的数据,导致前后读取不一致。

事务隔离性的设置语句

数据库共定义了四种隔离级别:

  • Serializable: 可避免脏读、不可重复读、虚读情况的发生。(串行化)
  • Repeatable read:可避免脏读、不可重复读情况的发生。(可重复读)
  • Read committed:可避免脏读情况发生。(读已提交)
  • Read uncommitted:最低级别,以上情况均无法保证。(读未提交)

set transaction isolation level 设置事务隔离级别

set @@tx_isolation 查询当前事务隔离级别

JAVA异常类的小问题

JAVA异常类的小问题

一个小原则:

  • 程序一般抛运行时异常,如果希望把这个异常当作返回值,则抛编译性异常(希望上层程序处理)。
  • 而当我们要抛很多层异常的时候,每一层都要自定义异常类。
    比如:Dao层抛DaoException, 如何创建呢?
public class DaoException extends RuntimeException{

    public DaoException() {
        super();
        // TODO Auto-generated constructor stub
    }

    public DaoException(String message, Throwable cause, boolean enableSuppression,
 boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
        // TODO Auto-generated constructor stub
    }

    public DaoException(String message, Throwable cause) {
        super(message, cause);
        // TODO Auto-generated constructor stub
    }

    public DaoException(String message) {
        super(message);
        // TODO Auto-generated constructor stub
    }

    public DaoException(Throwable cause) {
        super(cause);
        // TODO Auto-generated constructor stub
    }

}

直接继承调用父类方法即可。

SAXReader用法

public class XmlUtils {

    private static String filename = "user.xml";

    public static Document getDocument() throws DocumentException{

        URL url = XmlUtils.class.getClassLoader().getResource(filename);
        String realpath = url.getPath();

        SAXReader reader = new SAXReader();
        Document document = reader.read(new File(realpath));
        return document;
    }


    public static void write2Xml(Document document) throws IOException{

        URL url = XmlUtils.class.getClassLoader().getResource(filename);
        String realpath = url.getPath();

        OutputFormat format = OutputFormat.createCompactFormat();
         XMLWriter writer = new XMLWriter(new FileOutputStream(realpath), format );
        writer.write( document );

    }

}

JAVA异常类:checked exception与unchecked的小技巧

unchecked exception 不要求必须解决
checked exception 要求解决抛出

Orz 以后学更好再来详细化两种的区别,先说小技巧。

当我们注册需要判断是否用户存在时候
项目中存在一个这样的接口

    public void registerUser(User user)

这时候我们想返回一个判断是否用户存在的证据时候呢
因为公司需要我们不能改动类名

    public boolean registerUser(User user)

这时候我们就可以用到checked exception 让上一层程序处理

先创建一个异常类

       public class UserExistException extends Exception {

    public UserExistException() {
        super();
        // TODO Auto-generated constructor stub
    }

    public UserExistException(String message, Throwable cause) {
        super(message, cause);
        // TODO Auto-generated constructor stub
    }

    public UserExistException(String message) {
        super(message);
        // TODO Auto-generated constructor stub
    }

    public UserExistException(Throwable cause) {
        super(cause);
        // TODO Auto-generated constructor stub
    }

}

然后将异常抛出

   public void registerUser(User user) throws UserExistException{

        if(dao.find(user.getUsername())!=null){

            throw new UserExistException("注册的用户名存在!");
        }

        dao.add(user);
    }

JAVA分页问题

数据库分页

MySQL 分页的实现:

  • Select * from table limit M,N
    M:记录开始索引位置。
    N:取多少条记录。

完成WEB 页面的分页显示:

  • 先获得需分页显示的记录总数,然后在 web 页面中显示页码。
  • 根据页码,从数据库中查询相应的记录显示在 web 页面中。
  • 以上两项操作通常使用 Page 对象进行封装。
private List list;
    private int totalpage;//记录总页数

    private int totalrecord;
    private int pagesize = 3;

    private int pagenum;//代表用户想看的页
    private int startindex;//代表用户想看的页从数据库哪个地方开始取

    private int startPage;//记住JSP页面显示的起始页码
    private int endPage;//记住jsp页面显示的结束页码

    public Page(int totalrecord,int pagenum)
    {
        this.totalrecord = totalrecord;
        if(this.totalrecord%this.pagesize==0)
        {
            this.totalpage = this.totalrecord/this.pagesize;
        }else{
            this.totalpage = this.totalrecord/this.pagesize+1;
        }

        this.pagenum = pagenum ;
        this.startindex = (this.pagenum-1)*this.pagesize;

        //根据用户想看的页pagenum,算出jsp的起始和结束页码
        if(this.totalpage <= 10){
            this.startPage=1;
            this.endPage=10;
        }else{
            this.startPage=this.pagenum - 4;
            this.endPage=this.pagenum + 5;

            if (this.startPage < 1) {
                this.startPage=1;
                this.endPage=10;
            }
            if(this.endPage > this.totalpage)
            {
                this.endPage=this.totalpage;
                this.startPage = this.totalpage-9;
            }
        }

    }

Filter的学习(缓存数据到内存篇)

缓存数据到内存

  • 对于页面中很少更新的数据,例如商品分类、为避免每次都要从数据库查询分类数据,因此可把分类数据缓存在内存或文件中,以此来减轻数据库压力,提高系统响应速度。
public class CachedFilter implements Filter{
    private Map<String,byte[]> map = new HashMap<>(); 

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // TODO Auto-generated method stub

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {

        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;

        //1.得到用户请求的uri
        String uri = req.getRequestURI();
        //2.看缓存中有没有 uri 对应的数据
        byte b[] = map.get(uri);
        //3.如果缓存中有,直接拿缓存的数据打给浏览器,程序返回
        if (b!=null) {
            resp.getOutputStream().write(b);
        }
        //4.如果缓存没有,让目标资源执行,并捕获目标资源的输出
        if (b==null) {

        BufferResponse1 myResponse1 = new BufferResponse1(resp);
        chain.doFilter(req, myResponse1);
        byte out[] = myResponse1.getBuffer();
        //5.把资源的数据以用户请求的uri为关键字保存到缓存中
        map.put(uri, out);
        //6.把数据打给浏览器
        resp.getOutputStream().write(out);
        }
    }

    @Override
    public void destroy() {
        // TODO Auto-generated method stub

    }

}




class BufferResponse1 extends HttpServletResponseWrapper{
    private PrintWriter pw;
    private ByteArrayOutputStream bout = new ByteArrayOutputStream();
    private HttpServletResponse response;
    public BufferResponse1(HttpServletResponse response) {
        super(response);
        this.response = response;
    }

    @Override
    public ServletOutputStream getOutputStream() 
            throws IOException {

        return new MyServletOutputStream1(bout);
    }

    public byte[] getBuffer(){

        try {
            if (pw!=null) {
                pw.close();
            }
            if(bout!=null){
                bout.flush();//刷新 确保数据真正进入了数组
                return bout.toByteArray();
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return null;

    }

    @Override
    public PrintWriter getWriter() 
            throws IOException {
        pw = new PrintWriter(new OutputStreamWriter(bout, this.response.getCharacterEncoding()));
        return pw;
    }



}

class MyServletOutputStream1 extends ServletOutputStream{


    private ByteArrayOutputStream bout;
    public MyServletOutputStream1(ByteArrayOutputStream bout) {
        this.bout = bout;
    }

    @Override
    public void write(int b) throws IOException {
        this.bout.write(b);

    }

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

    @Override
    public void setWriteListener(WriteListener listener) {
        // TODO Auto-generated method stub

    }

}

JDBC的学习(入门篇)

JDBC的学习(入门篇)

JDBC全称为:Java Data Base Connectivity (java 数据库连接),它主要由接口组成。

组成 JDBC 的 2 个包:(不用自己导入,JAVA SE 已经包含)

  • java.sql
  • javax.sql

开发 JDBC 应用需要以上 2 个包的支持外,还需要导入相应 JDBC 的数据库实现(即数据库驱动)。

  • mysql-connector-java

第一个JDBC程序

1.新建数据表

1.新建库和表
create database day14 character set utf8 collate utf8_general_ci;

use day14;

create table users(
    id int primary key,
    name varchar(40),
    password varchar(40),
    email varchar(60),
    birthday date
);

insert into users(id,name,password,email,birthday)
value(1,'zs','123456','[email protected]','1980-12-04');

insert into users(id,name,password,email,birthday)
value(2,'list','123456','[email protected]','1980-12-04');

insert into users(id,name,password,email,birthday)
value(3,'wangwu','123456','[email protected]','1980-12-04');

2.入门示例:

public static void main(String[] args) throws SQLException {
                ResultSet rs = null;
                Connection conn = null;
                PreparedStatement statement = null;

        String url = "jdbc:mysql://localhost:3306/day14?useUnicode=true&characterEncoding=utf-
8";
        String user = "root";
        String password = "不能看不能看QAQ";

                      try{
              //1.加载驱动
              Class.forName("com.mysql.jdbc.Driver");
              //2.获取与数据库的链接
              conn = DriverManager.getConnection(url, user, password);
              //3.获得用于向数据库发送sql于语句的statement
 String sql  = "select id,name,password,email,birthday from users";
              statement = conn.prepareStatement(sql);
              //4.获得代表结果集的resultset
               rs = statement.executeQuery();
              //5.取出结果集的数据
              while(rs.next()){
                  System.out.println("id = " + rs.getObject("id"));
                  System.out.println("name = " + rs.getObject("name"));
                  System.out.println("password = " + rs.getObject("password"));
                      System.out.println("email = " + rs.getObject("email"));
                      System.out.println("birthday = " + rs.getObject("birthday"));

                }finally{
                        //6.关闭数据库
                if(rs!=null){
                    try {
                        rs.close();
                    } catch (Exception e) {
                        e.getStackTrace();
                    }
                                     rs = null;
                }
                if(statement!=null){
                    try {
                        statement.close();
                    } catch (Exception e) {
                        e.getStackTrace();
                    }
                                    statement = null;
                }

                if(conn!=null){
                    try {
                        conn.close();
                    } catch (Exception e) {
                        e.getStackTrace();
                    }
                                    conn = null;
                }
             }
    }

输出结果为

id = 1
name = zs
password = 123456
email = aa@qq.com
birthday = 1980-12-04
id = 2
name = list
password = 123456
email = bb@qq.com
birthday = 1980-12-04
id = 3
name = wangwu
password = 123456
email = cc@qq.com
birthday = 1980-12-04

程序详解 - DriverManager

Jdbc 程序中的 DriverManager 用于加载驱动,并创建与数据库的链接,这个 API 的常用方法:

  • DriverManager.registerDriver(new Driver())
  • DriverManager.getConnection(url,user,password)

注意:在实际开发中并不退圈用registerDriver 方法注册驱动,原因有:

  • 1.查看 Driver 的源代码可以看到,如果采用此种方法,会导致驱动程序注册两次,也就是在内存中会有两个 Driver 对象。
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    //
    // Register ourselves with the DriverManager
    //
    static {
        try {
            java.sql.DriverManager.registerDriver(new Driver());//自己加载自己
        } catch (SQLException E) {
            throw new RuntimeException("Can't register driver!");
        }
    }
  • 2.程序以来 mysql 的 api ,脱离 mysql 的 jar 包,程序将无法编译,将来程序切换底层数据库将会非常麻烦。
  • 推荐方式:Class.forName("com.mysql.jdbc.Driver");
  • 采用此种方法不会导致驱动对象在内存中重复出现,并且采用此种方式,程序仅只要一个字符串,不需要依赖具体的驱动,使程序的灵活性更高。
  • 同样,在开发中也不建议采用具体的驱动类型指向 getConnection 方法返回的 connection 对象。

数据库URL

URL 用于标识数据库的位置,程序员通过 URL 地址告诉 JDBC 程序连接哪个数据库, URL 的写法为:jdbc:mysql: [ ] //localhost:3306/test ? 参数名:参数值

jdbc:协议
mysql:子协议
localhost:3306 : 主机:端口
test:数据库

常用数据库URL地址的写法:

  • Oracle 写法:jdbc:oracle:thin:@localhost:1521:sid
  • SqlServer 写法: jdbc:microsoft:sqlserver://localhost:1433;DatabaseName=sid
  • MySql 写法:jdbc:mysql://localhost:3306/sid
  • Mysql 的 url 地址的简写形式: jdbc:mysql://sid
  • 常用属性:useUnicode = true & characterEncoding = UTF - 8

程序详解 Connection

Jdbc 程序中的 Connection,它用于代表数据库的链接,Collection 是数据库编程中最重要的一个对象,客户端与数据库所有交互都是通过 connection 对象完成的,这个对象的常用方法:

  • createStatement():创建向数据库发送 sql 的 statement 对象。
  • prepareStatement(sql):创建向数据库发送预编译 sql 的 PrepareStatement 对象。
  • prepareCall(sql):创建执行存储过程的 callableStatement 对象。
  • setAutoCommit(boolean autoCommit):设置事务是否自动提交。
  • commit():在链接上提交事务。
  • rollback():在此链接上回滚事务。

程序详解 Statement

Jdbc 程序中的 Statement 对象用于向数据库发送 SQL 语句, Statement 对象常用方法:

  • executeQuery(String sql):用于向数据发送查询语句。
  • executeUpdate(String sql):用于向数据库发送 insert,update 或 delete 语句。
  • execute(String sql):用于向数据库发送任意 sql 语句。
boolean b = statement.execute(sql);
if(b){
    ResultSet rs = statement.getResultSet();
}
  • addBatch(String sql):把多条 sql 语句放到一个预处理中。
  • executeBatch():向数据库发送一批 sql 语句执行。

程序详解 ResultSet

Jdbc 程序中的 ResultSet 用于代表 Sql 语句的执行结果。 Resultset 封装执行结果时,采用的类似于表单的方式。ResultSet 对象维护了一个指向表格数据行的游标,初始的时候,游标在第一行之间,调用 ResultSet.next() 方法,可以使游标指向具体的数据航,进行调用方法获取该行的数据。

ResultSet 既然用于封装执行结果的,所以该对象提供的都是用于获取数据的 get 方法:

** 获取任意类型的数据

  • getObject(int index)
  • getObject(String columnName)

** 获取指定类型的数据,例如:

  • getString(int index)
  • getString(String columnName)

常用数据类型转换表

SQL 类型 Jdbc 对应方法 返回类型
BIT(1) bit(10) getBoolean getBytes() Boolean byte[]
TINYINT getByte() byte
SMALLINT getShort() Short
Int getInt() Int
BIGINT getLong() long
CHAR,VARCHAR,LONGVARCHAR getString() String
Text(clob) Blob getClob getBlob Clob Blob
DATE getDate() java.sql.Date
TIME getTime() java.sql.Time
TIMESTAMP getTimestamp() java.sql.Timestamp

ResultSet 还提供了对结果集进行滚动的方法:

  • next() :移动到下一行。
  • Previous() :移动到前一行。
  • absolute(int row) :移动到指定行。
  • beforeFirst() :移动到 resultSet 的最前面。
  • afterLast() :移动到 resultSet 的最后面。
InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");

            Properties properties = new Properties();
            properties.load(in);
            driver = properties.getProperty("driver");
            url = properties.getProperty("url");
            username = properties.getProperty("username");
            password = properties.getProperty("password");

Filter的学习(入门篇)

Filter的学习(入门篇)

Filter 简介。

  • Filter 也称之为过滤器,它是 Servlet 技术中最激动人心的技术, WEB 开发人员通过 Filter 技术,对 Web 服务器管理的所有 web 资源:例如 Jsp、Servlet,静态图片文件或静态 html 文件等进行拦截,从而实现一些特殊的功能。例如实现 URL 级别的权限访问控制、过滤敏感词汇、压缩响应信息等一些高级功能。
  • Servlet API 中提供了一个 Filter 接口,开发 web 应用时,如果编写的 Java 类实现了这个接口,则把这个 java 类称之为过滤器 Filter。 通过 Filter 技术,开发人员可以实现用户在访问某个目标资源之前,对访问的请求和响应进行拦截,如下所示:

qq 20160221142334

fileter 的三种典型应用:

  • 1.可以在filter 中根据条件决定是否调用 chain.doFilter(request,response) 方法,即是否让目标资源执行。
  • 2.在让目标执行之前,可以对 request , response 作预处理,再让目标资源执行
  • 3.在目标资源执行之后,可以捕获目标资源的执行结果,从而实现一些特殊的功能。

常见应用:

  • 统一全站字符编码的过滤器。

    通过配置参数 encoding 指明使用何种字符编码,以处理 Html Form 请求参数的中文问题。

    private FilterConfig config = null;
    private String defaultCharset = "utf-8";


    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        // TODO Auto-generated method stub
        this.config = filterConfig;

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
            String charset = config.getInitParameter("charset");
            if (charset==null) {
                charset = defaultCharset;
            }
            request.setCharacterEncoding(charset);
            response.setCharacterEncoding(charset);
            response.setContentType("text/html;charset="+charset);

    }
  • 禁止浏览器缓存动态页面。
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;
        resp.setDateHeader("Expires", -1);
        resp.setHeader("Cache-Control", "no-cache");
        resp.setHeader("Pragma", "no-cache");
        chain.doFilter(req, resp);

    }
  • 让浏览器缓存静态资源
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;

        //1. 获取用户想访问的资源
        String url = req.getRequestURI(); 
        //2.得到用户想访问资源的后缀名
        String ext = url.substring(url.lastIndexOf(".")+1);
        // 得到资源需要缓存的时间
        String time = filterConfig.getInitParameter(ext);
        if (time !=null) {
            long t = Long.parseLong(time)*3600*1000;
            resp.setDateHeader("expires", System.currentTimeMillis()+t);
        }
        chain.doFilter(req, resp);

    }
  • 实现用户自动登陆功能

Filter 的部署 - 映射Filter

元素用于设置一个 Filter 所负责拦截的资源。一个 Filter 拦截的资源可通过两种方式来指定: Servlet 名称和资源访问的请求路径。

  • <filter - name> 子元素用于设置filter 的注册名称。该值必须是在元素中声明过的过滤器的名字。
  • 设置 filter 所拦截的请求路径(过滤器关联的 URL 样式)。
  • 指定过滤器所拦截的 Servlet 名称。
<filter>
  <filter-name>FilterDemo1</filter-name>
    <filter-class>com.zuiliushang.filter.FilterDemo1</filter-class>
  </filter>

  <filter-mapping>
    <filter-name>FilterDemo1</filter-name>
    <servlet-name>ServletDemo1</servlet-name>
  </filter-mapping>
  • 指定过滤器所拦截的资源被 Servlet 容器调用的方式,可以是 REQUEST,INCLUDE,FORWARD 和 ERROR 之一,默认是 REQUEST 。用户可以设置多个 子元素用来指定 Filter 对资源的多种调用方式进行拦截。
 <filter-mapping>
    <filter-name>FilterDemo1</filter-name>
    <url-pattern>/*</url-pattern>
    <dispatcher>FORWARD</dispatcher>
  </filter-mapping>

迭代List,Map以及Jsp中的迭代的方法

1.迭代Map

1、keySet()方法获取元素
原理:将Map集合中的所有键存入到Set集合中,因为Set集合具备迭代器,所以可以用迭代方式取出所有的键,再根据get方法获取每一个键对应的值。简单说就是:Map集合---->Set集合 ---->迭代器取出
示例:

   //创建Map集合,并添加元素  
        Map<Integer,String> map = new HashMap<Integer,String>();  
        map.put(2,"zhangsan");  
        map.put(6,"lisi");  
        map.put(3,"wangwu");  
        map.put(4,"heihei");  
        map.put(5,"xixi");  
        //获取map集合中的所有键的Set集合  
        Set<Integer> keySet = map.keySet();  
        //有了Set集合就可以获取其迭代器,取值  
        Iterator<Integer> it = keySet.iterator();  
        while (it.hasNext())  
        {  
            Integer i = it.next();  
            String s = map.get(i);  
            System.out.println(i + " = " + s);  
        }  

2、entrySet()方法获取元素:
原理:将Map集合中的映射关系存入到了Set集合中,而这个映射关系的数据类型是Map.Entry,在通过迭代器将映射关系存入到Map.Entry集合中,并通过其中的getKey()和getValue()放取出键值。
示例:

       //创建集合,存入元素  
        Map<String,String> map = new HashMap<String,String>();  
        map.put("01","lisi1");  
        map.put("02","lisi2");  
        map.put("03","lisi3");  
        map.put("04","lisi4");  
        //获取map集合中的所有键,存入到Set集合中,  
        Set<Map.Entry<String,String>> entry = map.entrySet();  
        //通过迭代器取出map中的键值关系,迭代器接收的泛型参数应和Set接收的一致  
        Iterator<Map.Entry<String,String>> it = entry.iterator();  
        while (it.hasNext())  
        {  
            //将键值关系取出存入Map.Entry这个映射关系集合接口中  
            Map.Entry<String,String>  me = it.next();  
            //使用Map.Entry中的方法获取键和值  
            String key = me.getKey();  
            String value = me.getValue();  
            System.out.println(key + " : " + value);  
        }  

3.jsp迭代

   <c:forEach items="${map}" var="entry"> 
       ${entry.value.bname} 
    </c:forEach> 

SUN公司常用的EL函数库

fn:toLowerCase

fn:toLowerCase函数将一个字符串中包含的所有字符转换为小写形式,并返回转换后的字符串,它接收一个字符串类型的参数,例如:

  • fn:toLowerCase("WWW.Zuiliushang.CoM")的返回值为字符串"www.zuiliushang.com"。
  • fn:toLowerCase("")的返回值为空字符串。

fn:toUpperCase

  • 将一个字符串中所包含的所有字符转换为大写的形式。返回一个字符串

fn:trim

  • 传入一个字符串删除首尾的空格,并返回修改后的字符串。

fn:length

  • fn:length函数返回一个集合或数组大小,或返回一个字符串中包含的字符的个数,返回值为int类型。fn:length函数接受一个参数,这个参数可以是<c:forEach>标签的items属性支持的任何类型,包括任意类型的数组,java.util.Collection,java.util.Iterator,java.util.Enumeration,java.util.Map等类的实例对象和字符串。
  • 如果fn:length函数的参数为null或者元素个数为0的集合或者数组对象,则函数返回0;如果参数是空字符串,则函数返回0。

fn:split

  • fn:split函数以指定字符串作为分隔符,将一个字符串分割成字符串数组并返回这个字符串数组。
  • fn:split函数接收两个字符串类型的参数,第一个参数表示要分割的字符串,第二个参数表示作为分隔符的字符串。
  • 例如, fn:split("www.zuiliushang.com",".")[1]的返回值为字符串"zuiliushang"。

fn:join

  • fn:join函数以一个指定的分隔符,将一个数组中的所有元素合并成为一个字符串。fn:join函数接收两个参数是作为分隔符的字符串。
  • fn:join函数的第二个参数是空字符串,则fn:join函数的返回值直接将元素连接起来。例如:
    fn:join(fn:split("www.zuiliushang.com","."),".")返回的是www.zuiliushang.com

fn:indexOf

  • fn:indexOf函数返回指定字符串中第一次出现的索引值,返回值为int类型。fn:indexOf函数接收两个字符串类型的参数。例如:
    fn:indexOf("www.zuiliushang.com","zuilius")返回的是4
    如果不包含返回-1,空值返回0。

fn:contains

  • fn:contains函数监测一个字符串是否包含指定的字符串,返回值为boolean类型。fn:contains函数在比较两个字符串是否相等时区分大小写。
  • fn:contains函数接收两个字符串类型的参数,如果第一个参数字符串中包含第二个参数字符串,则fn:contains函数返回true,否则函数返回false。如果第二个参数的值为空字符串,则fn:contains函数总是返回true。实际上,fn:contains(string,substring)等价于fn:indexOf(string,substring)!=-1。
  • 忽略大小写的EL函数:fn:containsIgnoreCase

fn:startsWith

  • fn:startsWith函数用于检测一个字符串是否是以指定字符串开始的,返回值为boolean。例如:
    fn:startsWith("www.zuiliushang.com","zuiliushang")的返回值为false。
    与之对应得EL函数:fn:endsWith。

fn:replace

-fn:replace函数将字符串中的部分指定字符串按制定字符串替换。例如:
fn:replace("www zuiliushang com"," ",".")返回为www.zuiliushang.com

fn:substring

  • fn:substring截取。例如:fn:substring("www.zuiliushang.com",4,14)返回zuiliushang。
    PS:不知道算错没 我可是双手双脚都用来算了(捂脸)。

fn:substringAfter

  • fn:substringAfter这样用!fn:substringAfter("www.zuiliushang.com",".")返回zuiliushang.com
    与之对应的是fn:substringBefore

fn:escapeXml

  • fn:escapeXml 转义!什么?不懂?给我看着-> fn:escapeXml("<a href='#'>点我</a>")返回
    <a href='#'>点我</a>

JSP如何自定义标签

JSP如何自定义标签(以实现输出本机ip为例子)

1.编写一个实现tag接口的标签处理器

在实现ViewIpTag.java时

  • 1.调用public void setPageContext(PageContext pageContext)) 得到pageContext
  • 2.调用public void setParent(Tag arg0) 得到父类标签
    完成初始化
  • 3.调用public int doStartTag() 调用之中会看是否有标签体 如果有就执行
  • 4.public int doEndTag()结束
  • 5.public void release()销毁释放资源
public class ViewIpTag implements Tag{

    private PageContext pageContext;
    @Override
    public int doEndTag() throws JspException {

        HttpServletRequest request = (HttpServletRequest) pageContext.getRequest();
        JspWriter out = pageContext.getOut();

        String ip = request.getRemoteAddr();
        try {
            out.write(ip);
        } catch (IOException e) {   
            throw new RuntimeException(e);  
        }   
        return 0;
    }
    @Override
    public int doStartTag() throws JspException {

        return 0;
    }
    @Override
    public Tag getParent() {    
        return null;
    }
    @Override
    public void release() { 
    }
    @Override
    public void setPageContext(PageContext pageContext) {   
        this.pageContext = pageContext;
    }
    @Override
    public void setParent(Tag arg0) {   
    }
}

2.在WEB-INF/目录下编写一个zuiliushang.tld文件

<?xml version="1.0" encoding="UTF-8" ?>
<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-
jsptaglibrary_2_0.xsd"
    version="2.0">
    <description>A tag library exercising SimpleTag handlers.</description>
    <tlib-version>1.0</tlib-version>
    <short-name>SimpleTagLibrary</short-name>
    <!--定义标签的uri地址 -->
    <uri>/zuiliushang</uri>

    <tag>
        <description>Outputs Hello, World</description>
         <!--定义标签的名称-->
        <name>viewIp</name>
         <!--标签的实现类-->
        <tag-class>com.zuiliushang.web.tag.ViewIpTag</tag-class>
         <!--是否有标签体-->
        <body-content>empty</body-content>
    </tag>

</taglib>

body-content的值有下面4种:

<xsd:enumeration value="tagdependent"/>
    <xsd:enumeration value="JSP"/>
    <xsd:enumeration value="empty"/>
    <xsd:enumeration value="scriptless"/>

tagdependent:标签体内容直接被写入BodyContent,由自定义标签类来进行处理,而不被JSP容器解释,
如下:

<test:myList>
select name,age from users
</test:myList>

JSP:接受所有JSP语法,如定制的或内部的tag、scripts、静态HTML、脚本元素、JSP指令和动作。如:

<my:test>
    <%=request.getProtocol()%>      // ②
</my:test>

具体可参考后面附源码。

empty:空标记,即起始标记和结束标记之间没有内容。
下面几种写法都是有效的,

<test:mytag />
<test:mytag uname="Tom" />
<test:mytag></test:mytag>

scriptless:接受文本、EL和JSP动作。如上述②使用 scriptless 则报错.

3.在jsp页面中导入并使用自定义标签

<%@taglib uri="/zuiliushang" prefix="zuiliushang" %>

下面是实例:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@taglib uri="/zuiliushang" prefix="zuiliushang" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
您的IP是:<zuiliushang:viewIp/>
</body>
</html>

image

  • 此时我们看下服务器翻译成之后的JAVA文件源码
try {
      response.setContentType("text/html; charset=UTF-8");
      pageContext = _jspxFactory.getPageContext(this, request, response,
                null, true, 8192, true);
      _jspx_page_context = pageContext;
      application = pageContext.getServletContext();
      config = pageContext.getServletConfig();
      session = pageContext.getSession();
      out = pageContext.getOut();
      _jspx_out = out;

      out.write("\r\n");
      out.write("\r\n");
      out.write("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\r\n");
      out.write("<html>\r\n");
      out.write("<head>\r\n");
      out.write("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">\r\n");
      out.write("<title>Insert title here</title>\r\n");
      out.write("</head>\r\n");
      out.write("<body>\r\n");
      out.write("您的IP是:");
      if (_jspx_meth_zuiliushang_005fviewIp_005f0(_jspx_page_context))//开始执行自定义标签
        return;
      out.write("\r\n");
      out.write("</body>\r\n");
      out.write("</html>");
    } catch (java.lang.Throwable t) {
      if (!(t instanceof javax.servlet.jsp.SkipPageException)){
        out = _jspx_out;
        if (out != null && out.getBufferSize() != 0)
          try {
            if (response.isCommitted()) {
              out.flush();
            } else {
              out.clearBuffer();
            }
          } catch (java.io.IOException e) {}
        if (_jspx_page_context != null) _jspx_page_context.handlePageException(t);
        else throw new ServletException(t);
      }
    } finally {
      _jspxFactory.releasePageContext(_jspx_page_context);
    }
  }
//执行自定义标签
  private boolean _jspx_meth_zuiliushang_005fviewIp_005f0(javax.servlet.jsp.PageContext 
_jspx_page_context)
          throws java.lang.Throwable {
    javax.servlet.jsp.PageContext pageContext = _jspx_page_context;//得到传入的pagecontext
    javax.servlet.jsp.JspWriter out = _jspx_page_context.getOut();//得到out
    //  zuiliushang:viewIp
    com.zuiliushang.web.tag.ViewIpTag _jspx_th_zuiliushang_005fviewIp_005f0 = 
(com.zuiliushang.web.tag.ViewIpTag) _005fjspx_005ftagPool_005fzuiliushang_005fviewIp_005fnobody.get(com.zuiliushang.web.tag.
ViewIpTag.class);
//执行setPageContext
    _jspx_th_zuiliushang_005fviewIp_005f0.setPageContext(_jspx_page_context);
//执行setParent
    _jspx_th_zuiliushang_005fviewIp_005f0.setParent(null);
//执行doStartTag
    int _jspx_eval_zuiliushang_005fviewIp_005f0 = _jspx_th_zuiliushang_005fviewIp_005f0.doStartTag();
//执行doEndTag
    if (_jspx_th_zuiliushang_005fviewIp_005f0.doEndTag() == javax.servlet.jsp.tagext.Tag.SKIP_PAGE) {
      _005fjspx_005ftagPool_005fzuiliushang_005fviewIp_005fnobody.reuse(_jspx_th_zuiliushang_005fvi
ewIp_005f0);
      return true;
    }
    _005fjspx_005ftagPool_005fzuiliushang_005fviewIp_005fnobody.reuse(_jspx_th_zuiliushang_005fvi
ewIp_005f0);
    return false;
  }
}

XML学习笔记

XML基础

文档声明
文档声明之前不可注释
声明XML文档的类型

XML文件以什么类型码表保存, 在XML文档里面就要设置相应的编码

standalone属性说明文档是否独立(IE打得开)

元素
区分大小写 不能以xml XML Xml等开头不能包含空格
属性

xml标签出现空格或者换行,XML会当做内容处理

CDATA

css样式输出

JSP如何自定义标签(进阶篇)

如何自定义一个标签的逻辑(多个Tag接口及实现类,第二楼是SimpleTag接口)

1.控制JSP页面某一部分内容是否执行

public class TagDemo1 extends TagSupport{

    @Override
    public int doStartTag() throws JspException {

        //return Tag.SKIP_BODY;//不输出
        return Tag.EVAL_BODY_INCLUDE;//输出
    }

}
   <tag>
        <description>Outputs Hello, World</description>
        <name>TagDemo1</name>
        <tag-class>com.zuiliushang.web.tag.TagDemo1</tag-class>
        <body-content>JSP</body-content>
    </tag>
<zuiliushang:TagDemo1>
aaaaa
</zuiliushang:TagDemo1>

2.控制整个JSP页面是否执行

public class TagDemo2 extends TagSupport {

    @Override
    public int doEndTag() throws JspException {

        return Tag.EVAL_PAGE;//继续执行余下的JSP
        //return Tag.SKIP_PAGE;//余下的不执行
    }

}
    <tag>
        <description>Outputs Hello, World</description>
        <name>TagDemo2</name>
        <tag-class>com.zuiliushang.web.tag.TagDemo2</tag-class>
        <body-content>empty</body-content>
    </tag>
<zuiliushang:TagDemo2/>


<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>

aaaaa
</body>
</html>

3.控制JSP内容重复执行

public class TagDemo3 extends TagSupport{

    int x=5;

    @Override
    public int doAfterBody() throws JspException {
        x--;
        if(x>0){
            return IterationTag.EVAL_BODY_AGAIN;
        }else {
            return IterationTag.SKIP_BODY;
        }
    }


    @Override
    public int doStartTag() throws JspException {
        return Tag.EVAL_BODY_INCLUDE;
    }



}
   <tag>
        <description>Outputs Hello, World</description>
        <name>TagDemo3</name>
        <tag-class>com.zuiliushang.web.tag.TagDemo3</tag-class>
        <body-content>JSP</body-content>
    </tag>
<zuiliushang:TagDemo3>
aaaaa
</zuiliushang:TagDemo3>

4.修改JSP内容重新输出

public class TagDemo4 extends BodyTagSupport{

    @Override
    public int doEndTag() throws JspException {


        String content = this.getBodyContent().getString();
        String result = content.toUpperCase();

        try {
            this.pageContext.getOut().write(result);
        } catch (IOException e) {

            throw new RuntimeException(e);
        }

        return Tag.EVAL_PAGE;
    }


    @Override
    public int doStartTag() throws JspException {

        return BodyTag.EVAL_BODY_BUFFERED;
    }



}
   <tag>
        <description>Outputs Hello, World</description>
        <name>TagDemo4</name>
        <tag-class>com.zuiliushang.web.tag.TagDemo4</tag-class>
        <body-content>JSP</body-content>
    </tag>
<zuiliushang:TagDemo1>
aaaaa
</zuiliushang:TagDemo1>

开发工具一些技巧

一些开发工具的小技巧

Eclipse快捷键

内容提示(内容助理) Alt + /

快速修正 CTRL + 1

导包 CTRL+SHIFT+O

格式化代码块 CTRL+SHIFT+F

JAVA开发-国际化

固定文本元素的国际化

  • 对于软件中的菜单栏,导航条,错误提示信息,状态信息等这些固定不变的文本信息,可以把他们写在一个properties文件中,并根据不同的国家编写不同的properties文件。这一组properties文件称之为一个资源包
  • JavaAPI中提供了一个ResourceBundle类用于描述一个资源包,并且ResourceBundle类提供了相应的方法getBundle,这个方法可以根据来访者的国家地区自动获取与之对应的资源文件予以显示。

创建资源包和资源文件

  • 一个资源包中的每个资源文件都必须拥有共同的基名。除了基名,每个资源文件的名称中还必须有标识其本地信息的附加部分。例如:一个资源包的基名师“myproperties”,则与中文,英文环境相对应的资源文件名则为:
    myproperites_zh.properties myproperites_en.properties
  • 每个资源包都应有一个默认资源文件,这个文件不带有标识本地信息的附加部分。若ResourceBundle对象在资源包中找不到与用户匹配的资源文件,它将选择该资源包中与用户最少相近的资源文件,如果再找不到,则使用默认资源文件。例如:
    myproperites.propertiese

资源文件的书写格式

  • 资源文件的内容通常采用“关键字=值”的形式,软件根据关键字检索值显示在页面上。一个资源包中的所有资源文件的关键字必须相同,值则为相应国家的文字。
  • 并且资源文件中采用的是properties格式文件,所以文件中的所有字符都必须是ASCII字码,对于像中文这样的非ACSII字符,须先进行编码。(JAVA提供了一个native2ascII命令用于编码)。
    例:

属性文件是不能保存中文的

username=username
password=password
submit=submit

        resource_en.properties文件
username=\u7528\u6237\u540d
password=\u5bc6\u7801
submit=\u63d0\u4ea4

        resource_zh.properties文件

编程实现固定文本的国际化

  • ResourceBundle类提供了一个静态方法getBundle,该方法用于装在资源文件,并创建ResourceBundle实例:
Locale currentLocale = Locale.getDefault();
ResourceBundle myResources = ResourceBundle.getBundle(basename,currentLocale);
    - basename为资源包基名(且必须为完整路径)。
    - 如果与该locale对象匹配的资源包子类找不到。一般情况下,<b>则选用默认资源文件予以显示</b>。
  • 加载资源文件后,程序就可以调用ResourceBundle实例对象的getString 方法获取指定的资源信息名称所对应的值。
    java String value = myResources.getString("key");
public static void main(String[] args) {
        // TODO Auto-generated method stub
        ResourceBundle bundle = 
ResourceBundle.getBundle("com.zuiliushang.resouce.myproperties",Locale.US);

        String username = bundle.getString("username");
        String password = bundle.getString("password");
        System.out.println(username);
        System.out.println(password);
    }
  • 在WEB应用中实现固定文本的国际化

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.