支撑Java框架的基础技术:泛型,反射,动态代理,cglib

以Spring为例要想看明白他的源码需要彻底理解Java的一些基础技术泛型,反射同时对于一些高级技术例如动态代理,cglib和字节码技术也需要掌握,下面就按章节来一一说清楚这些技术的核心部分,最后手写一个简单的Spring框架。   一.静态代码块和非静态代码块以及构造函数的调用顺序   静态代码块: https://stackoverflow

以Spring为例要想看明白他的源码需要彻底理解Java的一些基础技术泛型,反射同时对于一些高级技术例如动态代理,cglib和字节码技术也需要掌握,下面就按章节来一一说清楚这些技术的核心部分,最后手写一个简单的Spring框架。

 

一.静态代码块和非静态代码块以及构造函数的调用顺序

 

静态代码块:https://stackoverflow.com/questions/2420389/static-initialization-blocks

每次构造类的实例时都会调用它。在静态块只被调用一次,当类本身初始化,无论你如何创建该类型的许多对象。

例子二: 

 

下面这个例子用于演示完整的代码调用顺序。

先看父类: 

public class Parent {

static String name = "hello";

//非静态代码块

{

System.out.println("1");

}

//静态代码块

static {

System.out.println("2");

}

public Parent() {

System.out.println("3");

}

}

再看子类:

public class Child extends Parent {

static String childName = "hello";

{

System.out.println("4");

}

static {

System.out.println("5");

}

public Child() {

System.out.println("6");

}

}

调用测试的主函数:

public class StaticCodeBlockOrderTest {

public static void main(String[] args) {

new Child();

}

}

输出:

 

对象的初始化顺序:
首先执行父类静态的内容,父类静态的内容执行完毕后,接着去执行子类的静态的内容,当子类的静态内容执行完毕之后,再去看父类有没有非静态代码块,
如果有就执行父类的非静态代码块,父类的非静态代码块执行完毕,接着执行父类的构造方法;父类的构造方法执行完毕之后,它接着去看子类有没有非静态代码块,如果有就执行子类的非静态代码块。
子类的非静态代码块执行完毕再去执行子类的构造方法。

参考:java中静态代码块的用法 static用法详解
 

静态代码块的应用场景:

需要一个Util类,需要系统初始化的时候就初始化一个hashMap,部分代码省略以...代替

 

private static Map<String, List<String>> smap = new HashMap<String, List<String>>();

static {

for (int i = 0; i < k; i++) {

List<String> ls = new ArrayList<String>();

ls.add(...);

ls.add(...);

smap.put(..., ls);

}

}

 

这个一样的用法:Map的静态赋值 

 

二.泛型:

 

这个老外的视频讲的很好《Java Generics

1.泛型方法

static 后的<E>是泛型声明,这样才可以在这个方法参数和方法体用泛型

static <E>后的Set<E>是返回值

泛型类: 

不用泛型需要强转: 

 

2.泛型 Class<T>和Class<?>的差异

 

public class Box<T> {

private T t;

public Box(){

}

public Box(T data){

this.t=data;

}

public T getT() {

return t;

}

public void setT(T t) {

this.t = t;

}

}

调用

 

 

public static void main(String[] args) {

Box<String> s=new Box<String>("abc");

Box<Integer> i=new Box<Integer>(123);

System.out.println("s class:" + s.getClass());

System.out.println("i class:" + i.getClass());

System.out.println(s.getClass() == i.getClass());

}

输出

 

 

为什么有Class<T>还需要Class<?>呢?

其实看这个就明白了
在其他类中例如这个Test里,你可以定义

 

public static void getData(Box<String> data){

System.out.println("data :" + data.getT());

}


也可以定义

 

public static void getData(Box<Integer> data){

System.out.println("data :" + data.getT());

}

 

 

但是你要是同时定义这2个就会报错

名称冲突: getData(Box<Integer>)和getData(Box<String>)具有相同疑符


 

使用通配符?就可以解决这个问题

 

public class TestMain {

public static void main(String[] args) {

Box<String> s=new Box<String>("abc");

Box<Integer> i=new Box<Integer>(123);

System.out.println("s class:" + s.getClass());

System.out.println("i class:" + i.getClass());

System.out.println(s.getClass() == i.getClass());

getData(s);

getData(i);

}

public static void getData(Box<?> data){

System.out.println("data :" + data.getT());

}

}

 

 

参考:

Java总结篇系列:Java泛型

java中的泛型总结

 

再来看看Class<?>的用处

public class TestMain {

public static void main(String[] args) {

Box<String> s=new Box<String>("http://blog.csdn.net/unix21");

Box<Integer> i=new Box<Integer>(123);

System.out.println("s class:" + s.getClass());

System.out.println("i class:" + i.getClass());

System.out.println(s.getClass() == i.getClass());

getData(Box.class);

}

public static void getData(Class<?> clz){

try {

System.out.println(clz);

System.out.println("clz.hashCode():" + clz.hashCode());

Object o=clz.newInstance();

o.hashCode();

System.out.println("o.hashCode():" + o.hashCode());

}

catch (Exception e) {

System.out.println(e);

}

}

}

 

((Box)clz).getT();会报错:

不能将 "class java.lang.Class (no class loader)" 的实例强制转换为 "class test.Box (loaded by instance of sun.misc.Launcher$AppClassLoader(id=144))" 的实例

说明还没有class loader

((Box)o).getT();就已经实例化了。

 

3.Object类型

定义的所有类默认都是子类,所有的类都是以标准类Object为基础,Object类型的变量可以存储指向任意类类型对象的索引。

当要为一个方法来处理未知类型的对象时,这很有用。

 

//存储的地方

HashMap<String , Object> map = new HashMap<String , Object>();

User u1=new User();

u1.setId(1);

u1.setName("ww1");

//Object可以塞任意类型

map.put("user",u1);

User u=(User)map.get("user");

response.getWriter().println("Hello Servlet >>>"+u.getName());

String clazz ="com.w1.User"; //bean.getAttributeValue("class");

try {

//反射

Object o = Class.forName(clazz).newInstance();

map.put("user2",o);

User u2=(User)map.get("user2");

u2.setName("ww223");

response.getWriter().println("Hello Servlet >>>"+u2.getName());

} catch (Exception e) {

e.printStackTrace();

}

 

三.反射

反射可以参考本人的反射深入专贴:深入浅出Java反射原理和使用场景

这里限于篇幅只罗列一点精华内容。

类名.class, class.forName(), getClass()区别

1:Class cl=A.class;  
JVM将使用类A的类装载器, 将类A装入内存(前提是:类A还没有装入内存),不对类A做类的初始化工作.返回类A的Class的对象。
2:Class cl=对象引用o.getClass();
返回引用o运行时真正所指的对象(因为:子对象的引用可能会赋给父对象的引用变量中)所属的类的Class的对象 。
3:Class.forName("类名");
.装入类A,并做类的初始化
.getClass()是动态的,其余是静态的。
.class和class.forName()只能返回类内field的默认值,getClass可以返回当前对象中field的最新值
Class.forName() 返回的是一个类,.newInstance() 后才创建一个对象,Class.forName()的作用是要求JVM查找并加载指定的类,也就是说JVM会执行该类的。

public class Person {

private String name = "Alfira";

public void getName() {

System.out.println(name);

}

public void setName(String name, int a) {

this.name = name + a;

}

}

 

private static void show(String name) {

try {

// JVM将使用类A的类装载器,将类A装入内存(前提是:类A还没有装入内存),不对类A做类的初始化工作

Class classtype3 = Person.class;

// 获得classtype中的方法

Method getMethod3 = classtype3.getMethod("getName", new Class[] {});

Class[] parameterTypes3 = { String.class, int.class };

Method setMethod3 = classtype3.getMethod("setName", parameterTypes3);

// 实例化对象,因为这一句才会输出“静态初始化”以及“初始化”

Object obj3 = classtype3.newInstance();

// 通过实例化后的对象调用方法

getMethod3.invoke(obj3); // 获取默认值

setMethod3.invoke(obj3, "Setting new ", 3); // 设置

getMethod3.invoke(obj3); // 获取最新

System.out.println("----------------");

// 返回运行时真正所指的对象

Person p = new Person();

Class classtype = p.getClass();// Class.forName(name);

// 获得classtype中的方法

Method getMethod = classtype.getMethod("getName", new Class[] {});

Class[] parameterTypes = { String.class, int.class };

Method setMethod = classtype.getMethod("setName", parameterTypes);

getMethod.invoke(p);// 获取默认值

setMethod.invoke(p, "Setting new ", 1); // 设置

getMethod.invoke(p);// 获取最新

System.out.println("----------------");

// 装入类,并做类的初始化

Class classtype2 = Class.forName(name);

// 获得classtype中的方法

Method getMethod2 = classtype2.getMethod("getName", new Class[] {});

Class[] parameterTypes2 = { String.class, int.class };

Method setMethod2 = classtype2.getMethod("setName", parameterTypes2);

// 实例化对象

Object obj2 = classtype2.newInstance();

// 通过实例化后的对象调用方法

getMethod2.invoke(obj2); // 获取默认值

setMethod2.invoke(obj2, "Setting new ", 2); // 设置

getMethod2.invoke(obj2); // 获取最新

System.out.println("----------------");

} catch (Exception e) {

System.out.println(e);

}

}


调用

show("com.Person");


参考此文:http://www.cnblogs.com/feiyun126/archive/2013/08/01/3229492.html

http://blog.163.com/granite8@126/blog/static/853746082008610102657141/

 

四.JDK动态代理和CGLIB动态代理的区别

 

https://stackoverflow.com/questions/10664182/what-is-the-difference-between-jdk-dynamic-proxy-and-cglib

JDK动态代理只能通过接口代理(因此您的目标类需要实现一个接口,然后由代理类实现)。

CGLIB(和javassist)可以通过子类创建代理。在这种情况下,代理成为目标类的子类。不需要接口。

所以Java动态代理可以代理:public class Foo implements iFooCGLIB可以代理的地方:public class Foo

 

我应该提一下,因为javassist和CGLIB通过子类化使用代理,这就是你在使用依赖于它的框架时不能声明 final方法或使类 final的原因。这将阻止这些库允许子类化您的类并覆盖您的方法。

 

还应该注意的是,CGLib子类创建需要足够了解超类,以便能够使用正确的args调用正确的构造函数。与基于接口的代理不同,它不关心构造函数。这使得使用CGLib代理比JDK代理更少“自动”。另一个区别在于“堆叠”成本。JDK代理总是在每次调用时产生额外的堆栈帧,而CGLib可能不会花费任何额外的堆栈帧。随着应用程序越复杂,这变得越来越相关(因为堆栈越大,内存线程消耗越多)。

 

CGLIB不能代理 final方法,但不会抛出异常gist.github.com/mhewedy/7345403cfa52e6f47563f8a204ec0e80 -  Muhammad Hewedy 

 

使用CGLIB库动态创建代理

CGLIB库是ASM之上的高级层。它对代理不实现接口的类非常有用。本质上,它动态生成一个子类来覆盖代理类的非final方法,并连接回调用户定义的拦截器的钩子。它比JDK动态代理方法更快。

CGLIB是一个功能强大的高性能代码生成库。它是JDK动态代理的补充,因为它提供了不实现接口的代理类。在封面下,它使用ASM字节码操作框架。实质上,CGLIB动态生成一个子类来覆盖代理类的非final方法。它比使用Java反射的JDK动态代理方法更快。CGLIB不能使用任何最终方法代理最终类或类。对于一般情况,您使用JDK动态代理方法来创建代理。当接口不可用或性能问题时,CGLIB是一个很好的选择。

 

https://stackoverflow.com/questions/4411129/why-does-jdk-dynamic-proxy-only-work-with-interfaces

https://www.quora.com/In-Java-why-are-dynamic-proxies-only-allowed-to-proxy-interface-classes

我相信这个决定是在Java标准库中故意做出的,以支持接口而不是类继承。使用接口允许以不同方式引用相同的代理对象。

近年来,它也是Java最佳实践(或“类似Java”),它将对象称为接口类型而不是类,甚至是匿名类。匿名类通常用作扩展的基础 - 因此成语类C扩展BaseC实现InterfaceC。

对于更高级的用法,例如增强现有类,使用第三方库是可以接受的,因为这通常不是由最终用户开发人员完成的,而是由框架(例如Spring Framework)使用。

http://cliffmeyers.com/blog/2006/12/29/spring-aop-cglib-or-jdk-dynamic-proxies.html

即使您不是面向方面编程的忠实粉丝,如果您使用Spring框架的事务管理,您的应用程序将使用动态AOP代理,尽管是在幕后。Spring可以使用两种
不同的技术在运行时创建代理:CGLIB或JDK动态代理。

如果目标类实现了一个或多个接口,那么Spring将创建一个实现每个接口的JDK动态代理。如果目标类没有实现接口,Spring将使用CGLIB动态创建一个新类,它是目标类的子类(“extends”)。这导致了一个重要的区别:JDK动态代理无法转换为原始目标类,因为它只是一个动态代理,恰好实现了与目标相同的接口。如果在应用程序的模型中使用它们,这会“轻推”您对接口进行编程,因为通常会通过这些接口调用代理。 

另一方面,如果模型中完全没有接口,Spring将创建CGLIB代理,可以像目标类本身一样对待或多或少。还有一种方式,迫使其在Spring文档详细介绍这两种情况下CGLIB代理的创作就在这里

http://tutorials.jenkov.com/java-reflection/dynamic-proxies.html

使用Java Reflection可以在运行时创建接口的动态实现。你这样做是使用这个类java.lang.reflect.Proxy。这个类的名称就是我将这些动态接口实现称为动态代理的原因。动态代理可用于许多不同的目的,例如数据库连接和事务管理,用于单元测试的动态模拟对象,以及其他类似AOP的方法拦截目的。

已知动态代理至少用于以下目的:

  • 数据库连接和事务管理
  • 用于单元测试的动态模拟对象
  • DI容器适应自定义工厂接口
  • 类似AOP的方法拦截

https://www.logicbig.com/tutorials/core-java-tutorial/java-dynamic-proxies/runtime-interface-implementation.html

https://javax0.wordpress.com/2016/01/20/java-dynamic-proxy/

Java和CGLIB动态代理

 

下面举例演示使用方法:

public class SayHello {

public void say(){

System.out.println("hello everyone");

}

}

 

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;

import net.sf.cglib.proxy.MethodInterceptor;

import net.sf.cglib.proxy.MethodProxy;

public class CglibProxy implements MethodInterceptor {

private Enhancer enhancer = new Enhancer();

public Object getProxy(Class clazz) {

//设置需要创建子类的类

enhancer.setSuperclass(clazz);

enhancer.setCallback(this);

//通过字节码技术动态创建子类实例

return enhancer.create();

}

//实现MethodInterceptor接口方法

public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {

System.out.println("前置代理");

//通过代理类调用父类中的方法

Object result = proxy.invokeSuper(obj, args);

System.out.println("后置代理");

return result;

}

}

 

调用

CglibProxy proxy = new CglibProxy();

//通过生成子类的方式创建代理类

SayHello proxyImp = (SayHello)proxy.getProxy(SayHello.class);

proxyImp.say();

 

CGLib动态代理原理及实现

Java动态代理机制详解(JDK 和CGLIB,Javassist,ASM)[转]

cglib动态代理介绍(一)

 

五.自己手写代码实现Spring的基本功能

需要说这个作者写到这一系列文章非常好【SSH进阶之路】一步步重构容器实现Spring框架——彻底封装,实现简单灵活的Spring框架(十一)

代码:http://download.csdn.net/detail/jiuqiyuliang/8483981

这篇博文的目标是不仅形似Spring的IoC,而且要神似Spring的IoC,将对象的依赖关系进一步封装。

完整的项目结构

 

Dao接口和实现

 

public interface Dao {

public void daoMethod();

}

public class Dao4MySqlImpl implements Dao {

public void daoMethod(){

System.out.println("Dao4MySqlImpl.daoMethod()");

}

}

public class Dao4OracleImpl implements Dao {

public void daoMethod(){

System.out.println("Dao4OracleImpl.daoMethod()");

}

}


Service接口和实现

 

public interface Service {

public void serviceMethod();

}

public class ServiceImpl implements Service {

private Dao dao;

//依赖注入

public void setDao(Dao dao) {

this.dao= dao;

}

@Override

public void serviceMethod() {

dao.daoMethod();

}

}

 

public interface BeanFactory {

Object getBean(String beanName);

}

import java.util.ArrayList;

import java.util.List;

public class BeanDefinition {

private String id;

private String className;

private List<PropertyDefinition> propertys = new ArrayList<PropertyDefinition>();

public BeanDefinition(String id, String className) {

this.id = id;

this.className = className;

}

public String getId() {

return id;

}

public void setId(String id) {

this.id = id;

}

public String getClassName() {

return className;

}

public void setClassName(String className) {

this.className = className;

}

public List<PropertyDefinition> getPropertys() {

return propertys;

}

public void setPropertys(List<PropertyDefinition> propertys) {

this.propertys = propertys;

}

}

 

核心容器

import org.jdom.Document;

import org.jdom.Element;

import org.jdom.input.SAXBuilder;

import org.jdom.xpath.XPath;

import java.beans.Introspector;

import java.beans.PropertyDescriptor;

import java.lang.reflect.Method;

import java.util.ArrayList;

import java.util.HashMap;

import java.util.List;

import java.util.Map;

/**

* 容器

*

* @author liang

*

*/

public class ClassPathXmlApplicationContext implements BeanFactory {

// 用于存放Bean

private List<BeanDefinition> beanDefines = new ArrayList<BeanDefinition>();

// 用于存放Bean的实例

private Map<String, Object> sigletons =new HashMap<String, Object>();

public ClassPathXmlApplicationContext(String fileName) {

this.readXML(fileName);

this.instanceBeans();

this.injectObject();

}

/**

* 为bean对象的属性注入值

*/

private void injectObject() {

for (BeanDefinition beanDefinition :beanDefines) {

Object bean = sigletons.get(beanDefinition.getId());

if(bean != null){

try {

// 通过Introspector取得bean的定义信息,之后再取得属性的描述信息,返回一个数组

PropertyDescriptor[] ps = Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors();

for(PropertyDefinition propertyDefinition:beanDefinition.getPropertys()){

for(PropertyDescriptor properdesc: ps){

if(propertyDefinition.getName().equals(properdesc.getName())){

// 获取属性的setter方法,private

Method setter = properdesc.getWriteMethod();

if(setter != null){

Object value = sigletons.get(propertyDefinition.getRef());

// 允许访问私有方法

setter.setAccessible(true);

// 把引用对象注入到属性

setter.invoke(bean, value);

}

break;

}

}

}

} catch (Exception e) {

e.printStackTrace();

}

}

}

}

/**

* 完成bean的实例化

*/

private void instanceBeans() {

for(BeanDefinition beanDefinition : beanDefines){

try {

if(beanDefinition.getClassName() != null && !"".equals(beanDefinition.getClassName().trim())){

sigletons.put(beanDefinition.getId(),Class.forName(beanDefinition.getClassName()).newInstance() );

}

} catch (Exception e) {

e.printStackTrace();

}

}

}

/**

* 读取xml配置文件

*/

private void readXML(String fileName) {

// 创建SAXBuilder对象

SAXBuilder saxBuilder = new SAXBuilder();

try {

// 读取资源,获得document对象

Document doc = saxBuilder.build(this.getClass().getClassLoader()

.getResourceAsStream(fileName));

// 获取根元素

Element rootEle = doc.getRootElement();

// 从根元素获得所有的子元素,建立元素集合

List listBean = XPath.selectNodes(rootEle, "/beans/bean");

// 遍历根元素的子元素集合,扫描配置文件中的bean

for (int i = 0; i < listBean.size(); i++) {

// 将根元素beans下的bean子元素作为一个新的子根元素

Element elementBean = (Element) listBean.get(i);

//获取id属性值

String id = elementBean.getAttributeValue("id");

//获取class属性值

String clazz = elementBean.getAttributeValue("class");

BeanDefinition beanDefine = new BeanDefinition(id,clazz);

// 获取子根元素bean下的所有property子元素

List listProperty = elementBean.getChildren("property");

// 遍历子根元素的子元素集合(即遍历property元素)

for (int j = 0; j < listProperty.size(); j++) {

// 获取property元素

Element elmentProperty = (Element)listProperty.get(j);

// 获取name属性值

String propertyName = elmentProperty.getAttributeValue("name");

// 获取ref属性值

String propertyref = elmentProperty.getAttributeValue("ref");

PropertyDefinition propertyDefinition = new PropertyDefinition(propertyName,propertyref);

beanDefine.getPropertys().add(propertyDefinition);

}

// 将javabean添加到集合中

beanDefines.add(beanDefine);

}

} catch (Exception e) {

e.printStackTrace();

}

}

/**

* 获取bean实例

*/

@Override

public Object getBean(String beanName) {

return this.sigletons.get(beanName);

}

}

 

public class PropertyDefinition {

private String name;

private String ref;

public PropertyDefinition(String name, String ref) {

this.name = name;

this.ref = ref;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public String getRef() {

return ref;

}

public void setRef(String ref) {

this.ref = ref;

}

}

 

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>

<beans>

<bean id="dao" class="com.tgb.container.dao.impl.Dao4MySqlImpl" />

<bean id="service" class="com.tgb.container.service.impl.ServiceImpl">

<property name="dao" ref="dao"></property>

</bean>

</beans>

 

知秋君
上一篇 2024-07-03 15:31
下一篇 2024-07-03 15:31

相关推荐