Java基础---Java---基础加强---类加载器、委托机制、AOP、 动态代理技术、让动态生成的类成为目标类的代理、实现Spring可配置的AOP框架
类加载器
Java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器,每个类负责加载特定位置的类:BootStrap,ExtClassLoader,AppClassLoader
类加载器也是Java类,因为其他是java类的类加载器本身也要被类加载器加载,显然必须有第一个类加载器不是不是java类,这正是BootStrap。
Java虚拟机中的所有类装载器采用具有父子关系的树形结构进行组织,在实例化每个类装载器对象时,需要为其指定一个父级类装载器对象或者默认采用系统类装载器为其父级类加载。
类加载器也是一个具体的对象。
委托机制:最先找到上级(JRE/lib/rt.jar).然后逐步往下,也可以写一个加载器,然后让它指定去找。
当Java虚拟机要加载一个类时,到底派出哪个类加载器去加载呢?
首先当前线程的类加载器去加载线程中的第一个类。
如果类A中引用了类B,Java虚拟机将使用加载类A的类装载器来加载类B。
还可以直接调用ClassLoader.loadClass()方法来指定某个类加载器去加载某个类。
每个类加载器加载类时,又先委托给其上级类加载器。
当所有祖宗类加载器没有加载到类,回到发起者类加载器,还加载不了,则抛ClassNotFoundException,不是再去找发起者类加载器的儿子,因为没有getChild方法,即使有,那有多个儿子,找哪一个呢?
对着类加载器的层次结构图和委托加载原理,解释先前将ClassLoaderTest输出成jre/lib/ext目录下的.jar包中后,运行结果为ExtClassLoader的原因。
编写自己的类加载器
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
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);//路径的File,加1是说明从盘符下面开始
String destPath=destDir+"\\"+destFileName;
FileOutputStream fos=new FileOutputStream(destPath);
cypher(fis,fos);
fis.close();
fos.close();
}
private static void cypher(InputStream ips,OutputStream ops) throws Exception{
int b=-1;
while(ips.read()!=-1){
ops.write(b);
ops.write(b^0xff);
}
}
private String classDir;
@Override//类加载器
protected Class<?> findClass(String name) throws ClassNotFoundException {
// TODO Auto-generated method stub
String classFileName=classDir+"\\"+name+".class";//通过类找出硬盘上的文件。
try {
FileInputStream fis=new FileInputStream(classFileName);
ByteArrayOutputStream bos=new ByteArrayOutputStream();//定义一个字节数据流
cypher(fis,bos);//解密
fis.close();
byte[] bytes=bos.toByteArray();
return defineClass(bytes, 0, bytes.length);
} catch (Exception e) {//子类不能被父类抛出
e.printStackTrace();
}//加载这个文件
return super.findClass(name);//调用父类的class
}
//去哪个目录下寻找那份文件
public MyClassLoader(){
}
public MyClassLoader(String clasPath){
this.classDir=classDir;
}
}
类加载器不能加载这种非public的类
/*
Exception in thread "main" java.lang.IllegalAccessException: Class MyClassLoader
can not access a member of class MyTest with modifiers ""
*/
/*
class MyTest
{
public void test()
{
System.out.println("hello,www.it315.org");
}
}
*/
AOP:
系统中存在交叉业务,一个交叉业务就是要切入到系统中的一个方面,如下所示:
安全 事务 日志
StudentService ------|----------|------------|-------------
CourseService ------|----------|------------|-------------
MiscService ------|----------|------------|-------------
用具体的程序代码描述交叉业务:
method1 method2 method3
{ { {
------------------------------------------------------切面
.... .... ......
------------------------------------------------------切面
} } }
交叉业务的编程问题即为面向方面的编程(Aspect oriented program ,简称AOP),AOP的目标就是要使交叉业务模块化。可以采用将切面代码移动到原始方法的周围,这与直接在方法中编写切面代码的运行效果是一样的,如下所示:
------------------------------------------------------切面
func1 func2 func3
{ { {
.... .... ......
} } }
------------------------------------------------------切面
使用代理技术正好可以解决这种问题,代理是实现AOP功能的核心和关键技术。
动态代理技术
要为系统中的各种接口的类增加代理机制,那就将需要太多的代理类,全部采用静态代理方法,将是一件非常麻烦事,写成百上千个代理类,是不是太累!
代理类的各种方法中通常除了要调用目标和相应方法和对外返回目标返回的结果外,还可以在代理中的如下四个
1.在调用目标方法之前
2.在调用目标方法之后
3.在调用目标方法前后
4.在处理目标方法异常的catch块中
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;
import javax.xml.ws.spi.Invoker;
public class ProxyTest {
public static void main(String[] args) throws Exception{
Class clazzProxy1=Proxy.getProxyClass(Collection.class.getClassLoader(),Collection.class);
//对于clazz,我们通常认为它是字节码
System.out.println(clazzProxy1.getName());
System.out.println("begin constructors list-----:");
Constructor[] constructors=clazzProxy1.getConstructors();//得到它的构造方法
for(Constructor constructor:constructors){
String name=constructor.getName();
StringBuilder sBuilder=new StringBuilder();//用 StringBuilder效率更高一点
sBuilder.append('(');
Class [] clazzParams=constructor.getParameterTypes();//得到参数的类型,返回的是一个class的数组。
for(Class clazzParam: clazzParams){//取出每个参数的名字
sBuilder.append(clazzParam.getName()).append(',');
}
if(clazzParams!=null&&clazzParams.length!=0)
sBuilder.deleteCharAt(sBuilder.length()-1);//去掉最后一个参数
sBuilder.append(')');
System.out.println(sBuilder.toString());
}
//StringBuilder与StringBuffered的区别:
//在动态上,都是往字符串中添加字符,在单线程下,用StringBuilder效率要高一点,在多线程下StringBufferd要高点
System.out.println("----------begin methods list----------");
/*$Proxy0()
$Proxy0(InvocationHandler,int)*/
Method[] methods = clazzProxy1.getMethods();
for(Method method : methods){
String name = method.getName();
StringBuilder sBuilder = new StringBuilder(name);
sBuilder.append('(');
Class[] clazzParams = method.getParameterTypes();
for(Class clazzParam : clazzParams){
sBuilder.append(clazzParam.getName()).append(',');
}
if(clazzParams!=null && clazzParams.length != 0)
sBuilder.deleteCharAt(sBuilder.length()-1);
sBuilder.append(')');
System.out.println(sBuilder.toString());
}
//创建动态类的实例对象用调用方法
System.out.println("-----begin create instance-----");
//Object obj=clazzProxy1.newInstance();//不能这能调用构造参数的实例化方法。
//构造方法接受一个参数,然后再去调用构造方法。
Constructor constructor=clazzProxy1.getConstructor(InvocationHandler.class);
class MyInvocationHander1 implements InvocationHandler{
@Override
public Object invoke(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方法有返回值,clear方法没有。
Collection proxy2= (Collection) constructor.newInstance(new InvocationHandler(){
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
return null;
}
});
//代理对象
Collection proxy3=(Collection) Proxy.newProxyInstance(
Collection.class.getClassLoader(),
new Class[] {Collection.class},
new InvocationHandler(){
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{
ArrayList target=new ArrayList();
long beginTime=System.currentTimeMillis();
Object retVal=method.invoke(target, args);
long endTime=System.currentTimeMillis();
System.out.println(method.getName()+" running time of: "+(endTime-beginTime)+"ms");
return retVal;
}
}
);
proxy3.add("zxx");//每调用一个add方法,invoke就被执行
proxy3.add("lhm");
proxy3.add("hjl");
System.out.println(proxy3.size());
}
}
让动态生成的类成为目标类的代理:
import java.lang.reflect.Method;
public class MyAdvice implements Advice {
long beginTime = 0;
public void afterMethod(Method method) {
// TODO Auto-generated method stub
System.out.println("开始啦!");
long endTime = System.currentTimeMillis();
System.out.println(method.getName() + " running time of " + (endTime - beginTime));
}
public void beforeMethod(Method method) {
// TODO Auto-generated method stub
System.out.println("结束啦!");
beginTime = System.currentTimeMillis();
}
}
import java.lang.reflect.Method;
public interface Advice {
void beforeMethod(Method method);
void afterMethod(Method method);
}
object 中有三个方法交给handler,分别是hashcode,equals,toString.
其他的不委托,都有自己的实现方法。
实现类似spring的可配置的AOP框架:
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import cn.itcast.day3.Advice;
public class 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 catch block
e.printStackTrace();
}
return proxy;
}
return bean;
}
}
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 = new BeanFactory(ips).getBean("xxx");
System.out.println(bean.getClass().getName());
((Collection)bean).clear();
}
}
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
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() {
// TODO Auto-generated method stub
Object proxy3 = Proxy.newProxyInstance(
target.getClass().getClassLoader(),
/*new Class[]{Collection.class},*/
target.getClass().getInterfaces(),
new InvocationHandler(){
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
/*long beginTime = System.currentTimeMillis();
Object retVal = method.invoke(target, args);
long endTime = System.currentTimeMillis();
System.out.println(method.getName() + " running time of " + (endTime - beginTime));
return retVal;*/
advice.beforeMethod(method);
Object retVal = method.invoke(target, args);
advice.afterMethod(method);
return retVal;
}
}
);
return proxy3;
}
}
工厂类BeanFactory负责创建目标类或代理类的实例对象,并通过配置文件实现切换。其getBean方法根据参数字符串返回一个相应的实例对象,如果参数字符串在配置文件中对应的类名不是ProxyFactoryBean,则直接返回该类的实例对象,否则,返回该类实例对象的getProxy方法返回的对象。
BeanFactory的构造方法接收代表配置文件的输入流对象,配置文件格式如下:
#xxx=java.util.ArrayList
xxx=cn.day3.aopframework.ProxyFactoryBean //代理
xxx.advice=cn.day3.MyAdvice
xxx.target=java.util.ArrayList//目标
Java基础---Java---基础加强---类加载器、委托机制、AOP、 动态代理技术、让动态生成的类成为目标类的代理、实现Spring可配置的AOP框架的更多相关文章
- JAVA基础知识之JVM-——自定义类加载器
JVM中除了根加载器之外其他加载器都是ClassLoader的子类实例, 可以通过扩展ClassLoader的子类,通过重写方法来实现自定义的类加载器. ClassLoader中有两个关键的方法如下, ...
- Java虚拟机JVM学习05 类加载器的父委托机制
Java虚拟机JVM学习05 类加载器的父委托机制 类加载器 类加载器用来把类加载到Java虚拟机中. 类加载器的类型 有两种类型的类加载器: 1.JVM自带的加载器: 根类加载器(Bootstrap ...
- Java内存管理-掌握自定义类加载器的实现(七)
勿在流沙筑高台,出来混迟早要还的. 做一个积极的人 编码.改bug.提升自己 我有一个乐园,面向编程,春暖花开! 上一篇分析了ClassLoader的类加载相关的核心源码,也简单介绍了ClassLoa ...
- [转载] Java高新技术第一篇:类加载器详解
本文转载自: http://blog.csdn.net/jiangwei0910410003/article/details/17733153 首先来了解一下字节码和class文件的区别: 我们知道, ...
- Java高新技术第一篇:类加载器详解
首先来了解一下字节码和class文件的区别: 我们知道,新建一个Java对象的时候,JVM要将这个对象对应的字节码加载到内存中,这个字节码的原始信息存放在classpath(就是我们新建Java工程的 ...
- 【java虚拟机系列】JVM类加载器与ClassNotFoundException和NoClassDefFoundError
在我们日常的项目开发中,会经常碰到ClassNotFoundException和NoClassDefFoundError这两种异常,对于经验足够的工程师而言,可能很轻松的就可以解决,但是却不一定明白为 ...
- java 类加载器的委托机制
l 当Java虚拟机要加载一个类时,到底派出哪个类加载器去加载呢? 1.首先当前线程的类加载器去加载线程中的第一个类. 2.如果类A中引用了类B,Java虚拟机将使用加载类A的类装载器来加载类B. 3 ...
- Java内存管理-掌握虚拟机类加载器(五)
勿在流沙筑高台,出来混迟早要还的. 做一个积极的人 编码.改bug.提升自己 我有一个乐园,面向编程,春暖花开! 上一篇介绍虚拟机类加载机制,讲解了类加载机制中的三个阶段,分别是:加载.连接(验证.准 ...
- 【Java虚拟机8】自定义类加载器、类加载器命名空间、类的卸载
前言 学习类加载器就一定要自己实现一个类加载器,今天就从一个简单的自定义类加载器说起. 自定义类加载器 例1 一个简单的类加载器,从一个给定的二进制名字读取一个字节码文件的内容,然后生成对应的clas ...
随机推荐
- hdu 5317 合数分解+预处理
RGCDQ Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Submi ...
- 用js来实现那些数据结构11(字典)
我们这篇文章来说说Map这种数据结构如何用js来实现,其实它和集合(Set)极为类似,只不过Map是[键,值]的形式存储元素,通过键来查询值,Map用于保存具有映射关系的数据,Map里保存着两组数据: ...
- SpringBoot学习之集成dubbo
一.摘自官网的一段描述 1.背景 随着互联网的发展,网站应用的规模不断扩大,常规的垂直应用架构已无法应对,分布式服务架构以及流动计算架构势在必行,亟需一个治理系统确保架构有条不紊的演进. 单一应用架构 ...
- Spring学习笔记5——注解方式AOP
第一步:注解配置业务类 使用@Component("Pservice")注解ProductService 类 package com.spring.service; import ...
- STM8操作LCD5110总结
附上一小段代码: void LCD_init(void) { // 产生一个让LCD复位的低电平脉冲 //LCD_RST = 0; GPIO_WriteLow(LCD_PORTG, LCD_RST); ...
- 记一次java heap space的解决办法
问题缘由:后台上传excel导入到数据库,数据量太大,导致报错. 解决方案: 用jdk自带的性能分析器(jconsole)查看了一下,当excel开始导入的时候,发现堆空间直接爆掉. 增加堆空间,在c ...
- 在腾讯云的ubuntu服务器上面安装git服务器
GitHub是一个免费托管开源代码的远程仓库.但是对于某些视源代码如生命的商业公司来说,既不想公开源代码,又舍不得给GitHub交保护费,那就只能自己搭建一台Git服务器作为私有仓库使用.搭建Git服 ...
- Jmeter(一)_环境部署
简介: Apache JMeter是100%纯JAVA桌面应用程序,被设计为用于测试CS/BS的软件.它可以用来测试静态和动态资源的性能,可用于模拟大量负载来测试一台服务器,网络或者对象的健壮性或者分 ...
- 自己创建一个android studio在线依赖compile
我正参加2016CSDN博客之星评选麻烦帮下 奖品我随机送给投票者(写一个随机数抽取) http://blog.csdn.net/vote/candidate.html?username=qfanmi ...
- Scheme r5rs letrec的用法
说明,这是r5rs的用法. (letrec ((<variable> <init>) ...) <body>) 假设((<variable> <i ...