day23 框架之基础加强

day23 框架之基础加强

今日任务

aptana(javascript的eclipse插件):http://www.cnblogs.com/terrylin/archive/2012/06/20/2555962.html

 

jquery技术,查询文档。

  • 今天的总思路:读取配置文件,反射获取类信息,创建对象,监控方法的执行

 

SSH : struts2 spring hibernate

SSM :springMVC spring Mybaties

spring 核心:IOC——控制反转,创建对象,不再由着程序员,创建对象由spring管理

                 AOP——面向切面编程,面向整个功能(功能的实现,不止一个类)

 

  1. 反射技术
  2. 动态代理
  3. 注解技术

 

注解技术:老程序员看不上,新程序员特别喜欢。

使用配置文件:特别多,特别复杂(新程序员讨厌原因)

注解:简单,方便,内容少。(新程序员特别喜欢) 注解的配置:都在类上或者方法上,代码维护代价高,注解性能问题(老程序员看不上)

课堂笔记

读取配置文件的三种方式

 

第一种:String realPath = this.getServletContext().getRealPath("/upload");

只能在web工程中使用。

第二种:InputStream in = ClassLoader.getSystemResourceAsStream("peizhi.properties");

使用类加载器读取配置文件的方法(类加载器:加载类的对象),任何工程都可以使用

任何java工程都需要类加载器来,帮助我们加载类,运行,所以任何工程都可以使用类加载器加载配置文件。

 

ClassLoader是什么:加载类的对象。

 

第三种:ResourceBundle bundle = ResourceBundle.getBundle("peizhi");

注意:专门用来读取properties文件

 

演示读取配置文件的方式(第二种和第三种):

 

准备接口和实现类:

配置文件准备:

 

思路:

  1. 定义接口
  2. 书写实现类
  3. 定义配置文件

     

模拟框架开发:接口定义好的,如何知道程序员定义的实现类?

配置文件——定义好了接口的实现类。

 

通过读取配置文件,可以获取到实现类的全路径,通过反射获取实现类的对象

 

第二种代码演示:

/**

     * 测试使用类加载器,读取配置文件

     * @throws Exception

     */

    @Test

    public void test1() throws Exception{

        //获取输入流,读取配置文件

        InputStream in = ClassLoader.getSystemResourceAsStream("peizhi.properties");

        //InputStreamReader: 他的构造函数需要InputStream对象,将字节流转换成字符流

        BufferedReader reader = new BufferedReader(new InputStreamReader(in));

        

        String line = null;

        while((line = reader.readLine()) != null){

            System.out.println(line);

        }

    }

效果:

 

第三种代码演示:

/**

     * 测试使用ResourceBundle,读取配置文件

     * @throws Exception

     */

    @Test

    public void test2() throws Exception{

        //创建对象,读取配置文件

        //ResourceBundle 专门读取properties文件

        ResourceBundle bundle = ResourceBundle.getBundle("peizhi");

        String value = bundle.getString("servlet");

        System.out.println(value);

    }

 

效果:

 

注意:任何配置文件,修改过后,都需要重新启动java虚拟机。

原因:读取配置文件的动作只有一次。

 

如果想要不重新启动java虚拟机,将数据保存在数据库,然后,从数据库获取数据,再进行操作——这样的数据,一般叫做系统变量。

 

1、反射技术回顾

1.1、Class回顾

Class:相当于工厂在制作产品之前,先设计好的产品图纸。

反射的作用:可以动态的创建某个类的对象,然后动态调用这个类中的成员(方法和属性)。

    

  1. 对象的getClass方法
  2. 类名.class
  3. Class类forName方法

代码演示:

/**

     * 演示获取Class对象的三种方式

     * @throws ClassNotFoundException

     */

    @Test

    public void test3() throws ClassNotFoundException {

        MyServlet myServlet = new MyServlet();

        System.out.println(myServlet.getClass());

        

        System.out.println(MyServlet.class);

        //clazz:这是一个源远流长的习惯

        //框架经常使用第三种方式

        Class clazz = Class.forName("cn.itcast.web.impl.MyServlet");

        System.out.println(clazz);

    }

 

效果:

 

 

1.2、反射类中的成员回顾

    第一步:反射类中的构造方法:

    通过构造方法,就可以创建类的对象

Class:

Constructor:

/**

     * 演示获取对象的构造方法

     * @throws Exception

     */

    @Test

    public void test4() throws Exception{

        //读取配置文件

        ResourceBundle bundle = ResourceBundle.getBundle("peizhi");

        //获取类全名

        String className = bundle.getString("servlet");

        

        //获取类的构造方法

        Class<?> clazz = Class.forName(className);

        /*Constructor[] constructors = clazz.getConstructors();

        for (Constructor constructor : constructors) {

            System.out.println(constructor);

        }*/

        

        //获取指定的构造方法

        //parameterTypes:给的是构造函数的参数

        Constructor<?> constructor = clazz.getConstructor(null);

        System.out.println(constructor);

        

        Constructor<?> constructor2 = clazz.getConstructor(String.class,String.class);

        System.out.println(constructor2);

        

        //根据构造函数创建对象

        //initargs:构造函数使用需要的参数

        Object newInstance = constructor.newInstance(null);

        System.out.println(newInstance);

        

        Object newInstance2 = constructor2.newInstance("req","res");

        System.out.println(newInstance2);

    }

效果:

 

    反射类中的成员变量:

Class:

/**

     * 演示获取成员变量(属性)

     *

     * @throws Exception

     */

    @Test

    public void test5() throws Exception {

        // 读取配置文件

        ResourceBundle bundle = ResourceBundle.getBundle("peizhi");

        // 获取类全名

        String className = bundle.getString("servlet");

        // 获取Class对象

        Class<?> clazz = Class.forName(className);

        

        Field[] declaredFields = clazz.getDeclaredFields();

        /*for (Field field : declaredFields) {

            System.out.println(field);

        }*/

        Field declaredField = clazz.getDeclaredField("request");

        System.out.println(declaredField);

    }

 

效果:

    

    反射类中的成员方法:

获取所有方法:

/**

     * 演示获取类中的成员方法

     *

     * @throws Exception

     */

    @Test

    public void test6() throws Exception {

        // 读取配置文件

        ResourceBundle bundle = ResourceBundle.getBundle("peizhi");

        // 获取类全名

        String className = bundle.getString("servlet");

        // 获取Class对象

        Class<?> clazz = Class.forName(className);

        

        Method[] methods = clazz.getMethods();

        for (Method method : methods) {

            System.out.println(method);

        }

        

        System.out.println("================");

        Method method = clazz.getMethod("doGet", java.lang.String.class,java.lang.String.class);

        System.out.println(method);

        System.out.println("==============");

        //演示调用方法obj:调用方法的对象,args:方法的参数

        Object invoke = method.invoke(clazz.newInstance(), "haha","hehe");

        System.out.println(invoke);

    }

 

效果:

 

 

案例:模拟BeanUtils封装数据演示:

 

思路:

  1. 需要一个封装数据的对象(Person)
  2. 需要一个封装数据的map集合(key值和Person的字段名称一致)
  3. 获取Person的Class对象,只有获取Class对象,才能获取到所有的字段(属性)
  4. 获取所有字段
  5. 循环所有字段和map集合,当key值和字段名称一致,就封装数据

 

要使用的API:

Class:

Field:

 

 

代码演示:

测试类:

/**

     * 演示模拟beanUtils封装数据

     *

     * @throws Exception

     */

    @Test

    public void test7() throws Exception {

        //第一步:创建要被封装数据的对象

        User u = new User();

        System.out.println(u);

        //String:用来保存属性的名称(对应User中的属性)

        //Object:要被封装的数据,任意类型

        Map<String, Object> map = new HashMap<String, Object>();

        map.put("name", "张三");

        map.put("age", 11);

        //封装数据,使用BeanUtils.populate(u, map);

        BeanUtils.populate(u, map);

        System.out.println(u);

    }

 

工具类:

package cn.itcast.utils;

 

import java.lang.reflect.Field;

import java.util.Map;

 

public class BeanUtils {

 

    public static void populate(Object obj, Map<String, Object> map) {

        //第一步:通过Class对象来获取obj对象中的成员变量

        Class<?> clazz = obj.getClass();

        Field[] declaredFields = clazz.getDeclaredFields();

        //第二步:遍历循环所有的字段,将数据封装进去

        for (Field field : declaredFields) {

            for (String key : map.keySet()) {

                //判断,当前的字段名称是否和map集合中key值,一致,一致,才能封装数据

                if(key.equals(field.getName())){

                    //封装数据

                    //取消访问的检查

                    field.setAccessible(true);

                    try {

                        field.set(obj, map.get(key));

                    } catch (Exception e) {

                        e.printStackTrace();

                    }

                }

            }

        }

    }

 

}

 

 

效果:

 

 

2、动态代理

2.1、动态代理介绍

struts2 、 hibernate 底层使用的动态代理。

Spring 在AOP当中,使用了大量的动态代理,JDK自带的接口代理(有接口才能代理),子类代理(cglib代理)

 

动态代理:访问被代理的类,进行拦截,处理,处理完了,放行

 

代理设计模式:

注意:以上还只是代理设计模式,怎么变成动态代理?上面的故事,如果改成,看到姐姐之后,直接上帝创建了妹妹,就是动态代理。

 

2.2、动态代理演示

 

1、先获取创建代理对象的类,调用它的方法,创建出一个代理对象

2、代理对象具体要做什么事情(代理策略),需要设置

 

 

创建代理类的对象:

创建代理对象的方法:

 

代理策略对象:

代理策略方法:

 

 

需求和步骤(模拟框架读取配置文件,创建代理对象,并调用接口方法):

  1. 读取配置文件
  2. 获取类全名
  3. 反射获取Class对象
  4. 获取当前类接口(根据接口做代理)

  5. 创建代理类(Proxy)
  6. 代理策略:监控方法执行
  7. 获取代理类的接口方法,调用执行

 

代码演示:

/**

     * 演示动态代理

     */

    @Test

    public void test8()throws Exception{

        //1    读取配置文件

        ResourceBundle bundle = ResourceBundle.getBundle("peizhi");

        //2    获取类全名

        String className = bundle.getString("servlet");

        //3    反射获取Class对象

        final Class<?> clazz = Class.forName(className);

        

        //4    获取当前类接口(根据接口做代理)

        Class<?>[] interfaces = clazz.getInterfaces();

        //以上都是反射操作

        //5    创建代理类(Proxy),设置了代理的策略

        Object obj = Proxy.newProxyInstance(clazz.getClassLoader(), interfaces, new InvocationHandler() {

            

            @Override

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

                    throws Throwable {

                //6    代理策略:监控方法执行

                //method 当前被调用的方法

                //args 是调用方法时候的参数

                System.out.println("======方法启动=====");

                System.out.println("method:"+method);

                for (Object dd : args) {

                    System.out.println(dd);

                }

                //method是一个方法对象,可以调用它的invoke方法,让他去执行

                if(method.getName().equals("doGet")){

                    

                    Object invoke = method.invoke(clazz.newInstance(), args);

                }

                System.out.println("======执行完成=====");

                return null;

            }

        });

          

        

        //7    获取代理类的接口方法,调用执行

        //通过接口数组的第一个接口,获取第一个接口中的所有方法

        Method[] methods = interfaces[0].getMethods();

        //通过操作methods方法数组,获取第一个方法对象,通过他的invoke方法,调用方法执行

        methods[0].invoke(obj, "req","res");

    }

      

    

    //为什么要用动态代理?

    //1)可以通过代理对象,对指定的类的方法进行修改(拦截或者增强),使用动态代理修改类,不需要修改原来的代码,因为我们是创建一个新的类

    //2)一次动态代理,可以对当前指定的类,的所有方法,进行拦截或者增强的操作

    

    /**

     * 继承

     * 当你要写的这个类,与原来要被增强的那个类是同一类型事物(animal cat dog

     *

     * 装饰设计模式

     * 当你要写的这个类,与原来要被增强的那个类不是同一类型事物(animal dianziGou / request对象进行增强,也是使用装饰设计模式

     *

     * 动态代理

     * 需要,在java虚拟机启动之后,再去修改指定的类,那么就使用动态代理

     *

     * 怎么就突然出现了一个类呢?

     * Person.java -->javac命令 Person.class

     * 创建代理类,底层机制类似javac命令,所以不属于java范畴,后期,入职sun公司,请回来告诉我。

     *

     * */

 

总结:

使用动态代理中,可以一个类的方法执行前后做增强和过滤的动作

 

2.3、动态代理完成全站乱码处理

过滤器:全站乱码过滤器

 

需求:使用动态代理方式,处理乱码

  1. 过滤器:对请求进行增强
  2. 过滤器:过滤任务写在doFilter方法
  3. 过滤任务:通过创建代理对象来增强原来的request
  4. 根据不同请求方式处理:
  5. Post:setCharacterEncoding("utf-8")'
  6. Get:根据不同的方法,不同处理
  7. getParameter方法:new String();
  8. getParameterValues方法:遍历循环,new String();
  9. getParameterMap:循环遍历,new String();
  10. request对象还要其他方法,其他方法执行不做任何处理

 

package cn.itcast.filter;

 

import java.io.IOException;

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

import java.util.Map;

import java.util.Set;

 

import javax.servlet.Filter;

import javax.servlet.FilterChain;

import javax.servlet.FilterConfig;

import javax.servlet.ServletException;

import javax.servlet.ServletRequest;

import javax.servlet.ServletResponse;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

 

public class EncodingFilter2 implements Filter {

 

    @Override

    public void init(FilterConfig filterConfig) throws ServletException {

    }

 

    @Override

    public void doFilter(ServletRequest request, ServletResponse response,

            FilterChain chain) throws IOException, ServletException {

        final HttpServletRequest req = (HttpServletRequest) request;

        HttpServletResponse res = (HttpServletResponse) response;

        res.setContentType("text/html;charset=utf-8");

 

        // 使用动态代理创建代理对象,对request进行增强

        HttpServletRequest myrequest = (HttpServletRequest) Proxy

                .newProxyInstance(req.getClass().getClassLoader(), req

                        .getClass().getInterfaces(), new InvocationHandler() {

 

                    @Override

                    public Object invoke(Object proxy, Method method,

                            Object[] args) throws Throwable {

                        // 设置代理策略,对request的获取数据的方法进行增强

 

                        //获取请求方式

                        String method2 = req.getMethod();

                        if(method2.equalsIgnoreCase("post")){

                            req.setCharacterEncoding("utf-8");

                            return method.invoke(req, args);

                        }else if(method2.equalsIgnoreCase("get")){

                            //获取请求参数的方法

                            //根据不同的方法,进行不同增强操作

                            if("getParameter".equals(method.getName())){

                                String parameter = req.getParameter((String)args[0]);

                                if(parameter != null){

                                    parameter = new String(parameter.getBytes("iso-8859-1"),"utf-8");

                                }

                                return parameter;

                            }else if("getParameterValues".equals(method.getName())){

                                

                                String[] values = req.getParameterValues((String)args[0]);

                                if(values != null){

                                    for (int i = 0; i < values.length; i++) {

                                        values[i] = new String(values[i].getBytes("iso-8859-1"),"utf-8");

                                    }

                                }

                                return values;

                            }else if("getParameterMap".equals(method.getName())){

                                Map<String, String[]> map = req.getParameterMap();

                                if(map != null){

                                    Set<String> keySet = map.keySet();

                                    for (String key : keySet) {

                                        String[] values = map.get(key);

                                        for (int i = 0; i < values.length; i++) {

                                            values[i] = new String(values[i].getBytes("iso-8859-1"),"utf-8");

                                        }

                                    }

                                }

                                return map;

                            }else{

                                return method.invoke(req, args);

                            }

                        }else{

                            return method.invoke(req, args);

                        }

                    }

                });

 

        chain.doFilter(myrequest, res);

    }

 

    @Override

    public void destroy() {

    }

 

}

 

 

    

3、注解技术

3.1、注解和注释区别

    注释:它是对程序中的代码进行解释说明。注释是给人看。

    注解:它是给程序使用的。在程序运行的时候(编译的时候),会根据相应的注解,然后完成对应的操作。注解经常需要和反射一起使用。

 

    早期的程序:

        基本是程序配合配置文件一起工作,在程序运行的时候,会读取配置文件中的数据。

        

    现在程序:

        在程序运行的时候,不再使用配置文件(少量使用配置文件),而是在程序加入注解来代替配置文件。

3.2、注解格式和JDK注解简单演示

    注解的格式:@注解的名称( key=value , key=value, key=value。。。。。。 )

JDK中自带的3个注解:

        1、方法复写的时候注解:

            使用Override注解声明当前方法是复写父类的方法,或者是实现了接口中的方法

            

            jdk1.5@override不支持接口的方法复写,1.6以上可以

            

            

        2、过时的方法注解:

@Deprecated它声明某个方法已经过时。

            

                    

3、消除程序中的警告

@SuppressWarnings 消除警告。            

            

 

3.3、自己定义注解的实现

    自己定义注解:

    格式:public @interface 注解名字{

定义注解的成员变量;

}

    

    第一步:

    

    

    效果:

    

    自定义注解:

自定义注解使用:

      

    

3.4、注解的细节

 

@Test注解不能出现在类上:

获取Junit源代码:

第一步:找到junit在eclipse中的位置

第二步:使用反编译神器,将class文件还原成.java文件

 

 

 

发现:在@Test注解的底层类上有两个注解

 

@Retention(RetentionPolicy.RUNTIME)

指定注解什么时候存在

 

@Target({java.lang.annotation.ElementType.METHOD})

指定注解可以写在什么位置

 

1、在定义注解的时候,可以指定注解什么时候存在——@Retention

        这个注解是存在于源代码上,还是编译之后的class文件上,还是运行时期也要存在。

        @Retention 使用jdk中的Retention 来声明自己的注解将来在程序中存活的具体时间

        RetentionPolicy:声明注解存活的时间:

 

代码演示:

注意:声明注解的存活时间时,使用runtime是给反射程序使用的。

 

    2、在定义注解的时候,指定注解可以写在什么位置——@Target

 

        注解是使用在类上,方法上,构造方法上,成员变量,或者是其他注解上。

        在Java中提供的@Target注解,声明自己 定义的注解在其他的类中的存在位置。

        

 

代码演示:

效果:

 

        演示代码(反射获取注解):

@Test

    @MyAnnotation

    public void test9() throws Exception{

        

        Method method = this.getClass().getMethod("test9", null);

        MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);

        System.out.println(annotation);

    }

    

 

效果:

 

  1. 在注解中定义成员变量

     

    格式:数据类型 变量名() default 默认值

    测试代码:

                        

            效果:

            

        

        

3.5、使用注解完成数据库连接的获取

 

    定义注解:

package cn.itcast.zhujie;

 

import java.lang.annotation.Retention;

import java.lang.annotation.Target;

import java.lang.annotation.RetentionPolicy;

 

@Retention(RetentionPolicy.RUNTIME)

@Target({java.lang.annotation.ElementType.METHOD})

public @interface MyAnnotation {

 

    String driverClass();

    String jdbcUrl();

    String user();

    String password();

}

 

使用注解:

package cn.itcast.test;

 

import java.lang.annotation.Annotation;

import java.lang.reflect.Method;

import java.sql.Connection;

import java.sql.DriverManager;

 

import org.junit.Test;

 

import cn.itcast.zhujie.MyAnnotation;

 

 

 

public class TestUtils2 {

    @MyAnnotation(driverClass="com.mysql.jdbc.Driver",jdbcUrl="jdbc:mysql:///day14",user="root",password="root")

    @Test

    public void test() throws Exception{

        //获取注解上的值,获取数据库连接

        Method method = this.getClass().getMethod("test", null);

        MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);

        String driverClass = annotation.driverClass();

        String jdbcUrl = annotation.jdbcUrl();

        String user = annotation.user();

        String password = annotation.password();

        

        Class.forName(driverClass);

        

        Connection connection = DriverManager.getConnection(jdbcUrl, user, password);

        System.out.println(connection);

        

    }

}

 

posted @ 2017-01-10 21:42  beyondcj  阅读(253)  评论(0编辑  收藏  举报