码迷,mamicode.com
首页 > 其他好文 > 详细

黑马程序员—类加载、内省、注解、代理

时间:2015-03-08 20:14:33      阅读:338      评论:0      收藏:0      [点我收藏+]

标签:程序员

——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-
一、类加载器
类加载器用于加载Java的字节代码到Java虚拟机中,同时将配置文件等放在classpath目录下。
类加载器读取Java字节码文件并转换成Java.lang.Class类的一个实例,通过该实例的newInstance()方法可以创建该类对象(相当于调用空参数的构造方法)。基本上所有的类加载器都是 java.lang.ClassLoader类的一个实例。
ClassLoader getParent();//返回该类加载器的父类加载器
Class

public class ClassLoaderTree{
    public static void main(String[] args){
        ClassLoader loader = ClassLoaderTree.class.getClassLoader();
        While(loader!=null){
Sysotem.out.println(loader.getClass().getName());
loader = loader,getParent();
}
}
}

类加载器的委托代理机制:
首先当前线程的类加载器去加载线程中的第一个类,如果类A引用了类B,java虚拟机将使用加载类A的类加载器加载类B,还可以直接调用ClassLoader.laod()方法来指定某个类加载器去加载某个类。
每个类加载器加载类时,先委托给其上级父类加载器,当所有祖宗加载器都么有加载到类,则回到发起者类加载器,若发起者类加载器还加载不了,则抛出异常(ClassNotFoundException),而不是再去找发起者类加载器的儿子,因为ClassLoader类中没有getChild()方法。
自定义类加载器:
步骤:
1.编写一个加密程序
2.编写一个类加载器可以对加密过的类进行装载和解密
3.编写一个程序调用类加载器来加载类

public class MyClassLoader extends ClassLoader{
    public static void main(String[] args) throws Exception {
        String srcPath = args[0];//源路径
        String destDir = args[1];//目标的目录
        FileInputStream fis = new FileInputStream(srcPath);
        String destFileName = srcPath.substring(srcPath.lastIndexOf("\\")+1);
        String destPath = destDir +"\\"+destFileName;
        FileOutputStream fos = new FileOutputStream(destPath);//目标路径
        cypher(fis,fos);
        fis.close();
        fos.close();
    }
    private static void cypher(InputStream in,OutputStream out) throws Exception{
        int by = 0;
        while((by=in.read())!=-1){
            out.write(by^0xff);
        }
    }
    private String classDir;
    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException{//子类不能抛出比父类更广泛的异常
        String fileName = classDir+"\\"+name.substring(name.lastIndexOf(".")+1)+".class";
        try {
            FileInputStream fis = new FileInputStream(fileName);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            cypher(fis,baos);
            fis.close();
            byte[] bytes = baos.toByteArray();
            return defineClass(bytes, 0, bytes.length);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return super.findClass(name);
    }
    public MyClassLoader(){}
    public MyClassLoader(String classDir){
        this.classDir=classDir;
    }
}
public class ClassLoaderAttachment extends Date{
    @Override
    public String toString() {
        return "Hello,ClassLoader!";
    }
}
public class ClassLoaderTest {
    public static void main(String[] args) throws Exception {
    Class clazz = new MyClassLoader("itcastlib").loadClass("ClassLoaderAttachment");
        Date d1 = (Date)clazz.newInstance();
        System.out.println(d1);
    }
}

二、内省:
内省(Introspector)是Java 语言对Bean类属性、事件的一种缺省处理方法。JDK提供了对JavaBean进行操作的API,这套API就称为内省。
Java的内省是指在不知道Bean对象的属性的情况下,通过Introspector和属性描述器(PropertyDescriptor)来获取属性的getter/setter方法。
Java内省机制的一般做法是通过类Introspector来获取某个对象的BeanInfo信息,然后通过BeanInfo来获取属性的描述器(PropertyDescriptor),通过这个属性描述器就可以获取某个属性对应的getter/setter方法,然后我们就可以通过反射机制来调用这些方法。

public class IntrospectorTest {
    public static void main(String[] args) throws Exception {
        Point p = new Point(2,3);
        String propName="x";

        PropertyDescriptor propDesc = new PropertyDescriptor(propName, Point.class);
        Method methodGetX = propDesc.getReadMethod();
        Object obj = methodGetX.invoke(p);
        System.out.println(obj);

        PropertyDescriptor proDescriptor = new PropertyDescriptor(propName, Point.class);     
        Method methodSetX = proDescriptor.getWriteMethod();     
        methodSetX.invoke(p, 8);   
        System.out.println(p.getX());
    }
}
BeanInfo beanInfo = Introspector.getBeanInfo(p.getClass());
PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
for(PropertyDescriptor pd : pds){
    if(pds.getName().equals(propertyName)){
    Method getX = pd.getPeadMethod();
    retval = getX.invoke(p);
    break;
}
}

三、注解
注解相当于一种标记,加了注解就等于为程序打上了某种标记,可以用反射来了解你的类及各种元素上有何种标记。标记可以加在包、类、字段、方法、方法的参数以及局部变量上。
注解就相当于一个源程序中要调用的类,要在源程序中应用某个注解就得先准备这个注解类。
元注解:注解的注解叫做元注解,@Retention(RetentionPolicy.RUNTIME)
元注解有三个取值:RetetionPolicy.SOURCE、RetetionPolicy.CLASS、RetetionPolicy.RUNTIME;
一个注解的生命周期有三个阶段:java源文件–>class文件–>内存中的字节码,分别对应于元注解的RetetionPolicy.SOURCE、RetetionPolicy.CLASS、RetetionPolicy.RUNTIME
JDK默认的三个注解是:
@Deprecated:表示已经过时,编译时会产生警告信息,可以加载所有元素上。其Retention是RUNTIME阶段
@SuppressWarnings:表示屏蔽警告信息。其Retention是SOURCE阶段
@Override:表示该方法是重写父类的方法,只能用在方法上。其Retention是SOURCE阶段
自定义注解类:

@Retention(RetentionPolicy.RUNTIME)
public @interface ItcastAnnotation {
}
@ItcastAnnotation
public class AnnotationTest {
    public static void main(String[] args) {
    //通过反射来检查注解 if(AnnotationTest.class.isAnnotationPresent(ItcastAnnotation.class)){
            ItcastAnnotation annotation =
    AnnotationTest.class.getAnnotation(ItcastAnnotation.class);
            System.out.println(annotation);
        }
    }
}

注解的属性:一个注解相当于一个胸牌,如果贴了胸牌就是这个学校的学生,否则就不是。如果还想知道是哪个班哪个年级的学生就得用属性来区分。
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface ItcastAnnotation {
String color() default “blue”;//设置缺省属性
String value();
int[] arr() default {3,4,5};
EnumTest.TrafficLamp lamp() default EnumTest.TrafficLamp.RED;//枚举属性
MetaAnnotation annotationAttr() default @MetaAnnotation(“abc”);//注解属性
}
使用注解
@ItcastAnnotation(annotationAttr=@MetaAnnotation(“xyz”),color=”red”,value=”abc”,arr={1,2,3})
四、代理:
要为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能,编写一个与目标类具有相同接口的代理类,代理类的每个方法调用目标类的相同方法,并在调用方法时加上系统功能的代码。这样我们在调用目标类的方法时就可以不必直接调用目标类了,而是通过调用代理类来实现。
代理的作用是:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,在配置文件中配置是使用目标类、还是代理类,这样以后很容易切换。
AOP思想:系统中存在很多交叉业务,一个交叉业务就是要切入到系统中的一个方面。例如安全、事物、日志等功能要贯穿到好多模块中,这就是交叉业务。
技术分享
一个代理模式的例子:

public interface Subject {
    abstract public void request();
}
public class RealSubject implements Subject {
    public RealSubject() {}
    @Override
    public void request() {
        System.out.println("RealSubject");
    }
}
public class ProxySubject implements InvocationHandler {
    private Subject obj;
    public ProxySubject(){}
    public ProxySubject(Subject obj){
      this.obj=obj;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println("执行方法之前处理");
        method.invoke(obj, args);
        System.out.println("执行方法之后处理");
        return null;
    }
}
public class ProxyModel {
    public static void main(String[] args) {
        RealSubject realSub = new RealSubject();
        Class clazz = realSub.getClass();
        InvocationHandler handler = new ProxySubject(realSub);
        Subject sub = 
        (Subject)Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), handler);
        sub.request();
    } 
}

动态代理:JVM可以在运行期动态生成处类的字节码,这种动态生成的类往往被用作代理类,即动态代理。JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理。如果要为一个么有实现接口的类生成一个动态代理类,那么可以使用CGLIB库,CGLIB库可以动态生成一个类的子类,这个类的子类也可以用作该类的代理。
JVM创建动态类

Class clazz= Proxy.getProxyClass(Collection.class.getClassLoader,Collection.class);
Constructor[] constructors = clazz.getConstructor();
for(Constructor constructor: constructors){
    String name = constructor.getName();//获取构造方法名
    Class[] clazzParams = constructor.getParameterTypes();//获取构造方法参数名
    for(Class clazzParam : clazzParams ){
        clazzParam.getName();
    }
}

InvocationHandler对象的运行原理:
调用代理的方法时会调用代理中的InvocationHandler对象的invoke方法,invoke方法接受三个参数,invoke(Object proxy,Method,method,Object[] args)
第一个参数是代理对象,第二个参数是代理对象的方法,第三个参数是调用代理对象时传递的参数。
invoke方法返回的结果就是代理proxy的返回结果。

Collection proxy3 = (Collection)Proxy.new ProxyInstance(
    Collection.class.getClassLoader,
    new Class[]{Collection.class},
    new InvocationHandler(){
ArrayList al = new ArrayList();
public Object invoke(Object )
});

动态代理的设计原理:
客户端调用代理,代理的构造方法接收一个Handler对象,客户端调用代理的各个方法时,代理的相应方法会把调用请求转发给Handler对象,Handler对象又把请求分发给目标的响应方法。
实现类似Spring的可配置的AOP框架
思路:工厂类BeanFactory负责创建目标类或代理类的实例对象,并通过配置文件实现切换。其getBean方法根据参数字符串返回一个相应的实例对象,如果参数字符串在配置文件中对应的类名不是ProxyFactoryBean,则直接返回该类的实例对象,否则,返回该类实例对象的getProxy方法返回的对象。ProxyFactoryBean充当封装生成动态代理的工厂。
技术分享
实例代码:

public interface Advice {
    public void beforeMethod(Method method);
    public void afterMethod(Method method);
}
public class MyAdvice implements Advice {
    long beginTime = 0;
    long endTime = 0;
    @Override
    public void beforeMethod(Method method) {
        System.out.println("开始!");
        beginTime = System.currentTimeMillis();
    }
    @Override
    public void afterMethod(Method method) {
        System.out.println("结束!");
        endTime = System.currentTimeMillis();
        System.out.println(method.getName()+"运行时间"+(endTime-beginTime));
    }
}
public class BeanFactory {
    Properties prop = new Properties();
    public BeanFactory(InputStream in){
        try {
            prop.load(in);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public Object getBean(String name){
        String className = prop.getProperty(name);
        Object bean = null;
        try {
            Class clazz = Class.forName(className);
            bean = clazz.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        } 
        if(bean instanceof ProxyFactoryBean){
            Object proxy = null;
            ProxyFactoryBean proxyFactoryBean =(ProxyFactoryBean)bean;
            try {
                Advice advice = (Advice)Class.forName(prop.getProperty(name+".advice")).newInstance();
                Object target = Class.forName(prop.getProperty(name+".target")).newInstance();
                proxyFactoryBean.setAdvice(advice);
                proxyFactoryBean.setTarget(target);
                proxy = proxyFactoryBean.getProxy();
            } catch (Exception e) {
                e.printStackTrace();
            } 
            return proxy;
        }
        return bean;
    }
}
public class ProxyFactoryBean{
    private Advice advice;
    private Object target;
    public Advice getAdvice() {
        return advice;
    }
    public void setAdvice(Advice advice) {
        this.advice = advice;
    }
    public Object getTarget() {
        return target;
    }
    public void setTarget(Object target) {
        this.target = target;
    }
    public Object getProxy() {
        Object proxy3 = Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), 
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args)
                            throws Throwable {
                        advice.beforeMethod(method);
                        Object retVal = method.invoke(target, args);
                        advice.afterMethod(method);
                        return retVal;
                    }
                });
        return proxy3;
    }
}
public class AopFrameWorkTest {
    public static void main(String[] args) {
        InputStream in = AopFrameWorkTest.class.getResourceAsStream("config.properties");
        Object bean = new BeanFactory(in).getBean("xxx");
        System.out.println(bean.getClass().getName());
        ((Collection)bean).clear();//调用clear方法
    }
}

config.properties配置文件

xxx=java.util.ArrayList

xxx=AOPFrameWrok.ProxyFactoryBean
xxx.advice=AOPFrameWrok.MyAdvice
xxx.target=java.util.ArrayList

黑马程序员—类加载、内省、注解、代理

标签:程序员

原文地址:http://blog.csdn.net/yuluoqianmu/article/details/44134829

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!