最近我手写实现了Spring的核心功能,通过手写更深入理解点击打开链接了反射/动态代理
Spring核心包括两部分:AOP/IOC
完整代码可以去我的GitHub
介绍AOP和IOC之前,先 看看动态代理和反射
代理是一种设计模式,假如我们要找媳妇,我们可以不用自己去找,可以去婚姻介绍所,
婚姻介绍所就是代理,他可以帮我们找到需要的对象.当然了,在得到对象之前,我们需要告诉他需要那个,他会给我们看照片吧,看对象的介绍,然后我们看上哪个了就得到了(假设),在得到后,介绍所会收取一定费用(使用动态代理对方法进行增强).
关于动态代理,我们可以用jdk自带的(需要接口来生成代理类)
需要写一个类实现InvocationHandler接口
然后实现invoke方法,当我们使用代理类调用里面的方法时,会走这个方法,这里我们
我们在构造里接受参数 c 就是需要被代理的对象
public JdkInvocationHandler(Object c){ this.c=c; }
然后可以在调用被代理对象的方法之前输出"之前" ,然后在执行代理类方法后输出"之后"
这就是动态代理的好处,可以对方法进行增强
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("之前");//这里第一个参数必须是Object---------------------- method.invoke( c ,args) ;
System.out.println("之后");return proxy ;}
然后我们可以这样获得代理类
JdkInvocationHandler jdkInvocationHandler=new JdkInvocationHandler(c.newInstance()); Object o = Proxy.newProxyInstance(jdkInvocationHandler.getClass().getClassLoader(), c.getInterfaces(), jdkInvocationHandler);
先创建一个实现了InvocationHandler接口的类,传入一个需要代理的对象
然后通过Proxy.newInstance()方法,传递一个类加载器,接口,实现了InvocationHandler接口的类来生成代理类
然后我们就能使用这个代理类了
可以对他进行强转,然后调用方法就能看见效果了
**jdk的动态代理是有弊端的,那就是只能使用接口来生成代理类
假如我们需要代理的对象没有实现接口怎么办
这时候我们可以用cglib,可以通过继承的办法来生成代理类
-------
和jdk的动态代理差不多,我们需要写一个实现了Methodinterceptor接口的类
执行被代理类的方法前打印“之前”,执行后打印“之后”
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("之前"); methodProxy.invokeSuper(o,objects);
System.out.println("之后");return o ;}
怎么获得对象呢,先创建一个增强器,然后设置回调(就是调用代理类的方法需要做的事),然后create就得到对象了
Enhancer enhancer = new Enhancer(); enhancer.setCallbacks(new Callback[]{new CGlibMethodInterceptor(listListEntry.getKey(), listListEntry.getValue())}); enhancer.setSuperclass(c); Object o = enhancer.create();强转后就能直接调用了
----------
接下来是关于反射的
我们可以通过Class.forName(String className)来传入类名来获得类对象
可以理解成数据库的select操作,通过类名查找Class
可以用Method.invoke(Object o,Object[] args)来调用指定对象里面的方法
可以用Field.set(Object o,Object v) 来设置类里面的字段值
----------------------
AOP
面向切面编程,可以让我们专注于业务逻辑开发(用动态代理实现)
具体可以看这个
使用动态代理来对方法进行增强
-----------------------
IOC
控制反转(用反射实现)
可以让框架来管理类
让类的创建交给框架处理,
比如我们获得通过反射获得一个类对象,然后需要对类进行初始化,并且要对加了@Autowired注解的字段进行设置值,我们就把对字段的设置交给了框架进行处理,就实现了控制反转
还有对需要增强方法的类进行增强,通过动态代理来生成代理类,也实现了控制反转
接下来我们看一下关于AOP和IOC的具体流程(纯手写,仅实现核心功能)
1.首先,我们需要定义注解
After/Aspect/Autofired/BackPackage/Before/Controller/RequestMapping/Service
2.然后写一个DispatcherServlet(核心)
并在web.xml中配置这个Servlet
当我们访问任意路径, 都会调用这个servleet来执行
这个Servlet有个init方法,会在初始化的时候执行
我们可以在init方法中写初始化"框架"的方法
1.获得需要扫描的包名
2.然后通过递归扫描指定包下的所有类,放到容器里
3.接下来就是初始化切面容器,我们需要将条件和之前/之后需要调用的方法和类对象放到容器里
由于我们切面是这样使用的,之前要执行的方法可以有很多个,之后要执行的方法可能也有很多个
这里我们可以把他放到Map中
这样放到map中,用于后面为需要增强方法的对象通过动态代理进行增强
4.然后我们需要对类进行初始化
逻辑是:
遍历放了所有类的容器
如果类全名在切面容器内出现了
判断这个类时候实现了接口
如果实现了接口,就使用jdk动态代理,将切面容器中的之前之后方法传到jdkInvocationHandler中,生成代理对象并放到容器里用于后面对使用了Autowired注解的字段进行注入
如果没有实现接口,就使用cglib生成代理对象并放大容器里,用于后面注入
如果没有在切面容器里出现,就直接newInstance 放到容器里后面再注入
5.然后我们需要遍历容器里面存放的对象引用
遍历对象引用,并且遍历所有字段,如果加了Autowired注解,就从instanceMap中拿到并通过反射来注入值
通过Field.set(Object o,Object v)方法
6.然后我们需要扫描所有Controller
获得所有映射和方法/类放到容器里
7.然后在doGet和doPost中写方法处理请求
这个方法就是获得请求,然后在url映射容器里找需要执行的方法,和类,然后通过反射进行调用
如果返回了String 就表示要显示jsp页面,就直接forword
Mybatis的手写核心功能也完成了,明天写博客,睡觉
完整代码可以去我的GitHub