黑马程序员_java高新(四)_类加载器、动态代理

时间:2023-02-17 16:55:36

--------------------- <a href="http://www.itheima.com"target="blank">ASP.Net+Unity开发</a>、<a href="http://www.itheima.com"target="blank">.Net培训</a>、期待与您交流! ----------------------

类加载器:

 

 

类加载器就是加载类的工具。

 

 

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

 

 

BootStrap,   

 

ExtClassLoader,   

 

AppClassLoader 。

 

 

类加载器也是Java类,因为其他是java类的类加载器本身也要被类加载器加载,显然必须有第一个类加载器不

 

 

是java类,就是BootStrap。

 

 


黑马程序员_java高新(四)_类加载器、动态代理
 

 

 

类加载器的委托机制:

 

 

加载过程:

 

 

1、首先当前线程的类加载器去加载线程中的第一个类。

 


2、如果类A中引用了类B,Java虚拟机将使用加载类A的类装载器来加载类B。

 

 

3、还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类。

 

 

4、每个类加载器加载类时,又先委托给其上级类加载器。

 

 

自定义一个类加载器:

 

Java代码   黑马程序员_java高新(四)_类加载器、动态代理
  1. public class MyClassLoader extends ClassLoader {  
  2.   
  3.     public static void main(String[] args) throws Exception {  
  4.   
  5.         String srcPath = args[0];  
  6.         String destDir = args[1];  
  7.           
  8.         FileInputStream fis = new FileInputStream(srcPath);  
  9.           
  10.         String destFileName = srcPath.substring(srcPath.lastIndexOf('\\')+1);//目标文件名  
  11.         String destPath = destDir + "\\" + destFileName;//目标路径  
  12.           
  13.         FileOutputStream fos = new FileOutputStream(destPath);  
  14.           
  15.         cypher(fis,fos);//加密导出  
  16.           
  17.         fis.close();  
  18.           
  19.         fos.close();  
  20.     }  
  21.   
  22.     //加密方法  
  23.     private static void cypher(InputStream ips, OutputStream ops) throws Exception {  
  24.           
  25.         int b = -1;  
  26.         while((b=ips.read()) != -1) {  
  27.               
  28.             ops.write(b ^ 0xff);  
  29.         }  
  30.     }  
  31.   
  32.     private String classDir;  
  33.   
  34.     //重写findClass方法  
  35.     @Override  
  36.     protected Class<?> findClass(String name) throws ClassNotFoundException {  
  37.   
  38.         String classFileName = classDir + "\\" + name + ".class";  
  39.         FileInputStream fis = null;  
  40.         try {  
  41.             fis = new FileInputStream(classFileName);  
  42.             ByteArrayOutputStream bos = new ByteArrayOutputStream();  
  43.             cypher(fis,bos);  
  44.             fis.close();  
  45.             byte[] bytes = bos.toByteArray();  
  46.             return defineClass(bytes,0,bytes.length);  
  47.         } catch (Exception e) {  
  48.             e.printStackTrace();  
  49.         }  
  50.         return super.findClass(name);  
  51.     }  
  52.       
  53.     public MyClassLoader() {  
  54.           
  55.     }  
  56.       
  57.     public MyClassLoader(String classDir) {  
  58.           
  59.         this.classDir = classDir;  
  60.     }  
  61.       
  62. }  

 

 

-----------------------------------------------------------------------------------------

 

 

动态代理:

 

 

AOP(面向切面编程):

 

 

系统中存在交叉业务,一个交叉业务就是要切入到系统中的一个方面,如下所示:

 

 

                                    安全      事务         日志

StudentService  ------|----------|------------|-------------

 

CourseService   ------|----------|------------|-------------

 

MiscService     ------|----------|------------|-------------

 

 

 

用具体的程序代码描述交叉业务:

 

 

method1         method2          method3

{                        {                           {

------------------------------------------------------切面

....            ....              ......

------------------------------------------------------切面

}                          }                           }

 

 

 

交叉业务的编程问题即为面向方面的编程(Aspect oriented program ,简称AOP),AOP的目标就是要使交叉业

 

 

务模块化。可以采用将切面代码移动到原始方法的周围,这与直接在方法中编写切面代码的运行效果是一样

 

 

的,如下所示:

 

 

 

------------------------------------------------------切面

func1         func2            func3

{                   {                     {

....            ....              ......

}                    }                     }

------------------------------------------------------切面

 

 

 

使用代理技术正好可以解决这种问题,代理是实现AOP功能的核心和关键技术。

 

 

JVM可以在运行期动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理类。

 

 

JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理。

 

 

CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现

 

 

接口的类生成动态代理类,那么可以使用CGLIB库。

 

 

创建动态类的实例对象过程:

 


1、用反射获得构造方法

 


2、编写一个最简单的InvocationHandler类

 


3、调用构造方法创建动态类的实例对象,并将编写的InvocationHandler类的实例对象传进去

 


4、打印创建的对象和调用对象的没有返回值的方法和getClass方法,演示调用其他有返回值的方法报告了

 

 

异常。

 


5、将创建动态类的实例对象的代理改成匿名内部类的形式编写,锻炼大家习惯匿名内部类。

 

 

动态代理的原理图:

 

 
黑马程序员_java高新(四)_类加载器、动态代理
 

 

编写代理类:

 

 

         通过反射得到构造方法,创建代理对象:

 

Java代码   黑马程序员_java高新(四)_类加载器、动态代理
  1. Constructor constructor = clazzProxy1.getConstructor(InvocationHandler.class);  
  2.         Collection proxy1 = (Collection) constructor.newInstance(new InvocationHandler(){  
  3.   
  4.             @Override  
  5.             public Object invoke(Object proxy, Method method, Object[] args)  
  6.                     throws Throwable {  
  7.                   
  8.                 return null;  
  9.             }  
  10.               
  11.         });  

 

 

由代理直接创建代理对象:

 

Java代码   黑马程序员_java高新(四)_类加载器、动态代理
  1. Collection proxy2 = (Collection)Proxy.newProxyInstance(  
  2.                 Collection.class.getClassLoader(),  
  3.                 new Class[]{Collection.class},  
  4.                 new InvocationHandler(){  
  5.   
  6.                     ArrayList target = new ArrayList();  
  7.                     @Override  
  8.                     public Object invoke(Object proxy, Method method,  
  9.                             Object[] args) throws Throwable {  
  10.                         Object retVal = method.invoke(target, args);  
  11.                           
  12.                         return retVal;  
  13.                     }  
  14.                       
  15.                 }  
  16.                 );  

 

 

AOP简单框架的实现:

 

 

 工厂类BeanFactory负责创建目标类或代理类的实例对象,并通过配置文件实现切换。其getBean方法根据

 

 

参数字符串返回一个相应的实例对象,如果参数字符串在配置文件中对应的类名不是ProxyFactoryBean,

 

 

则直接返回该类的实例对象,否则,返回该类实例对象的getProxy方法返回的对象。

 

 

ProxyFacotryBean充当封装生成动态代理的工厂,需要为工厂类提供目标(target)、通告(advice)。

 

 

ProxyFactoryBean代码:

 

Java代码   黑马程序员_java高新(四)_类加载器、动态代理
  1. public class ProxyFactoryBean {  
  2.   
  3.     private Advice advice;//要插入的系统功能代码  
  4.       
  5.     private Object target;//目标对象  
  6.       
  7.     public Advice getAdvice() {  
  8.         return advice;  
  9.     }  
  10.   
  11.     public void setAdvice(Advice advice) {  
  12.         this.advice = advice;  
  13.     }  
  14.   
  15.     public Object getTarget() {  
  16.         return target;  
  17.     }  
  18.   
  19.     public void setTarget(Object target) {  
  20.         this.target = target;  
  21.     }  
  22.   
  23.     //获得代理对象  
  24.     public Object getProxy() {  
  25.         Object proxy = Proxy.newProxyInstance(//由代理类直接创建代理对象  
  26.                 target.getClass().getClassLoader(),//目标对象的类加载器  
  27.                 target.getClass().getInterfaces(),//目标对象实现的接口  
  28.                 new InvocationHandler(){  
  29.                   
  30.                     public Object invoke(Object proxy, Method method, Object[] args)  
  31.                             throws Throwable {  
  32.   
  33.                         advice.beforeMethod(method);//方法前advice  
  34.                         Object retVal = method.invoke(target, args);//调用目标要调用的方法  
  35.                         advice.afterMethod(method);//方法后advice  
  36.                         return retVal;                        
  37.                           
  38.                     }  
  39.                 }  
  40.                 );  
  41.         return proxy;  
  42.     }  
  43. }  

 

 

BeanFactory代码:

 

 

Java代码   黑马程序员_java高新(四)_类加载器、动态代理
  1. public class BeanFactory {  
  2.   
  3.     Properties props = new Properties();  
  4.     //使用构造方法导入资源配置文件  
  5.     public BeanFactory(InputStream ips) {  
  6.           
  7.         try {  
  8.             props.load(ips);  
  9.         } catch (IOException e) {  
  10.             e.printStackTrace();  
  11.         }  
  12.     }  
  13.       
  14.     //获得bean方法  
  15.     public Object getBean(String name) {  
  16.         String className = props.getProperty(name);  
  17.           
  18.         Class clazz = null;  
  19.         Object bean = null;  
  20.         try {  
  21.             clazz = Class.forName(className);//加载这个类  
  22.             bean = clazz.newInstance();//创建该类的对象  
  23.               
  24.         } catch (ClassNotFoundException e) {  
  25.               
  26.             e.printStackTrace();  
  27.               
  28.         } catch (InstantiationException e) {  
  29.             e.printStackTrace();  
  30.               
  31.         } catch (IllegalAccessException e) {  
  32.             e.printStackTrace();  
  33.               
  34.         }  
  35.           
  36.         if(bean instanceof ProxyFactoryBean) {//判断这个类是不是代理  
  37.             Object proxy = null;  
  38.             try {  
  39.                 ProxyFactoryBean proxyFactoryBean = (ProxyFactoryBean) bean;//拿到一个代理bean工厂  
  40.                 //创建advice  
  41.                 Advice advice = (Advice) Class.forName(  
  42.                         props.getProperty(name + ".advice")).newInstance();  
  43.                 //创建目标对象  
  44.                 Object target = Class.forName(  
  45.                         props.getProperty(name + ".target")).newInstance();  
  46.                 proxyFactoryBean.setAdvice(advice);  
  47.                 proxyFactoryBean.setTarget(target);  
  48.                 proxy = proxyFactoryBean.getProxy();  
  49.             } catch (Exception e) {  
  50.                 e.printStackTrace();  
  51.             }  
  52.             return proxy;  
  53.         }  
  54.         return bean;  
  55.     }  
  56. }  

 

 

MyAdvice代码:

 

Java代码   黑马程序员_java高新(四)_类加载器、动态代理
  1. public class MyAdvice implements Advice {  
  2.       
  3.     private long beginTime = 0;  
  4.   
  5.     @Override  
  6.     public void afterMethod(Method method) {  
  7.           
  8.         long endTime = System.currentTimeMillis();  
  9.         System.out.println(method.getName() + " running time of " + (endTime - beginTime));  
  10.     }  
  11.   
  12.     @Override  
  13.     public void beforeMethod(Method method) {  
  14.   
  15.         beginTime = System.currentTimeMillis();  
  16.     }  
  17.   
  18. }  

--------------------- <a href="http://www.itheima.com"target="blank">ASP.Net+Unity开发</a>、<a href="http://www.itheima.com"target="blank">.Net培训</a>、期待与您交流! ----------------------