载类的工具-----类加载器
✔java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器
每个类负责加载特定位置的类
BootStrap,ExtClassLoader,AppClassLoader
✔类加载器也是java类,因为其他是java类的类加载器本身也要被类加载器加载,显然必须有第一个类加载器不java类,这正是BootStrap
✔java虚拟机中的所有类加载器采用具有斧子关系的树形结构进行
组织,在实例化每个类加载器对象时,需要为其指定一个夫级类
加载器对象或者默认采用系统类加载器为其夫级类加载
类加载器之间的父子关系和管辖范围
注意:如果AppClassLoader(儿子)下和ExClassLoader(爸爸)下,都有一个共同的.class文件,
那么,优先去加载的是ExClassLoader(爸爸)…其实这个就是下面的委托机制….
通常不可以,因为有委托机制存在,每次加载都是Bootstrap加载rt包中的System类.但是,我也有办法,自己可以写一个加载器.让他挂在AppClassLoader上,指定他去加载我编写的System
java.lang
类 ClassLoader
内部会先用loadClass方法,去找他的爸爸.看爸爸能不能干.
Class<?> |
当,爸爸返回来信息说不能干后,再调用findClass方法自己干
protected Class<?> |
这是一种模版方法设计模式(了解):
(ClassLoader)父类中有àloadClass()&findClass()&definClass()[得到class文件转换成字节码]
|----子类A(自己干只需覆盖findClass,局部细节自己干)
|----子类B(自己干覆盖findClass,不覆盖loadClass是为了保留,流程)
总体的流程,在父类中已经规定好了,而有一些细节,就空出来,留给子类去覆盖.
子类A和子类B的流程是一样的.但是子类A和B自己干的方法都各不相同.
package cn.itcast.day2;
import java.util.Date;
public class ClassLoaderTest {
public static void main(String[] args) throws Exception {
System.out.println(//查看该类是由那一个类加载器加载的
"该类是由"+ClassLoaderTest.class.getClassLoader().getClass().getName()+"类加载器,加载进来的");
//引出Bootstrap加载器:
//下面语句会报出空指针异常,一看到空指针异常就,应该想到"."(点)前的东西为"null"
//System.out.println(//java.lang.NullPointerException
//System.class.getClassLoader().getClass().getName());
//所以,起初判断.getName()前面的.getClass()为null.但再想一下,只有一个java对象,
//就一定是一个Class搞出来的,这里是不可能的.---所以,再往前推,有可能
//getClassLoader()这个根就等于null.实践如下:
System.out.println(
System.class.getClassLoader());//result:null
//从这个例子中得知,System,并不是,没有加载器,而是证明了他,是有一个特殊的加载器(Bootstrap)加载的
//引出Bootstrap,ExtClassLoader,AppClassLoader三个加载器之间的关系.
//定义一个[ClassLoader]对象,得到的是那个AppClassLoader
ClassLoader loader=ClassLoaderTest.class.getClassLoader();
while(loader!=null){
System.out.println(loader.getClass().getName());//打印出基层的类加载器
loader=loader.getParent();//将基层类加载器的父亲覆盖给基层
}
System.out.println(loader);//上面循环,是以前从孙子-->爸爸--->爷爷(null)
//打印出加密后的类,来看看效果.
// System.out.println(new ClassLoaderAttachment().toString());
//-------------注意这里,父类是AppClassLoader,他只能加载ClassPaht下的,所以要加上包名
Class clazz=new MyClassLoader("itcastlib").loadClass("cn.itcast.day2.ClassLoaderAttachment");
//这里不能直接用那个加密过的类,因为系统识别不了,一堆乱码.所以,这里提前集成的Date父类就起作用了.
Date d1=(Date)clazz.newInstance();
System.out.println(d1);
}
}
代理
代理的概念与作用
程序中的代理
要为已存在的多个具有相同接口的目标类的各个方法增加一些系统功能,例如,异常处理,日志,计算方法的运行时间,事务管理,等等
编写一个与目标类具有相同接口的代里类,代理类的每个方法调用目标类的相同方法,并在调用方法时加上系统功能的代码
如果采用工厂模式和配置文件的方式进行管理,则不需要修改客户端程序,在配置文件中配置是使用目标类,还是代理类,这样以后很容易切换,譬如,想要日志功能时就配置代理类,否则配置目标类,这样,增加系统功能很容易,以后运行一段事件后,又想去掉系统功能也很容易
AOP(面向方面的编程语言)
系统中存在交叉业务,一个交叉业务就是要切入到系统中的一个方面,如下所示:
安全 事物 日志
StudentService -------|--------|--------|-----
CourseService -------|------- |--------|-----
MiscService -------|--------|--------|-----
具体的程序代码描述交叉业务:
Method1 method2 method3
{ { {
-------------------------------------切面
…… ……. ………..
-------------------------------------切面
} } }
交叉业务的编程问题即为面向方面的编程(Aspect oriented program 简称AOP),AOP的目标就是要使交叉业务模块化,可以采用将切面代码移动到原始方法的周围,这与直接在方法中编写切面代码的运行效果是一样的,如下所示
-------------------------------------切面
Func1 func2 func3
{ { {
…….. …….. …….
} } }
-------------------------------------切面
使用代理技术正好可以解决这种问题,代理是实现AOP功能的核心和关键技术
动态代理技术
l 要为系统中的各种接口的类增加代理功能,那将需要太多的代理类,全部采用静态代理方式,将是一件非常麻烦的事情,写成百上千的代理类,很痛苦
l JVM可以在运行期动态生成出类的字节码(以前都是手动用javac编译出class文件,现在JVM自动的生成字节码),这种动态生成的类往往被用作代理类,即动态代理类
l JVM生成的动态类必须实现一个或多个接口,所以,JVM生成的动态类只能用作具有相同接口的目标类的代理
l CGLIB库可以动态生成一个类的子类,一个类的子类也可以用作该类的代理,所以,如果要为一个没有实现接口的类生成动态代理类,那么可以使用CGLIB库
l 代理类的各个方法中通常除了要调用目标的相应方法和对外返回目标返回的结果外,还可以在代理方法中的如下四个位置加上系统功能代码:
1. 在调用目标方法之前
2. 在调用目标方法之后
3. 在调用目标方法前后
4. 在处理目标方法异常的catch块中
分析JVM动态生成的类
1.创建实现了Collection接口的动态类和查看其方法名称
在java.lang.reflect.Proxy类中有个getProxyClass()方法:返回class字节码,就是造出一个类 ,此方法需要指定类加载器和实现的接口(此例子实现的接口是Collection),而使用的类加载器通与接口的一样,
public static Class<?> getProxyClass(ClassLoader loader,
Class<?>... interfaces)
参数:
loader - 定义代理类的类加载器
interfaces - 代理类要实现的接口列表
返回:
用指定的类加载器定义的代理类,它可以实现指定的接口
使用Class类中的getProxyClass方法获取方法的所有所有参数
列出动态类中的所有构造方法和参数签名
列出动态类中的所有方法和参数签名
2.创建动态类的实例对象(三种方式)
1.因为没有无参的构造方法,所以需要先获取有参的构造方法
用反射获得构造方法
getConstructor方法获取了构造方法,再通过通过构造方法Constructor类中的newInstance方法,来创建对象,因为在newInstance中需要传入参数的类型是此构造方法的实际类型(InvocationHandler对象),而InvocationHandler是一个接口,
不能new对象,只能先创建一个类实现这个接口
编写一个是简单的实现InvocationHandler接口的类
调用构造方法创建动态类的实例对象,并将编写的invocationHandler类的实例 对象传进去
2.也可以将创建动态类实例对象的代理改成匿名内部类的形式编写,
3.用Proxy.newInstance方法直接一步就就能创建代理对象
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
参数:
loader - 定义代理类的类加载器
interfaces - 代理类要实现的接口列表
h - 指派方法调用的调用处理程序(就是实现的接口对象)
返回:
一个带有代理类的指定调用处理程序的代理实例,它由指定的类加载器定义,并实现指定的接口
package cn.itcast.day3;
//reflect[映射]
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;
//代理
public class ProxyTest {
public static void main(String[] args)throws Exception {
//*****用jvm虚拟机生成一个动态类....用到 Proxy.getProxyClass()
//类加载器通常用和这个类接口相同的加载器
//一般前缀为clazz的变量,都是一份字节码
Class clazzProxy1=Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
System.out.println(clazzProxy1.getName());//$Proxy0
//打印规定格式化的所有构造方法.
System.out.println("-----------------begin constructors list-----------------");
/**$Proxy0()
$Proxy0(InvocationHandler,int)*/
//这个类上有什么构造方法?
//getConstructors()获得,该类中所有的构造方法.以数组方式存放.
Constructor[] constructors=clazzProxy1.getConstructors();
//遍历构造方法,数组.
for(Constructor constructor:constructors){
String name=constructor.getName();//获取构造方法的名字,并存储
StringBuilder sBuilder=new StringBuilder(name);
sBuilder.append("(");//格式"("
/*Class<?>[] getParameterTypes()
按照声明顺序返回一组 Class 对象,这些对象表示此 Constructor对象所表示构造方法的形参类型。*/
Class[] clazzParams=constructor.getParameterTypes();
//遍历形参类型
for(Class clazzParam: clazzParams){
sBuilder.append(clazzParam.getName()).append(",");//一个参数后加一个逗号..格式
}
//在这判断下,因为,有可能该构造方法,没有参数
if(clazzParams.length!=0)
//删除最后一个多余的","逗号
sBuilder.deleteCharAt(sBuilder.length()-1);
sBuilder.append(")");//格式")"
System.out.println(sBuilder.toString());
}
//打印出所有的方法
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.length!=0)
sBuilder.deleteCharAt(sBuilder.length()-1);
sBuilder.append(")");
System.out.println(sBuilder.toString());
}
//创建这个动态累的实例对象
System.out.println("-----------------begin create instance object-----------------");
//clazzProxy1.newInstance();Class中的newInstrance(),为调用无参的构造方法.
Constructor constructor=clazzProxy1.getConstructor(InvocationHandler.class);
//提前建立一个类实现这个接口InvocationHandler(是代理实例的调用处理程序 实现的接口)
class MyInvocationHander1 implements InvocationHandler{
@Override//唯一的一个子类
/**invoke(Object proxy, Method method, Object[] args)
在代理实例上处理方法调用并返回结果。*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
return "第一种方法,准备一个InvocationHandler的子类,测试";
}
}
Collection proxy1=(Collection)constructor.newInstance(new MyInvocationHander1());
//proxy1.toString(),为null..并不是该对象没有建立.
System.out.println(proxy1);
//注意:只有:hashCode和equals和toString这三个方法是交给代理中handler.
//clear(),没有返回值,不报异常
proxy1.clear();
//因为,这个代理中也有size()方法,这个size()方法的返回值是整数(因为集成了同一个接口方法的参数和返回值自然一样)
//proxy1.size();有返回值,报异常
Collection proxy2=(Collection)constructor.newInstance(new InvocationHandler(){
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// TODO Auto-generated method stub
return "第二种方法,匿名InvocationHandler子类测试";
}
});
//proxy1.toString(),为null..并不是该对象没有建立.
System.out.println(proxy2);
//第三种方式,直接一步到位,同时搞出字节码,和实例对象..*************************************************
/** static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。 */
ArrayList target=new ArrayList();
Collection proxy3 = (Collection)getProxy(target,new MyAdvice());
//注意:每调一次add方法,他就会去找一次handler的invoke方法
proxy3.add("bls");
//System.out.println("+++++"+proxy3.add("zxx"));
System.out.println(proxy3);
System.out.println("size:"+proxy3.size());
}
//给我目标给我系统功能,我就会生成一个代理.这个代理就会执行目标,并在该
//目标中添加功能代码.
private static Object getProxy(final Object target,final Advice advice) {
//Proxy的静态方法,直接一步到位
Object proxy3=Proxy.newProxyInstance(
target.getClass().getClassLoader(),//第一个参数
//new Class[]{Collection.class},//第二个参数不能用可变参数,因为,可变参数,要放到最后哦..
target.getClass().getInterfaces(),
new InvocationHandler(){//第三个参数***************************
//指定一个目标
@Override
//invoke在代理实例上处理方法调用并返回结果。
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));*/
advice.beforeMethod(method);
Object retVal=method.invoke(target, args);
advice.afterMethod(method);
//返回这个值.(目标)
return retVal;
//return method.invoke(proxy, args);
}
}
);
return proxy3;
}
}
package cn.itcast.day3.aopframework;
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 {
// ---- load()从输入流中读取属性列表(键和元素对)。
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);
// 调用不带参数的构造方法//javaBean必须要有一个
bean = clazz.newInstance();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 如果是ProxyFactoryBean类型,就创造一个代理----spring中ProxyFactoryBean是一个接口
if (bean instanceof ProxyFactoryBean) {
Object proxy = null;
ProxyFactoryBean proxyFactoryBean = (ProxyFactoryBean) bean;
try {
// 从配置文件中获取类名后,再获取相应的字节码,后在new一个无参数的实例对象
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;
}
}
package cn.itcast.day3.aopframework;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import cn.itcast.day3.Advice;
public class ProxyFactoryBean {
private Advice advice;
public Advice getAdvice() {
return advice;
}
public void setAdvice(Advice advice) {
this.advice = advice;
}
private Object target;
public void setTarget(Object target) {
this.target = target;
}
public Object getTarget() {
return target;
}
public Object getProxy() {
// TODO Auto-generated method stub
return getProxy(target,advice);
}
//给我目标,给我系统功能,我就会生成一个代理.这个代理就会执行目标,并在该
//目标中添加功能代码.
private static Object getProxy(final Object target,final Advice advice) {
Object proxy3=Proxy.newProxyInstance(
target.getClass().getClassLoader(),//第一个参数
//new Class[]{Collection.class},//第二个参数不能用可变参数,因为,可变参数,要放到最后哦..
target.getClass().getInterfaces(),
new InvocationHandler(){//第三个参数
//指定一个目标
@Override
//invoke在代理实例上处理方法调用并返回结果。
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));*/
System.out.println("ok");
advice.beforeMethod(method);
Object retVal=method.invoke(target, args);
advice.afterMethod(method);
//返回这个值.(目标)
return retVal;
//return method.invoke(proxy, args);
}
}
);
System.out.println(proxy3);
return proxy3;
}
}