Spring—SSJ集成&声明式事务管理

1.   课程介绍

  • 1.  SSJ集成;(掌握)
  • 2.  声明式事务管理;(掌握)
  1. 什么是三大框架

2.1.  ssh

Struts/Struts2 Spring Hibernate

2.2.  ssm

SpringMVC Spring MyBatis

2.3.  ssj

Struts2 Spring JPA

SpringMVC Spring JPA

SpringMVC Spring Data JPA

  1. Spring集成JPA;(掌握)

Spring4 + Struts2 + jpa/hibernate4

建议:先完成Spring与jpa的集成

3.1.  创建动态web工程:

注意修改classpath路径,创建web文件取名为webapp

3.2.  拷贝Spring共16的jar文件

11个spring的jar文件

spring-framework-3.2.0.RELEASE-dependencies搜索dbcp,pool,aop,wea,logging

 

3.3.  拷贝JPA共11的jar文件

hibernate-release-4.3.8.Finallibjpa*.jar

hibernate-release-4.3.8.Finallib equired*.jar

 

3.4.  拷贝数据库驱动1个jar文件

Spring+jpa+驱动共28个jar文件

3.5.  写一个domain对象,配置JPA映射

@Entity

@Table(name = "t_product")

public class Product {

         @Id

         @GeneratedValue

         private Long id;

         private String name;

3.6.  Spring的配置文件

3.6.1.   Bean对象注入的顺序

jdbc.properties->dataSource->entityManagerFactory->dao->service->junit->action

3.6.2.   加载jdbc.properties

jdbc.driverClassName=com.mysql.jdbc.Driver

jdbc.url=jdbc:mysql:///ssj

jdbc.username=root

jdbc.password=root

 

<?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"

         xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans.xsd

http://www.springframework.org/schema/context

http://www.springframework.org/schema/context/spring-context.xsd">

 

         <!-- 加载jdbc.properties -->

         <context:property-placeholder location="jdbc.properties" />

3.6.3.   配置连接池dataSource

<!-- 配置连接池dataSource -->

<!-- destroy-method="close当前bean销毁的时候,会先调用close方法,关闭连接" -->

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">

         <!-- 依赖注入连接池需要的属性 -->

         <!-- property name="是BasicDataSource的set方法,本质属性" -->

         <!-- property value="是jdbc.properties配置文件的key" -->

         <property name="driverClassName" value="${jdbc.driverClassName}" />

         <property name="url" value="${jdbc.url}" />

         <property name="username" value="${jdbc.username}" />

         <property name="password" value="${jdbc.password}" />

</bean>

3.7.  配置entityManagerFactory

3.7.1.   Spring-Reference_2.5_zh_CN.chm

 

12.6.1.3. LocalContainerEntityManagerFactoryBean

 

3.7.2.   配置信息

<!-- org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter引入默认entityManagerFactory名称 -->

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">

         <!-- 1.注入DataSource -->

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

         <!-- 2.从哪个包去扫描@Entity,domain包 -->

         <!-- public void setPackagesToScan(String... packagesToScan) { -->

         <property name="packagesToScan" value="cn.itsource.ssj.domain" />

         <!-- 3.配置JPA的实现 -->

         <!-- private JpaVendorAdapter jpaVendorAdapter; -->

         <property name="jpaVendorAdapter">

                  <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">

                           <!-- org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter -->

                           <!-- private boolean showSql = false;是否显示sql语句 -->

                           <property name="showSql" value="true" />

                           <!-- private boolean generateDdl = false;是否建表 -->

                           <property name="generateDdl" value="true" />

                           <!-- private String databasePlatform;原来方言 -->

                           <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" />

                  </bean>

         </property>

</bean>

3.8.  IProductDao

public interface IProductDao {

         void save(Product product);

 

         void update(Product product);

 

         void delete(Long id);

 

         Product get(Long id);

 

         List<Product> getAll();

}

3.9.  ProductDaoImpl

@Repository

public class ProductDaoImpl implements IProductDao {

 

         // @Autowired 不能使用这个注入

         @PersistenceContext // 持久层上下文管理器

         private EntityManager entityManager;

 

         @Override

         public void save(Product product) {

                  entityManager.persist(product);

         }

 

         @Override

         public void update(Product product) {

                  entityManager.merge(product);

         }

 

         @Override

         public void delete(Long id) {

                  Product product = get(id);

                  if (product != null) {

                           entityManager.remove(product);

                  }

         }

 

         @Override

         public Product get(Long id) {

                  return entityManager.find(Product.class, id);

         }

 

         @Override

         public List<Product> getAll() {

                  String jpql = "select o from Product o";

                  return entityManager.createQuery(jpql).getResultList();

         }

 

}

3.10.         组件扫描

<!-- 扫描dao、service、action组件 -->

<!-- 可以处理@Repository, @Service, and @Controller,@Autowired,@PersistenceContext 注解-->

<context:component-scan base-package="cn.itsource.ssj" />

3.11.         IProductService

public interface IProductService {

         void save(Product product);

 

         void update(Product product);

 

         void delete(Long id);

 

         Product get(Long id);

 

         List<Product> getAll();

}

3.12.         ProductServiceImpl

@Service

public class ProductServiceImpl implements IProductService {

         @Autowired

         private IProductDao productDao;

 

         @Override

         public void save(Product product) {

                  productDao.save(product);

         }

 

         @Override

         public void update(Product product) {

                  productDao.update(product);

         }

 

         @Override

         public void delete(Long id) {

                  productDao.delete(id);

         }

 

         @Override

         public Product get(Long id) {

                  return productDao.get(id);

         }

 

         @Override

         public List<Product> getAll() {

                  return productDao.getAll();

         }

 

}

3.13.         声明式事务管理(注解版本)

在spring的配置文件添加一点事务配置,并且在service层类上面添加一些注解,就可以实现事务管理

3.13.1.        添加一个tx命名空间

<?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:tx="http://www.springframework.org/schema/tx"

         xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans.xsd

http://www.springframework.org/schema/context

http://www.springframework.org/schema/context/spring-context.xsd

http://www.springframework.org/schema/tx

http://www.springframework.org/schema/tx/spring-tx.xsd">

3.13.2.        添加事务配置

<!-- 配置事务管理器 -->

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">

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

</bean>

<!-- 开启注解事务管理 ,解析@Transactional事务注解 -->

<!-- transaction-manager="transactionManager"默认找bean.id=transactionManager事务管理器 -->

<tx:annotation-driven />

3.13.3.        ProductServiceImpl

@Service

// 默认事务配置

// @Transactional

// 上面配置等价于下面配置

@Transactional(propagation = Propagation.REQUIRED)

public class ProductServiceImpl implements IProductService {

         @Autowired

         private IProductDao productDao;

 

         @Override

         public void save(Product product) {

                  productDao.save(product);

         }

 

         @Override

         public void update(Product product) {

                  productDao.update(product);

         }

 

         @Override

         public void delete(Long id) {

                  productDao.delete(id);

         }

 

         @Override

         @Transactional(readOnly=true,propagation=Propagation.SUPPORTS)

         public Product get(Long id) {

                  return productDao.get(id);

         }

 

         @Override

         @Transactional(readOnly = true, propagation = Propagation.SUPPORTS)

         public List<Product> getAll() {

                  return productDao.getAll();

         }

 

}

3.14.         Junit

@Autowired

IProductService productService;

 

@Test

public void save() throws Exception {

         System.out.println("代理类:" + productService.getClass());

         Product product = new Product();

         product.setName("苹果的弟弟");

         productService.save(product);

}

代理类:class com.sun.proxy.$Proxy23

  1. Spring集成Struts2

4.1.  添加struts2的12个jar文件

没有导入javassist-3.11.0.GA.jar

 

4.2.  配置web.xml

<!-- 添加struts核心过滤器 -->

<filter>

         <filter-name>struts2</filter-name>

         <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>

</filter>

 

<filter-mapping>

         <filter-name>struts2</filter-name>

         <url-pattern>/*</url-pattern>

</filter-mapping>

4.3.  配置struts.xml

<package name="default" namespace="/" extends="struts-default">

 

         <!-- action代表一个控制器, name="对应的访问的url地址" method="返回action的方法" -->

         <!-- spring集成struts之后,class可以写全限定类名,也可以写bean.id -->

         <!-- product 没有写_的时候,默认就是访问当前action的execute -->

         <!-- product_*写了_input,访问input方法 ,对应后面method={1} -->

 

         <action name="product_*" class="cn.itsource.ssj.action.ProductAction" method="{1}">

                  <!-- 显示列表页面 -->

                  <result>/WEB-INF/views/product.jsp</result>

                  <!-- 显示添加获取修改页面 -->

                  <result name="input">/WEB-INF/views/product_input.jsp</result>

                  <!-- 保存或者删除之后进行重定向 -->

                  <result name="reload" type="redirectAction">product</result>

         </action>

 

</package>

4.4.  ProductAction

public class ProductAction extends ActionSupport {

 

         @Autowired

         IProductService productService;

 

         @Override

         public String execute() throws Exception {

                  ActionContext.getContext().put("products", productService.getAll());

                  return SUCCESS;

         }

}

4.5.  配置好,启动tomcat,抛出一下异常

4.5.1.   具体异常信息

严重: Exception starting filter struts2

Class: com.opensymphony.xwork2.spring.SpringObjectFactory

File: SpringObjectFactory.java

Method: getClassInstance

Line: 245 - com/opensymphony/xwork2/spring/SpringObjectFactory.java:245:-1

         at org.apache.struts2.dispatcher.Dispatcher.init(Dispatcher.java:493)

         at org.apache.struts2.dispatcher.ng.InitOperations.initDispatcher(InitOperations.java:74)

         at org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter.init(StrutsPrepareAndExecuteFilter.java:57)

         at org.apache.catalina.core.ApplicationFilterConfig.initFilter(ApplicationFilterConfig.java:279)

         at org.apache.catalina.core.ApplicationFilterConfig.getFilter(ApplicationFilterConfig.java:260)

         at org.apache.catalina.core.ApplicationFilterConfig.<init>(ApplicationFilterConfig.java:105)

         at org.apache.catalina.core.StandardContext.filterStart(StandardContext.java:4700)

         at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5340)

         at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:145)

         at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1408)

         at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1398)

         at java.util.concurrent.FutureTask.run(FutureTask.java:266)

         at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)

         at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)

         at java.lang.Thread.run(Thread.java:745)

Caused by: java.lang.NullPointerException

         at com.opensymphony.xwork2.spring.SpringObjectFactory.getClassInstance(SpringObjectFactory.java:245)

         at com.opensymphony.xwork2.config.providers.XmlConfigurationProvider.verifyResultType(XmlConfigurationProvider.java:608)

         at com.opensymphony.xwork2.config.providers.XmlConfigurationProvider.addResultTypes(XmlConfigurationProvider.java:578)

         at com.opensymphony.xwork2.config.providers.XmlConfigurationProvider.addPackage(XmlConfigurationProvider.java:534)

         at com.opensymphony.xwork2.config.providers.XmlConfigurationProvider.loadPackages(XmlConfigurationProvider.java:295)

         at org.apache.struts2.config.StrutsXmlConfigurationProvider.loadPackages(StrutsXmlConfigurationProvider.java:112)

         at com.opensymphony.xwork2.config.impl.DefaultConfiguration.reloadContainer(DefaultConfiguration.java:264)

         at com.opensymphony.xwork2.config.ConfigurationManager.getConfiguration(ConfigurationManager.java:67)

         at org.apache.struts2.dispatcher.Dispatcher.getContainer(Dispatcher.java:967)

         at org.apache.struts2.dispatcher.Dispatcher.init_PreloadConfiguration(Dispatcher.java:435)

         at org.apache.struts2.dispatcher.Dispatcher.init(Dispatcher.java:479)

         ... 14 more

4.5.2.   为什么会出现异常?

因为原来struts2的对象都是由xwork的对象处理,现在添加struts2-spring-plugin-2.3.20.jar文件,之后由spring接管了对原来struts2对象的创建

原因是在struts2-spring-plugin-2.3.20.jar文件里面有这个配置struts-plugin.xml

<struts>

    <bean type="com.opensymphony.xwork2.ObjectFactory" name="spring" class="org.apache.struts2.spring.StrutsSpringObjectFactory" />

   

    <!--  Make the Spring object factory the automatic default -->

    <constant name="struts.objectFactory" value="spring" />

 

    <constant name="struts.class.reloading.watchList" value="" />

    <constant name="struts.class.reloading.acceptClasses" value="" />

    <constant name="struts.class.reloading.reloadConfig" value="false" />

 

    <package name="spring-default">

        <interceptors>

            <interceptor name="autowiring" class="com.opensymphony.xwork2.spring.interceptor.ActionAutowiringInterceptor"/>

        </interceptors>

    </package>   

</struts>

而spring容器并没有被启动

4.5.3.   在web.xml添加一个监听器,来实例化spring容器

<!-- 添加一个监听器,来实例化spring容器 -->

<listener>

         <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>

</listener>

4.5.4.   再次启动tomcat,又抛出异常

Caused by: java.io.FileNotFoundException: Could not open ServletContext resource [/WEB-INF/applicationContext.xml]

         at org.springframework.web.context.support.ServletContextResource.getInputStream(ServletContextResource.java:141)

         at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:330)

         ... 21 more

默认会去WEB-INF下面找配置文件,而此路径没有

4.5.5.   在web.xml添加一个上下文的初始化参数,来告诉spring从哪里加载配置文件

<!-- 添加一个上下文的初始化参数 -->

<context-param>

         <param-name>contextConfigLocation</param-name>

         <param-value>classpath:applicationContext.xml</param-value>

</context-param>

4.5.6.   怎样快速找contextConfigLocation名字

找监听器ContextLoaderListener的父类ContextLoader

有一个常量配置

public static final String CONFIG_LOCATION_PARAM = "contextConfigLocation";

4.5.7.   不能加载jdbc.properties异常

Caused by: java.io.FileNotFoundException: Could not open ServletContext resource [/jdbc.properties]

4.5.8.   修改spring的配置文件

<!-- 加载jdbc.properties -->

<!-- 跑web必须在前面添加classpath:前缀 -->

<context:property-placeholder location="classpath:jdbc.properties" />

4.5.9.   再次启动tomcat,没有异常

4.5.10.        访问http://localhost/product,出现404异常,

因为没有写jsp页面,此jsp页面和原来写JPA集成Struts2的jsp是一致的。

 

4.5.11.        product.jsp

<body>

         <table border="1">

                  <tr>

                           <th>编号</th>

                           <th>产品名称</th>

<!--                   <th>产品类型名称</th> -->

                           <th>操作</th>

                  </tr>

                  <s:iterator value="#products">

                           <tr>

                                    <td>${id}</td>

                                    <td>${name}</td>

<%--                                     <td>${dir.name}</td> --%>

                                    <td><a href="product_input?product.id=${id}">修改</a> <a

                                             href="product_delete?product.id=${id}">删除</a></td>

                           </tr>

                  </s:iterator>

         </table>

         <a href="product_input">添加</a>

</body>

  1. 以同样的方式添加产品类型

5.1.  添加产品类型模型

模型ProductDir,修改Product添加@ManyToOne

写dao,service

5.2.  修改web.xml,解决延迟加载的异常

<!-- 添加关闭entityManger过滤器,必须在struts2过滤器之前 -->

<filter>

         <filter-name>OpenEntityManagerInViewFilter</filter-name>

         <filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>

</filter>

 

<filter-mapping>

         <filter-name>OpenEntityManagerInViewFilter</filter-name>

         <url-pattern>/*</url-pattern>

</filter-mapping>

  1. struts.xml class属性配置原理

6.1.  struts2加载配置文件顺序

struts2-core-2.3.20.jarorgapachestruts2default.properties

struts2-core-2.3.20.jarstruts-default.xml

struts2-spring-plugin-2.3.20.jarstruts-plugin.xml

...

classpath:struts.xml

6.2.  struts2-spring-plugin-2.3.20.jar  struts-plugin.xml

没有使用这个jar文件,struts2的对象创建是由xwork处理

添加这个这个jar文件之后struts2的对象就由spring创建

<bean type="com.opensymphony.xwork2.ObjectFactory" name="spring" class="org.apache.struts2.spring.StrutsSpringObjectFactory" />

   

<!--  Make the Spring object factory the automatic default -->

<constant name="struts.objectFactory" value="spring" />

6.3.  default.properties

默认情况下在struts.xml的action节点里面class属性写全限定类名是按照byName注入

 

### valid values are: name, type, auto, and constructor (name is the default)

struts.objectFactory.spring.autoWire = name

 

6.4.  class属性

6.4.1.   方案1 建议使用,填写spring的bean  id,

注意xml版本配置scope=’prototype’

    注解版本配置@Scope(“prototype”)

 

好处1:可以使用小写的url地址,从使用通配符

<package name="default" namespace="/" extends="struts-default">

 

         <!-- action代表一个控制器, name="对应的访问的url地址" method="返回action的方法" -->

 

         <!-- spring集成struts之后,class可以写全限定类名,也可以写bean.id -->

 

 

<!-- *_* 请求的url_请求的方法 -->

<!-- 第1个*代表url地址,比如现在product,后面使用{1}来代替,url地址小写开头,正好对应action的bean.id的定义 -->

<!-- 第2个*代表访问action的方法,比如现在product_input,后面使用{2}来代替 -->

 

         <action name="*_*" class="{1}Action" method="{2}">

                  <!-- 显示列表页面 -->

                  <result>/WEB-INF/views/{1}.jsp</result>

                  <!-- 显示添加获取修改页面 -->

                  <result name="input">/WEB-INF/views/{1}_input.jsp</result>

                  <!-- 保存或者删除之后进行重定向 -->

                  <result name="reload" type="redirectAction">{1}</result>

         </action>

 

</package>

 

好处2:因为使用action是由spring管理

可以对action添加aop额外处理

 

坏处:spring配置文件写action的配置

@Controller//bean.id=productAction

@Scope("prototype")//配置多例

public class ProductAction extends ActionSupport {

6.4.2.   方案2 填写全限定类名,

如果填写全限定类名,则返回是小写url的地址,要求cn.itsource.ssj.action.ProductAction,类名对应要小写,不符合java命名规范;不能使用*_*通配符

@Autowired先按照类型,如果接口有2个实现,会报错

可以添加@Qualifier("productServiceImpl")名称来进行注入

 

public class ProductAction extends ActionSupport {

         @Autowired

         private IProductService productService;

  1. 课程总结

7.1.  重点

Ssj集成必须写熟练,1遍,2遍,3遍

7.2.  难点

Spring的配置文件

<?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:tx="http://www.springframework.org/schema/tx"

         xsi:schemaLocation="http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans.xsd

http://www.springframework.org/schema/context

http://www.springframework.org/schema/context/spring-context.xsd

http://www.springframework.org/schema/tx

http://www.springframework.org/schema/tx/spring-tx.xsd">

 

         <!-- jdbc.properties->dataSource->entityManagerFactory->dao->service->junit->action -->

 

         <!-- 扫描dao、service、action组件 -->

         <!-- @Repository, @Service, and @Controller,@Autowired,@PersistenceContext -->

         <context:component-scan base-package="cn.itsource.ssj" />

 

<!-- 加载jdbc.properties -->

<!-- 跑web必须在前面添加classpath:前缀 -->

<context:property-placeholder location="classpath:jdbc.properties" />

 

         <!-- 配置连接池dataSource -->

         <!-- destroy-method="close当前bean销毁的时候,会先调用close方法,关闭连接" -->

         <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">

                  <!-- 依赖注入连接池需要的属性 -->

                  <!-- property name="是BasicDataSource的set方法,本质属性" -->

                  <!-- property value="是jdbc.properties配置文件的key" -->

                  <property name="driverClassName" value="${jdbc.driverClassName}" />

                  <property name="url" value="${jdbc.url}" />

                  <property name="username" value="${jdbc.username}" />

                  <property name="password" value="${jdbc.password}" />

         </bean>

 

         <!-- org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter引入默认entityManagerFactory名称 -->

         <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">

                  <!-- 1.注入DataSource -->

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

                  <!-- 2.从哪个包去扫描@Entity,domain包 -->

                  <!-- public void setPackagesToScan(String... packagesToScan) { -->

                  <property name="packagesToScan" value="cn.itsource.ssj.domain" />

                  <!-- 3.配置JPA的实现 -->

                  <!-- private JpaVendorAdapter jpaVendorAdapter; -->

                  <property name="jpaVendorAdapter">

                           <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">

                                    <!-- org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter -->

                                    <!-- private boolean showSql = false;是否显示sql语句 -->

                                    <property name="showSql" value="true" />

                                    <!-- private boolean generateDdl = false;是否建表 -->

                                    <property name="generateDdl" value="true" />

                                    <!-- private String databasePlatform;原来方言 -->

                                    <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" />

                           </bean>

                  </property>

         </bean>

 

         <!-- 配置事务管理器 -->

         <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">

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

         </bean>

         <!-- 开启注解事务管理 ,解析@Transactional注解 -->

         <!-- transaction-manager="transactionManager"默认找bean.id=transactionManager事务管理器 -->

         <tx:annotation-driven />

 

 

</beans>

  1. 常见异常
  2. javax.persistence.TransactionRequiredException: No transactional EntityManager available

事务没有配置成功

没有配置<tx:annotation-driven />

  1. No qualifying bean of type [cn.itsource.ssj.service.IProductService] is defined: expected single matching bean but found 2: productServiceImpl,productServiceImpl2

Could not autowire field: private cn.itsource.ssj.service.IProductService cn.itsource.ssj.action.ProductAction.productService; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of

type [cn.itsource.ssj.service.IProductService] is defined: expected single matching bean but found 2: productServiceImpl,productServiceImpl2

接口有2个实现

原文地址:https://www.cnblogs.com/Src-z/p/11218908.html