JDBC问题总结 * 数据库连接创建,释放频繁,浪费系统资源 * SQL语句硬编码 * SQL参数设置存在硬编码 * 结果解析存在硬编码
框架设计思路 使⽤端: 提供核⼼配置⽂件:
sqlMapConfig.xml : 存放数据源信息,引⼊mapper.xml
<configuration > <property name ="driverClass" value ="com.mysql.jdbc.Driver" > </property > <property name ="jdbcUrl" value ="jdbc:mysql:///mybatis" > </property > <property name ="user" value ="root" > </property > <property name ="password" value ="root" > </property > <mapper resource ="mapper.xml" > </mapper > </configuration >
Mapper.xml : sql语句的配置⽂件信息
<mapper namespace ="com.ww.mapper.IUserDao" > <select id ="findAll" resultType ="com.ww.pojo.User" > select * form user </select > <select id ="findByCondition" resultType ="com.ww.pojo.User" paramterType ="com.ww.pojo.User" > select * from user where id = #{id} and username = #{username} </select > </mapper >
框架端: 1.读取配置⽂件,封装到实体配置类中
@Data public class Configuration { private DataSource dataSource; private Map<String, MappedStatement> mapppedStatementMap = new HashMap <>(); }
@Data public class MappedStatement { private String id; private String resultType; private String paramterType; private String sql; }
2.解析配置⽂件
InputStream resource = Resources.class.getClassLoader().getResourceAsStream(path);
使用dom4j解析配置封装到Configuration
public Configuration parseConfig (InputStream inputStream) throws Exception{ Document document=new SAXReader ().read(inputStream); Element rootElement=document.getRootElement(); List<Element> list=rootElement.selectNodes("//property" ); Properties properties=new Properties (); for (Element element:list){ String name=element.attributeValue("name" ); String value=element.attributeValue("value" ); properties.setProperty(name,value); } ComboPooledDataSource comboPooledDataSource=new ComboPooledDataSource (); comboPooledDataSource.setDriverClass(properties.getProperty("driverClass" )); comboPooledDataSource.setJdbcUrl(properties.getProperty("jdbcUrl" )); comboPooledDataSource.setUser(properties.getProperty("username" )); comboPooledDataSource.setPassword(properties.getProperty("password" )); configuration.setDataSource(comboPooledDataSource); List<Element> list1=rootElement.selectNodes("//mapper" ); for (Element element:list1){ String mapperPath=element.attributeValue("resource" ); InputStream resource=Resources.getResourceAsStream(mapperPath); XMLMapperBuilder xmlMapperBuilder=new XMLMapperBuilder (configuration); xmlMapperBuilder.parse(resource); } return configuration;
3.通过SqlSessionFactoryBuilder的build方法,创建SqlSessionFactory
public interface SqlSessionFactory { public SqlSession openSession () ; }
4.通过SqlSessionFactory的openSession方法,创建SqlSession
5.在sqlSession中封装增删改查方法
public interface SqlSession { public <E> List<E> selectList (String statementId, Object... params) throws Exception; public <T> T selectOne (String statementId, Object... params) throws Exception; public <T> T getMapper (Class<?> mapperClass) ; }
public <T> T getMapper (Class<?> mapperClass) { Object proxyInstance=Proxy.newProxyInstance(DefaultSqlSession.class.getClassLoader(),new Class []{mapperClass},new InvocationHandler (){ @Override public Object invoke (Object proxy,Method method,Object[]args) throws Throwable{ String methodName=method.getName(); String className=method.getDeclaringClass().getName(); String statementId=className+"." +methodName; Type genericReturnType=method.getGenericReturnType(); if (genericReturnType instanceof ParameterizedType){ List<Object> objects=selectList(statementId,args); return objects; } return selectOne(statementId,args); } }); return (T)proxyInstance; }
6.定义Executor实现JDBC原生操作,以及出入参映射操作
public class SimpleExecutor implements Executor { @Override public <E> List<E> query (Configuration configuration, MappedStatement mappedStatement, Object... params) throws Exception { Connection connection = configuration.getDataSource().getConnection(); String sql = mappedStatement.getSql(); BoundSql boundSql = getBound(sql); PreparedStatement preparedStatement = connection.prepareStatement(boundSql.getSqlText()); String paramterType = mappedStatement.getParamterType(); Class<?> paramterTypeClass = getClassType(paramterType); List<ParameterMapping> parameterMappingList = boundSql.getParameterMappingList(); for (int i = 0 ; i < parameterMappingList.size(); i++) { ParameterMapping parameterMapping = parameterMappingList.get(i); String content = parameterMapping.getContent(); Field declaredField = paramterTypeClass.getDeclaredField(content); declaredField.setAccessible(true ); Object o = declaredField.get(params[0 ]); preparedStatement.setObject(i + 1 , o); } ResultSet resultSet = preparedStatement.executeQuery(); String resultType = mappedStatement.getResultType(); Class<?> resultTypeClass = getClassType(resultType); List<Object> list = new ArrayList <>(); while (resultSet.next()) { Object o = resultTypeClass.newInstance(); ResultSetMetaData metaData = resultSet.getMetaData(); for (int i = 1 ; i <= metaData.getColumnCount(); i++) { String columnName = metaData.getColumnName(i); Object value = resultSet.getObject(columnName); PropertyDescriptor propertyDescriptor = new PropertyDescriptor (columnName, resultTypeClass); Method writeMethod = propertyDescriptor.getWriteMethod(); writeMethod.invoke(o, value); } list.add(o); } return (List<E>) list; } private Class<?> getClassType(String paramterType) throws ClassNotFoundException { if (null != paramterType) { Class<?> aClass = Class.forName(paramterType); return aClass; } return null ; } private BoundSql getBound (String sql) { ParameterMappingTokenHandler parameterMappingTokenHandler = new ParameterMappingTokenHandler (); GenericTokenParser genericTokenParser = new GenericTokenParser ("#{" , "}" , parameterMappingTokenHandler); String parseSql = genericTokenParser.parse(sql); List<ParameterMapping> parameterMappings = parameterMappingTokenHandler.getParameterMappings(); BoundSql boundSql = new BoundSql (parseSql, parameterMappings); return boundSql; } }
具体实现
https://github.com/wangwangit/mybatis-study.git
重点剖析 架构设计
主要组件
组件
描述
SqlSession
表示与数据库交互的会话,完成必要数据库增删改查功能
Executor
MyBatis的执行器,负责SQL语句的生成和查询缓存的维护
StatementHandler
封装了JDBC Statement操作,负责对JDBC Statement操作,如设置参数
ParameterHandler
参数处理
ResultSetHandler
结果集处理
TypeHandler
java类型与jdbc类型的转换
MappedStatement
维护mapper.xml中 一条select,update,insert,delete节点的封装
SqlSource
根据用户传递的parameterObject动态的生成SQL并封装到BoundSql中
BoundSql
表示动态生成的SQL语句,以及相应的参数信息
一.缓存使用 1.二级缓存开启三步走
<settings > <setting name ="cacheEnabled" value ="true" /> </settings >
在需要使用二级缓存的mapper文件中添加配置标签
<select id ="findById" resultType ="com.lagou.pojo.User" useCache ="true" > select * from user where id = #{id} </select >
2.config中的配置解析,将配置设置到configuration中
private void parseConfiguration (XNode root) { try { propertiesElement(root.evalNode("properties" )); Properties settings=settingsAsProperties(root.evalNode("settings" )); loadCustomVfs(settings); loadCustomLogImpl(settings); typeAliasesElement(root.evalNode("typeAliases" )); pluginElement(root.evalNode("plugins" )); objectFactoryElement(root.evalNode("objectFactory" )); objectWrapperFactoryElement(root.evalNode("objectWrapperFactory" )); reflectorFactoryElement(root.evalNode("reflectorFactory" )); settingsElement(settings); environmentsElement(root.evalNode("environments" )); databaseIdProviderElement(root.evalNode("databaseIdProvider" )); typeHandlerElement(root.evalNode("typeHandlers" )); mapperElement(root.evalNode("mappers" )); }catch (Exception e){ throw new BuilderException ("Error parsing SQL Mapper Configuration. Cause: " +e,e); } }
3.mapper中的cache标签解析,以nameSpace为key存储cache到configuration中
private void cacheElement (XNode context) { if (context!=null ){ String type=context.getStringAttribute("type" ,"PERPETUAL" ); Class<?extends Cache > typeClass=typeAliasRegistry.resolveAlias(type); String eviction=context.getStringAttribute("eviction" ,"LRU" ); Class<?extends Cache > evictionClass=typeAliasRegistry.resolveAlias(eviction); Long flushInterval=context.getLongAttribute("flushInterval" ); Integer size=context.getIntAttribute("size" ); boolean readWrite=!context.getBooleanAttribute("readOnly" ,false ); boolean blocking=context.getBooleanAttribute("blocking" ,false ); Properties props=context.getChildrenAsProperties(); builderAssistant.useNewCache(typeClass,evictionClass,flushInterval,size,readWrite,blocking,props); } } public Cache useNewCache (Class<?extends Cache> typeClass, Class<?extends Cache> evictionClass, Long flushInterval, Integer size, boolean readWrite, boolean blocking, Properties props) { Cache cache=new CacheBuilder (currentNamespace) .implementation(valueOrDefault(typeClass,PerpetualCache.class)) .addDecorator(valueOrDefault(evictionClass,LruCache.class)) .clearInterval(flushInterval) .size(size) .readWrite(readWrite) .blocking(blocking) .properties(props) .build(); configuration.addCache(cache); currentCache=cache; return cache; }
4.每个sql语句中的userCache配置,在buildStatementFromContext时设置给每个MappedStatement对象
public MappedStatement addMappedStatement ( String id, SqlSource sqlSource, StatementType statementType, SqlCommandType sqlCommandType, Integer fetchSize, Integer timeout, String parameterMap, Class<?> parameterType, String resultMap, Class<?> resultType, ResultSetType resultSetType, boolean flushCache, boolean useCache, boolean resultOrdered, KeyGenerator keyGenerator, String keyProperty, String keyColumn, String databaseId, LanguageDriver lang, String resultSets) { if (unresolvedCacheRef){ throw new IncompleteElementException ("Cache-ref not yet resolved" ); } id=applyCurrentNamespace(id,false ); boolean isSelect=sqlCommandType==SqlCommandType.SELECT; MappedStatement.Builder statementBuilder=new MappedStatement .Builder(configuration,id,sqlSource,sqlCommandType) .resource(resource) .fetchSize(fetchSize) .timeout(timeout) .statementType(statementType) .keyGenerator(keyGenerator) .keyProperty(keyProperty) .keyColumn(keyColumn) .databaseId(databaseId) .lang(lang) .resultOrdered(resultOrdered) .resultSets(resultSets) .resultMaps(getStatementResultMaps(resultMap,resultType,id)) .resultSetType(resultSetType) .flushCacheRequired(valueOrDefault(flushCache,!isSelect)) .useCache(valueOrDefault(useCache,isSelect)) .cache(currentCache); ParameterMap statementParameterMap=getStatementParameterMap(parameterMap,parameterType,id); if (statementParameterMap!=null ){ statementBuilder.parameterMap(statementParameterMap); } MappedStatement statement=statementBuilder.build(); configuration.addMappedStatement(statement); return statement; }
5.执行查询语句时,逻辑如下
public <E> List<E> query (MappedStatement ms,Object parameterObject,RowBounds rowBounds,ResultHandler resultHandler) throws SQLException{ BoundSql boundSql=ms.getBoundSql(parameterObject); CacheKey key=this .createCacheKey(ms,parameterObject,rowBounds,boundSql); return this .query(ms,parameterObject,rowBounds,resultHandler,key,boundSql); } public <E> List<E> query (MappedStatement ms,Object parameterObject,RowBounds rowBounds,ResultHandler resultHandler,CacheKey key,BoundSql boundSql) throws SQLException{ Cache cache=ms.getCache(); if (cache!=null ){ this .flushCacheIfRequired(ms); if (ms.isUseCache()&&resultHandler==null ){ this .ensureNoOutParams(ms,boundSql); List<E> list=(List)this .tcm.getObject(cache,key); if (list==null ){ list=this .delegate.query(ms,parameterObject,rowBounds,resultHandler,key,boundSql); this .tcm.putObject(cache,key,list); } return list; } } return this .delegate.query(ms,parameterObject,rowBounds,resultHandler,key,boundSql); }
6.二级缓存安全问题,以及为什么需要提交才能更新缓存值
public class TransactionalCacheManager { private final Map<Cache, TransactionalCache> transactionalCaches = new HashMap <>(); public void clear (Cache cache) { getTransactionalCache(cache).clear(); } public Object getObject (Cache cache, CacheKey key) { return getTransactionalCache(cache).getObject(key); } public void putObject (Cache cache, CacheKey key, Object value) { getTransactionalCache(cache).putObject(key, value); } public void commit () { for (TransactionalCache txCache : transactionalCaches.values()) { txCache.commit(); } } public void rollback () { for (TransactionalCache txCache : transactionalCaches.values()) { txCache.rollback(); } } private TransactionalCache getTransactionalCache (Cache cache) { return MapUtil.computeIfAbsent(transactionalCaches, cache, TransactionalCache::new ); } } public class TransactionalCache implements Cache { private static final Log log = LogFactory.getLog(TransactionalCache.class); private final Cache delegate; private boolean clearOnCommit; private final Map<Object, Object> entriesToAddOnCommit; private final Set<Object> entriesMissedInCache; public TransactionalCache (Cache delegate) { this .delegate = delegate; this .clearOnCommit = false ; this .entriesToAddOnCommit = new HashMap <>(); this .entriesMissedInCache = new HashSet <>(); } @Override public String getId () { return delegate.getId(); } @Override public int getSize () { return delegate.getSize(); } @Override public Object getObject (Object key) { Object object = delegate.getObject(key); if (object == null ) { entriesMissedInCache.add(key); } if (clearOnCommit) { return null ; } else { return object; } } @Override public void putObject (Object key, Object object) { entriesToAddOnCommit.put(key, object); } @Override public Object removeObject (Object key) { return null ; } @Override public void clear () { clearOnCommit = true ; entriesToAddOnCommit.clear(); } public void commit () { if (clearOnCommit) { delegate.clear(); } flushPendingEntries(); reset(); } public void rollback () { unlockMissedEntries(); reset(); } private void reset () { clearOnCommit = false ; entriesToAddOnCommit.clear(); entriesMissedInCache.clear(); } private void flushPendingEntries () { for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) { delegate.putObject(entry.getKey(), entry.getValue()); } for (Object entry : entriesMissedInCache) { if (!entriesToAddOnCommit.containsKey(entry)) { delegate.putObject(entry, null ); } } } private void unlockMissedEntries () { for (Object entry : entriesMissedInCache) { try { delegate.removeObject(entry); } catch (Exception e) { log.warn("Unexpected exception while notifying a rollback to the cache adapter. " + "Consider upgrading your cache adapter to the latest version. Cause: " + e); } } } }
二.插件开发 1.MyBatis允许拦截的方法
执⾏器Executor (update、query、commit、rollback等⽅法);
SQL语法构建器StatementHandler (prepare、parameterize、batch、updates query等⽅ 法);
参数处理器ParameterHandler (getParameterObject、setParameters⽅法);
结果集处理器ResultSetHandler (handleResultSets、handleOutputParameters等⽅法);
2.这四大对象创建时,都是调用interceptorChain.pluginAll(parameterHandler)返回的代理对象
public ParameterHandler newParameterHandler (MappedStatement mappedStatement,Object parameterObject,BoundSql boundSql) { ParameterHandler parameterHandler=mappedStatement.getLang().createParameterHandler(mappedStatement,parameterObject,boundSql); parameterHandler=(ParameterHandler)interceptorChain.pluginAll(parameterHandler); return parameterHandler; }
public Object pluginAll (Object target) { for (Interceptor interceptor:interceptors){ target=interceptor.plugin(target); } return target; } default Object plugin (Object target) { return Plugin.wrap(target,this ); } public static Object wrap (Object target,Interceptor interceptor) { Map<Class<?>,Set<Method>>signatureMap=getSignatureMap(interceptor); Class<?> type=target.getClass(); Class<?>[]interfaces=getAllInterfaces(type,signatureMap); if (interfaces.length>0 ){ return Proxy.newProxyInstance( type.getClassLoader(), interfaces, new Plugin (target,interceptor,signatureMap)); } return target; }
3.因此调用这四大对象的方法时,会执行Plugin类中的invoke()方法逻辑
@Override public Object invoke (Object proxy,Method method,Object[]args) throws Throwable{ try { Set<Method> methods=signatureMap.get(method.getDeclaringClass()); if (methods!=null &&methods.contains(method)){ return interceptor.intercept(new Invocation (target,method,args)); } return method.invoke(target,args); }catch (Exception e){ throw ExceptionUtil.unwrapThrowable(e); } }
4.上述处理逻辑中当方法匹配时,调用了intercept方法,所以会执行拦截器中的方法进行增强
@Intercepts({ @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}) }) public class ExamplePlugin implements Interceptor { @Override public Object intercept (Invocation invocation) throws Throwable { System.out.println("对方法进行了增强" ); return invocation.proceed(); } @Override public Object plugin (Object target) { return Plugin.wrap(target, this ); } @Override public void setProperties (Properties properties) { System.out.println("获取的配置文件参数是" + properties); Interceptor.super .setProperties(properties); } }
三.动态代理 userMapper=sqlSession.getMapper(IUserMapper.class); public <T> T getMapper (Class<T> type) { return this .configuration.getMapper(type,this ); } public <T> T getMapper (Class<T> type,SqlSession sqlSession) { return mapperRegistry.getMapper(type,sqlSession); } 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); } } public T newInstance (SqlSession sqlSession) {final MapperProxy<T> mapperProxy=new MapperProxy <>(sqlSession,mapperInterface,methodCache); return newInstance(mapperProxy); } protected T newInstance (MapperProxy<T> mapperProxy) { return (T)Proxy.newProxyInstance(mapperInterface.getClassLoader(),new Class []{mapperInterface},mapperProxy); }
public class MapperProxy <T> implements InvocationHandler , Serializable { 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 { 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); } public Object execute (SqlSession sqlSession, Object[] args) { Object result; switch (command.getType()) { case INSERT: { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.insert(command.getName(), param)); break ; } case UPDATE: { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.update(command.getName(), param)); break ; } case DELETE: { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.delete(command.getName(), param)); break ; } case SELECT: 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 if (method.returnsCursor()) { result = executeForCursor(sqlSession, args); } else { Object param = method.convertArgsToSqlCommandParam(args); result = sqlSession.selectOne(command.getName(), param); if (method.returnsOptional() && (result == null || !method.getReturnType().equals(result.getClass()))) { result = Optional.ofNullable(result); } } break ; case FLUSH: result = sqlSession.flushStatements(); break ; default : 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; } }
四.延迟加载 1.开启全局延迟加载配置
<settings > <setting name ="lazyLoadingEnabled" value ="true" /> </settings >
2.配置局部延迟加载
<resultMap id ="userMap" type ="user" > <id column ="id" property ="id" > </id > <result column ="username" property ="username" > </result > <result column ="password" property ="password" > </result > 全局延迟加载 在Mybatis的核⼼配置⽂件中可以使⽤setting标签修改全局的加载策略。 注意 7.。 <result column ="birthday" property ="birthday" > </result > <collection property ="orderList" ofType ="order" column ="id" select ="com.lagou.dao.OrderMapper.findByUid" fetchType ="lazy" > </collection > </resultMap > <select id ="findAll" resultMap ="userMap" > SELECT * FROM `user` </select >
3.Configuration中的配置
public class Configuration { protected boolean aggressiveLazyLoading; protected Set<String> lazyLoadTriggerMethods = new HashSet <String> (Arrays.asList(new String []{"equals" , "clone" , "hashCode" , "toString" })); protected boolean lazyLoadingEnabled = false ; public void setProxyFactory (ProxyFactory proxyFactory) { if (proxyFactory == null ) { proxyFactory = new JavassistProxyFactory (); } this .proxyFactory = proxyFactory; } }
4.查看ResultSetHandler如何实现延迟加载
private Object createResultObject (ResultSetWrapper rsw,ResultMap resultMap,ResultLoaderMap lazyLoader,String columnPrefix) throws SQLException{ this .useConstructorMappings=false ; final List<Class<?>>constructorArgTypes=new ArrayList <>();final List<Object> constructorArgs=new ArrayList <>(); Object resultObject=createResultObject(rsw,resultMap,constructorArgTypes,constructorArgs,columnPrefix); if (resultObject!=null &&!hasTypeHandlerForResultObject(rsw,resultMap.getType())){ final List<ResultMapping> propertyMappings=resultMap.getPropertyResultMappings(); for (ResultMapping propertyMapping:propertyMappings){ if (propertyMapping.getNestedQueryId()!=null &&propertyMapping.isLazy()){ resultObject=configuration.getProxyFactory().createProxy(resultObject,lazyLoader,configuration,objectFactory,constructorArgTypes,constructorArgs); break ; } } } this .useConstructorMappings=resultObject!=null &&!constructorArgTypes.isEmpty(); return resultObject; }
5.代理对象执行逻辑
private static class EnhancedResultObjectProxyImpl implements MethodHandler { private final Class<?> type; private final ResultLoaderMap lazyLoader; private final boolean aggressive; private final Set<String> lazyLoadTriggerMethods; private final ObjectFactory objectFactory; private final List<Class<?>> constructorArgTypes; private final List<Object> constructorArgs; private EnhancedResultObjectProxyImpl (Class<?> type, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) { this .type = type; this .lazyLoader = lazyLoader; this .aggressive = configuration.isAggressiveLazyLoading(); this .lazyLoadTriggerMethods = configuration.getLazyLoadTriggerMethods(); this .objectFactory = objectFactory; this .constructorArgTypes = constructorArgTypes; this .constructorArgs = constructorArgs; } public static Object createProxy (Object target, ResultLoaderMap lazyLoader, Configuration configuration, ObjectFactory objectFactory, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) { final Class<?> type = target.getClass(); EnhancedResultObjectProxyImpl callback = new EnhancedResultObjectProxyImpl (type, lazyLoader, configuration, objectFactory, constructorArgTypes, constructorArgs); Object enhanced = crateProxy(type, callback, constructorArgTypes, constructorArgs); PropertyCopier.copyBeanProperties(type, target, enhanced); return enhanced; } @Override public Object invoke (Object enhanced, Method method, Method methodProxy, Object[] args) throws Throwable { final String methodName = method.getName(); try { synchronized (lazyLoader) { if (WRITE_REPLACE_METHOD.equals(methodName)) { Object original; if (constructorArgTypes.isEmpty()) { original = objectFactory.create(type); } else { original = objectFactory.create(type, constructorArgTypes, constructorArgs); } PropertyCopier.copyBeanProperties(type, enhanced, original); if (lazyLoader.size() > 0 ) { return new JavassistSerialStateHolder (original, lazyLoader.getProperties(), objectFactory, constructorArgTypes, constructorArgs); } else { return original; } } else { if (lazyLoader.size() > 0 && !FINALIZE_METHOD.equals(methodName)) { if (aggressive || lazyLoadTriggerMethods.contains(methodName)) { lazyLoader.loadAll(); } else if (PropertyNamer.isSetter(methodName)) { final String property = PropertyNamer.methodToProperty(methodName); lazyLoader.remove(property); } else if (PropertyNamer.isGetter(methodName)) { final String property = PropertyNamer.methodToProperty(methodName); if (lazyLoader.hasLoader(property)) { lazyLoader.load(property); } } } } } return methodProxy.invoke(enhanced, args); } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } } }