黑马程序员_类加载器、代理

本文详细介绍了Java中类加载器的工作原理及自定义类加载器的实现方式,并深入探讨了动态代理技术,包括其应用场景、实现机制以及如何利用动态代理来模拟Spring框架。

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

------- android培训 java培训 、期待与您交流! ----------

一、类加载器

1.实际上就是将.class文件加载进内存,也可以将普通类加载进内存。

2.Java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器,每个类负责加载特定位置的类:BootStrap,ExtClassLoader,AppClassLoader

BootStrap在Java JVM中用于加载别的类

ExtClassLoader在JRE/lib/ext/*.jar加载jar包

AppClassLoader用于加载ClassPath指定的所有jar或目录。

3.类加载器的委托机制:

每个ClassLoader本身只能分别加载特定位置和目录中的类,但它们可以委托其他的类装载器去加载类,这就是类加载器的委托模式。类装载器一级级委托到BootStrap类加载器,当BootStrap无法加载当前所要加载的类时,然后才一级级回退到子孙类装载器去进行真正的加载。当回退到最初的类装载器时,如果它自己也不能完成类的装载,那就应报告ClassNotFoundException异常。

自定义类加载器并实现加密解密:

代码如下:

public class MyClassLoader extendsClassLoader{
    public static void main(String[] args) throws Exception {
        String srcPath=args[0];
        String desDir=args[1];
        FileInputStream fileIn=new FileInputStream(srcPath);
        String desPath=desDir+"\\"+srcPath.substring(srcPath.lastIndexOf('\\')+1);
        FileOutputStream fileOut=new FileOutputStream(desPath);
        copyfile(fileIn,fileOut);
        fileIn.close();
        fileOut.close();
    }
    public static void copyfile(InputStream in,OutputStream out) throws Exception
    {
           int i=-1;
            while((i=in.read())!=-1)
            {
                out.write(i^0xff);
            }
    }
    private String desfiles;
         
@Override
protected Class findClass(String name) throws ClassNotFoundException {
    String file="javalib"+"//"+name+".class";
    try {
        FileInputStream In=new FileInputStream(file);
        ByteArrayOutputStream by=new ByteArrayOutputStream();
        copyfile(In,by);
        In.close();
        byte[]bytes=by.toByteArray();
        return defineClass(bytes,0,bytes.length);
    } catch(Exception e) {
        e.printStackTrace();
    }
return null;  
}
public MyClassLoader()
{
    }
public MyClassLoader(String desfiles)
{
    this.desfiles=desfiles;
    }
}
//测试类
public class ClassLoaderTest {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        Class calzz=
new MyClassLoader("javalib").loadClass("ClassLoaderAttachment");
            Date d=(Date) calzz.newInstance();
            System.out.println(d);
    }
}
总结:自定义类加载器必须继承ClassLoader

      要覆盖findClass方法,不用覆盖loadClass方法。原因是:为了保留原来的流程体系。

二、代理

概念:实际上就是要为已经存在的多个具有相同接口的目标类的各个方法增加一些系统功能。

代理的作用:实际上就是在你的目标代码上,添加一些别的代码,你本身的代码也运行了。

            这个新的代码就是代理。

代理类的好处:生成代理类后但你想要改变程序是不需要改变源程序。

交叉业务的编程问题即面向方面的编程(AOP),AOP的目标就是使交叉业务模块化,可以采用将切面代理移动到原始方法的周围,这与直接在方法中编写切面代理的过程效果是一样的。

实际上是面向一个面的,在一些目标方法的前后位置插入一些代码。

 动态代理技术
概述:
    1、要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,
       这时就不能采用静态代理方式,需用动态代理技术。
      注意:JVM生成的动态类必须实现一或多个接口,所以JVM生成的动态代理类只能用作具有相同接口的目标类代理。
      CGLIB库可以动态生成一个类的子类,一个类的子类也可以作为该类的代理,
     所以,如果要为一个没有实现接口的类生成动态代理,那么可以使用CGLIB库

创建一个动态类:

Proxy:代理类

//得到一个类的class

Class clazz=

proxy.getProxyClass(Collection.class.getClassLoader(),Collection.class)

这个代理类只有一个构造方法:

$proxy0(InvocationHandler i);

InvocationHandler是一个接口

//获取构造函数

Constructor constructor=clazz.getConstructor(InvocationHandler.class);

//创建动态类的实例对象方式一

class MyInvocationHander1 implements InvocationHandler{
          public Objectinvoke(Object proxy, Method method, Object[] args)
                  throws Throwable{
              // TODOAuto-generated method stub
              return null;
          }
      }

//创建动态类的实例对象方式二

Collection proxy2 = (Collection)constructor.newInstance(newInvocationHandler(){
          public Objectinvoke(Object proxy, Method method, Object[] args)
                  throws Throwable{
              return null;
          }
      });

//获得对象   

Collection proxy1 =(Collection)constructor.newInstance(new MyInvocationHander1());
      System.out.println(proxy1);
      proxy1.clear();
      proxy1.size();

注意:不能调用有返回值的size方法,因为:size的返回int类型的,代码中是返回的null,null没有办法转变成int

//可以直接创建对象:

Object proxy3 = Proxy.newProxyInstance(
               target.getClass().getClassLoader(),
               target.getClass().getInterfaces(),
               newInvocationHandler(){
               publicObject invoke(Object proxy, Method method, Object[] args)
                           throwsThrowable { 
                   }

目标类中的方法怎么在代理类中执行的呢?

实际上目标类中的方法每被调用一次,就会执行一次invote方法。

InvocationHander的运行原理是:


下面是一个代理类的代码:

//代理类

public class ProxyTest {
    public static void main(String[] args) throws Exception {
        ArrayList al=new ArrayList();
        Collectionconllection = (Collection) getProxy(al,new MyAdvice());
        conllection.add("aaaa");
        conllection.add("bbbb");
        conllection.add("cccc");
        conllection.add("dddd");
        System.out.println(conllection);
    }
    private static Object getProxy(final Objectobj,final Advice ad)
    {
        Collection conllection=(Collection)Proxy.newProxyInstance(
            obj.getClass().getClassLoader(),
            obj.getClass().getInterfaces(),
             new InvocationHandler() {
                ArrayList al=new ArrayList();
                public Objectinvoke(Object proxy, Method method, Object[] args)
                        throws Throwable {
                    ad.beforeMethod(method);
                Object val=method.invoke(al, args);
                 ad.afterMethod(method);
                    return val;
                }
            });
        returnconllection;
    }      
} 

import java.lang.reflect.Method;
public class MyAdvice implements Advice {
long start;
    @Override
    public void afterMethod(Method method) {
        // TODO Auto-generated method stub
        long start=System.currentTimeMillis();
    }
    @Override
    public void beforeMethod(Method method) {
        // TODO Auto-generated method stub
        longend=System.currentTimeMillis();
       
        System.out.println(method.getName()+"yun xing de shijan"+(end-start));
    }
}
 
import java.lang.reflect.Method;
public interface Advice {
 void beforeMethod(Method method);
void afterMethod(Method method);
}
总结:实现一个代理,实际上就是在目标类的前后夹代码,上面代码把要加的方法抽出来了,抽成了一个MyAdvice类。这样可以随时改变要加入的方法。又抽成了Advice接口,有利于扩展性。

利用动态代理模拟Spring框架

代码如下:

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
importcn.itcast.day3.Advice;
publicclass BeanFactory {
    Properties props = new Properties();
    public BeanFactory(InputStream ips){
        try {
            props.load(ips);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    public Object getBean(String name){
        String className =props.getProperty(name);
        Object bean = null;
        try {
            Class clazz = Class.forName(className);
            bean = clazz.newInstance();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        if(bean instanceof ProxyFactoryBean){
            Object proxy = null;
            ProxyFactoryBean proxyFactoryBean =(ProxyFactoryBean)bean;
            try {
                Advice advice =(Advice)Class.forName(props.getProperty(name +".advice")).newInstance();
                Object target =Class.forName(props.getProperty(name + ".target")).newInstance();
                proxyFactoryBean.setAdvice(advice);
                proxyFactoryBean.setTarget(target);
                proxy =proxyFactoryBean.getProxy();
            } catch (Exception e) {
                // TODO Auto-generated catchblock
                e.printStackTrace();
            }
            return proxy;
        }
        return bean;
    }
}
总结:这个代码主要是从配置文件中读取信息,并且判断返回的是代理对象,还是普通的对象。

import  java.lang.reflect.InvocationHandler;
import  java.lang.reflect.Method;
import  java.lang.reflect.Proxy;
import  cn.itcast.day3.Advice;
publicclass 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(){
                    public Object invoke(Objectproxy, Method method, Object[] args)
                            throws Throwable {
                        advice.beforeMethod(method);
                        Object retVal =method.invoke(target, args);
                        advice.afterMethod(method);
                        return retVal;                     
                    }
                }
                );
        return proxy3;
    }
 
}
总结:这段代码是用于创建代理类。

import  java.io.InputStream;
import  java.util.Collection;
public class AopFrameworkTest {
    /**
     *@param args
     */
    public static void main(String[] args)throws Exception {
        // TODO Auto-generated method stub
        InputStream ips =AopFrameworkTest.class.getResourceAsStream("config.properties");
        Object bean = newBeanFactory(ips).getBean("xxx");
        System.out.println(bean.getClass().getName());
        ((Collection)bean).clear();
    }
}
总结:这是用于进行测试的类。

实际上模拟Spring框架,就是让类动态的切换代理类和普通类,也动态的可以加载配置问价中的内容,当你的需求想要改变时,只需该配置问件即可。


------- android培训java培训、期待与您交流! ----------
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值