向大神求教,Spring 使用 cglib动态代理问题

lookthatgirl 2014-07-02 09:45:31
我要使用spring的AOP做操作日志功能,想代理springmvc的controller

如果使用jdk自带的代理,目标类就必须实现某个接口,对controller不适用。

如果使用cglib,由于我数据访问层使用的是mybatis,自动生成的mapper,没有默认的构造方法,cglib要求代理的目标类要有默认的构造方法,启动就报错了。

请问大神们有碰到这种问题吗,能不能给个解决思路。

<aop:aspectj-autoproxy proxy-target-class="true"/>,加上红色部分,就是使用cglib代理了,否则使用jdk的默认代理。

报错内容如下:
Error creating bean with name 'userMapper': Post-processing of the FactoryBean's object failed; nested exception is org.springframework.aop.framework.AopConfigException: Could not generate CGLIB subclass of class [class com.sun.proxy.$Proxy22]: Common causes of this problem include using a final class or a non-visible class; nested exception is java.lang.IllegalArgumentException: Cannot subclass final class class com.sun.proxy.$Proxy22
...全文
5595 14 打赏 收藏 转发到动态 举报
写回复
用AI写文章
14 条回复
切换为时间正序
请发表友善的回复…
发表回复
_chenh 2016-10-13
  • 打赏
  • 举报
回复
AreaDao.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="www.wanfin.com.pauthority.providers.modules.sys.dao.AreaDao">
    
	<sql id="areaColumns">
		a.id,
		a.parent_id AS "parent.id",
		a.parent_ids,
		a.code,
		a.name,
		a.sort,
		a.type,
		a.remarks,
		a.create_by AS "createBy.id",
		a.create_date,
		a.update_by AS "updateBy.id",
		a.update_date,
		a.del_flag,
		p.name AS "parent.name"
	</sql>
	
	<sql id="areaJoins">
		LEFT JOIN sys_office p ON p.id = a.parent_id
    </sql>
    
	<select id="get" resultType="Area">
		SELECT
			<include refid="areaColumns"/>
		FROM sys_area a
		<include refid="areaJoins"/>
		WHERE a.id = #{id}
	</select>
	
	<select id="findList" resultType="Area">
		SELECT
			<include refid="areaColumns"/>
		FROM sys_area a
		<include refid="areaJoins"/>
		WHERE a.del_flag = #{DEL_FLAG_NORMAL}
		<!-- 数据范围过滤 -->
		${sqlMap.dsf}
		OR a.id = #{currentUser.office.area.id}
		ORDER BY a.code
	</select>
	
	<select id="findAllList" resultType="Area">
		SELECT
			<include refid="areaColumns"/>
		FROM sys_area a
		<include refid="areaJoins"/>
		WHERE a.del_flag = #{DEL_FLAG_NORMAL}
		ORDER BY a.code
	</select>
	
	<select id="findByParentIdsLike" resultType="Area">
		SELECT
			a.id,
			a.parent_id AS "parent.id",
			a.parent_ids
		FROM sys_area a
		WHERE a.del_flag = #{DEL_FLAG_NORMAL} AND a.parent_ids LIKE #{parentIds}
		ORDER BY a.code
	</select>
	
	<insert id="insert">
		INSERT INTO sys_area(
			id, 
			parent_id, 
			parent_ids, 
			code, 
			name, 
			sort,
			type, 
			create_by, 
			create_date, 
			update_by, 
			update_date, 
			remarks, 
			del_flag
		) VALUES (
			#{id}, 
			#{parent.id}, 
			#{parentIds}, 
			#{code}, 
			#{name}, 
			#{sort}, 
			#{type}, 
			#{createBy.id}, 
			#{createDate}, 
			#{updateBy.id}, 
			#{updateDate}, 
			#{remarks}, 
			#{delFlag}
		)
	</insert>
	
	<update id="update">
		UPDATE sys_area SET 
			parent_id = #{parent.id}, 
			parent_ids = #{parentIds}, 
			code = #{code}, 
			name = #{name}, 
			sort = #{sort}, 
			type = #{type}, 
			update_by = #{updateBy.id}, 
			update_date = #{updateDate}, 
			remarks = #{remarks}
		WHERE id = #{id}
	</update>
	
	<update id="updateParentIds">
		UPDATE sys_area SET 
			parent_id = #{parent.id}, 
			parent_ids = #{parentIds}
		WHERE id = #{id}
	</update>
	
	<update id="delete">
		UPDATE sys_area SET 
			del_flag = #{DEL_FLAG_DELETE}
		WHERE id = #{id} OR parent_ids LIKE 
					<if test="dbName == 'oracle'">'%,'||#{id}||',%'</if>
					<if test="dbName == 'mssql'">'%,'+#{id}+',%'</if>
					<if test="dbName == 'mysql'">CONCAT('%,', #{id}, ',%')</if>
	</update>
	
</mapper>
_chenh 2016-10-13
  • 打赏
  • 举报
回复
下面付上配置文件代码:spring-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context" xmlns:jdbc="http://www.springframework.org/schema/jdbc"  
	xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:util="http://www.springframework.org/schema/util" xmlns:aop="http://www.springframework.org/schema/aop" 
    xmlns:task="http://www.springframework.org/schema/task" xsi:schemaLocation="
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
		http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-4.1.xsd
		http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.1.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd  
		http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.1.xsd
		http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.1.xsd"
	default-lazy-init="true">

	<description>Spring Configuration</description>
    <!-- 加载配置属性文件 -->
	<context:property-placeholder ignore-unresolvable="true" location="classpath:jeesite.properties" />
	
	<!-- 加载应用属性实例,可通过  @Value("#{APP_PROP['jdbc.driver']}") String jdbcDriver 方式引用 -->
    <util:properties id="APP_PROP" location="classpath:jeesite.properties" local-override="true"/>
	
	<aop:aspectj-autoproxy/>
	<!-- <aop:aspectj-autoproxy /> -->
	<context:annotation-config />
	
	<!-- 使用Annotation自动注册Bean,解决事物失效问题:在主容器中不扫描@Controller注解,在SpringMvc中只扫描@Controller注解。  -->
	<context:component-scan base-package="www.wanfin.com"><!-- base-package 如果多个,用“,”分隔 -->
		<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
	</context:component-scan>
	
 	<!-- MyBatis begin -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="typeAliasesPackage" value="www.wanfin.com"/>
        <property name="typeAliasesSuperType" value="www.wanfin.com.pauthority.common.persistence.BaseEntity"/>
        <!-- <property name="mapperLocations" value="classpath:/mappings/**/*.xml"/>
		<property name="configLocation" value="classpath:/mybatis-config.xml"></property> -->
        <property name="mapperLocations" value="classpath:/mappings/**/*.xml"/>
		<property name="configLocation" value="classpath:/META-INF/spring/spring-mybatis.xml"></property>
    </bean>
    
    <!-- 扫描basePackage下所有以@MyBatisDao注解的接口 -->
    <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
        <property name="basePackage" value="www.wanfin.com"/>
        <property name="annotationClass" value="www.wanfin.com.pcore.common.persistence.annotation.MyBatisDao"/>
    </bean>
    
    <!-- 定义事务 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource" />
	</bean>
	
	<!-- 配置 Annotation 驱动,扫描@Transactional注解的类定义事务  -->
	<tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>
    <!-- MyBatis end -->
    
	<!-- 配置 JSR303 Bean Validator 定义 -->
	<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />

	<!-- 缓存配置 -->
	<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
		<property name="configLocation" value="classpath:${ehcache.configFile}" />
	</bean>
	
	<!-- 计划任务配置,用 @Service @Lazy(false)标注类,用@Scheduled(cron = "0 0 2 * * ?")标注方法 -->
    <task:executor id="executor" pool-size="10"/> <task:scheduler id="scheduler" pool-size="10"/>
    <task:annotation-driven scheduler="scheduler" executor="executor" proxy-target-class="true"/>
    
	<!-- 数据源配置, 使用 BoneCP 数据库连接池 -->
	<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> 
	    <!-- 数据源驱动类可不写,Druid默认会自动根据URL识别DriverClass -->
	    <property name="driverClassName" value="${jdbc.driver}" />
	    
		<!-- 基本属性 url、user、password -->
		<property name="url" value="${jdbc.url}" />
		<property name="username" value="${jdbc.username}" />
		<property name="password" value="${jdbc.password}" />
		
		<!-- 配置初始化大小、最小、最大 -->
		<property name="initialSize" value="${jdbc.pool.init}" />
		<property name="minIdle" value="${jdbc.pool.minIdle}" /> 
		<property name="maxActive" value="${jdbc.pool.maxActive}" />
		
		<!-- 配置获取连接等待超时的时间 -->
		<property name="maxWait" value="60000" />
		
		<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
		<property name="timeBetweenEvictionRunsMillis" value="60000" />
		
		<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
		<property name="minEvictableIdleTimeMillis" value="300000" />
		
		<property name="validationQuery" value="${jdbc.testSql}" />
		<property name="testWhileIdle" value="true" />
		<property name="testOnBorrow" value="false" />
		<property name="testOnReturn" value="false" />
		
		<!-- 打开PSCache,并且指定每个连接上PSCache的大小(Oracle使用)
		<property name="poolPreparedStatements" value="true" />
		<property name="maxPoolPreparedStatementPerConnectionSize" value="20" /> -->
	    
		<!-- 打开removeAbandoned功能 -->
		<property name="removeAbandoned" value="true" />
		<!-- 1800秒,也就是30分钟 -->
		<property name="removeAbandonedTimeout" value="1800" />
		<!-- 关闭abanded连接时输出错误日志 -->
		<property name="logAbandoned" value="true" />
		
		<!-- 配置监控统计拦截的filters -->
	    <property name="filters" value="stat" /> 
	</bean>
	
	<!-- 数据源配置, 使用应用服务器的数据库连接池 
	<jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/jeesite" />-->

	<!-- 数据源配置, 不使用连接池 
	<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName" value="${jdbc.driver}" />
		<property name="url" value="${jdbc.url}" />
		<property name="username" value="${jdbc.username}"/>
		<property name="password" value="${jdbc.password}"/>
	</bean>-->
</beans>
Area.java
import org.hibernate.validator.constraints.Length;

import www.wanfin.com.pauthority.common.persistence.TreeEntity;

/**
 * 区域Entity
 * @author ThinkGem
 * @version 2013-05-15
 */
public class Area extends TreeEntity<Area>{
	private static final long serialVersionUID = 1L;
	private String code; 	// 区域编码
	private String type; 	// 区域类型(1:国家;2:省份、直辖市;3:地市;4:区县)
	
	public Area(){
		super();
		this.sort = 30;
	}

	public Area(String id){
		super(id);
	}
	
	public Area getParent() {
		return parent;
	}

	public void setParent(Area parent) {
		this.parent = parent;
	}

	@Length(min=1, max=1)
	public String getType() {
		return type;
	}

	public void setType(String type) {
		this.type = type;
	}

	@Length(min=0, max=100)
	public String getCode() {
		return code;
	}

	public void setCode(String code) {
		this.code = code;
	}
	
	@Override
	public String toString() {
		return name;
	}
}
AreaDao.java
import www.wanfin.com.pauthority.common.persistence.TreeDao;
import www.wanfin.com.pcore.common.persistence.annotation.MyBatisDao;
import www.wanfin.com.pauthority.api.modules.sys.entity.Area;
@MyBatisDao
public interface AreaDao extends TreeDao<Area> {
	
}
IAreaService.java
import java.util.List;

import www.wanfin.com.pauthority.api.modules.sys.entity.Area;

/**
 * 区域Service
 * @author ThinkGem
 * @version 2014-05-16
 */
public interface IAreaService{

	public Area get(String id);

	public Area get(Area entity);

	public List<Area> findAll();

	public void save(Area entity);

	public Integer delete(Area entity);
}
AreaServiceImpl .java

@Service("areaService")
@Transactional(readOnly = true)
public class AreaServiceImpl extends TreeService<AreaDao, Area> implements IAreaService{
	@Override
	public Area get(String id) {
		return super.get(id);
	}
	
	public List<Area> findAll(){
		return UserUtils.getAreaList();
	}

	@Transactional(readOnly = false)
	public void save(Area area) {
		super.save(area);
		UserUtils.removeCache(UserUtils.CACHE_AREA_LIST);
	}
	
	@Transactional(readOnly = false)
	public Integer delete(Area area) {
		Integer id = super.delete(area);
		UserUtils.removeCache(UserUtils.CACHE_AREA_LIST);
		return id;
	}
}

_chenh 2016-10-13
  • 打赏
  • 举报
回复
我也遇到了类似的问题!请楼主教我解决一下好么?
丶太阳 2016-05-16
  • 打赏
  • 举报
回复
引用 9 楼 lookthatgirl 的回复:
[quote=引用 8 楼 whos2002110 的回复:] 如果你要代理controller, 在spring mvc的配置文件里面配下aop代理就好了呀
谢谢! 原因是使用了:
<aop:aspectj-autoproxy proxy-target-class="true"/>
配置proxy-target-class="true"则强制使用了CGLIB生成代理,mybatis的mapper没有默认构造方法,会报错:
 Could not generate CGLIB subclass of class [class com.sun.proxy.$Proxy22]: Common causes of this problem include using a final class or a non-visible class; nested exception is java.lang.IllegalArgumentException: Cannot subclass final class class com.sun.proxy.$Proxy22
修改成 <aop:aspectj-autoproxy/> 这个的意思我原本以为是完全不使用cglib的代理,现在发现应该是spring会自动在JDK动态代理和CGLIB之间转换。 谢谢大家的热心帮助,现在问题解决了,多谢大家!@whos2002110,@EggItayi [/quote] 怎么解决的,你倒是说说啊
lookthatgirl 2014-07-02
  • 打赏
  • 举报
回复
引用 3 楼 zaxer 的回复:
proxy-target-class="true" 是强制使用CGLIB生成代理 去掉这个试试呢?
非常感谢你,你说的是对的,我一开始没有完全理解你的意思,我被自己的认知套住了。
lookthatgirl 2014-07-02
  • 打赏
  • 举报
回复
引用 8 楼 whos2002110 的回复:
如果你要代理controller, 在spring mvc的配置文件里面配下aop代理就好了呀
谢谢! 原因是使用了:
<aop:aspectj-autoproxy proxy-target-class="true"/>
配置proxy-target-class="true"则强制使用了CGLIB生成代理,mybatis的mapper没有默认构造方法,会报错:
 Could not generate CGLIB subclass of class [class com.sun.proxy.$Proxy22]: Common causes of this problem include using a final class or a non-visible class; nested exception is java.lang.IllegalArgumentException: Cannot subclass final class class com.sun.proxy.$Proxy22
修改成 <aop:aspectj-autoproxy/> 这个的意思我原本以为是完全不使用cglib的代理,现在发现应该是spring会自动在JDK动态代理和CGLIB之间转换。 谢谢大家的热心帮助,现在问题解决了,多谢大家!@whos2002110,@EggItayi
whos2002110 2014-07-02
  • 打赏
  • 举报
回复
如果你要代理controller, 在spring mvc的配置文件里面配下aop代理就好了呀
whos2002110 2014-07-02
  • 打赏
  • 举报
回复
引用 6 楼 lookthatgirl 的回复:
[quote=引用 5 楼 whos2002110 的回复:] mapper 是什么? controller么? 配置发出来看下
mapper:
public interface UserMapper extends IBaseMapper<UserExample, User, Integer> {
}
IBaseMapper:
package com.szyungu.ecommunity.base.service;

import java.util.List;
import org.apache.ibatis.annotations.Param;

public interface IBaseMapper<C,R,PK> {
	
	int countByCondition(C condition);
	
	int insert(R record);

	int insertSelective(R record);
	
    int updateByPrimaryKeySelective(R record);

	int updateByPrimaryKey(R record);
    
    int updateByConditionSelective(@Param("record") R record, @Param("example") C condition);

    int updateByCondition(@Param("record") R record, @Param("example") C condition);

    int deleteByCondition(C condition);

    int deleteByPrimaryKey(PK id);
    
    R selectByPrimaryKey(PK id);

    List<R> selectByCondition(C condition);   
}
controller: 就是普通的spring mvc的controller [/quote] 那你这个userMapper在代理中是什么角色呢? 就是他跟controller什么关系? 你这错误:Error creating bean with name 'userMapper': Post- 没搞清楚你是要代理什么? 配置发出来看看
lookthatgirl 2014-07-02
  • 打赏
  • 举报
回复
引用 5 楼 whos2002110 的回复:
mapper 是什么? controller么? 配置发出来看下
mapper:
public interface UserMapper extends IBaseMapper<UserExample, User, Integer> {
}
IBaseMapper:
package com.szyungu.ecommunity.base.service;

import java.util.List;
import org.apache.ibatis.annotations.Param;

public interface IBaseMapper<C,R,PK> {
	
	int countByCondition(C condition);
	
	int insert(R record);

	int insertSelective(R record);
	
    int updateByPrimaryKeySelective(R record);

	int updateByPrimaryKey(R record);
    
    int updateByConditionSelective(@Param("record") R record, @Param("example") C condition);

    int updateByCondition(@Param("record") R record, @Param("example") C condition);

    int deleteByCondition(C condition);

    int deleteByPrimaryKey(PK id);
    
    R selectByPrimaryKey(PK id);

    List<R> selectByCondition(C condition);   
}
controller: 就是普通的spring mvc的controller
whos2002110 2014-07-02
  • 打赏
  • 举报
回复
mapper 是什么? controller么? 配置发出来看下
lookthatgirl 2014-07-02
  • 打赏
  • 举报
回复
引用 3 楼 zaxer 的回复:
proxy-target-class="true" 是强制使用CGLIB生成代理 去掉这个试试呢?
去掉这个没有问题,但是不能对controller代理,jdk的默认代理必须要目标类实现接口。
EggItayi 2014-07-02
  • 打赏
  • 举报
回复
proxy-target-class="true" 是强制使用CGLIB生成代理 去掉这个试试呢?
lookthatgirl 2014-07-02
  • 打赏
  • 举报
回复
千万别沉啊,等解决啊
lookthatgirl 2014-07-02
  • 打赏
  • 举报
回复
求大神来看看啊

67,515

社区成员

发帖
与我相关
我的任务
社区描述
J2EE只是Java企业应用。我们需要一个跨J2SE/WEB/EJB的微容器,保护我们的业务核心组件(中间件),以延续它的生命力,而不是依赖J2SE/J2EE版本。
社区管理员
  • Java EE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧