目录 :
1 ) . 类加载器及其委托机制的深入分析
2 ) . 自定义加载器的编写原理分析
3 ) . 编写对class文件进行加密的工具类
4 ) . 编写和测试自己编写的解密类加载器
5 ) . 类加载器的一个高级问题的实验分析
6 ) . 分析代理类的作用与原理和AOP概念
7 ) . 创建动态类及查看其方法列表信息
8 ) . 创建动态类的实例对象及调用其方法
9 ). 完成InvocationHandle对象的内部功能
10 ). 分析InvocationHandler对象的运行原理
11 ). 总结分析动态代理类的设计原理与结构
12 ). 编写可生成代理和插入通告的通用方法
13 ). 实现类似spring的可配置的AOP框架
一
. 类加载器及其委托机制的深入分析
1 ) . 明确 :
1.1 类加载器是什么?
[1] 类加载器本质也是java类,用来加载其他类,那类加载器是谁加载的呢? 他本身无需加载,嵌套在虚拟机上,底层是C++写的,名字叫 BootStrap
1.2 类加载器的作用?
[1]类加载器就是将用到的某个类的字节码文件从硬盘加载进内存,以可视化的方式展现的过程
1.3 java虚拟机中的类加载器了解?
[1] Java虚拟机中可以安装多个类加载器
[2] 系统默认三个主要类加载器,每个类负责加载特定位置: BootStrap , ExtClassLoader,AppClassLoade
2 ) . Demo :
package
cn.ittext.day02;
/**
*
@author
winter
*
@Date
2018年3月29日下午9:24:11
*
@tel
1394869214
@qq.com
*
@version
1.0
*
@describe
类功能描述
*/
public
class
ClassLoaderTest {
public
static
void
main(String[]
args
)
{
/**
*
* 庖丁解牛 :
*
* 1. 三种不同的加载器
*
* [1] Bootstrap
*
* [2] ExtClassLoader
*
* [3] AppClassLoader
*
*
* 优先级是 [1] <– [2] <– [3]
*
*
* 2.如何自定义加载器
*
* [1] 通过classLoader类,作为类加载器的子类从而实现自定义加载器
*
* 3.类加载器的委托机制
*
* [1] 无论是哪个类加载器加载,最先去判断是否可加载的加载器都是 Bootstrap,他加载不了再向下尝试
*
* 4.问题 : 当虚拟机要加载一个类时,到底先派出那个类加载器进行加载呢?
*
* [1] 首先得有 总的加载器 ,因此 当前线程的类加载器去加载线程中的第一个类 null
*
* [2] 然后通过委托机制依次从总的加载器向下传递的去类加载其指定的目录进行加载直到找到可加载的类 : Bootstrap
–>
ExtClassLoader
–>
AppClassLoader
*
*
*
ps
:
*
* (1) 若类A引用了类B,Java虚拟机则使用加载类A的加载器去加载类B
*
* (2) 还可通过ClassLoader.loadClass()来直接指定某个加载器去加载某各类
*
* (3) 我们还可通过自定义类加载对指定目录进行加载
*
*/
ClassLoader
systemName
= System.
class
.getClassLoader();
//
sop
(“System系统类的类加载器是什么:”+systemName); //空代表是 顶级的类加载器是 Bootstrap ,用来加载系统类
String
ownName
= ClassLoaderTest.
class
.getClassLoader().getClass().getName();
//
sop
(“ClassLoaderTest自定义类的类加载器是什么:”+ownName); //用来加载ClassPath指定的所有jar和目录 –>AppClassLoader
/**
* 遍历出所有的类加载器
*
*/
ClassLoader
classLoader
= ClassLoaderTest.
class
.getClassLoader();
//获取其中一个类加载器
while
(
classLoader
!=
null
)
{
String
name
=
classLoader
.getClass().getName();
//获取其类加载其的名字
sop
(
name
);
classLoader
=
classLoader
.getParent();
//不断的向上获取直到获取到顶级加载器也就是Null后停止
}
sop
(
classLoader
);
//输出顶级加载器
}
public
static
void
sop(Object
obj
)
{
System.
out
.println(
obj
);
}
}
3 ) . 类加载器图解 :
小结 :
1. 了解类加载器的委托机制,明白加载器的优先级
2. 记得每个不同的加载器所加载的目录文件
3. 我们还可通过自定义加载器去加载我们指定的目录,这样其他人没有我们的加载器是不能加载我们的代码的
二. 自定义加载器的编写原理分析
1 ) .
2 ) . 自定义加载器的须知 :
2.1 首先自定义加载器先继承ClassLoader
2.3 其次覆盖findClass方法 –>用来获取字符式的字节码 : 若 需要更改委托机制的优先级, 则也可覆盖掉 loadClass方法
2.4 最后编写defineClass方法 –> 用来将字符的字节码转化为类的实例
3 ) . 自定义加载器步骤:
3.1 编写一个对文件内容进行简单加密的程序
3.2 编写了一个自己的类装载器,可实现对加密过的类进行装载和解密
3.3 编写一个程序调用类加载器加载类,在源程序中不能用该类名定义引用变量,因为编译器无法识别这个类,程序中可以出了使用classLoader.load方法之外,还可使用设置线程的上下
文类加载器或者系统类加载器,然后再使用Class.forName
4 ) . 实验步骤 :
4.1 对不带包名的class文件进行加密,加密结果存放到另外一个目录,例如 : java MyClassLoader MyTest class F:\itcast
4.2 运行加载类的程序,结果能够被正常加载,但打印出来的类加载器名称为AppClassLoader: java MyClassLoader MyTest F:\itcast
4.3 用加密后的类文件替换ClassPath环境下的类文件,再执行上一步操作就出现问题了,错误说明是AppClassLoader类装载器装载失败
4.4 删除Class文件,重新进行生成
小结 :
1. 模板方法设计模型就是 提供通用框架, 而后填充实现具体细节的过程就是模板设计方式
三. 编写对class文件进行加密的工具类
1 ) . Demo :
package
cn.ittext.day02;
import
java.io.FileInputStream;
import
java.io.FileOutputStream;
import
java.io.IOException;
import
java.io.InputStream;
import
java.io.OutputStream;
/**
*
@author
winter
*
@Date
2018年3月30日上午10:13:20
*
@tel
1394869214
@qq.com
*
@version
1.0
*
@describe
自定义类加载器并实现加密
*/
public
class
MyClassLoader {
public
static
void
main(String[]
args
)
throws
IOException
{
/**
*
* 庖丁解牛:
*
* [1] 加密解密算法测试
–>
通过异或的方式进行加密,再次异或即可解密
*
*
* [2] 加密工具类的封装
*/
//加密
/* FileInputStream
fis
=new FileInputStream(“S:\\develop\\DevWorkSpace\\WebWorkSpace2\\
javapractice
\\
src
\\
cn
\\
ittext
\\day02\\Person.java”);
FileOutputStream
fos
=new FileOutputStream(“S:\\develop\\DevWorkSpace\\WebWorkSpace2\\
javapractice
\\
src
\\
cn
\\
ittext
\\encryptClass\\encryptClass.file”);
encryptClass(
fis
,
fos
);*/
//解密
FileInputStream
fis
=
new
FileInputStream(
“S:\\develop\\DevWorkSpace\\WebWorkSpace2\\javapractice\\src\\cn\\ittext\\encryptClass\\encryptClass.class”
);
FileOutputStream
fos
=
new
FileOutputStream(
“S:\\develop\\DevWorkSpace\\WebWorkSpace2\\javapractice\\src\\cn\\ittext\\encryptClass\\DecodeClass.java”
);
encryptClass
(
fis
,
fos
);
new
entryClassUtils(
fis
,
fos
);
//加密工具类的方式
}
public
static
void
encryptClass(InputStream
ips
, OutputStream
ops
)
throws
IOException
{
int
temp
=0;
while
((
temp
=
ips
.read())!=-1)
{
ops
.write(
temp
^0xff);
}
}
public
static
void
sop(Object
obj
)
{
System.
out
.println(
obj
);
}
}
2 ) . 加密工具类:
package
cn.ittext.day02;
import
java.io.IOException;
import
java.io.InputStream;
import
java.io.OutputStream;
/**
*
@author
winter
*
@Date
2018年3月30日上午11:29:22
*
@tel
1394869214
@qq.com
*
@version
1.0
*
@describe
类功能描述
*/
public
class
entryClassUtils {
private
InputStream
ips
;
private
OutputStream
ops
;
public
entryClassUtils(InputStream
ips
, OutputStream
ops
)
throws
IOException
{
super
();
this
.
ips
=
ips
;
this
.
ops
=
ops
;
encryptClass
(
ips
,
ops
);
}
public
static
void
encryptClass(InputStream
ips
, OutputStream
ops
)
throws
IOException
{
int
temp
=0;
while
((
temp
=
ips
.read())!=-1)
{
ops
.write(
temp
^0xff);
}
}
public
static
void
sop(Object
obj
)
{
System.
out
.println(
obj
);
}
}
小结 :
1. 有包名的类不可调用无包名的类
四. 编写和测试自己编写的解密类加载器 –->未能实现,以下代码不可用
1 ) . 类加密文件 :
package
cn.ittext.day02;
import
java.io.IOException;
import
java.io.InputStream;
import
java.io.OutputStream;
/**
*
@author
winter
*
@Date
2018年3月30日上午11:29:22
*
@tel
1394869214
@qq.com
*
@version
1.0
*
@describe
类加密
*/
public
class
entryClassUtils {
private
InputStream
ips
;
private
OutputStream
ops
;
public
entryClassUtils(InputStream
ips
, OutputStream
ops
)
throws
IOException
{
super
();
this
.
ips
=
ips
;
this
.
ops
=
ops
;
encryptClass
(
ips
,
ops
);
}
public
static
void
encryptClass(InputStream
ips
, OutputStream
ops
)
throws
IOException
{
int
temp
=0;
while
((
temp
=
ips
.read())!=-1)
{
ops
.write(
temp
^0xff);
}
}
public
static
void
sop(Object
obj
)
{
System.
out
.println(
obj
);
}
}
2 ) . Person类文件
package
cn.ittext.encryptClass;
import
java.util.Date;
/**
*
@author
winter
*
@Date
2018年3月29日下午5:12:32
*
@tel
1394869214
@qq.com
*
@version
1.0
*
@describe
类功能描述
*/
public
class
Person
extends
Date{
private
Integer
id
;
private
String
name
;
private
Integer
age
;
public
Integer getId() {
return
id
;
}
public
void
setId(Integer
id
) {
this
.
id
=
id
;
}
public
String getName() {
return
name
;
}
public
void
setName(String
name
) {
this
.
name
=
name
;
}
public
Integer getAge() {
return
age
;
}
public
void
setAge(Integer
age
) {
this
.
age
=
age
;
}
public
Person(Integer
id
, String
name
, Integer
age
) {
super
();
this
.
id
=
id
;
this
.
name
=
name
;
this
.
age
=
age
;
}
@
Override
public
String toString() {
return
“Person [id=”
+
id
+
“, name=”
+
name
+
“, age=”
+
age
+
“]”
;
}
}
3 ) . 加载器文件
package cn.ittext.day02;
import
java.io.ByteArrayOutputStream;
import
java.io.FileInputStream;
import
java.io.IOException;
/**
*
@author
winter
*
@Date
2018年3月30日下午2:04:31
*
@tel
1394869214
@qq.com
*
@version
1.0
*
@describe
自定义类加载器
*/
public
class
decodeMyClassLoader
extends
ClassLoader
{
private
String
classDir
;
//传入的目录地址
decodeMyClassLoader()
{
}
decodeMyClassLoader(String
classDir
)
{
this
.
classDir
=
classDir
;
}
@Override
protected
Class<?> findClass(String
name
)
throws
ClassNotFoundException
{
String
fileName
= getFileName(
name
);
//获取文件地址
try
{
byte
[]
bytes
= getClassBytes(
fileName
);
// 通过文件地址获取其中的字节码
return
defineClass
(
bytes
, 0,
bytes
.
length
)
;
//通过自定义加载器将字节码数据返回
}
catch
(IOException
e
) {
e
.printStackTrace();
}
return
super
.findClass(
name
);
//通过父类的加载器将字节码数据返回
}
private
byte
[] getClassBytes(String
fileName
)
throws
IOException
{
FileInputStream
fis
=
new
FileInputStream(
fileName
);
//源
ByteArrayOutputStream
baos
=
new
ByteArrayOutputStream();
//目标
new
entryClassUtils(
fis
,
baos
);
//解密
fis
.close();
byte
[]
byteArray
=
baos
.toByteArray();
//换算成数组的形式
return
byteArray
;
}
//获取文件
private
String getFileName(String
name
) {
String
pathClass
=
classDir
+
“\\”
+
name
+
“.class”
;
//目的+文件名 = 文件地址
return
pathClass
;
//返回文件全地址
}
public
static
void
sop(Object
obj
)
{
System.
out
.println(
obj
);
}
}
4 ) . 测试文件
package
cn.ittext.day02;
import
java.io.FileNotFoundException;
import
java.io.IOException;
import
java.util.Date;
/**
*
@author
winter
*
@Date
2018年3月30日下午2:37:19
*
@tel
1394869214
@qq.com
*
@version
1.0
*
@describe
这是一个测试类
*/
public
class
entryDemo {
public
static
void
main(String[]
args
)
throws
FileNotFoundException, IOException, ClassNotFoundException, InstantiationException, IllegalAccessException
{
/* File source =new File(“S:\\develop\\DevWorkSpace\\WebWorkSpace2\\
javapractice
\\
src
\\
cn
\\
ittext
\\encryptClass\\Person.java”);
File target =new File(“S:\\develop\\DevWorkSpace\\WebWorkSpace2\\
javapractice
\\
itcast
\\Person.class”);
new entryClassUtils(new FileInputStream(source), new FileOutputStream(target));*/
Class
loadClass
=
new
decodeMyClassLoader(
“itcast”
).loadClass(
“Person”
);
//传入自定义目录和加载的对象
Date
newInstance
= (Date)
loadClass
.newInstance();
//实例化
sop
(
newInstance
);
}
public
static
void
sop(Object
obj
)
{
System.
out
.println(
obj
);
}
}
小结 :
1. 通过将自定义加载器挂到加载器的体系中,从而实现特定加载器去加载特定目录下的文件
五
. 类加载器的一个高级问题的实验分析
3 ) . 小知识:
3.1 Servlet是被Tomcat提供的类加载器加载的
3.2 WEB项目与Java项目中class的区别在于 , web中的class是可被 servlet 加载的class, java的class 是被虚拟机加载的class
4 ) . 结论 :
4.1 Servlet有着自己的加载器, 是 apache 旗下的,当出现500等页面找不到的问题时,也有可能是 加载器的问题
小结 :
1. Tomcat是非常多的类加载器的集合,Tomcat是服务器
六. 分析代理类的作用与原理和AOP概念
1 ) . 代理 :
1.1 生活中的代理 :
[1] A客户去 B商户 购买 一件商品, 来回用时 2小时 , 商品1950+ 油费50 共计 2000元
[2] A客户去 B商户 购买一件商品,全权交由代理商送货上门 , 商品1950元 + 服务费 50元 共计 2000元
[3] 比较 : 代理购买方案 显然 比 传统购买方案要 省时, 时间如同金钱
1.2 程序中的代理 : –>在程序扩展方面的应用
[1] 直接方式 : A客户端 直接 调用 B 服务端 代码 进行 服务
[2] 代理方式 :
(1) A客户端 直接调用 B服务端的代理类 : 创建一个代理 类 实现 B服务端的 接口 ,从而 将B服务端的 原始方法 实现 ,而后对代理类 进行 方法完善 即可
(2) 采用工厂模式和配置文件的方式 进行管理 ,则无需修改客户端程序,直接在配置文件中修改是使用目标类还是代理类
(3) 优势 : 灵活性,已修改,可扩展
2 ) . AOP (面向切面编程) — > 面向代理编程 :
2.1 系统中存在交叉业务,交叉业务就是要切入到系统中的一个方面,如图所示 :
2.2 面向切面编程就是 将交叉业务 模块化,独立出来,提供公共访问的方式
2.3 如何实现切面? 就是通过代理技术的方式, 因此 代理是实现AOP功能的核心关键技术
3 ) . 什么是动态代理类?
3.1 JVM可以再运行期间动态生成出类的字节码,这种动态生成的类往往被用作代理类,即动态代理类
3.2 JVM生成的动态类必须实现一个或多个接口,因此JVM生成的动态类只能用作具有相同接口的目标类的代理
3.3 若一个没有实现接口的类要生成动态代理类,则通过CGLIB库动态生成一个类的子类
4 ) . 代理类各个方法中除了实现目标类的相应方法和返回相应结果之外,还可在代理方法中的如下四个位置加上系统完善功能代码:
4.1 在调用目标方法之前
4.2 在调用目标方法之后
4.3 在调用目标方法前后
4.4 在处理目标方法异常的catch块中
小结 :
1. 代理就是代替自己理解并完成任务的一种方式,优势是节省了自身时间
2. 交叉业务就是 每个模块都需要的通用业务 ,将其 分离出来,以公地的形式出现,而后 贯穿到每个模块中
3. 如果我们需要使用的那个类没有接口, 无法进行方法的实现, 那么就是用CGLib进行动态的创建子类
七. 创建动态类及查看其方法列表信息
1 ) . 核心内容 : 通过 JVM生成动态类 并获取其 动态类的所有 构造函数和所有方法
2 ) . Demo :
package
cn.ittext.day03;
import
java.lang.reflect.Constructor;
import
java.lang.reflect.Executable;
import
java.lang.reflect.Method;
import
java.lang.reflect.Proxy;
import
java.util.Collection;
/**
*
@author
winter
*
@Date
2018年3月31日下午1:46:58
*
@tel
1394869214
@qq.com
*
@version
1.0
*
@describe
创建collection的动态类并获取动态类中的构造函数和所有方法
*/
public
class
ProxyTest {
public
static
void
main(String[]
args
)
{
/**
* 庖丁解牛 :
*
* [1] 如何 获取 collection的动态类
*
* [2] 如何获取动态类中的构造函数 和参数列表
*
* [3] 如何获取动态类中的所有方法 和参数列表
*
*
ps
: 以上的动态类指代理类
*
*/
//通过
Proxy
类调用获取代理类的方法getProxyClass ,其中传入 要获取的 类的 加载器 和 类的 字节码文件 ,之后返回 其代理类的字节码文件
Class
proxyClazz
= Proxy.
getProxyClass
(Collection.
class
.getClassLoader(), Collection.
class
);
//[1]
getConstructors
(
proxyClazz
);
//获取其动态类中的所有构造函数 和参数列表 //[2]
getMethods
(
proxyClazz
);
//获取其动态类中的所有方法 和参数列表 //[3]
}
private
static
void
getMethods(
Class
proxyClazz
)
{
sop
(
“————————–beagin method list————————–“
);
StringBuilder
sbMethod
=
new
StringBuilder();
//获取collection的代理类的所有方法
Method[]
methods
=
proxyClazz
.getMethods();
for
(Method
method
:
methods
)
{
String
conname
=
method
.getName();
sbMethod
.append(
conname
);
sbMethod
.append(
“(“
);
getParameter
(
sbMethod
,
method
);
//获取其 方法中的参数列表
sbMethod
.append(
“)”
);
sbMethod
.append(
“\r\n”
);
}
sop
(
sbMethod
.toString());
//输出 StringBuilder中方法的内容
}
private
static
void
getConstructors(
Class
proxyClazz
)
{
//获取collection的代理类的所有构造函数
Constructor
[]
constructors
=
proxyClazz
.getConstructors();
StringBuilder
sbConstru
=
new
StringBuilder();
sop
(
“————————–beagin constructor list————————–“
);
//迭代代理类中的构造函数
for
(
Constructor
constructor
:
constructors
)
{
String
conname
=
constructor
.getName();
sbConstru
.append(
conname
);
sbConstru
.append(
“(“
);
getParameter
(
sbConstru
,
constructor
);
//获取其 构造函数中的参数列表
sbConstru
.append(
“)”
);
sop
(
sbConstru
.toString());
//输出 StringBuilder中构造函数的内容
}
}
//专门用来获取构造函数/方法的参数名 并将 参数名放入 StringBuilder的方法 –>这是公用方法
private
static
<T>
void
getParameter(StringBuilder
sb
, T
method
) {
Class
[]
classparameters
= ((Executable)
method
).getParameterTypes();
for
(
Class
parameterclazze
:
classparameters
)
{
String
parametername
=
parameterclazze
.getName();
sb
.append(
parametername
+
“,”
);
}
if
(
classparameters
!=
null
&
classparameters
.
length
!=0)
sb
.deleteCharAt(
sb
.length()-1);
}
public
static
void
sop(Object
obj
)
{
System.
out
.println(
obj
);
}
}
小结 :
1. 在单线程的情况下,进行动态字符串拼接用StringBuilder ,在多线程的情况下,进行动态字符串拼接用StringBuffer
八
. 创建动态类的实例对象及调用其方法
1 ) . 实例化动态类 并 调用 动态类中的方法 用以实验
2 ) . Demo:
package
cn.ittext.day03;
import
java.lang.reflect.Constructor;
import
java.lang.reflect.InvocationHandler;
import
java.lang.reflect.InvocationTargetException;
import
java.lang.reflect.Method;
import
java.lang.reflect.Proxy;
import
java.util.ArrayList;
import
java.util.Collection;
import
java.util.Iterator;
/**
*
@author
winter
*
@Date
2018年3月31日下午3:10:03
*
@tel
1394869214
@qq.com
*
@version
1.0
*
@describe
创建collection的动态类,并使用动态类进行相关操作
*/
public
class
ProxyTest01 {
public
static
void
main(String[]
args
)
throws
NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException
{
//实现InvocationHandler接口的类,用来作为实例化代理类的对象参数
class
MyInvocationHandler
implements
InvocationHandler
{
@Override
public
Object invoke(Object
proxy
, Method
method
, Object[]
args
)
throws
Throwable {
return
null
;
}
}
//获取collection动态类的字节码文件
@SuppressWarnings
(
“rawtypes”
)
Class
proxyClazz
= Proxy.
getProxyClass
(Collection.
class
.getClassLoader(), Collection.
class
);
//通过字节码文件获取其构造函数
@SuppressWarnings
({
“unchecked”
,
“rawtypes”
})
Constructor
constructorClazz
=
proxyClazz
.getConstructor(InvocationHandler.
class
);
//通过构造函数进行实例化,但构造函数有参数,需要将InvocationHandler接口作为参数传入,因此我采用了实现此接口,将其实现类传入
@SuppressWarnings
({
“unchecked”
,
“rawtypes”
})
Collection<String>
collProxy
= (Collection)
constructorClazz
.newInstance(
new
MyInvocationHandler());
//通过实例化ArrayList构造函数
collProxy
=
new
ArrayList()
;
//使用collection的代理类调用方法
collProxy
.add(
“A”
);
collProxy
.add(
“B”
);
collProxy
.add(
“C”
);
//迭代集合中的元素
for
(Iterator<String>
it
=
collProxy
.iterator();
it
.hasNext();)
{
sop
(
it
.next());
}
/**
*
*
* 庖丁解牛 :
*
* [1] 获取代理类的字节码
*
* [2] 获取代理类的构造函数的字节码
*
* [3] 通过构造函数字节码实例化代理类对象的方式
–>
这里需要传入对象参数InvocationHandler
*
* [4] 通过将实例化对象接口的可实例化子类进行关联,从而进行增加操作并迭代
*
*
* 小结:
*
* [1] 我们可发现动态类中有其目标类中的所有方法,可直接拿来使用
*
* [2] 想要实例化代理类,需要将构造函数中传入 InvocationHandler 接口的实现类才行
*
*
*
*/
}
public
static
void
sop(Object
obj
)
{
System.
out
.println(
obj
);
}
}
小结 :
1. 反射获取方法的时候出入的是方法的参数类型,当实例化方法时传入的是 类型的具体值
2. 当实例化一个对象将其变量打印输出返回null时, 只能说明其 对象的 toString ()方法返回的null,因为若对象为NUll则会直接报告空指针异常
九
. 完成InvocationHandle对象的内部功能
1 ) . 代理类的三种实现方式 :
package cn.ittext.day03;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;/**
* @author winter
* @Date 2018年3月31日下午4:13:39
* @tel
1394869214@qq.com
* @version 1.0
* @describe 实现代理类的三种方式
*/public class ProxyTest03 {
public static void main(String[] args) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException
{
/**
*
*
* 庖丁解牛 : 代理类的三种实现方式
*
* [1] 自定义InvocationHandler的实现类的方式
*
* [2] InvocationHandler的匿名内部类的方式
*
* [3] 直接使用Proxy调用其方法newProxyInstance(iterfaceClassLoader,iterface,InvocationHandler) —> 要用就用第三种
*
*
*
*/
//方式一: 自定义InvocationHandler的实现类的方式 —————————————————————————-
/* class MyInvocationHandler implements InvocationHandler
{
ArrayList arr =new ArrayList(); //一个实例化类型 用来让 代理类指定
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
//实例化前的代理类只是个接口,只有实例化后才是一个实力类,才能被应用
Object invoke = method.invoke(arr, args); //通过一个实体类调用 代理类,也就是 为是实例化代理类
return invoke; //返回实例化后的代理类
}
}
//获取collection动态类的字节码文件
Class proxyClazz1 = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
//通过字节码文件获取其构造函数
Constructor constructorClazz = proxyClazz1.getConstructor(InvocationHandler.class);
//通过构造函数进行实例化,
Collection<String> collProxy = (Collection) constructorClazz.newInstance(new MyInvocationHandler());
collProxy.add(“A”);
collProxy.add(“B”);
collProxy.add(“C”);
//迭代集合中的元素
for(Iterator<String> it = collProxy.iterator();it.hasNext();)
{
sop(it.next());
} */
//方式二:InvocationHandler的匿名内部类的方式 —————————————————————————-
/*
//获取collection动态类的字节码文件
Class proxyClazz2 = Proxy.getProxyClass(Collection.class.getClassLoader(), Collection.class);
//通过字节码文件获取其构造函数
Constructor constructorClazz = proxyClazz2.getConstructor(InvocationHandler.class);
//通过构造函数进行实例化,
@SuppressWarnings(“unchecked”)
Collection<String> collProxy = (Collection) constructorClazz.newInstance(new InvocationHandler()
{
ArrayList arr =new ArrayList();
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object invoke = method.invoke(arr, args);
return invoke;
}
});
collProxy.add(“A”);
collProxy.add(“B”);
collProxy.add(“C”);
//迭代集合中的元素
for(Iterator<String> it = collProxy.iterator();it.hasNext();)
{
sop(it.next());
}*/
//方式三:直接使用Proxy调用其方法newProxyInstance()的方式 +InvocationHandler的匿名内部类的方式 —————————————————————————-
//获取collection的代理类ArrayList的方式
/* @SuppressWarnings(“unchecked”)
Collection<String> collProxy3 =(Collection<String>) Proxy.newProxyInstance(
Collection.class.getClassLoader(),
new Class[] {Collection.class},
new InvocationHandler()
{
ArrayList<String> arr =new ArrayList<String>();
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
Object invoke = method.invoke(arr, args);
return invoke;
}
});
collProxy3.add(“A”);
collProxy3.add(“B”);
collProxy3.add(“C”);
//迭代集合中的元素
for(Iterator<String> it = collProxy3.iterator();it.hasNext();)
{
sop(it.next());
}
*/
}public static void sop(Object obj) {
System.out.println(obj);
}
}
小结 :
1. 如若某个方法需要将接口作为参数传入的话,有两种方式 一将自定义的一个实现了此接口的实现类传入 , 二 将 此接口以匿名内部类的方式传入
2. 通过proxy代理类的方法 newProxyInstance() 直接实例化一个 相关 接口的 代理类 ,并传入 InvocationHandler 对其进行管理
十. 分析InvocationHandler对象的运行原理
1 ) . InvocationHandler 对象 其中的 invoke 如同代理的切面 , 可直切 其代理对象的 方法内部或外部
2 ) . Demo:
package
cn.ittext.day03;
import
java.lang.reflect.InvocationHandler;
import
java.lang.reflect.Method;
import
java.lang.reflect.Proxy;
import
java.util.ArrayList;
import
java.util.Collection;
import
java.util.Iterator;
/**
*
@author
winter
*
@Date
2018年3月31日下午6:10:18
*
@tel
1394869214
@qq.com
*
@version
1.0
*
@describe
剖析实现代理类的第三种方式
*/
public
class
ProxyTest04 {
public
static
void
main(String[]
args
) {
/**
*
* 庖丁解牛: 剖析实现代理类的第三种方式 :
proxy
类中的 newProxyInstance()方法
*
* invoke(Object
proxy
, Method method, Object[]
args
)
*
* [1]
proxy
–>
调用哪个对象
*
* [2] method
–>
调用对象的哪个方法
*
* [3]
args
–>
传递的哪个参数
*
*
* 委托给InvocationHandler的方法有 : equals , hashCode,toString
*
*
* 小结 :
*
* [1] 我们可在invoke () 方法中 对 add() 方法的前后添加完善代码
*
* [2] 我们还可在invoke()方法中 对 add()方法的参数值 进行过滤等操作
*
* [3] 总之我们可在 invoke ()方法中对 代理类所调用的任何方法进行完善改进等操作
*
*/
// 通过 InvocationHandler作为代理类ArrayList作为目标类的方式
@SuppressWarnings
(
“unchecked”
)
Collection<String>
collProxy3
=(Collection<String>) Proxy.
newProxyInstance
(
Collection.
class
.getClassLoader(),
new
Class[] {Collection.
class
},
new
InvocationHandler()
//代理类
{
ArrayList<String>
target
=
new
ArrayList<String>();
//目标类
@Override
//这里的method 就是 下边的add
public
Object invoke(Object
proxy
, Method
method
, Object[]
args
)
throws
Throwable
{
sop
(
“开始计时:………………”
);
long
startTime
=System.
currentTimeMillis
();
Object
invoke
=
method
.invoke(
target
,
args
);
//将add()方法的运行指定到 目标类,然后运行
long
endTime
=System.
currentTimeMillis
();
sop
(
“结束计时:”
+(
endTime
–
startTime
));
return
invoke
;
}
});
collProxy3
.add(
“A”
);
//在这里每add一次,上边的 invoke 就运行一次 ,这些值添加到目标类中
collProxy3
.add(
“B”
);
collProxy3
.add(
“C”
);
//迭代集合中的元素
for
(Iterator<String>
it
=
collProxy3
.iterator();
it
.hasNext();)
{
sop
(
it
.next());
}
}
public
static
void
sop(Object
obj
) {
System.
out
.println(
obj
);
}
}
小结 :
1. 无论是代理类调用哪个方法都会走 匿名内部类InvocationHandler中的invoke()方法
,从而指定 调用方法的 目标类是哪一个 ,因此我们可明白 invoke () 就是 对 接口对象的代理类进行操作的 一把 切面刀
十一. 总结分析动态代理类的设计原理与结构
1 ) .Demo :
见下一章实现 动态代理
2 ) . 描述 :
2.1 流程 : 客户端调用接口方法 –> 接口方法 转交代理类 InvocationHandler去调取–> 代理类通过invoke() 方法 将调取的方法与指定目标类 相关联,通过目标类运行并返回结果 , 并且可在 invoke 方法中 对 原有方法进行改善 –> >代理类 InvocationHandler将返回值 返回到 目标类中
2.2 关系 解析 :
[1] 接口中植入了 InvocationHandler,因此 InvocationHandler可随意访问 接口中的方法
[2] 目标类也是接口 的 子类,因此 自然也具有 接口的方法
[3] 客户端调用 接口 , 接口 转交 代理类 InvocationHandler , 代理类 再次转交 目标类 即可 ,然后 将值 返回到 接口实例化对象中
3 ) . 动态代理图解 :
小结 :
1. javaScript和
python
支持动态语言,java不支持动态语言
2. 将代码封装进对象,将对方放入invoke即可实现 , 在调用相关方法时 将灵活调用其它方法(log())3. 动态代理的核心 在invoke方法中 , 其参数值需传入两个对象 ,一个是 目标类对象 用来执行原有方法, 一个是系统类对象封装了需要进行切面的方法(也就是通用方法)
十二
. 编写可生成代理和插入通告的通用方法 –> 以下有动态代理的方法
1 ) . Demo :
package
cn.ittext.day03;
import
java.lang.reflect.InvocationHandler;
import
java.lang.reflect.Method;
import
java.lang.reflect.Proxy;
import
java.util.ArrayList;
import
java.util.Collection;
import
java.util.Iterator;
/**
*
@author
winter
*
@Date
2018年3月31日下午10:37:16
*
@tel
1394869214
@qq.com
*
@version
1.0
*
@describe
实现AOP原理
*/
public
class
ProxyTest05 {
public
static
void
main(String[]
args
)
{
/**
* 庖丁解牛 :
*
* [1] 将代理的方法抽取出 通用型的代理方法
*
* [2] 写出插入通告的通用方法并作出测试
*/
// primitive();
final
ArrayList<String>
target
=
new
ArrayList<String>();
// 目标类
@SuppressWarnings
(
“unchecked”
)
Collection<String>
colProxy
= (Collection<String>)
getProxy
(
target
,
new
Adtives());
//将目标类 放入代理 类中 用来 实现 AOP的操作
colProxy
.add(
“A”
);
colProxy
.add(
“S”
);
colProxy
.add(
“D”
);
for
(Iterator<String>
it
=
colProxy
.iterator();
it
.hasNext();)
{
sop
(
it
.next());
}
}
//用来获取代理类的方法 : 这是 将原始代理的核心代码抽取过后的代码 –>以下使用了代码重构
private
static
Object getProxy(
final
Object
target
,
final
Adtive
adtive
) {
Object
colProxy
=Proxy.
newProxyInstance
(
target
.getClass().getClassLoader(),
//动态获取 接口的 加载器
target
.getClass().getInterfaces(),
//动态的活期 接口
new
InvocationHandler() {
//代理类的管理者
@Override
public
Object invoke(Object
proxy
, Method
method
, Object[]
args
)
throws
Throwable
{
/*
* 在这里边有先后顺序的是 beforeMethod 和 afterMethod方法, invoke 方法永远都是最后执行 的
*
*/
adtive
.beforeMethod();
Object
invoke
=
method
.invoke(
target
,
args
);
adtive
.afterMethod();
return
invoke
;
}
});
return
colProxy
;
}
//———————————————————————————————
//原始的 collection 代理类 实现核心逻辑
/* public static void primitive()
{
Collection colProxy =(Collection)Proxy.newProxyInstance(
Collection.class.getClassLoader(),
new Class[]{Collection.class},
new InvocationHandler() {
ArrayList target =new ArrayList();
@Override
public Object invoke(Object
proxy
, Method method, Object[]
args
) throws Throwable
{
Object invoke = method.invoke(target,
args
);
return invoke;
}
});
colProxy.add(“A”);
colProxy.add(“S”);
colProxy.add(“D”);
for(Iterator it =colProxy.iterator(); it.hasNext();)
{
sop
(it.next());
}
}
*/
public
static
void
sop(Object
obj
)
{
System.
out
.println(
obj
);
}
}
2 ) . Iterface
package
cn.ittext.day03;
/**
*
@author
winter
*
@Date
2018年3月31日下午11:08:54
*
@tel
1394869214
@qq.com
*
@version
1.0
*
@describe 切面接口
*/
public
interface
Adtive
{
void
beforeMethod();
void
afterMethod
();
}
3 ) . IterfaceImpl
package
cn.ittext.day03;
/**
*
@author
winter
*
@Date
2018年3月31日下午11:08:39
*
@tel
1394869214
@qq.com
*
@version
1.0
*
@describe 切面接口实现类
*/
public
class
Adtives
implements
Adtive{
@Override
public
void
beforeMethod()
{
System.
out
.println(
“start………………”
);
}
@Override
public
void
afterMethod()
{
System.
out
.println(
“end………………”
);
}
}
小结 :
1. AOP最重要的部分就是 动态 代理的实现 ,然后 通过 对象的方式插入通告 提供 便捷性
2. 动态代理方法的角度最重要的两部分就是 :
[1]
newProxyInstance ,通过这个方法 将 目标类接口加载器 ,目标类接口 和 InvocationHandler 代理类的管理者 关联到一起
[2] InvocationHandler ,通过 这个 类的invoke () 调用目标方法 和 实现 目标方法前后 的控制
十三
. 实现类似spring的可配置的AOP框架
1 ) . Demo:
1.1 beanFactory
package
cn.ittext.day3.aopframework;
import
java.io.IOException;
import
java.io.InputStream;
import
java.util.Properties;
import
cn.ittext.day3.Adtive;
/**
*
@author
winter
*
@Date
2018年4月1日下午1:45:25
*
@tel
1394869214
@qq.com
*
@version
1.0
*
@describe
Bean的实例化工厂
*/
public
class
BeanFactory {
Properties
properties
=
new
Properties();
//创建一个资源文件引用,用来接收 输入流的数据
public
BeanFactory(InputStream
ips
)
{
try
{
properties
.load(
ips
);
//将输入流的数据 放入 properties 文件中 ,因此 properties 是键值对 ,后期好获取值
}
catch
(IOException
e
) {
e
.printStackTrace();
}
}
public
Object getBean(String
name
)
{
Object
bean
=
null
;
try
{
String
className
=
properties
.getProperty(
name
);
//获取 资源配置文件中传入的 键 对应的值
System.
out
.println(
“start………………..”
+
className
);
Class<?>
clazz
= Class.
forName
(
className
);
System.
out
.println(
“end………………..”
);
bean
=
clazz
.newInstance();
//通过字节码文件将其实例化
}
catch
(Exception
e
) {
e
.printStackTrace();
}
/**
*
*
*
* 若判断 是代理类 ProxyFactoryBean 则 通过代理类去实现
*
* 若判断 不是代理类 ,则 直接返回 bean即可
*
*
*
*
*/
if
(
bean
instanceof
ProxyFactoryBean)
///判断 bean 是否 是代理类实例化的
{
ProxyFactoryBean
proxyFactoryBean
= (ProxyFactoryBean)
bean
;
//将 bean实力类 赋给 代理类
Object
proxy
=
null
;
try
{
//获取到资源配置文件中 的
adtive
切面 的 值 并 实例化
Adtive
adtive
= (Adtive) Class.
forName
((String)
properties
.get(
name
+
“.adtive”
)).newInstance();
//获取到资源配置文件中的 target目标类的值 并实例化
Object
target
= Class.
forName
((String)
properties
.get(
name
+
“.target”
)).newInstance();
//将 切面与 目标类 传给 代理类
proxyFactoryBean
.setAdtive(
adtive
);
proxyFactoryBean
.setTarget(
target
);
proxy
=
proxyFactoryBean
.getProxy();
//调用 代理方法
}
catch
(Exception
e
) {
e
.printStackTrace();
}
}
return
bean
;
}
}
1.2 ProxyFactory
package
cn.ittext.day3.aopframework;
import
java.lang.reflect.InvocationHandler;
import
java.lang.reflect.Method;
import
java.lang.reflect.Proxy;
import
cn.ittext.day3.Adtive;
/**
*
@author
winter
*
@Date
2018年4月1日下午1:52:50
*
@tel
1394869214
@qq.com
*
@version
1.0
*
@describe 代理类的实例化工厂
*/
//代理类的实例化工厂
public
class
ProxyFactoryBean {
private
Adtive
adtive
;
private
Object
target
;
public
Adtive getAdtive() {
return
adtive
;
}
public
void
setAdtive(Adtive
adtive
) {
this
.
adtive
=
adtive
;
}
public
Object getTarget() {
return
target
;
}
public
void
setTarget(Object
target
) {
this
.
target
=
target
;
}
public
Object getProxy() {
Object
colProxy
=Proxy.
newProxyInstance
(
target
.getClass().getClassLoader(),
//动态获取 接口的 加载器
target
.getClass().getInterfaces(),
//动态的活期 接口
new
InvocationHandler() {
//代理类的管理者
@Override
public
Object invoke(Object
proxy
, Method
method
, Object[]
args
)
throws
Throwable
{
/*
* 在这里边有先后顺序的是 beforeMethod 和 afterMethod方法, invoke 方法永远都是最后执行 的
*
*/
adtive
.beforeMethod();
Object
invoke
=
method
.invoke(
target
,
args
);
adtive
.afterMethod();
return
invoke
;
}
});
return
colProxy
;
}
}
1.3 切面接口 及 切面 实现类
package
cn.ittext.day3;
/**
*
@author
winter
*
@Date
2018年3月31日下午11:08:54
*
@tel
1394869214
@qq.com
*
@version
1.0
*
@describe切面接口
*/
public
interface
Adtive
{
void
beforeMethod();
void
afterMethod();
}
package
cn.ittext.day3;
/**
*
@author
winter
*
@Date
2018年3月31日下午11:08:39
*
@tel
1394869214
@qq.com
*
@version
1.0
*
@describe 切面实现类
*/
public
class
Adtives
implements
Adtive{
@Override
public
void
beforeMethod()
{
System.
out
.println(
“start………………”
);
}
@Override
public
void
afterMethod()
{
System.
out
.println(
“end………………”
);
}
}
1.4 config.properties
#
xxx
= cn.ittext.day3.aopframework.ProxyFactoryBean
xxx =
java.util.ArrayList
xxx.adtive =
cn.ittext.day3.
Adtives
xxx.target =
java.util.ArrayList
#
# 之前配置文件中遇到的问题 : = 前后 需要用空格 隔开一下
# 注释掉 第二行,说明传入ArrayList类作为 实例化bean
# 注释掉第三行,说明传入代理类作为实例化bean
#
#
1.5 Test
package
cn.ittext.day3.aopframework;
import
java.io.InputStream;
import
java.util.ArrayList;
import
java.util.Iterator;
/**
*
@author
winter
*
@Date
2018年4月1日下午2:10:54
*
@tel
1394869214
@qq.com
*
@version
1.0
*
@describe
类功能描述
*/
public
class
AopFrameworkTest {
public
static
void
main(String[]
args
)
throws
InstantiationException, IllegalAccessException
{
/**
* 庖丁解牛:
*
* 如何自制一个Spring框架 :
*
* 1. 需要的类
*
* [1] BeanFactory : 用来接收 获取 资源配置文件的内容 ,并 判断是代理类的方式 还是 普通类的方式而 做出相应的反应
*
* [2] ProxyFactoryBean : 用来接收 代理类的 目标类和 切面 , 从而实现 代理类的实例化
*
*
*
*
*/
//通过自身类获取一个资源配置文件,将其赋给 输入流
InputStream
ips
= AopFrameworkTest.
class
.getResourceAsStream(
“config.properties”
);
//向bean工厂内传入资源配置文件,并获取 实例化bean方法
Object
bean
=
new
BeanFactory(
ips
).getBean(
“xxx”
);
sop
(
bean
.getClass().getName());
//获取到的实例化bean方法 , 来做个测试 ,我们知道 配置文件中配置的就是 arrayList ,因此 可 转化为
arraylist
ArrayList<String>
arr
=
(ArrayList<String>)
bean
.getClass().newInstance()
;
arr
.add(
“A”
);
arr
.add(
“S”
);
arr
.add(
“D”
);
for
(Iterator<String>
it
=
arr
.iterator();
it
.hasNext();)
{
sop
(
it
.next());
}
}
public
static
void
sop(Object
obj
) {
System.
out
.println(
obj
);
}
}
2 ) . 关系图:
3 ) . 小结 :
3.1 AOP框架的核心 :
[1] 代理类的实例化工厂
[2] 普通类的实例化工厂
[3] 测试 : 需要 传入 配置文件 ,获取其 bean的名字 即可
小结 : 若是代理类则 通过 其自定义的 代理方式去实例化,若是普通类 则通过 正常方式实例化
小结 :
1. Aop就是 面向切面编程.所谓的切面 就是 交叉方法, 也就是我们在用代理类实现的过程当中写 通用方法的地方
2. IOC就是 控制反转,就是将 实例化对象的方式通过反射交由给了配置文件,以此 实现了 控制权的反转
十四. 总结
1 ) . 加载器就是 用来加载类的 一个容器 , 不同的加载器不同的地方在于规定的加载文件的位置的不同,每一个框架也都有属于自己的加载器 ,自定义的加载器还可实现 加密解密 从而 使得代码只能自己观看
2 ) . spring底层所使用的 是 代理机制 + IO流 + 反射 + 加载器
所谓的动态代理 就是 通过反射的方式 将 获取的方法与目标类相连接 ,从而实现 代理 , 框架的底层用的都是反射 ,因为 要动态获取
代理的最大优势就是 可在 代码的执行前后 过滤自己想过滤的内容
IO流在Spring中用来获取 资源文件并转化成 可通过 键值对获取的 properties
反射在Spring中用来动态的获取对象的字节码以此来实例化对象
加载器在Spring中用来配合代理类的实现