java中加载一个类,需要把.java源文件编译成.class文件,然后在把.class文件通过类加载器,装载到JVM虚拟机中。大部分情况下,我们的类都是通过这种静态加载编译后的.class文件生成的。此时在项目工程下的target文件夹中,都会有相应的.class文件。但动态代理则不一样,java文件和.class文件都是不可见的。通过代理技术会生成我们需要的java文件,然后在编译成.class文件,最后通过类加载器把相应的.class文件加载到jvm内存中,加载完毕后会把这些java和.class文件删掉。此时在工程中,看不到任何代理生成的相关文件。但我们想要的功能的确帮我们实现了。
java中动态代理技术主要有以下两种
- jdk动态代理(实现InvocationHandle接口)
- cglib代理(继承类)
jdk动态代理
通过模仿mybatis DAO 来记录下jdk动态代理过程。
- 定义一个dao接口
- 定义一个实现InvocationHandle接口的类
- 创建一个代理工厂类
需求:模仿mybatis 实现用户的查询和新增功能
定义DAO接口
public interface UserDao { @Select("select * from xx") void query(); @Insert("insert user(id,name) values(id,name)") void insert(); }
创建一个UserDao接口,里面包含两个方法,query()和insert()方法。其中每个方法上面用了自定义注解修饰。功能类比mybatis中的xml 配置。
@Select
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Select { String[] value(); }
@Insert
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Insert { String[] value(); }
实现InvocationHandle接口
public class UserDaoProx implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (method.isAnnotationPresent(Select.class)) { System.out.println("开始查询....."); Select annotation = method.getAnnotation(Select.class); String[] value = annotation.value(); if (value.length > 0) { System.out.println(value[0]); } } else if (method.isAnnotationPresent(Insert.class)) { System.out.println("开始插入....."); Insert annotation = method.getAnnotation(Insert.class); String[] value = annotation.value(); if (value.length > 0) { System.out.println(value[0]); } } return null; } }
这里对要代理的方法做了判断,获取方法相应注解的value,然后打印sql语句
创建代理工厂类
public class MyProxyFactory { public static Object getProxyObject(Class clazz){ return Proxy.newProxyInstance(MyProxyFactory.class.getClassLoader(),new Class[] {clazz},new UserDaoProx()); } }
jdk动态代理运用了Proxy的newProxyInstance()方法,帮我们完成创建代理类并完成编译和加载到jvm内存中这一过程。原理就是干了我们编译器的事。
newProxyInstance()方法有三个参数
- ClassLoader loader:类加载器
- Class[] interfaces:要代理的接口
- InvocationHandler h:接口方法的回调。这个也是为什么要定义一个实现InvocationHandle接口的原因
测试类
public class App { public static void main( String[] args ) { UserDao userDao = (UserDao) MyProxyFactory.getProxyObject(UserDao.class); userDao.query(); userDao.insert(); } }
用我们创建的代理工厂方法,帮我们创建一个UserDao实现。调用query和insert方法。看后台打印
目前已完成运用jdk动态代理技术模仿mybatis的DAO功能。