-
利用动态代理动态的为接口生成实现类
2019-09-16 16:31:251.为什么会有这样的想法去生成接口的实现类 在学习mybatis的过程中,其存在一种机制,接口式编程。利用接口对我们sql的查询进行约束 我们只是写了接口,mybatis能给我们返回一个该接口的实例,这个实例是怎么来的...1.为什么会有这样的想法去生成接口的实现类
在学习mybatis的过程中,其存在一种机制,接口式编程。利用接口对我们sql的查询进行约束
我们只是写了接口,mybatis能给我们返回一个该接口的实例,这个实例是怎么来的?
思考:由于我们没有写该接口的实现类,mybatis返回的对象肯定不是简单的动态代理
但是我们打印其返回对象的class发现
class com.sun.proxy.$Proxy0
可以看出其就是jdk的动态代理对象。
2.实现动态的为接口生成实现类
我们利用jdk的动态代理,来对接口进行实现。
传统的使用动态代理的方式,需要传入一个被代理类来进行动态代理
我们这里由于没有接口实现类所以我们在写InvocationHandler的实现类的时候,不为其内部添加实现类。
代码如下:
要生成实现类的接口
public interface Person { public void showName(); public void saying(); }
InvocationHandler的实现类,内部没有上面接口的实现类,也就是没有被代理类
public class InvokeHandler implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // object的公用方法直接调用当前invoke对象的。 if (Object.class.equals(method.getDeclaringClass())) { return method.invoke(this, args); // 针对接口的不同方法书写我们具体的实现 } else if ("showName".equals(method.getName())) { System.out.println("张三"); } else if ("saying".equals(method.getName())) { System.out.println("我叫张三"); } return null; } }
测试:
public static void main(String[] args) { Person person = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(),new Class[]{Person.class}, new InvokeHandler()); person.showName(); person.saying(); System.out.println(person); System.out.println(person.getClass()); }
张三 我叫张三 toString D.InvokeHandler@6bc7c054 class com.sun.proxy.$Proxy0
这样我们就动态的简单的生成了一个接口的实现类。其原理还是动态代理,只是抛弃掉了被代理类
3.总结
mybaties内部动态的生成接口的实现类,其基本原理就是是如上面的代码所展示
可以去看其MapperProxy的源码:
public class MapperProxy<T> implements InvocationHandler, Serializable { // 可以看出内部字段是没有被代理类的 private static final long serialVersionUID = -6424540398559729838L; private final SqlSession sqlSession; private final Class<T> mapperInterface; private final Map<Method, MapperMethod> methodCache; public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) { this.sqlSession = sqlSession; this.mapperInterface = mapperInterface; this.methodCache = methodCache; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { if (Object.class.equals(method.getDeclaringClass())) { return method.invoke(this, args); } else if (isDefaultMethod(method)) { return invokeDefaultMethod(proxy, method, args); } } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } final MapperMethod mapperMethod = cachedMapperMethod(method); return mapperMethod.execute(sqlSession, args); } private MapperMethod cachedMapperMethod(Method method) { return methodCache.computeIfAbsent(method, k -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration())); } private Object invokeDefaultMethod(Object proxy, Method method, Object[] args) throws Throwable { final Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class .getDeclaredConstructor(Class.class, int.class); if (!constructor.isAccessible()) { constructor.setAccessible(true); } final Class<?> declaringClass = method.getDeclaringClass(); return constructor .newInstance(declaringClass, MethodHandles.Lookup.PRIVATE | MethodHandles.Lookup.PROTECTED | MethodHandles.Lookup.PACKAGE | MethodHandles.Lookup.PUBLIC) .unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args); } /** * Backport of java.lang.reflect.Method#isDefault() */ private boolean isDefaultMethod(Method method) { return (method.getModifiers() & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) == Modifier.PUBLIC && method.getDeclaringClass().isInterface(); } }
-
动态生成类_问:为什么 JDK 的动态代理只能使用接口?
2021-01-14 22:22:33JDK 的动态代理大家都熟悉...简单而不简单的答案之所以 JDK 的动态代理只能通过接口实现,原因是因为运行时 newProxyInstance 内部会缓存形式先通过字节码生成一个代理类,这个代理类默认已经继承了 Proxy 类,同时...JDK 的动态代理大家都熟悉,也都会用,但是你有没有深度思考一个问题,为什么 JDK 的动态代理只能使用接口? 想必有些人看到这个问法后就一脸懵逼了吧,小编下面就带大家揭密这个问题的本质。
简单而不简单的答案
之所以 JDK 的动态代理只能通过接口实现,原因是因为运行时 newProxyInstance 内部会缓存形式先通过字节码生成一个代理类,这个代理类默认已经继承了 Proxy 类,同时实现了我们传入的一堆接口;由于 Java 是单继承的,所以 JDK 动态代理只能代理接口,接口可以实现多个,但是类只能继承实现一个。
譬如我们使用动态代理样例如下:
// Foo 为接口
InvocationHandler handler = new MyInvocationHandler(...);
Class> proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(), Foo.class);
Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(), new Class>[] { Foo.class }, handler);上面代码运行时会在内存中默认通过字节码生成一个动态代理类,大致样子如下:
public class $Proxy1 extends Proxy implements Foo {
......
}这就是为什么 JDK 动态代理只能通过接口实现的原因。
深度思考 揭开面纱
分析为什么就得先从动态代理入口开始着手,即
Proxy.newProxyInstance(ClassLoader loader, Class>[] interfaces, InvocationHandler h)
,下面是其源码://JDK 创建动态代理
public static Object newProxyInstance(ClassLoader loader,
Class>[] interfaces,
InvocationHandler h)throws IllegalArgumentException{
......
// 重点
final Class>[] intfs = interfaces.clone();
......
// 生成增强之后的动态代理 Class
Class> cl = getProxyClass0(loader, intfs);
// 创建增强之后的动态代理 Class 实例对象
try {
......
final Constructor> cons = cl.getConstructor(constructorParams);
......
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
......
}
}上面代码中有一个重点语句
Class> cl = getProxyClass0(loader, intfs);
,源码如下:private static final WeakCache[], Class>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());private static Class> getProxyClass0(ClassLoader loader,
Class>... interfaces) {if (interfaces.length > 65535) {throw new IllegalArgumentException("interface limit exceeded");
}// proxyClassCache 是 Proxy 的静态变量,是 WeakCache 类,// 里面封装了两个类 KeyFactory、ProxyClassFactory// 重点!!! 本质调用的 ProxyClassFactory 的 apply 方法return proxyClassCache.get(loader, interfaces);
}顺着上面代码我们继续看下
ProxyClassFactory
,如下:private static final class ProxyClassFactoryimplements BiFunction<ClassLoader, Class>[], Class>>{
// 所有代理类的前缀
private static final String proxyClassNamePrefix = "$Proxy";
// 一个用来生成唯一类名的数字
private static final AtomicLong nextUniqueNumber = new AtomicLong();
// 重点,这个方法被上面的 proxyClassCache.get 调用,也就是被 WeakCache 的 get 调用
@Override
public Class> apply(ClassLoader loader, Class>[] interfaces) {
...... // 一堆对接口的校验逻辑,省略
String proxyPkg = null; // 代理类包名
int accessFlags = Modifier.PUBLIC | Modifier.FINAL; // flag
......
long num = nextUniqueNumber.getAndIncrement(); // 唯一类名
// 拼接的唯一全限定代理类名
String proxyName = proxyPkg + proxyClassNamePrefix + num;
//重点!!!这里生成了增强的代理类字节码文件
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
// 调用 native 方法加载代理类字节码到内存
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
......
}
}
}接近真相了,我们从上面代码可以知道,代理类其实是通过
ProxyGenerator
生成字节码的,其生成的代理类解构大致如下:public class $Proxy1 extends Proxy implements 传入的接口1, 传入的接口2... {
......
}到现在大家都应该明白了吧,JDK 动态代理的原理是根据定义好的继承 Proxy 类规则,用传入的接口创建一个新类,这就是为什么采用动态代理时为什么只能用接口引用指向代理,而不能用传入的类引用执行动态类。
然后呢?
在安卓中你可能就此认为完事了?不是的哈,其实动态代理的核心不就是动态嘛,如果想突破接口,我们完全可以采用别的方式实现,有了字节码操作什么不能玩啊,只是 JDK 默认是为了通用,所以牺牲了一些特性而已。
在后台开发中,一种典型的实现就是基于 cglib 的动态代理,他就能突破接口限制,采用的是用创建一个继承实现类的子类,用 ASM 库动态修改子类的代码来实现的,所以可以用传入的类引用执行代理类。
到此结题,扫描右侧微信加个好友呗,朋友圈更精彩。小编 朋友圈技术更精彩>
▼往期精彩回顾▼Android 主线程崩溃与子线程崩溃有什么本质区别?你是怎么处理的?逆天!可以直接拿市场 APK 上二次开发写代码的神器!
点击左下角阅读原文查看历史经典技术问题汇总,看完顺手一键三连呀~
-
SprignMVC+myBatis整合+mybatis源码分析+动态代理实现流程+如何根据mapper接口生成其实现类
2017-04-03 16:12:30首先熟悉三个概念: ...–根据指定的Mapper接口生成Bean实例 MapperScannerConfigurer –根据指定包批量扫描Mapper接口并生成实例SqlSessionFactoryBean: 在单独使用MyBatis时,所有操作都是围绕SqlSes首先熟悉三个概念:
SqlSessionFactoryBean
–为整合应用提供SqlSession对象资源
MapperFactoryBean
–根据指定的Mapper接口生成Bean实例
MapperScannerConfigurer
–根据指定包批量扫描Mapper接口并生成实例SqlSessionFactoryBean:
在单独使用MyBatis时,所有操作都是围绕SqlSession展开的,SqlSession是通过SqlSessionFactory获取的,SqlSessionFactory又是通过SqlSessionFactoryBuilder创建生成的。
在SpringMvc+MyBatis整合时,同样需要SqlSession。SqlSessionFactoryBean这个组件通过原来的SqlSessionFactoryBuilder生成SqlSessionFactory对象,为整合应用提供SqlSession对象。
Java代码<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="oracle.jdbc.OracleDriver" /> <property name="url" value="jdbc:oracle:thin:@localhost:1521:XE" /> <property name="username" value="jsd1403" /> <property name="password" value="root" /> </bean> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="myDataSource" /> <property name="mapperLocations" value="classpath:com/lydia/entity/*.xml" /> </bean>
MapperFactoryBean:
其作用是根据Mapper接口获取我们想要的Mapper对象,它封装了原有的session.getMapper()功能的实现。
在定义MapperFactoryBean时,需要注入一下两个属性:
–SqlSessionFactoryBean对象,用于提供SqlSession
–要返回Mapper对象的Mapper接口
MapperFactoryBean配置如下:
Java代码<!-- 方法一:定义mapper --> <bean id="deptMapper" class="org.mybatis.spring.mapper.MapperFactoryBean"> <property name="mapperInterface" value="com.lydia.entity.DeptMapper"></property> <!-- 指定SqlSessionFactoryBean对象 --> <property name="sqlSessionFactory" ref="sqlSessionFactory"></property> </bean>
MapperScannerConfigurer配置使用:
注意:使用MapperFactoryBean时,当有一个Mapper(可以理解为表对应的映射文件)就MapperFactoryBean,当mapper少数可以通过applicationContext配置文件,通过id获取。
如果大量的mapper,需要使用mybatis-spring.jar通过的MapperScannerConfigurer组件,通过这个组件可以自动扫描指定包下的各个Mapper接口,并注册对应的MapperFactoryBean对象。把之前的MapperFactoryBean的配置注释掉,换成如下配置依然执行通过:
Java代码<!--方法2: 可以把扫描到的Mapper接口变成Mapper对象--> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!--指定要扫描包: 多个包用逗号隔开 --> <property name="basePackage" value="com.lydia,com.tarena" /> <!--指定sqlSessionFactory --> <property name="sqlSessionFactory" ref="sqlSessionFactory"></property> </bean>
注意:上面sqlSessionFactory属性也可以不用指定,默认会以Autowired方式注入。
如果指定的某个包下并不完全是我们定义的Mapper接口,我们也可以通过自定义注解的方式指定生成MapperFactoryBean对象。
配置如下:<!--方法3: 只要Mapper类前面加上@MyBatisRepository 这个自己指定的注解就OK--> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.lydia" /> <property name="annotationClass" value="com.lydia.annotation.MyBatisRepository" /> </bean>
自定义注解:MyBatisRepository.java
Java代码public @interface MyBatisRepository { }
在DeptMapper接口中使用:
//@Repository("deptMapper") @MyBatisRepository public interface DeptMapper { void addDept(Dept dept); void deleteDept(Dept dept); void updateDept(Dept dept); ...... }
测试:
public class TestCase { @Test public void testFindAll() throws Exception { String conf = "applicationContext.xml"; ApplicationContext ac = new ClassPathXmlApplicationContext(conf); //获取对应的mapper对象,并调用mapper接口中对应的方法 DeptMapper mapper = ac.getBean("deptMapper", DeptMapper.class); List<Dept> lists = mapper.findAllDept(); for (Dept dept : lists) { System.out.println(dept); } } }
以上内容来源于http://lydia-fly.iteye.com/blog/2153076
MapperScannerConfigurer处理过程源码分析
本文将分析mybatis与spring整合的MapperScannerConfigurer的底层原理,之前已经分析过java中实现动态,可以使用jdk自带api和cglib第三方库生成动态代理。本文分析的mybatis版本3.2.7,mybatis-spring版本1.2.2。
MapperScannerConfigurer介绍
MapperScannerConfigurer是spring和mybatis整合的mybatis-spring jar包中提供的一个类。想要了解该类的作用,就得先了解MapperFactoryBean。
MapperFactoryBean的出现为了代替手工使用SqlSessionDaoSupport或SqlSessionTemplate编写数据访问对象(DAO)的代码,使用动态代理实现。
比如下面这个官方文档中的配置:
<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean"> <property name="mapperInterface" value="org.mybatis.spring.sample.mapper.UserMapper" /> <property name="sqlSessionFactory" ref="sqlSessionFactory" /> </bean>
org.mybatis.spring.sample.mapper.UserMapper是一个接口,我们创建一个MapperFactoryBean实例,然后注入这个接口和sqlSessionFactory(mybatis中提供的SqlSessionFactory接口,MapperFactoryBean会使用SqlSessionFactory创建SqlSession)这两个属性。
之后想使用这个UserMapper接口的话,直接通过spring注入这个bean,然后就可以直接使用了,spring内部会创建一个这个接口的动态代理。
当发现要使用多个MapperFactoryBean的时候,一个一个定义肯定非常麻烦,于是mybatis-spring提供了MapperScannerConfigurer这个类,它将会查找类路径下的映射器并自动将它们创建成MapperFactoryBean。
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="org.mybatis.spring.sample.mapper" /> </bean>
这段配置会扫描org.mybatis.spring.sample.mapper下的所有接口,然后创建各自接口的动态代理类。
MapperScannerConfigurer底层代码分析
以以下代码为示例进行讲解(部分代码,其他代码及配置省略):package org.format.dynamicproxy.mybatis.dao; public interface UserDao { public User getById(int id); public int add(User user); public int update(User user); public int delete(User user); public List<User> getAll(); } <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="org.format.dynamicproxy.mybatis.dao"/> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/> </bean>
我们先通过测试用例debug查看userDao的实现类到底是什么。
我们可以看到,userDao是1个MapperProxy类的实例。
看下MapperProxy的源码,没错,实现了InvocationHandler,说明使用了jdk自带的动态代理。public class MapperProxy<T> implements InvocationHandler, Serializable { private static final long serialVersionUID = -6424540398559729838L; private final SqlSession sqlSession; private final Class<T> mapperInterface; private final Map<Method, MapperMethod> methodCache; public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) { this.sqlSession = sqlSession; this.mapperInterface = mapperInterface; this.methodCache = methodCache; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (Object.class.equals(method.getDeclaringClass())) { try { return method.invoke(this, args); } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } } final MapperMethod mapperMethod = cachedMapperMethod(method); return mapperMethod.execute(sqlSession, args); } private MapperMethod cachedMapperMethod(Method method) { MapperMethod mapperMethod = methodCache.get(method); if (mapperMethod == null) { mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()); methodCache.put(method, mapperMethod); } return mapperMethod; } }
下面开始分析MapperScannerConfigurer的源码
MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor接口,BeanDefinitionRegistryPostProcessor接口是一个可以修改spring工长中已定义的bean的接口,该接口有个postProcessBeanDefinitionRegistry方法。
然后我们看下ClassPathMapperScanner中的关键是如何扫描对应package下的接口的。
其实MapperScannerConfigurer的作用也就是将对应的接口的类型改造为MapperFactoryBean,而这个MapperFactoryBean的属性mapperInterface是原类型。MapperFactoryBean本文开头已分析过。所以最终我们还是要分析MapperFactoryBean的实现原理!
MapperFactoryBean继承了SqlSessionDaoSupport类,SqlSessionDaoSupport类继承DaoSupport抽象类,DaoSupport抽象类实现了InitializingBean接口,因此实例个MapperFactoryBean的时候,都会调用InitializingBean接口的afterPropertiesSet方法。
DaoSupport的afterPropertiesSet方法:
MapperFactoryBean重写了checkDaoConfig方法:
然后通过spring工厂拿对应的bean的时候:
这里的SqlSession是SqlSessionTemplate,SqlSessionTemplate的getMapper方法:
Configuration的getMapper方法,会使用MapperRegistry的getMapper方法:
MapperRegistry的getMapper方法:
MapperProxyFactory构造MapperProxy:
没错! MapperProxyFactory就是使用了jdk组带的Proxy完成动态代理。
MapperProxy本来一开始已经提到。MapperProxy内部使用了MapperMethod类完成方法的调用:
下面,我们以UserDao的getById方法来debug看看MapperMethod的execute方法是如何走的。@Test public void testGet() { int id = 1; System.out.println(userDao.getById(id)); } <select id="getById" parameterType="int" resultType="org.format.dynamicproxy.mybatis.bean.User"> SELECT * FROM users WHERE id = #{id} </select>
以上转载于http://www.cnblogs.com/fangjian0423/p/spring-mybatis-MapperScannerConfigurer-analysis.html
java动态代理浅析
最近在公司看到了mybatis与spring整合中MapperScannerConfigurer的使用,该类通过反向代理自动生成基于接口的动态代理类。
于是想起了java的动态代理,然后就有了这篇文章。
本文使用动态代理模拟处理事务的拦截器。
接口:
public interface UserService { public void addUser(); public void removeUser(); public void searchUser(); }
实现类:
public class UserServiceImpl implements UserService { public void addUser() { System.out.println("add user"); } public void removeUser() { System.out.println("remove user"); } public void searchUser() { System.out.println("search user"); } }
java动态代理的实现有2种方式
1.jdk自带的动态代理
使用jdk自带的动态代理需要了解InvocationHandler接口和Proxy类,他们都是在java.lang.reflect包下。InvocationHandler介绍:
InvocationHandler是代理实例的调用处理程序实现的接口。
每个代理实例都具有一个关联的InvocationHandler。对代理实例调用方法时,这个方法会调用InvocationHandler的invoke方法。
Proxy介绍:
Proxy 提供静态方法用于创建动态代理类和实例。
实例(模拟AOP处理事务):
public class TransactionInterceptor implements InvocationHandler { private Object target; public void setTarget(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("start Transaction"); method.invoke(target, args); System.out.println("end Transaction"); return null; } }
测试代码:
public class TestDynamicProxy { @Test public void testJDK() { TransactionInterceptor transactionInterceptor = new TransactionInterceptor(); UserService userService = new UserServiceImpl(); transactionInterceptor.setTarget(userService); UserService userServiceProxy = (UserService) Proxy.newProxyInstance( userService.getClass().getClassLoader(), userService.getClass().getInterfaces(), transactionInterceptor); userServiceProxy.addUser(); } }
测试结果:
start Transaction add user end Transaction
很明显,我们通过userServiceProxy这个代理类进行方法调用的时候,会在方法调用前后进行事务的开启和关闭。
2. 第三方库cglib
CGLIB是一个功能强大的,高性能、高质量的代码生成库,用于在运行期扩展Java类和实现Java接口。
它与JDK的动态代理的之间最大的区别就是:
JDK动态代理是针对接口的,而cglib是针对类来实现代理的,cglib的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。
实例:
public class UserServiceCallBack implements MethodInterceptor { @Override public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("start Transaction by cglib"); methodProxy.invokeSuper(o, args); System.out.println("end Transaction by cglib"); return null; } }
测试代码:
public class TestDynamicProxy { @Test public void testCGLIB() { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(UserServiceImpl.class); enhancer.setCallback(new UserServiceCallBack()); UserServiceImpl proxy = (UserServiceImpl)enhancer.create(); proxy.addUser(); } }
测试结果:
start Transaction by cglib add user end Transaction by cglib
以上转载于http://www.cnblogs.com/fangjian0423/p/java-dynamic-proxy.html
mybatis如何根据mapper接口生成其实现类
mybatis里头给sqlSession指定执行哪条sql的时候,有两种方式,一种是写mapper的xml的namespace+statementId,如下:
public Student findStudentById(Integer studId) {
logger.debug(“Select Student By ID :{}”, studId);
SqlSession sqlSession = MyBatisSqlSessionFactory.getSqlSession();
try {
return sqlSession.selectOne(“com.mybatis3.StudentMapper.findStudentById”, studId);
} finally {
sqlSession.close();
}
}
另外一种方法是指定mapper的接口:public Student findStudentById(Integer studId) {
logger.debug(“Select Student By ID :{}”, studId);
SqlSession sqlSession = MyBatisSqlSessionFactory.getSqlSession();
try {
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
return studentMapper.findStudentById(studId);
} finally {
sqlSession.close();
}
}
一般的话,比较推荐第二种方法,因为手工写namespace和statementId极大增加了犯错误的概率,而且也降低了开发的效率。问题
mapper的实现类如何生成如果使用mapper接口的方式,问题来了,这个是个接口,通过sqlSession对象get出来的一定是个实现类,问题是,我们并没有手工去写 实现类,那么谁去干了这件事情呢?答案是mybatis通过JDK的动态代理方式,在启动加载配置文件时,根据配置mapper的xml去生成。
mybatis-spring帮忙做了什么
自动open和close session
一、mapper代理类是如何生成的
启动时加载解析mapper的xml如果不是集成spring的,会去读取节点,去加载mapper的xml配置
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <settings> <setting name="cacheEnabled" value="true"/> <setting name="lazyLoadingEnabled" value="true"/> <setting name="multipleResultSetsEnabled" value="true"/> <setting name="useColumnLabel" value="true"/> <setting name="useGeneratedKeys" value="false"/> <setting name="defaultExecutorType" value="SIMPLE"/> <setting name="defaultStatementTimeout" value="2"/> </settings> <typeAliases> <typeAlias alias="CommentInfo" type="com.xixicat.domain.CommentInfo"/> </typeAliases> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/demo"/> <property name="username" value="root"/> <property name="password" value=""/> </dataSource> </environment> </environments> <mappers> <mapper resource="com/xixicat/dao/CommentMapper.xml"/> </mappers> </configuration>
如果是集成spring的,会去读spring的sqlSessionFactory的xml配置中的mapperLocations,然后去解析mapper的xml
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <!-- 配置mybatis配置文件的位置 --> <property name="configLocation" value="classpath:mybatis-config.xml"/> <property name="typeAliasesPackage" value="com.xixicat.domain"/> <!-- 配置扫描Mapper XML的位置 --> <property name="mapperLocations" value="classpath:com/xixicat/dao/*.xml"/> </bean>
然后绑定namespace(XMLMapperBuilder)
private void bindMapperForNamespace() { String namespace = builderAssistant.getCurrentNamespace(); if (namespace != null) { Class<?> boundType = null; try { boundType = Resources.classForName(namespace); } catch (ClassNotFoundException e) { //ignore, bound type is not required } if (boundType != null) { if (!configuration.hasMapper(boundType)) { // Spring may not know the real resource name so we set a flag // to prevent loading again this resource from the mapper interface // look at MapperAnnotationBuilder#loadXmlResource configuration.addLoadedResource("namespace:" + namespace); configuration.addMapper(boundType); } } } }
这里先去判断该namespace能不能找到对应的class,若可以则调用
configuration.addMapper(boundType); configuration委托给MapperRegistry: public <T> void addMapper(Class<T> type) { mapperRegistry.addMapper(type); }
生成该mapper的代理工厂(MapperRegistry)
public <T> void addMapper(Class<T> type) { if (type.isInterface()) { if (hasMapper(type)) { throw new BindingException("Type " + type + " is already known to the MapperRegistry."); } boolean loadCompleted = false; try { knownMappers.put(type, new MapperProxyFactory<T>(type)); // It's important that the type is added before the parser is run // otherwise the binding may automatically be attempted by the // mapper parser. If the type is already known, it won't try. MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type); parser.parse(); loadCompleted = true; } finally { if (!loadCompleted) { knownMappers.remove(type); } } } }
这里的重点就是MapperProxyFactory类:
public class MapperProxyFactory<T> { private final Class<T> mapperInterface; private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>(); public MapperProxyFactory(Class<T> mapperInterface) { this.mapperInterface = mapperInterface; } public Class<T> getMapperInterface() { return mapperInterface; } public Map<Method, MapperMethod> getMethodCache() { return methodCache; } @SuppressWarnings("unchecked") protected T newInstance(MapperProxy<T> mapperProxy) { return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); } public T newInstance(SqlSession sqlSession) { final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); } }
getMapper的时候生成mapper代理类
@SuppressWarnings("unchecked") public <T> T getMapper(Class<T> type, SqlSession sqlSession) { final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type); if (mapperProxyFactory == null) { throw new BindingException("Type " + type + " is not known to the MapperRegistry."); } try { return mapperProxyFactory.newInstance(sqlSession); } catch (Exception e) { throw new BindingException("Error getting mapper instance. Cause: " + e, e); } }
new出来MapperProxy
public T newInstance(SqlSession sqlSession) { final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache); return newInstance(mapperProxy); } @SuppressWarnings("unchecked") protected T newInstance(MapperProxy<T> mapperProxy) { return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy); } 这里给代理类注入了sqlSession
MapperProxy实现InvocationHandler接口进行拦截代理
public class MapperProxy<T> implements InvocationHandler, Serializable { private static final long serialVersionUID = -6424540398559729838L; private final SqlSession sqlSession; private final Class<T> mapperInterface; private final Map<Method, MapperMethod> methodCache; public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) { this.sqlSession = sqlSession; this.mapperInterface = mapperInterface; this.methodCache = methodCache; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (Object.class.equals(method.getDeclaringClass())) { try { return method.invoke(this, args); } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } } final MapperMethod mapperMethod = cachedMapperMethod(method); return mapperMethod.execute(sqlSession, args); } private MapperMethod cachedMapperMethod(Method method) { MapperMethod mapperMethod = methodCache.get(method); if (mapperMethod == null) { mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()); methodCache.put(method, mapperMethod); } return mapperMethod; } }
这里的代理拦截,主要是寻找到MapperMethod,通过它去执行SQL。
MapperMethod委托给SqlSession去执行sqlpublic Object execute(SqlSession sqlSession, Object[] args) { Object result; if (SqlCommandType.INSERT == command.getType()) { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.insert(command.getName(), param)); } else if (SqlCommandType.UPDATE == command.getType()) { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.update(command.getName(), param)); } else if (SqlCommandType.DELETE == command.getType()) { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.delete(command.getName(), param)); } else if (SqlCommandType.SELECT == command.getType()) { if (method.returnsVoid() && method.hasResultHandler()) { executeWithResultHandler(sqlSession, args); result = null; } else if (method.returnsMany()) { result = executeForMany(sqlSession, args); } else if (method.returnsMap()) { result = executeForMap(sqlSession, args); } else { Object param = method.convertArgsToSqlCommandParam(args); result = sqlSession.selectOne(command.getName(), param); } } else if (SqlCommandType.FLUSH == command.getType()) { result = sqlSession.flushStatements(); } else { throw new BindingException("Unknown execution method for: " + command.getName()); } if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) { throw new BindingException("Mapper method '" + command.getName() + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ")."); } return result; }
其实这里就回到了第一种模式,该模式是直接指定了statement的Id(这里是command.getName()),而通过mapper的接口方式,则多了这么步骤,最后通过MapperMethod,给sqlSession传入statement的id。
sqlSession其实自己也不执行sql,它只是mybatis对外公布的一个api入口,具体它委托给了executor去执行sql。
什么时候去getMapper
手工get,比如
public void createStudent(Student student) {
SqlSession sqlSession = MyBatisSqlSessionFactory.getSqlSession();
try {
StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);
studentMapper.insertStudent(student);
sqlSession.commit();
} finally {
sqlSession.close();
}
}- 集成spring的话
在spring容器给指定的bean注入mapper的时候get出来(见MapperFactoryBean的getObject方法)
二、mybatis-spring帮忙做了什么
通过MapperScannerConfigurer将mapper适配成spring bean
<!-- 配置扫描Mapper接口的包路径 --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/> <property name="basePackage" value="com.xixicat.dao"/> </bean>
这里使用 MapperFactoryBean将Mapper接口配置成 Spring bean 实体同时注入sqlSessionFactory。
MapperScannerConfigurer给每个mapper生成对应的MapperFactoryBeanpublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { if (this.processPropertyPlaceHolders) { processPropertyPlaceHolders(); } ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry); scanner.setAddToConfig(this.addToConfig); scanner.setAnnotationClass(this.annotationClass); scanner.setMarkerInterface(this.markerInterface); scanner.setSqlSessionFactory(this.sqlSessionFactory); scanner.setSqlSessionTemplate(this.sqlSessionTemplate); scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName); scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName); scanner.setResourceLoader(this.applicationContext); scanner.setBeanNameGenerator(this.nameGenerator); scanner.registerFilters(); scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS)); }
委托给ClassPathMapperScanner去scan
public Set<BeanDefinitionHolder> doScan(String... basePackages) { Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages); if (beanDefinitions.isEmpty()) { logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration."); } else { for (BeanDefinitionHolder holder : beanDefinitions) { GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition(); if (logger.isDebugEnabled()) { logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + definition.getBeanClassName() + "' mapperInterface"); } // the mapper interface is the original class of the bean // but, the actual class of the bean is MapperFactoryBean definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName()); definition.setBeanClass(MapperFactoryBean.class); definition.getPropertyValues().add("addToConfig", this.addToConfig); boolean explicitFactoryUsed = false; if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) { definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName)); explicitFactoryUsed = true; } else if (this.sqlSessionFactory != null) { definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory); explicitFactoryUsed = true; } if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) { if (explicitFactoryUsed) { logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored."); } definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName)); explicitFactoryUsed = true; } else if (this.sqlSessionTemplate != null) { if (explicitFactoryUsed) { logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored."); } definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate); explicitFactoryUsed = true; } if (!explicitFactoryUsed) { if (logger.isDebugEnabled()) { logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'."); } definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE); } } } return beanDefinitions; }
这里出现了MapperFactoryBean的身影,然后判断配置文件是指定注入sqlSessionFactory,还是 sqlTemplate(二者不能同时指定,这里是指定了sqlSessionFactory)。这里通过 sqlSessionFactoryBeanName暂时先注入引用,因为此时还在给spring托管的bean进行create,不确定 sqlSessionFactory是否已经被创建。
关于MapperFactoryBeanpublic class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> { private Class<T> mapperInterface; private boolean addToConfig = true; /** * Sets the mapper interface of the MyBatis mapper * * @param mapperInterface class of the interface */ public void setMapperInterface(Class<T> mapperInterface) { this.mapperInterface = mapperInterface; } /** * If addToConfig is false the mapper will not be added to MyBatis. This means * it must have been included in mybatis-config.xml. * <p> * If it is true, the mapper will be added to MyBatis in the case it is not already * registered. * <p> * By default addToCofig is true. * * @param addToConfig */ public void setAddToConfig(boolean addToConfig) { this.addToConfig = addToConfig; } /** * {@inheritDoc} */ @Override protected void checkDaoConfig() { super.checkDaoConfig(); notNull(this.mapperInterface, "Property 'mapperInterface' is required"); Configuration configuration = getSqlSession().getConfiguration(); if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) { try { configuration.addMapper(this.mapperInterface); } catch (Throwable t) { logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", t); throw new IllegalArgumentException(t); } finally { ErrorContext.instance().reset(); } } } /** * {@inheritDoc} */ public T getObject() throws Exception { return getSqlSession().getMapper(this.mapperInterface); } /** * {@inheritDoc} */ public Class<T> getObjectType() { return this.mapperInterface; } /** * {@inheritDoc} */ public boolean isSingleton() { return true; } }
注意这里继承了SqlSessionDaoSupport,在spring把sqlSessionFactory创建出来后,会去把之前注入的引用改为真的实例,调用SqlSessionDaoSupport的setSqlSessionFactory方法。
public abstract class SqlSessionDaoSupport extends DaoSupport { private SqlSession sqlSession; private boolean externalSqlSession; public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) { if (!this.externalSqlSession) { this.sqlSession = new SqlSessionTemplate(sqlSessionFactory); } } public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) { this.sqlSession = sqlSessionTemplate; this.externalSqlSession = true; } /** * Users should use this method to get a SqlSession to call its statement methods * This is SqlSession is managed by spring. Users should not commit/rollback/close it * because it will be automatically done. * * @return Spring managed thread safe SqlSession */ public SqlSession getSqlSession() { return this.sqlSession; } /** * {@inheritDoc} */ protected void checkDaoConfig() { notNull(this.sqlSession, "Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required"); } }
这里值得注意的是setSqlSessionFactory方法new了一个SqlSessionTemplate。
SqlSessionTemplate
它的一个重要的构造器为public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) { notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required"); notNull(executorType, "Property 'executorType' is required"); this.sqlSessionFactory = sqlSessionFactory; this.executorType = executorType; this.exceptionTranslator = exceptionTranslator; this.sqlSessionProxy = (SqlSession) newProxyInstance( SqlSessionFactory.class.getClassLoader(), new Class[] { SqlSession.class }, new SqlSessionInterceptor()); }
mybatis-srping比传统mybatis方法多做的事情就在于此,生成了一个sqlSessionProxy。这里static import了java.lang.reflect.Proxy.newProxyInstance;也就是使用使用jdk代理进行了 SqlSessionInterceptor拦截。
SqlSessionInterceptorprivate class SqlSessionInterceptor implements InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { SqlSession sqlSession = getSqlSession( SqlSessionTemplate.this.sqlSessionFactory, SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator); try { Object result = method.invoke(sqlSession, args); if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) { // force commit even on non-dirty sessions because some databases require // a commit/rollback before calling close() sqlSession.commit(true); } return result; } catch (Throwable t) { Throwable unwrapped = unwrapThrowable(t); if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) { // release the connection to avoid a deadlock if the translator is no loaded. See issue #22 closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory); sqlSession = null; Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped); if (translated != null) { unwrapped = translated; } } throw unwrapped; } finally { if (sqlSession != null) { closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory); } } } }
到了这里就明白了mybatis-spring帮忙做了session的open和close。
以上转载于http://www.cnblogs.com/ChenLLang/p/5307590.html
-
jdk动态代理为什么必须实现接口
2020-04-03 18:49:28通过jdk实现的动态代理会使用Proxy的newProxyInstance方法: 写一个类实现InvocationHandler,内部要注入对应原代理类的实现; 测试代码: TestServiceImpl testService = new TestServiceImpl(); TestServiceIm...使用jdk生成代理类
通过jdk实现的动态代理会使用Proxy的newProxyInstance方法:
写一个类实现InvocationHandler,内部要注入对应原代理类的实现;
测试代码:TestServiceImpl testService = new TestServiceImpl(); TestServiceImplHandler testServiceImplHandler = new TestServiceImplHandler(testService); TestService testServiceProxy = (TestService)Proxy.newProxyInstance(testService.getClass().getClassLoader(), testService.getClass().getInterfaces(), testServiceImplHandler); testServiceProxy.test();
看到生成的代理类父类是Proxy类,通过jdk代理生成的类都继承Proxy类:
因为Java是单继承的,而代理类又必须继承自Proxy类,所以通过jdk代理的类必须实现接口.生成代理的字节码
还可以通过生成代理类的字节码查看,
package java.lang.reflect;包的Proxy类
通过将生成一半的代理类写到文件里面去:
生成的文件:
-
SpringAOP动态代理技术自动生成代理类原理演示
2018-12-11 15:28:00//如下是自动生成代理类的逻辑演示: ...为了解决这类问题使用AOP动态代理自动生成代理类 public interface Man { public void alive();//提供一个接口 } public class ManImpl implements M... -
【Mybatis源码】Mybatis如何为mapper接口生成代理对象--JDK动态代理
2021-01-09 14:27:58引言 mybatis版本:3.5.1 mybatis-spring:2.0.1 ...Mybatis是如何为mapper接口生成代理对象的? Mybatis又是如何将mapper对象交给Spring管理? 我们在整合mybatis与spring时,都会使用MyBatis-Spring 将 MyBatis -
动态代理类生成Hotspot VM干了什么
2020-06-21 18:55:09一、动态代理类生成主流程 1、类加载、代理类实现接口Class对象集合获取 2、校验是否为为接口(java是单继承而动态生成的代理李继承了Proxy所以其他的只能是接口) 3、判断接口是不是public的,如果不是public则... -
Android开发如何理解Java静态代理 动态代理及动态生成代理对象原理 看这篇就够了
2019-06-09 11:39:02静态代理是代理模式实现方式之一,比较简单,主要分为三个角色...在动态代理中,不需要我们再手动创建代理类,只需要编写一个动态处理器及指定要代理的目标对象实现的接口类型,真正的代理对象由JDK在运行时为我们创建 -
java的动态代理接口
2019-09-24 17:23:57动态代理是在运行过程中动态生成代理对象,增强被代理类的方法功能,或者是代理接口,动态生成接口实现类,实现接口功能。 二、动态代理的使用 1.接口如下: public interface ProxyInf {... -
JDK 动态生成的代理对象在调用方法时,为什么总会掉我们实现的InvocationHandler接口里面的invoke方法
2019-07-31 15:53:15JDK 动态生成的代理类 查看 在生成代理类前加入System.getProperties().put(“sun.misc.ProxyGenerator.saveGeneratedFiles”,“true”); 设置,即在 Proxy.newProxyInstance(getClass().getClassLoader(), new ... -
JDK动态代理为什么必须针对接口
2020-03-29 21:51:001.JDK动态代理为什么必须针对接口? 由于java的单继承,动态生成的代理类已经继承了Proxy类的,就不能再继承其他的类,所以只能靠实现被代理类的接口的形式,故JDK的动态代理必须有接口。 2.为何调用代理类的方法就... -
为什么spring注入接口正确而注入接口的实现类错误?
2019-03-29 17:00:45原因所在:出现如果直接注入实现类会出现没有对应的bean,因为我们通过实现类来继承的接口,然而,必须使用jdk提供的动态代理放法,而不使用接口直接对实现类进行注入,则为cglib的注入,而不能既继承接口又使用实现... -
静态代理、动态代理的区别 以及 JVM动态代理与Cglib动态代理的实现与区别
2020-06-18 11:54:18静态代理、动态代理的区别 以及 JVM动态代理与Cglib动态代理的实现与区别 静态代理&动态代理 设计模式中有一种模式就叫做代理模式,分成动态代理和静态代理 静态代理在编译的时候就将接口,被代理类,代理类等... -
动态生成数组_动态代理——JDK动态代理原理&示例解析(图文并茂)
2021-01-08 18:10:50觉得可以的话点个关注,转个发,加...两者区别:JDK动态代理:需要被代理对象的类实现了某些接口,生成的代理类也会实现相应的接口CGLIB动态代理:不需要被代理对象的类实现了某些接口,生成的代理类为目标对象的类... -
Spring aop 基于JDK动态代理和CGLIB代理的原理以及为什么JDK代理需要基于接口
2019-02-10 20:45:56Spring AOP的实现对于接口来说就是使用的JDK的动态代理来实现的,而对于类的代理使用CGLIB来实现。 JDK动态代理 JDK的动态代理,就是在程序运行的过程中,根据被代理的接口来动态生成代理类的class文件,并加载运行... -
CGLib动态代理的原理和实现
2020-07-25 23:35:31首先我们知道,使用jdk创建动态代理有个局限,就是他只能为接口创建代理实例,对于那些没有实现接口的业务类,就不能使用jdk动态代理了;所以我们就需要使用CGLib动态代理。 CGLib动态代理 CGLib是一个强大高性能的... -
java的几种动态代理实现
2020-03-10 17:03:46文章目录1 基于jdk的动态代理1.1 准备父接口1.2 准备实现类(被代理类)1.3 代理对象生成方法1.4 生成代理对象1.5 控制台输出2 基于cglib的动态代理2.1 准备类(被代理类)2.2 代理对象生成方法2.3 生成代理对象2.4 ... -
JDK动态代理实现原理及手写JDK动态代理
2019-05-20 15:49:54在使用jdk动态代理的时候,必须要实现InvocationHandler接口,invoke方法Invoke 方法中该三个参数分别表示为:代理对象、被代理执行的方法、参数 使用jdk动态代理获取代理类对象(JDK自动生成代理类)$Proxy0.... -
为什么JDK代理不像CGLib代理一样继承目标类就可以动态代理呢?
2020-09-02 16:26:57为什么JDK代理不像CGLib代理一样继承目标类反而去实现其接口呢? 因为JDK代理生成的代理类,默认会继承Proxy 类,由于java是单继承,所以不能继承目标类只能实现其接口 1.首先把JDK动态代理生成的类: 继承了Proxy类... -
转:JDK动态代理为什么必须用接口以及与CGLIB的对比
2019-09-26 03:29:46参考链接:JDK动态代理为什么必须用接口以及与CGLIB的对比 文章中提到:试验了JDK动态代理与CGLIB动态代理。从Spring的AOP框架介绍中得知对于使用接口的类,Spring使用JDK动态代理(原来做项目中试图从Bean强制转换... -
JDK实现动态代理
2019-12-28 20:29:44JDK动态代理实现步骤实例如下:用途 实现步骤 约定好接口(自定义名字),实现这个接口的类(自定义名字)设为Client 代理类 ,一定要实现InvocationHandler接口 调用Proxy.newProxyInstance(Client的类加载器,接口列表,... -
Java动态代理与字节码实现AOP
2020-10-27 20:48:04jdk动态代理机制,在运行期间,为相应的接口动态生成对应的代理对象。将横切关注点封装到动态代理的InvocationHandler中,在系统运行期间,根据横切关注点需要织入的模块位置,将横切逻辑织入到相应的代理类中,以... -
Spring AOP动态代理的实现方式
2019-06-06 23:20:57"JDK"动态代理和"CGLIB"动态代理 ...CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成某个类的子类,注意,CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那... -
Java 使用反射生成JDK动态代理
2018-02-02 18:28:43参考资料 [1]....如果在程序中为一个或多个接口动态地生成实现类,就可以使用Proxy来创建动态代理类;如果需要为一个或多个接口动态地创建实例,也可以使用Proxy来创建动态代理实例。 示例1 Per -
动态代理_JDK自带的动态代理实现
2015-07-22 16:19:00动态代理是指在运行时,动态生成代理。即,代理类的字节码将在运行时生成并载入当前的ClassLoader。与静态代理相比,动态代理有很多...其次,使用一些动态代理的生成方法甚至可以在运行时指定代理类的执行逻辑,从...