注:本文转自我的javaeye博客 http://lixuehui.javaeye.com/blog/71314 上面有magicframework源代码,可以下载
看了一看
springframework
的源代码,
IOC
和
AOP
部分值得学习
.
我也写了一个能够具有同样功能的框架
,IOC
和
AOP,(
前后花了
8
个小时的时间
),
虽然都实现了
IOC
和
AOP
功能,不过扩展性,以及其它特性不及
spring
的强大了
,
主要是让大家了解一下
spring
的内部实现机制和设计思想是什么样的
,
其实我们也可以设计自己的框架
,
就是这么简单
.
不罗嗦了
,
开始讲了
....
1,2
节为讨论
AOP
和
IOC
的底层实现原理,如果对这两部分了解的同学请直接看第三节
1,
先谈谈
AOP
的底层是如何实现的吧
动态代理来实现
AOP
,已经不是一个时髦的话题了,但是今天再拿出来再嚼一嚼,体会体会,恐怕大家仿佛也会有更多东西理解的更为深刻一些。
恐怕在
java
的反射机制被创造出来的时候,那些大师们也没曾想到,反射的发展导致
AOP
会如此流行,无论是事务处理还是权限处理,
getHibernateTemplate
的
session
开启与关闭,无不显现着
AOP
的身影,
spring
对
aop
的应用之广泛,另很多问题化简为易。
下面就用一个 java 的动态代理的例子阐述一下 AOP 的实现思路。
java
代码
1.
1
,首先,简单的接口,简单的实现
2.
public
interface
MyFace {
3.
public
String hello();
4.
}
5.
public
class
MyFaceImpl
implements
MyFace {
6.
public
String hello() {
7.
System.out.println(
"Hello,World!"
);
8.
return
"Hello,World!"
;
9.
}
10.
}
11.
动态代理助手类
12.
import
java.lang.reflect.InvocationHandler;
13.
import
java.lang.reflect.Method;
14.
import
java.lang.reflect.Proxy;
15.
16.
public
class
DynamicProxyHandler
implements
InvocationHandler {
17.
//
定义需要代理的目标对象
18.
private
Object target;
19.
20.
//
设置代理目标对象
21.
Object setObject(Object target) {
22.
this
.target = (Object) target;
23.
Object obj = Proxy.newProxyInstance(target.getClass().getClassLoader(),
24.
target.getClass().getInterfaces(),
this
);
25.
//
这里第三个参数是实现
InvocationHandler
对象,在下面
main
方法中调用
hello()
时就是调用传入的这个对象里的
invoke
方法,通过传入的第
26.
27.
一和第二个参数
classloader
和这个类实现的接口反射出方法来,以便执行真实方法中的内容,可以在真实方法执行的前后任意施展。
28.
return
obj;
29.
}
30.
31.
public
Object invoke(Object proxy, Method method, Object[] args)
32.
throws
Throwable {
33.
System.out.println(
"Before method"
+ method.getName());
34.
/**
35.
*
这里可以实现接口编程,依赖注入实现,动态去实现这部分的代码
36.
*
真正达到
AOP
效果
37.
*
38.
*/
39.
method.invoke(target, args);
40.
System.out.println(
"After method"
+ method.getName());
41.
return
null
;
42.
}
43.
}
44.
测试类
45.
public
class
Test {
46.
47.
public
static
void
main(String[] args) {
48.
MyFace impl =
new
MyFaceImpl();
49.
50.
DynamicProxyHandler hander =
new
DynamicProxyHandler();
51.
52.
MyFace myface = (MyFace) hander.setObject(impl);
53.
//
这个
myface
对象,已经不是简单意义上的
MyFaceImpl
的实例,而是
MyFace
的一个代理。
54.
55.
56.
myface.hello();
57.
//
由于
myface
是个代理,所以调用
hello
方法时,进入的是
DynamicProxyHandler
中的
invoke
,通过反射执行它的实现
58.
59.
MyFaceImpl
中的方法。
60.
}
61.
}
62.
控制台输出:
63.
Before methodhello
64.
Hello,World!
65.
After methodhello
一组干干净净的接口与接口的实现,没有与外界有任何的联系,经过一番包装,达到了方法拦截的效果,通过动态代理类实现
AOP
,说一千,道
一万,都是对反射机制的运用,连
ioc
也是反射机制的巧妙实现。
动态代理的实质是:创造出一个需要被
AOP
的类的对象的一个替代品,这个替代品身上含有原对象的一切属性(实现的接口,
classloader
)
,
然
后这个替代品会调用自己的方法
(invoke),
在这个方法里通过反射,反射出需要被
AOP
的那个对象,以及它的方法,执行,在执行前后,替代品
可以做一些另外的事情,在外界看来,就在原对象方法执行的前后被绑定了一些其他的操作。
举个例子来说说:
java
代码
1.
1
,你的老板想买一台笔记本电脑,正常是话,是他自己亲自去买,到电脑城,问问价钱,付帐,完事,
2.
MyFace myface =
new
MyFaceImpl();
//
创建老板对象
3.
myface.hello();
//
买电脑操作
4.
2
,但是他现在觉得他是老板,这件事应该派他的秘书去买,因为秘书可以和商家侃侃价什么的,他不好意思那么做,于是他让他的秘书去了
5.
DynamicProxyHandler hander =
new
DynamicProxyHandler();
//
创建一个秘书(代理助手类)
6.
MyFace myface = (MyFace) hander.setObject(impl);
//
将这个秘书安排为做这个老板的秘书
7.
3
,秘书到了电脑城看了很多牌子,眼都看花了,最后打个电话问问老板到底买哪款,最后老板决定说购买
lenovo ThinkPad T60.
8.
DynamicProxyHandler hander =
new
DynamicProxyHandler();
9.
MyFace myface = (MyFace) hander.setObject(impl);
10.
myface.hello();
//
这个
myface
就是秘书(代理类)
执行这个
hello,
是秘书在买电脑,而不是老板,因此执行的是
11.
//
而且
Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(), this);
这个时候传的
12.
13.
第三个对象是
DynamicProxyHander
对象本身,因此执行它其中的
invoke
方法
14.
15.
DynamicProxyHandler
中的
invoke(Object proxy, Method method, Object[] args)
方法。
16.
17.
打电话问问老板到底买哪款
18.
相当与
invoke
方法中的
method.invoke(target, args);
//
呼叫原类的
hello
方法,问老板,让他决定买哪个
19.
20.
21.
4
,老板告诉她以后,她与经销商侃了侃价,付帐,临走又给了点小费。
22.
public
Object invoke(Object proxy, Method method, Object[] args)
23.
throws
Throwable {
24.
System.out.println(
"Before method"
+ method.getName());
//
侃侃价
25.
method.invoke(target, args);
26.
System.out.println(
"After method"
+ method.getName());
//
给小费
27.
return
null
;
28.
}
AOP
大概其就是这么实现的
,
下面谈一谈
IOC
是如何实现的吧
.
2,IOC
的实现原理
IOC
也是利用
JAVA
的反射机制
,IOC
注重的是代码关系之间的无侵入性,具体来说
spring
更注意这个特点,就是说要将代码之间的关系隔离到代码外部
,
在
xml
文件中进行配置
,
每当代码结构变更时
,
双方的代码都不要被改动即可实现移植
.
"
如果你需要一个东西
,
那么不是你自己去取
,
而是发个指令,有人会送给你
","
站着别动,我来找你
".
这两句话什么意思呢
,
比方说
java
代码
1.
public
UserServiceImpl{
2.
UserDao userDao;
3.
setUserDao(UserDao userDao){
4.
this
.userDao = userDao;
5.
}
6.
}
这个
userDao,
不需要
UserServiceImpl
自己去创建这个
UserDao
对象
,
由
IOC
容器主动根据
UserServiceImpl
提供的
setUserDao
将
UserDao
实现的对象赋给
userDao,
这种用
set
函数设值的方式有叫做
type3,
用构造器设值叫做
type2,
用接口设值成为
type1.
那么怎么才能在运行时期把
userDao
设置上去呢
,
答案是通过反射机制
,
在运行时
,
通过反射反射出
UserDao
的实现类的对象
,
并通过反射得到
UserServiceImpl
的
setUserDao
方法
,
将刚才反射得到的
UserDao
实现类的对象传给
setUserDao
方法
,
就完成了设值工作。
这些设值的工作是在加载
spring
文件构造
Bean
工厂的时候就完成的
.
所以启动完毕,每个对象中的需要注射的属性是有值的
.
就是这样的达到容器管理
Bean
的效果
.
好了
,
罗嗦了这么多
,
下面才是本篇的核心
3,magiaframework
框架核心代码全解
1,
首先是一组接口与实现。
java
代码
1.
public
interface
MyContext {
2.
public
MyBeanFactory getMyBeanFacoty();
3.
}
4.
/**
5.
*
本类负责管理和维护
bean
的工厂
6.
*
7.
*
8.
* @author lixuehui
9.
*
10.
*/
11.
public
class
MyXmlClasspathContext
implements
MyContext {
12.
public
static
Map beanMap =
new
HashMap();
13.
14.
// private static MyContext context = new MyXmlClasspathContext();
15.
16.
public
MyBeanFactory getMyBeanFacoty() {
17.
return
new
MyBeanFactory(beanMap);
18.
}
19.
20.
private
MyXmlClasspathContext() {
21.
22.
}
23.
24.
//
为了支持多配置文件并存的现象
25.
private
MyXmlClasspathContext(String[] xml) {
26.
//
逐个
xml
文件解析,将所有的
bean
一起放在最终的大
Map
里边去
27.
28.
}
29.
30.
public
MyXmlClasspathContext(String xml) {
31.
//
根据
xml
得到它内部的一些
bean
的信息
32.
//
一个大
map,
大
map
里放着
MyBean
对象
,MyBean
对象中含有属性
map
33.
// Map beanMap = new HashMap();
34.
MyBean myBean =
null
;
35.
Map propTempMap =
null
;
36.
int
aopcount =
0
;
37.
try
{
38.
SAXReader reader =
new
SAXReader();
39.
Document doc = reader.read(
new
File(xml));
40.
Element root = doc.getRootElement();
41.
Element foo;
42.
for
(Iterator i = root.elementIterator(
"bean"
); i.hasNext();) {
43.
myBean =
new
MyBean();
44.
45.
foo = (Element) i.next();
46.
for
(Iterator j = foo.elementIterator(
"property"
); j.hasNext();) {
47.
Element foo2 = (Element) j.next();
48.
Attribute name = foo2.attribute(
"name"
);
49.
Attribute ref = foo2.attribute(
"ref"
);
50.
51.
Attribute methodname = foo2.attribute(
"methodname"
);
52.
53.
if
(methodname !=
null
) {
54.
}
55.
// StringBuffer result = new StringBuffer(name.getValue());
56.
// if (ref != null) {
57.
// result.append("/t" + ref.getValue());
58.
// }
59.
// property
结点的两个属性值
name
和
ref
被得到
60.
if
(methodname ==
null
) {
61.
myBean.addPropertyElement(name.getValue(), ref
62.
.getValue());
63.
}
else
{
64.
myBean.addPropertyElement(
"aopM"
+ aopcount, methodname
65.
.getValue());
66.
}
67.
}
68.
Attribute id = foo.attribute(
"id"
);
69.
Attribute cls = foo.attribute(
"class"
);
70.
Attribute aop = foo.attribute(
"aop"
);
71.
Attribute aoptype = foo.attribute(
"aoptype"
);
72.
if
(aop !=
null
|| aoptype !=
null
) {
73.
myBean.setAop(aop.getValue());
74.
myBean.setAoptype(aoptype.getValue());
75.
}
76.
myBean.setId(id.getValue());
77.
myBean.setClasstarget(cls.getValue());
78.
//
构造装
bean
的大
Map!!!
79.
beanMap.put(id.getValue(), myBean);
80.
//
构造完的这个
bean
的大
Map,
以单例的形式放在内存中
81.
propTempMap =
null
;
82.
myBean =
null
;
83.
}
84.
85.
}
catch
(Exception e) {
86.
e.printStackTrace();
87.
}
88.
}
89.
90.
}
以上的
public MyXmlClasspathContext(String xml),
是传入配置文件,根据配置文件
,
构造
MyBeanFactory
对象
,
也就是
IOC
一节讲的
Bean
工厂
,
就是一个对
XML
进行解析的过程,将
xml
中的参数
,
读取封装为单个的
MyBean
对象
,
放入一个大的
Map
中,这个大的
Map
便是生产加工
Bean
的工厂。
java
代码
1.
/**
2.
* Bean
工厂接口
3.
*
4.
* @author lixuehui
5.
*
6.
*/
7.
public
interface
BeanFactory {
8.
public
Object getMyBean(String beanName);
9.
}
java
代码
1.
2.
/**
3.
*
本类负责管理和维护
bean
的工厂
4.
*
5.
*
6.
* @author lixuehui
7.
*
8.
* msn:xuehui_lee@hotmail.com
9.
*
10.
*
11.
*/
12.
public
class
MyBeanFactory
implements
BeanFactory {
13.
private
static
Logger log = Logger.getLogger(MyBeanFactory.
class
);
14.
15.
Map beanMap;
//
用来存放
bean
对象的大
Map
16.
17.
public
MyBeanFactory(Map beanMap) {
18.
this
.beanMap = beanMap;
19.
}
20.
21.
public
Object getMyBean(String beanName) {
22.
//
根据
bean
名从
beanMap
里去提取
Bean
名
23.
MyBean myBean = (MyBean) beanMap.get(beanName);
24.
// Bean
中的
property
存放在
Map
中
,property
中存放着哪些对象需要注射
,
注射给谁的信息
25.
Map propMap = myBean.getPropertys();
26.
Object oldtarget =
null
;
//
属性需要注射的对象
27.
Class c1;
28.
try
{
29.
//
这里是不需要方法拦截的情况
30.
//
反射出需要注射的对象
31.
c1 = Class.forName(myBean.getClasstarget());
32.
oldtarget = c1.newInstance();
33.
34.
if
(myBean.getAop() !=
null
) {
35.
//
调用动态代理
来实现这个
Bean
36.
AopHandler hander =
new
AopHandler();
37.
38.
String aopClass = myBean.getAop();
39.
MyBean aopBean = (MyBean) beanMap.get(aopClass);
40.
41.
MyAdvisor advisor = (MyAdvisor) Class.forName(
42.
aopBean.getClasstarget()).newInstance();
43.
44.
if
(
"before"
.equals(myBean.getAoptype())) {
45.
hander.setBeforeAdvisor(advisor);
46.
}
else
{
47.
hander.setAfterAdvisor(advisor);
48.
}
49.
Object obj = hander.setObject(oldtarget);
50.
51.
Set keySet = propMap.keySet();
52.
53.
for
(Iterator iter = keySet.iterator(); iter.hasNext();) {
54.
String key = (String) iter.next();
55.
String value = (String) propMap.get(key);
56.
//
也就是说把需要注射的类的
key
属性注射为
value
对应的对象即可
57.
MyBean taget = (MyBean) beanMap.get(value);
58.
// 2,
反射出被注射的
bean
的对象
59.
Class c2 = Class.forName(taget.getClasstarget());
60.
//
将
key
的第一个字母变为大写的
61.
StringBuffer tempStr =
new
StringBuffer(key);
62.
Method method = c1.getMethod(
"set"
63.
+ tempStr.substring(
0
,
1
).toUpperCase()
64.
+ tempStr.deleteCharAt(
0
), c2.getInterfaces()[
0
]);
65.
method.invoke(oldtarget,
new
Object[] { c2.newInstance() });
66.
}
67.
return
obj;
68.
69.
}
else
{
//
以下是不需要方法拦截的情况下是这么做的
70.
71.
//
以下从
beanMap
中得到属性需要注射的这个对象的内部信息,获知哪些属性需要需要注射,注射什么,注射成什么
72.
Set keySet = propMap.keySet();
73.
for
(Iterator iter = keySet.iterator(); iter.hasNext();) {
74.
String key = (String) iter.next();
75.
String value = (String) propMap.get(key);
76.
//
也就是说把需要注射的类的
key
属性注射为
value
对应的对象即可
77.
MyBean taget = (MyBean) beanMap.get(value);
78.
// 2,
反射出被注射的
bean
的对象
79.
Class c2 = Class.forName(taget.getClasstarget());
80.
//
将
key
的第一个字母变为大写的
81.
StringBuffer tempStr =
new
StringBuffer(key);
82.
//
反射出
set
方法,设置已接口定义的需要被注射的对象引用
83.
Method method = c1.getMethod(
"set"
84.
+ tempStr.substring(
0
,
1
).toUpperCase()
85.
+ tempStr.deleteCharAt(
0
), c2.getInterfaces()[
0
]);
86.
method.invoke(oldtarget,
new
Object[] { c2.newInstance() });
87.
c2.newInstance();
88.
}
89.
}
90.
}
catch
(Exception e) {
91.
e.printStackTrace();
92.
}
93.
return
oldtarget;
94.
}
95.
}
以上,主要是个
getMyBean
方法,从
Bean
工厂中
(
大的装
Bean
的
Map)
里
,
根据
Bean
的名字获得
Bean
对象
,
以及通过反射机制获得原有对象
,
获得被注射的对象
,
以及用反射机制得到
set
方法的过程,完成
IOC
注射
.
注意,如果判断是需要
AOP
进行方法拦截的情况,则用动态代理类
AopHandler
来包装原始类
,
MyAdvisor advisor = (MyAdvisor) Class.forName(
aopBean.getClasstarget()).newInstance(); 得到在配置文件中配置的客户指定的方法拦截类 .
aopBean.getClasstarget()).newInstance(); 得到在配置文件中配置的客户指定的方法拦截类 .
if ("before".equals(myBean.getAoptype())) {
hander.setBeforeAdvisor(advisor);
} else {
hander.setAfterAdvisor(advisor);
} // 如果配置为 before 则是方法执行前拦截,反之,方法执行后拦截。
hander.setBeforeAdvisor(advisor);
} else {
hander.setAfterAdvisor(advisor);
} // 如果配置为 before 则是方法执行前拦截,反之,方法执行后拦截。
java
代码
1.
2.
public
class
AopHandler
implements
InvocationHandler {
3.
//
定义需要代理的目标对象
4.
private
Object target;
5.
6.
MyAdvisor after;
//
定义方法前置顾问
7.
8.
MyAdvisor before;
//
定义方法后置顾问
9.
10.
//
设置代理目标对象
11.
public
Object setObject(Object target) {
12.
this
.target = (Object) target;
13.
Object obj = Proxy.newProxyInstance(target.getClass().getClassLoader(),
14.
target.getClass().getInterfaces(),
this
);
15.
//
用
newProxyInstance
生成一个代理类
,
参数是传入的这个类的
classloader,
它的接口和实现
InvocationHandler
的对象
16.
17.
/**
18.
*
模拟这里的一些动作过程
1,
根据
classloader
和接口
反射出对象
19.
*
20.
* 2,
调用
DynamicProxyHandler
类的
invoke
方法,把反射出来的对象传入
,
和根据反射生成的方法以及方法参数
21.
*
达到
AOP
的效果
22.
*
23.
*/
24.
return
obj;
25.
}
26.
27.
public
Object invoke(Object proxy, Method method, Object[] args)
28.
throws
Throwable {
29.
if
(before !=
null
)
30.
before.doInAdvisor();
31.
// System.out.println("Before method" + method.getName());
32.
//
这里注入要
AOP
的对象,它的方法在这里运行
33.
method.invoke(target, args);
34.
// System.out.println("After method" + method.getName());
35.
if
(after !=
null
)
36.
after.doInAdvisor();
37.
//
这里注入要
AOP
的对象,它的方法在这里运行
38.
return
null
;
39.
}
40.
41.
public
void
setBeforeAdvisor(MyAdvisor advisor) {
42.
this
.before = advisor;
43.
}
44.
45.
public
void
setAfterAdvisor(MyAdvisor advisor) {
46.
this
.after = advisor;
47.
}
48.
}
49.
public
interface
MyAdvisor {
50.
public
void
doInAdvisor();
51.
}
52.
public
class
BeforeMethodAdvisor
implements
MyAdvisor {
53.
public
void
doInAdvisor() {
54.
System.out.println(
"This is Aops before!!"
);
55.
}
56.
}
57.
public
class
AfterMethodAdvisor
implements
MyAdvisor {
58.
59.
public
void
doInAdvisor() {
60.
System.out.println(
"This is Aops after!!"
);
61.
}
62.
63.
}
1,AopHandler
同
AOP
一节的动态代理类
,
实现对象代理功能
2,MyAdvisor
方法切面的一个接口
3AfterMethodAdvisor
和
BeforeMethodAdvisor
分别为实现
MyAdvisor
的扩展切面
.
使用时
,
与
spring
无类似
java
代码
1.
MyBeanFactory facotry =
new
MyXmlClasspathContext(
"C://context.xml"
)
2.
.getMyBeanFacoty();
3.
Object obj = facotry.getMyBean(
"userservice"
);
4.
System.out.println(obj.getClass());
5.
UserService userservice = (UserService) facotry
6.
.getMyBean(
"userservice"
);
7.
userservice.myMethod();
同样,从
Bean
工厂中获得被注射完毕的
Bean,
进行使用,同时,可以达到配置实现
AOP
的目的
.
大体代码如上,我相信大家很容易看懂
.
再来介绍一下使用方法,我做了一个
webdemo.
和使用
spring
类似
1,
配置
struts
插件
java
代码
1.
public
class
MySpringWebPlugin
implements
PlugIn {
2.
3.
public
void
destroy() {
4.
}
5.
6.
public
void
init(ActionServlet arg0, ModuleConfig arg1)
7.
throws
ServletException {
8.
MyContext context =
new
MyXmlClasspathContext(
"/context.xml"
);
9.
ServletContext servletContext = arg0.getServletContext();
10.
servletContext.setAttribute(Constants.MYSPRING_CONFIG_NAME, context);
11.
MySpringServiceLocator.setServletContext(servletContext);
12.
}
13.
14.
}
将
magicframework
上下文设置到
servlet
的上下文中。
2,
同时,用
ServiceLocator
的形式从
servlet
上下文中获取
MagicFrameWork
上下文
.
java
代码
1.
public
class
MySpringServiceLocator {
2.
private
static
MySpringServiceLocator locator;
// = new
3.
4.
// MySpringServiceLocator();
5.
6.
static
MyBeanFactory beanFactory;
7.
8.
private
ServletContext servletContext;
9.
10.
private
MySpringServiceLocator() {
11.
12.
}
13.
14.
public
UserService getUserService() {
15.
return
(UserService) beanFactory.getMyBean(
"userservice"
);
16.
}
17.
18.
public
static
void
setServletContext(ServletContext servletContext) {
19.
locator =
new
MySpringServiceLocator();
20.
locator.servletContext = servletContext;
21.
}
22.
23.
public
static
MySpringServiceLocator getInstance() {
24.
MyContext context = (MyContext) locator.servletContext
25.
.getAttribute(Constants.MYSPRING_CONFIG_NAME);
26.
beanFactory = context.getMyBeanFacoty();
27.
return
locator;
28.
}
1.
2.
}
xml
代码
1.
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"?>
2.
<
beans
xmlns:xsi
=
"http://www.w3.org/2001/XMLSchema-instance"
3.
xsi:noNamespaceSchemaLocation
=
"magicframework.xsd">
4.
5.
<
bean
id
=
"userdao"
6.
class
=
"org.magicframework.demo.dao.UserDaoImpl">
7.
</
bean
>
8.
9.
<
bean
id
=
"roledao"
10.
class
=
"org.magicframework.demo.dao.RoleDaoImpl">
11.
</
bean
>
12.
13.
<
bean
id
=
"userservice"
14.
class
=
"org.magicframework.demo.service.UserServiceImpl"
aop
=
"myaop"
15.
aoptype
=
"before">
16.
<
property
name
=
"userdao"
ref
=
"userdao"
/>
17.
<
property
name
=
"roledao"
ref
=
"roledao"
/>
18.
</
bean
>
19.
20.
<
bean
id
=
"myaop"
21.
class
=
"org.magicframework.demo.interceptor.MyTestBeforeMethodAdvisor">
22.
<
property
methodname
=
"myMethod"
/>
23.
</
bean
>
24.
25.
</
beans
>
26.
同时,
xml
配置如上
,
xsi:noNamespaceSchemaLocation
=
"magicframework.xsd">
这里我自己建立了
XSD,
所以引用本地的
与
spring
类似
,
相信你能看的懂
,
我就不罗嗦了
.
本文介绍了AOP与IOC的基本原理及实现方式,并提供了一个具备这两种功能的轻量级框架magicframework的实现细节。

被折叠的 条评论
为什么被折叠?



