1. Spring Data JPA 配置:
0.框架知识概述:
Spring Data JPA 是一个持久层的框架。而一个持久层框架所做的事情不过两件:
连接数据库(比如 JDBD连接数据库) 2.操作数据库(比如 sql 操作数据库);连接数据库:配置参数抽取: config.properties 配置:
# oracle jdbc properties
jdbc.url = jdbc:oracle:thin:@localhost:1521:XE
jdbc.driver= oracle.jdbc.driver.OracleDriver
jdbc.user = bos
jdbc.password = bos
applicationContext-dataSource.xml 配置:数据库连接池配置: config.properties
在主配置文件 applicationContext.xml 中通过配置加载属性文件:
<!-- 加载properties文件 -->
<context:property-placeholder location=\"classpath:config.properties\" />
( ${jdbc.url} 这种类似EL表达式的是SpringEL 表达式 )
<bean id=\"dataSource\" class=\"com.mchange.v2.c3p0.ComboPooledDataSource\">
<property name=\"driverClass\" value=\"${jdbc.driver}\" />
<property name=\"jdbcUrl\" value=\"${jdbc.url}\" />
<property name=\"user\" value=\"${jdbc.user}\" />
<property name=\"password\" value=\"${jdbc.password}\" />
</bean>
Spring整合JPA配置:
(在 entityManagerFactory中配置了连接池 和 domain中的bean 。这就相当于是用连接把实体类与数据库表建立了联系。 [这里说明一点:实体和表之间的准确对应关系是依赖实体类中的注解来准确定位的] 这种联系是非常紧密的。实体对象属性值的变化直接会反映到数据库表中。 而JPA作为接口规范。这里选择 hibernate作为持久化提供者; 然后就是一些数据库的基本配置了;)
<!-- 整合JPA配置 -->
<bean id=\"entityManagerFactory\"
class=\"org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean\">
<property name=\"dataSource\" ref=\"dataSource\" />
<property name=\"packagesToScan\" value=\"cn.itcast.bos.domain\" />
<property name=\"persistenceProvider\">
<bean class=\"org.hibernate.jpa.HibernatePersistenceProvider\" />
</property>
<property name=\"jpaVendorAdapter\">
<bean class=\"org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter\">
<property name=\"generateDdl\" value=\"true\" />
<property name=\"database\" value=\"ORACLE\" />
<property name=\"databasePlatform\" value=\"org.hibernate.dialect.Oracle10gDialect\" />
<property name=\"showSql\" value=\"true\" />
</bean>
</property>
<property name=\"jpaDialect\">
<bean class=\"org.springframework.orm.jpa.vendor.HibernateJpaDialect\" />
</property>
<!-- <property name=\"jpaPropertyMap\"> -->
<!-- <map> -->
<!-- <entry key=\"hibernate.query.substitutions\" value=\"true 1。 false 0\" /> -->
<!-- <entry key=\"hibernate.default_batch_fetch_size\" value=\"16\" /> -->
<!-- <entry key=\"hibernate.max_fetch_depth\" value=\"2\" /> -->
<!-- <entry key=\"hibernate.generate_statistics\" value=\"true\" /> -->
<!-- <entry key=\"hibernate.bytecode.use_reflection_optimizer\" -->
<!-- value=\"true\" /> -->
<!-- <entry key=\"hibernate.cache.use_second_level_cache\" value=\"false\" /> -->
<!-- <entry key=\"hibernate.cache.use_query_cache\" value=\"false\" /> -->
<!-- </map> -->
<!-- </property> -->
</bean>
2.操作数据库:
整合Spring Data JPA (相当于扫描了Dao。 这样dao的操作才能被识别;)
这里说明一点: dao的操作能被识别是CRUD的哪种操作。 至于操作哪个表。 是由在接口创建时传入的泛型参数确定的。 传入了一个泛型类。这个类代表着某张表; 简而言之:dao描述了两个问题: 1.我要执行什么操作。 2.我要操作那张表;
<!-- 整合spring data jpa -->
<jpa:repositories base-package=\"cn.itcast.bos.dao\" />
3.声明式事务管理配置:
1. 配置事务管理器:
(JDBC事务管理的本质。是对Session的管理。 那么这里配置了事务管理器。 就相当于指明了由谁来管理事务。 而同时属性注入了 entityManagerFactory 实体管理者工厂。这就相当于将数据库操作中的session交给了事务管理器。也就容易实现事务的管理了)
<!-- JPA事务管理器 -->
<bean id=\"transactionManager\" class=\"org.springframework.orm.jpa.JpaTransactionManager\" >
<property name=\"entityManagerFactory\" ref=\"entityManagerFactory\" />
</bean>
配置”事务注解驱动”
(这样就可以识别 Service 中的 @Transactional了)
<!-- 注解管理事务 -->
<tx:annotation-driven transaction-manager=\"transactionManager\" proxy-target-class=\"true\"/>
4.Spring Data JPA 配置小结:
1. SpringDataJPA 的配置就是通过将基本数据库连接参数:比如driven 。 url 。 username。 password通过SPEL读取到连接池DataSource。 这个时候连接池其实已经拿到了数据库的连接connection。 理论上已经可以通过sql操作数据库了。但是此时我们又将: dataSource。 。 domain 。 persistenceProvider 通过属性注入到 entityManagerFactory 。其实这个时候 ”实体管理器工厂” 已经利用 DataSource中的 connection 将 domain 下的实体类 entity 与数据库中的表 table 建立了紧密联系。 此时已经完成类与表的强相关; 至此。我们对数据库的操作对象不再是真真正正的表了。而是与之强相关的类了。操作的实现不再是sql语句。而是使用java代码来实现了; 至于哪个类对应哪个表。这就是 映射文件 hbm.xml 或者是类中的映射注解配置来细化了;
2. 在第1步获得类与数据库的连接以后。我们就要操作数据库了。我们是通过:
<jpa:repositories base-package=\"cn.itcast.bos.dao\" />
这个相当于是扫描指定包下的 repository 接口。 (当然这些repository 继承了jpaRepository。或者JpaSpecificationExecutor接口)。 这样其实底层也是利用了AOP 当我们在Service中 @autowired 这些接口实现时。 AOP 会给我们注入 SpringDataJPA 默认实现。这些实现了本身已经封装了一些常见的CRUD操作。所以我们可以省去常见CRUD操作的sql的编写。但是对于一些复杂的CRUD操作。 SpringDataJPA 也给我们留了一个口子:
比如一些标准的操作:findById() 。 findByProvinceAndCityAndDistrict 等更复杂的操作:
@Query(value=\"update Courier set deltag='1' where id=?\")
@Modifying
public void updateDeltag(int id);
或者是这个:
@Query(value=\"update Courier set deltag='' where id=?\")
@Modifying
public void doRestore(int id);
3 . 至于事务管理: 原理是Spring 有IOC 。那么类的创建。准确来说是Service类的创建。也是由Spring来创建。 根据Spring创建bean的生命周期方法的执行流程。我们知道。后处理bean中的方法执行时机是在我们获得bean对象之前。那么。后处理bean会利用AOP思想。在我们获得的Service对象的方法执行开始都加上session的事务开启。在方法执行末尾加上session的事务提交。和事务回滚。那么在我们拿到IOC 容器给我们创建的bean时。这个bean就是在事务管理之中的bean了; 这就完成了事务管理;
===============以上SpringDataJPA == 以下 WebService====================
WebService的配置:
0.框架知识概述:
(WebService 按目前使用来看就是提供了跨服务器远程数据获取的一种方式。 那我们只会关心两个问题: 1. 远程服务的创建与发布。 2。本地对远程服务的访问与数据接收; 其实远程服务的创建。就是一个service接口 中方法访问路径。访问方式。接收数据格式。返回数据格式的声明。 至于服务的发布其实是交给了配置文件; 至于本地客户端可能只需要WebClient 加上一个正确格式的url就可以了)我们这里说一下 JAX-RS 的配置:
1.首先说一下服务端配置:
作为一个服务提供端。它需要识别到对 它的所有WebService请求。 那么它就是通过在 web.xml中配置一个 CXFServlet 用来专门接收并处理WebService请求:
比如下面配置会拦截所有端口号后以 /services 开头的url请求:
<servlet>
<servlet-name>CXFService</servlet-name>
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>CXFService</servlet-name>
<url-pattern>/services/*</url-pattern>
</servlet-mapping>
在第1步拦截到所有WebService请求的基础上。它会根据请求的url 去寻找对应的Service 。 那么到哪里去寻找呢? 当然是到已经发布到服务器中的Service中去找。 那么怎么才算是发布到服务器中了呢? 这里我们是通过在 .xml配置文件来实现发布的:
比如下面配置 是将 PromotionServiceImpl 发布到服务器中
<jaxrs:server id=\"promotionService\" address=\"/promotionService\">
<jaxrs:serviceBeans>
<bean class=\"cn.itcast.bos.service.take_delivery.impl.PromotionServiceImpl\" />
</jaxrs:serviceBeans>
<jaxrs:inInterceptors>
<bean class=\"org.apache.cxf.interceptor.LoggingInInterceptor\"></bean>
</jaxrs:inInterceptors>
<jaxrs:outInterceptors>
<bean class=\"org.apache.cxf.interceptor.LoggingOutInterceptor\"></bean>
</jaxrs:outInterceptors>
</jaxrs:server>
--------------------------------下面附上 Service接口中方法声明---------------------------------------
// 根据page和rows 返回分页数据
@Path(\"/pageQuery\")
@GET
@Produces({ \"application/xml\"。 \"application/json\" })
PageBean<Promotion> findPageData(@QueryParam(\"page\") int page。
@QueryParam(\"rows\") int rows);
--------------------------------下面附上 Service实现编写示例---------------------------------------
@Override
public PageBean<Promotion> findPageData(int page。 int rows) {
Pageable pageable = new PageRequest(page - 1。 rows);
Page<Promotion> pageData = promotionRepository.findAll(pageable);
// 封装到Page对象
PageBean<Promotion> pageBean = new PageBean<Promotion>();
pageBean.setTotalCount(pageData.getTotalElements());
pageBean.setPageData(pageData.getContent());
return pageBean;
}
2.服务端配置小结:
其实以上已经说明了Service 接口编写。 Service接口实现编写。 Service服务在服务器端发布。 以及服务器端对 WebService 请求的拦截;
3.客户端请求操作:
客户端作为服务的调用者。相对来说不需要特别的配置。只需要使用一个客户端请求对象WebClient 加上正确的url 就可以发送请求了:其中 WebClient.create 创建了请求; .accept 指明了是接收数据(接收用accept。 发送用 type) ; .get 指明了请求方式是get请求(get请求是查询操作。 post请求是添加操作。 put请求是更新操作。 delete 请求是删除操作)
@Action(value = \"promotion_pageQuery\"。 results = { @Result(name = \"success\"。 type = \"json\") })
public String pageQuery() {
// 基于WebService 获取 bos_management的 活动列表 数据信息
PageBean<Promotion> pageBean = WebClient
.create(Constants.BOS_MANAGEMENT_URL
+ \"/bos_management/services/promotionService/pageQuery?page=\"
+ page + \"&rows=\" + rows)
.accept(MediaType.APPLICATION_JSON).get(PageBean.class);
ActionContext.getContext().getValueStack().push(pageBean);
return SUCCESS;
}
4.WebService配置小结:
其实我们来理顺整个过程: 客户端 使用WebClient.create();发起请求-------->服务端 web.xml配置 CXFServlet 拦截所有 /services 开头请求--------> applicationContext-webService.xml 发布服务-------> Service接口中的具体方法。 声明服务访问方式。-------> Service实现类中 提供了服务真正的操作内容;
=================以上WebService == 以下 ActiveMQ ======================
3.ActiveMQ 的配置:
0.框架知识概述:
ActiveMQ 是JMS java消息服务的规范的实现: 个人感觉它是一个服务器。有着自己的访问端口。但是它的工作却是存储消息。 又很像一个存放消息的数据库。所以在和 Spring整合时特像一个数据库: 其实:ActiveMQ 的事情也是两件:1.给生产者提供存放消息的入口。 2.给消费者提供消费消息的入口:
基础连接配置:
无论是 “生产消息” 还是 “消费消息” :我们都要有基础的连接配置:
ActiveMQ 本身给我们提供了一个连接工厂: ActiveMQConnectionFactory
<!-- ActiveMQ 连接工厂 -->
<!-- 真正可以产生Connection的ConnectionFactory。由对应的 JMS服务厂商提供-->
<!-- 如果连接网络:tcp://ip:61616;未连接网络:tcp://localhost:61616 以及用户名。密码-->
下面这种连接方式需要是为了简化<bean>配置。但需要下载第三方的支持。开始运行时较慢:
<!-- <amq:connectionFactory id=\"amqConnectionFactory\" -->
<!-- brokerURL=\"tcp://localhost:61616\" userName=\"admin\" password=\"admin\" /> -->
<bean id=\"amqConnectionFactory\"
class=\"org.apache.activemq.ActiveMQConnectionFactory\">
<property name=\"brokerURL\" value=\"tcp://localhost:61616\"></property>
<property name=\"userName\" value=\"admin\"></property>
<property name=\"password\" value=\"admin\"></property>
</bean>
Spring 对 ActiveMQ 连接工厂的封装 CachingConnectionFactory
Spring在 JMS服务供应商提供连接工厂的基础上。给我们进行了一次封装。这个封装可以理解成 Spring 提供的API 与 ActiveMQ 之间的适配器 ; 达到了解耦的作用。 这样Spring就可以整合各个JMS供应商提供的连接工厂了;
<!-- Spring Caching连接工厂 -->
<!-- Spring用于管理真正的ConnectionFactory的ConnectionFactory -->
<bean id=\"mqConnectionFactory\" class=\"org.springframework.jms.connection.CachingConnectionFactory\">
<!-- 目标ConnectionFactory对应真实的可以产生JMS Connection的ConnectionFactory -->
<property name=\"targetConnectionFactory\" ref=\"amqConnectionFactory\"></property>
<!-- 同上。同理 -->
<!-- <constructor-arg ref=\"amqConnectionFactory\" /> -->
<!-- Session缓存数量 -->
<property name=\"sessionCacheSize\" value=\"1\" />
</bean>
基础配置小结
不管我们用那种JMS产品。它都会提供一个连接工厂。而与Spring整合时。我们用 CachingConnectionFactory 将JMS服务厂商提供的连接工厂进行封装。 那么我们就可以不需要针对不同的 连接工厂操作了。只需要对同一个 CachingConnectionFactory 进行操作。达到了解耦的目的; 流程如下:
JMS厂商提供ActiveMQ 提供 ActiveMQConnectionFactory 连接工厂------>Spring 提供 CachingConnectionFactory 缓存连接工厂 封装厂商提供的连接工厂
生产者配置:这个消息生产者配置。Spring使用了它惯用的模板思想。给你提供了一个模板对象。没有什么特别之处。这个模板对象封装了连接工厂。就相当于持有了对ActiveMQ的连接。 那么这个时候向ActiveMQ中添加消息应该是可以的; 下边配置了两种类型的jmsTemplate: 1. 点对点模式: 队列Queue。 2. 发布订阅模式的: 话题Topic ; 他们的配置大同小异。唯一不同之处在于 :
<property name=\"pubSubDomain\" value=\"false\" />
的配置的value 值是false 还是 true; (true:发布订阅模式 false:点对点模式;)
<!-- 定义JmsTemplate的Queue类型 -->
<bean id=\"jmsQueueTemplate\" class=\"org.springframework.jms.core.JmsTemplate\">
<!-- 这个connectionFactory对应的是我们定义的Spring提供的那个ConnectionFactory对象 -->
<constructor-arg ref=\"mqConnectionFactory\" />
<!-- 非pub/sub模型(发布/订阅)。即队列模式 -->
<property name=\"pubSubDomain\" value=\"false\" />
</bean>
<!-- 定义JmsTemplate的Topic类型 -->
<bean id=\"jmsTopicTemplate\" class=\"org.springframework.jms.core.JmsTemplate\">
<!-- 这个connectionFactory对应的是我们定义的Spring提供的那个ConnectionFactory对象 -->
<constructor-arg ref=\"mqConnectionFactory\" />
<!-- pub/sub模型(发布/订阅) -->
<property name=\"pubSubDomain\" value=\"true\" />
</bean>
<!--Spring JmsTemplate 的消息生产者 end-->
3.生产者编程:
1.在消息生产者配置的基础上。配置了一个jmsTemplate模板。我们可以使用:
// 注入queue消息模板
@Autowired
@Qualifier(\"jmsQueueTemplate\")
private JmsTemplate jmsQueueTemplate;
注入到我们的程序中。然后调用send方法。向activeMQ中生产消息:
// 调用MQ服务。发送短信验证码
jmsQueueTemplate.send(\"bos_sms\"。 new MessageCreator() {
@Override
public Message createMessage(Session session) throws JMSException {
MapMessage mapMessage = session.createMapMessage();
mapMessage.setString(\"telephone\"。 model.getTelephone());
mapMessage.setString(\"checkCode\"。 checkCode);
return mapMessage;
}
});
简单解释一下: send(消息名称。 消息创建接口);
这个地方相当复杂:利用了两个不容易理解的知识点: 我们简单聊聊:
第一个问题: jmsQueueTemplate 的Send()方法需要 MessageCreator 的createMessage()方法创建的消息内容。但是 MessageCreator 创建消息时需要 session。但是它却没有; 然而 jmsQueueTemplate 的execute()方法 却可以获得session 。 所以 jmsQueueTemplate 对 MessageCreator 说:你先过来。然后我给你弄个session 。 然后你给我弄message。 这里边体现的是。 接口作为参数时。实现了主调方法的资源对接口中的方法的共享。 这里主调方法是 send 。它可以想办法拿到 session。然后 它把session共享给了 createMessage()方法;
如果说一般数据类型数据做为形参。主调方法是为了获取实参的值。
那么接口作为形参时。主调方法是为了获得接口的方法。 还有一点附带效果:传入的接口中的方法可以共享主调方法的参数访问权限(就是主调方法可以用的参数。接口中的方法也可以用;)
第二个问题: send 发送的消息内容是不固定的(有:1.MapMessage;2.ByteMessage;3.ObjectMessage;4.StreamMessage;5.TextMessage)。也就是说 创建消息的方法体是不固定的。那么是如何实现的呢? 用接口作为参数传入。由用户动态定义方法的实现;
如果说我们在方法中调用类的静态方法。是将一段固定代码插入到主调方法中;
那么对接口方法的调用。就是将一段动态代码插入到主调方法中;
4.生产者总结:
ActiveMQ 的生产者:
厂商的 ActiveMQConnectionFactory 注入到 ------>Spring的 CachingConnectionFactory ------>注入到 Spring的 JmsTemplate ; 配置提供了这个模板对象之后。我们就可以在java代码中注入这个对象。进行消息的创建;
5.消费者配置:
消费者要从ActiveMQ中拿消息。 肯定是要连接ActiveMQ服务器的。所以基础连接配置。消费者配置中也需要一份。我们假设消费者配置中。已经配置好了基础连接; 那么这个时候已经可以拿到 CachingConnectionFactory 。 这意味着我们已经可以通过这个连接工厂获得与ActiveMQ的连接了; 如何完成消费呢? 首先编写一个类实现 MessageListener 接口 。然后将这个类注册到消息监听器容器中; 这里说明一点。类实现了 MessageListener 接口本质上是为了让这个类实现监听器应该具有的方法。 然而并没有真正监听ActiveMQ中的消息。这个时候需要配置到消息监听器的容器(listener-container)中才能生效; 而这个(listener-container) 到底如何工作的呢? 我们来看看这个。消费者配置:
<!-- 定义Queue监听器 -->
<jms:listener-container destination-type=\"queue\" container-type=\"default\"
connection-factory=\"connectionFactory\" acknowledge=\"auto\">
<!-- 默认注册bean名称。应该是类名首字母小写 -->
<jms:listener destination=\"bos_sms\" ref=\"smsConsumer\"/>
</jms:listener-container>
<!-- 定义Topic监听器 -->
<!-- <jms:listener-container destination-type=\"topic\" container-type=\"default\"
connection-factory=\"connectionFactory\" acknowledge=\"auto\">
<jms:listener destination=\"spring_topic\" ref=\"topicConsumer1\"/>
<jms:listener destination=\"spring_topic\" ref=\"topicConsumer2\"/>
</jms:listener-container> -->
我们可以看到:在消息监听容器中。注入了两个参数:
connection-factory=\"connectionFactory\"
这个是配置了连接工厂。 是获得与ActiveMQ服务器连接的;
<jms:listener destination=\"bos_sms\" ref=\"smsConsumer\"/>
这个是配置了监听的消息目标: destination=\"bos_sms\"
(监听者)消费者: ref=\"smsConsumer\"
6.消费者编程:
(监听者)消费者 本身是一个类。实现了 MessageListener 接口:
@Service(\"smsConsumer\")
public class SmsConsumer implements MessageListener {
@Override
public void onMessage(Message message) {
MapMessage mapMessage = (MapMessage) message;
String telephone = null;
String checkCode = null;
try {
telephone = mapMessage.getString(\"telephone\");
checkCode = mapMessage.getString(\"checkCode\");
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 用输出语句代替短信发送
System.out.println(telephone+\" 的验证码是 : \"+checkCode);
}
}
7.消费者总结:
其实消费者无非就是从ActiveMQ中获得获得消息。 那么首先要获得与ActiveMQ的连接。这个是通过基础配置完成的。 连接以后要从里边拿消息呀。这个地方是在配置文件中配置一个监听器容器。 将连接工厂和监听器(其实就是一个实现了MessageListener的java类) 注入到这个监听器中。这个时候就相当于通过监听器容器。将监听器和ActiveMQ建立了联系;
=================以上ActiveMQ == 以下 Quartz=====================
4.Quartz的配置:
0.框架知识概述:
Quartz是一个定时任务调度框架。 它关心的只有两件事情:
要做什么事情---->jobDetail(即任务详情)什么时候做。频率是什么---->SimpleTrigger(依据时间)/CronTriggerFactoryBean(依据日历) 即:任务执行策略;
Ps:
任务调度员scheduler。这个是将 jobDetail 和 SimpleTrigger 进行组合。生成一个日程。另外还有一点要说明一下。在实际生产中。我们 jobDetail(即任务详情)可能并不在Spring的管理之中。 但是。 jobDetail(即任务详情) 却需要@autowired 被Spring管理的Service 或者其他bean。 这就违背了Spring规定:一个对象要想被注入对象。必须自己在Spring 管理之中。这个时候该怎么解决呢。 我们知道。 Struts2与Spring整合时。 action 没有被Spring管理。却可以注入被Spring管理的Service。 这里我们使用同样的方法解决这个问题。那就是创建了一个可以使 我们的 jobDetail 具有可以自动注入功能的工厂类 JobFactory ;我们在实际配置中是将 jobDetail 注入 SimpleTrigger 。再将 SimpleTrigger 注入 SchedulerFactoryBean 。那么。此时我们将 JobFactory 也注入到 SchedulerFactoryBean 中。那么创建出来的 jobDetail 就具有了自动注入的功能; 问题得以解决;JobDetailFactoryBean配置:
JobDetailFactoryBean 是job的工厂。我们在里边注入我们的job类对象。 下面的配置中job并不在Spring管理中;
<bean id=\"promotionJob\" class=\"org.springframework.scheduling.quartz.JobDetailFactoryBean\">
<property name=\"jobClass\" value=\"cn.itcast.bos.quartz.PromotionJob\"></property>
</bean>
Job类编写:
public class PromotionJob implements Job{
@Autowired
private PromotionService promotionService;
@Override
public void execute(JobExecutionContext context)
throws JobExecutionException {
//每分钟执行一次。当前时间大于promotion数据表中的endDate 。活动已经过期。设置status='2'
promotionService.updateStatus(new Date());
}
}
SimpleTriggerFactoryBean 配置:
将JobDetailFactoryBean 注入到 SimpleTriggerFactoryBean中:
<bean id=\"simpleTrigger\" class=\"org.springframework.scheduling.quartz.SimpleTriggerFactoryBean\">
<property name=\"jobDetail\" ref=\"promotionJob\"></property>
<property name=\"startDelay\" value=\"0\"></property>
<property name=\"repeatInterval\" value=\"10000\"></property>
</bean>
SchedulerFactoryBean的配置:
将 SimpleTriggerFactoryBean 注入到 SchedulerFactoryBean中。同时还注入了一个 jobFactory这个jobFactory 注入的目的是是为了让我们的Job具有Spring自动注入功能;
<bean id=\"\" class=\"org.springframework.scheduling.quartz.SchedulerFactoryBean\">
<property name=\"jobFactory\" ref=\"jobFactory\"></property>
<property name=\"triggers\">
<list>
<ref bean=\"simpleTrigger\"/>
</list>
</property>
</bean>
附上 jobFactory的代码:
@Service(\"jobFactory\")
public class JobFactory extends AdaptableJobFactory {
@Autowired
private AutowireCapableBeanFactory capableBeanFactory;
@Override
protected Object createJobInstance(TriggerFiredBundle bundle)
throws Exception {
Object jobInstance = super.createJobInstance(bundle);
capableBeanFactory.autowireBean(jobInstance);
return jobInstance;
}
}
5.redis配置:
Redis是一个内存数据库。由于存取速度非常快。往往用作缓存; 作为一个数据库。 无非就是考虑两个问题:
怎么连接这个数据库。怎么操作这个数据库;配置连接池基本参数:
<!-- jedis 连接池配置 -->
<bean id=\"poolConfig\" class=\"redis.clients.jedis.JedisPoolConfig\">
<property name=\"maxIdle\" value=\"300\" />
<property name=\"maxWaitMillis\" value=\"3000\" />
<property name=\"testOnBorrow\" value=\"true\" />
</bean>
配置连接工厂:
<!-- jedis 连接工厂 -->
<bean id=\"redisConnectionFactory\"
class=\"org.springframework.data.redis.connection.jedis.JedisConnectionFactory\"
p:host-name=\"localhost\" p:port=\"6379\" p:pool-config-ref=\"poolConfig\"
p:database=\"0\" />
配置模板:
<!-- spring data 提供 redis模板 -->
<bean id=\"redisTemplate\" class=\"org.springframework.data.redis.core.RedisTemplate\">
<property name=\"connectionFactory\" ref=\"redisConnectionFactory\" />
<!-- 如果不指定 Serializer -->
<property name=\"keySerializer\">
<bean class=\"org.springframework.data.redis.serializer.StringRedisSerializer\" />
</property>
<property name=\"valueSerializer\">
<bean class=\"org.springframework.data.redis.serializer.StringRedisSerializer\">
</bean>
</property>
</bean>
Java代码编写示例:
// 2.将激活码保存到redis中 可以设置存货时间;
redisTemplate.opsForValue().set(model.getTelephone()。 activeCode。 24。 TimeUnit.HOURS);
6.SpringDataElasticSearch配置:
0.框架知识概述:
简化操作ElasticSearch的技术: 是SpringData 项目下的一个子项目。为了整合 ElasticSearch ; 经过一系列配置。提供了类似SpringDataJPA的操作模式;
ElasticSearch是一个全文搜索服务器 。 但本质上是一个索引库: 它的一些基本概念和关系型数据库概念对应关系如下:
索引(index)------->表(table)
文档(document)------->一条数据实体(entity)
域(filed) --------> 字段(field)
作为一个数据库。我们还是关心两个老问题:1.怎么连接上这个数据库。 2.怎么操作这个数据库;
连接ElasticSearch:
1.配置客户端 client
<!-- 配置elasticsearch 连接 其实这个地方配置的是客户端 :transportclient -->
<elasticsearch:transport-client id=\"client\" cluster-nodes=\"localhost:9300\" />
2.配置模板对象: 注入client对象
<!-- spring data elasticsearch DAO 必须依赖 elasticsearchTemplate -->
<bean id=\"elasticsearchTemplate\"
class=\"org.springframework.data.elasticsearch.core.ElasticsearchTemplate\">
<constructor-arg name=\"client\" ref=\"client\" />
</bean>
3.实体类要配置映射
这个映射与hibernate中的对象映射有些类似。但是还有不同。这个映射配置。 其实配置了实体的存储策略(就想当于指明关系数据库的表结构怎么建)。检索策略。分词策略。等;
4.连接配置小结:
配置客户端:client ------>注入 elasticsearchTemplate ----->实体配置映射注解;
这个地方实体映射配置某种程度上说是一个 单纯的映射配置的载体; 模板对象读取这个类时。可以读取这个对象中的映射文件;
操作ElasticSearch:
配置扫描dao 。 并且我们的dao继承了 ElasticSearchRepository 接口。这样我们在
@autowired dao实现时。就会注入框架默认的实现;
<!-- 扫描DAO包 自动创建实现 -->
<elasticsearch:repositories base-package=\"cn.itcast.dao\" />
3.ElasticSearch 配置小结:
我们连接ElasticSearch 是通过配置客户端操作对象 transportclient 。 其实拿着这个client已经可以操作 ElasticSearch 了。但是 我们又对这个client进行了一次封装。封装成了一个 template (模板)。 这个模板已经可以完成了一些基本操作了。 然而在此时。我们没有止步。而是又做了一层封装。 封装成了一个默认实现。我们编写一个dao 继承 ElasticsearchRepository 这个接口。然后AOP就会给我们返回一个默认实现的 repository。这个repository 实现了基本增删改。 规范的find查询方法。等等 这点有点儿类似SpringDataJPA ;
7.shiro配置:
Shiro是一个权限控制框架:
权限控制流程: applicationCode -----> Subject------> ShiroSecurityManager----->Realm ;
其实权限控制问题简单来说就是: 哪些user 可以访问哪些 url的问题;
1.粗粒度url权限验证流程:
request 请求发出--->web.xml中的DelegatingFilterProxy 代理过滤器过滤到请求----> 交给applicationContext.xml 中的真正的过滤器 ShiroFilterFactoryBean----> 过滤器将请求交给 \"安全管理器\" securityManager ----->\"安管\"读取过滤器中的 \"资源与访问权限对应清单\" filterChainDefinitions ----->\"安管\"找到request要访问的资源需要的权限:permission1 ---->
\"安管\" 调用 \"小弟\" realm 的 doGetAuthorizationInfo()方法。获得当前用户所持有的权限: permission2 ---->此时:\"安管\"已经从过滤器中拿到 1. 要访问的资源需要的权限:permission1 2. 前用户所持有的权限: permission2 。所以\"安管\"可以很轻松地将两个权限做比较。如果匹配。那么就允许用户访问资源。如果不匹配。则拒绝访问该资源。并跳转到没有权限访问的页面;
2.细粒度方法级别权限验证流程:
request请求发出----> 调用 save()方法----> Spring 尝试利用 IOC 创建cglib 动态代理对象。却发现目标类 的save()方法上有shiro注解。然后就利用AOP 在代理类的save方法上加上前置通知: AuthorizationAttributeSourceAdvisor 来读取save()方法上的 shiro注解。并将结果交给\"安管\"----->securityManager 获得 save()方法访问应具备的权限 -----> 调用realm的doGetAuthorizationInfo()方法。来获得当前用户所持有的权限。 然后安管自己会对两个权限作对比。判定是否可以访问资源...;
3.两种权限验证方式对比分析:
粗粒度url权限控制 与 细粒度方法级别权限控制 对比分析:
粗粒度url权限控制
细粒度方法级别权限控制
资源控制者
securityManager
securityManager
资源访问所需权限获取来源
过滤器 shiroFilter 中的属性配置
Spring框架 (IOC和AOP共同作用)从方法的注解读取
当前用户访问权限获取来源
小弟: realm
小弟:realm
权限不足时处理方式
过滤器配置相应友好提示页面
抛出相应异常给方法调用者;
缓存框架Ehcache 的配置
0.框架知识概述:
当我们用Shiro做权限管理的时候。我们每一次对资源进行访问时我们的 “安全管理器” securityManager 会对当前用户进行权限的授权信息获取。 如果每次获取当前用户的授权信息都到数据库中查询。这样无疑是降低了资源获取的速度。用户体验不好。所以这种情况。我们必然要对用户权限信息进行缓存处理;
Flag:缓存技术不一定用在权限管理。但权限管理一定会用到缓存;
复制自带配置文件 ehcache-failsafe.xml
Ehcache 的jar包下有默认配置文件。我们将它复制到与applicationContext.xml 同级目录下;
我们在这个配置文件里边创建自定义的缓存空间;
Spring整合(管理)Ehcache
Ehcache 的 EhCacheManager “缓存管理器” 交给Spring的EhCacheManagerFactoryBean “缓存管理器工厂bean” 来管理;
至此。 ehcache已经在Spring的管理之中了;
<!-- Ehcache配置 (ehcache的 \"缓存管理器\" 纳入Spring管理) -->
<bean id=\"ehCacheManager\" class=\"org.springframework.cache.ehcache.EhCacheManagerFactoryBean\">
<property name=\"configLocation\" value=\"classpath:ehcache.xml\"></property>
</bean>
Ehcache 对Shiro 的权限数据进行数据缓存Shiro 整合 Ehcache
Ehcache 底层是Map结构实现。所以Shiro要对Ehcache 的缓存管理器再做一层封装。指明什么作为key。什么作为value 。这个封装类就是: EhCacheManager 。我们将ehcache的 \"缓存管理器\" 属性注入到shiro 的 EhCacheManager :
<!-- shiro配置 (将ehcache的 \"缓存管理器\" 属性注入到shiro) -->
<!-- ehcache 底层是Map实现。所以Shiro要对ehcache的缓存管理器在做一层封装。指明什么作为key。什么作为value -->
<bean id=\"shiroEhCacheManager\" class=\"org.apache.shiro.cache.ehcache.EhCacheManager\">
<property name=\"cacheManager\" ref=\"ehCacheManager\"></property>
</bean>
Shiro “安全管理器” 获得 “Ehcache缓存管理器”
根据Shiro权限验证执行流程: 应用程序代码------> subject ------> securityManager----->
Realm ------> 访问安全数据 ;
<!-- 安全管理器 -->
<bean id=\"securityManager\" class=\"org.apache.shiro.web.mgt.DefaultWebSecurityManager\">
<property name=\"realm\" ref=\"bosRealm\"></property>
<property name=\"cacheManager\" ref=\"shiroEhCacheManager\"></property>
</bean>
上边配置的结果是: “安全管理器” 获得了缓存操作权限; 这样它的小弟 realm 就可以向 “安全管理器” 申请一个缓存空间。 什么意思呢? 就是小弟realm说:你把 “bos” 这个缓存区给我。以后你找我要数据。你先到这个缓存区看看有没有。如果没有。我再去数据库帮你找到。然后你要把数据放进去。下次不要找我要了;
Reaml 指定将 哪些 数据存入 哪个 缓存区
<!-- Realm配置。 指明将 \"授权数据\" 缓存到 \"bos\" 缓冲区-->
<bean id=\"bosRealm\" class=\"cn.itcast.bos.realm.BosRealm\">
<property name=\"authorizationCacheName\" value=\"bos\"></property>
</bean>
Ehcache对权限数据缓存的 配置流程总结:
在Ehcache的 ehcache-failsafe.xml中自定义缓存区 ------> Spring与Ehcache的整合类(EhCacheManagerFactoryBean)读取配置文件 得到 ehCacheManager------> ehCacheManager
注入到 Shiro的 shiroEhCacheManager ------> shiroEhCacheManager 注入到 “安全管理器” securityManager ------> Reaml 通过属性配置 来 声明要缓存数据。以及要缓存的区域 (比如 bos) ;
4. Ehcache对一般数据的缓存配置:
1. Spring “缓存管理器” 封装 Ehcache的 “缓存管理器”
在Shiro 整合Ehcache时。有一个 shiroEhCacheManager 来管理 key-value的生成策略。 那么在对一般数据的缓存配置时。肯定是不能用这个缓存管理器了。 这个时候Spring 就出来说话了。他说 我定义一个 “缓存管理器” 来管理 一般数据的key-value的生成策略;
<!-- Spring 封装Ehcache的缓存管理器。它规范了普通数据缓存时的key-value 生成策略 -->
<bean id=\"springCacheManager\" class=\"org.springframework.cache.ehcache.EhCacheCacheManager\">
<property name=\"cacheManager\" ref=\"ehCacheManager\"/>
</bean>
这个缓存管理器一方面是获得 Ehcache 的缓存管理器。拥有了缓存操作权限 ; 另一方面是提供了一些标签。它可以解释这些标签。来执行缓存操作 ;
2.在Spring的配置文件中 激活spring 缓存注解
目的就是让Spring 能够识别它管理的类中的缓存注解;
<!-- 激活spring 缓存注解 -->
<cache:annotation-driven cache-manager=\"springCacheManager\"/>
3.在被Spring管理的bean的方法上使用 @Cacheable() 。 @CacheEvict() :
@Cacheable(value=”缓存区”) : 应用缓存。 对方法返回结果进行缓存。 这个注解多用在查询方法上; 至于谁是key 。谁是value 由Spring的缓存管理器来负责; 如果被注解的方法有不同的 传入参数。 可在注解中使用SpEL (Spring表达式) 自定义key值;
@CacheEvict (value=”缓存区”。allEntries=”true”): 清除缓存区的数据。 allEntries代表所有数据;
缓存配置中的注意点:
被缓存的对象数据对应的类要序列化 : implements Serializable ;
Freemarker 模板引擎
Struts2 默认是使用 Freemarker作为自定义标签模板。所以项目导入了Struts2 就已经导入了Freemarker的jar包;
0.技术概述:
Freemarker就是一个模板引擎。它类似我们之前学习的jsp; FreeMarker 它通过编写一个模板文件。结合我们程序中的动态数据。输出标准的文本内容。一般情况下我们用它来生成html文件比较多;
简单来说: 模板文件 + java数据对象 == 输出 (任何格式文本)
FreeMarker 模板文件。通常扩展名 .ftl (当然也可以用 .html / .jsp)
我们编写前。要安装一个Freemarker的插件。安装到eclipse 。这样我们编辑时就会有提示; Freemarker 模板文件中的变量 跟EL 表达式一样。都是 ${变量名}
编写模板文件 promotion_detail.ftl
<link rel=\"stylesheet\" type=\"text/css\" href=\"css/promotion_detail.css\">
<div class=\"container promotions\" >
<div class=\"col-md-2 prolist\">
<h5 class=\"title\"><a href=\"#/promotion\"><strong>返回促销列表</strong></a></h5>
<img src=\"images/pro.jpg\" class=\"img-responsive\">
</div>
<div class=\"col-md-10 procontent\">
<h5 class=\"title\">${promotion.title}</h5>
<div class=\"intro\">
<p>活动范围: ${promotion.activeScope}</p>
<p>活动时间: ${promotion.startDate?string(\"yyyy-MM-dd\")} -
${promotion.endDate?string(\"yyyy-MM-dd\")}</p>
</div>
<div class=\"partline clearfix\"></div>
<div class=\"promotionbox\">
${promotion.description}
</div>
</div>
</div>
编写java代码
// 用面向对象思想。将配置文件目录封装为configuration对象
Configuration configuration = new Configuration(
Configuration.VERSION_2_3_22);
configuration.setDirectoryForTemplateLoading(new File(
ServletActionContext.getServletContext().getRealPath(
\"/WEB-INF/freemarker_templates\")));
// 用 configuration 获取模板对象 template
Template template = configuration
.getTemplate(\"promotion_detail.ftl\");
// 动态数据
Promotion promotion = WebClient
.create(Constants.BOS_MANAGEMENT_URL
+ \"/bos_management/services/promotionService/promotion/\"
+ model.getId()).accept(MediaType.APPLICATION_JSON)
.get(Promotion.class);
//构造动态数据 ()
Map<String。 Object> parameterMap = new HashMap<String。 Object>();
parameterMap.put(\"promotion\"。 promotion);
//使用模对象合并数据 template 合并输出
/* @param dataModel the holder of the variables visible from the template (name-value pairs); usually a {@code Map<String。 Object>} or a JavaBean (where the JavaBean properties will be the variables) */
template.process(parameterMap。 new OutputStreamWriter(
new FileOutputStream(htmlFile)。 \"utf-8\"));
模板对象 template 合并数据时。将数据模型数据dataModel 输出到writer流中; 而这个dataModel 的数据类型一般是 Map<String。 Object> 。 当然也可以是 JavaBean 。这个 JavaBean 的属性是模板中的变量; 另外要说一点。如果模板中有变量必须全部赋值。 如果存在一个没有赋值。 好像会报错;
Spring与Mybatis整合配置
PS: mybatis框架的特别之处是。它实现了sql语句与java代码的分离。所以它要解决的核心问题是如何让mapper.xml中的sql语句在数据库操作时起作用;
另外说明一点:
在mybatis单独使用时: mapper.xml-----> SqlMapConfig.xml ------> sqlSessionFactory ----->sqlSession 。 所以这个时候 mapper.xml的引用在sqlSession中。因此 外置sql语句的调用是由sqlSession来完成的;
在mybatis与spring整合时: SqlMapConfig.xml ----> sqlSessionFactory ----> MapperFactoryBean; 由于在mybatis与spring整合时。SqlMapConfig.xml是一个空的文件。所以里边没有mapper.xml的引用。所以没有办法使用里边的sql。这个时候怎么解决这个问题呢? 通过一个强制要求: 映射文件(mapper.xml)必须与相应的接口同名且在同一个目录下。这样在创建代理时。在读取接口的同时会在相同目录下去寻找映射文件。这样就同时利用接口和映射文件生成了代理对象;
连接数据库数据库连接参数抽取: db.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8
jdbc.username=root
jdbc.password=123
Spring核心配置文件applicationContext.xml 中加载db.properties并配置连接池BasicDataSource
1.简单说明:
连接池的作用是提供连接。并且不用反复创建销毁数据库连接;
2.配置内容:
<!-- 加载属性配置文件 -->
<context:property-placeholder location=\"classpath:db.properties\"/>
<!-- 配置连接池 -->
<bean id=\"dataSource\" class=\"org.apache.commons.dbcp.BasicDataSource\"
destroy-method=\"close\">
<property name=\"driverClassName\" value=\"${jdbc.driver}\" />
<property name=\"url\" value=\"${jdbc.url}\" />
<property name=\"username\" value=\"${jdbc.username}\" />
<property name=\"password\" value=\"${jdbc.password}\" />
<property name=\"maxActive\" value=\"10\" />
<property name=\"maxIdle\" value=\"5\" />
</bean>
配置Spring与Mybatis整合类 org.mybatis.spring.SqlSessionFactoryBean
1.简单说明:
sqlSessionFactory 跟hibernate的SessionFactory 。 SpringDataJPA的EntityManagerFactory 的功能非常类似:
1.注入连接池。获得了连接数据库的能力;
2. 注入了映射配置文件位置; 区别在于:hibernate的映射配置文件是配置了实体与表之间的映射。最后干脆通过注解。将映射配置放到了实体类中。最后sql语句(或者hql)的编写就写到dao中了; 然后就是SpringDataJPA。 它是在Hibernate基础之上。将一部分sql语句做了封装实现。这样我们在对数据库进行一些简单操作的时候。我们可以直接通过调用一些方法就可以了。等于是帮我们已经完成了一些简单的CRUD操作; 但是一些复杂的查询操作。我们还是需要自己在 Dao中编写查询语句(hql或者是sql)。不论是hibernate还是 SpringDataJPA 都没有将Sql语句与java 代码分离。 而mybatis却做了这一点: mybatis的映射配置文件中有三部分: 1.输入映射; 2.输出映射;3. sql语句映射; 其中输入输出映射完成了类似orm映射的功能。完成类与表的对应; 而sql语句映射其实是提供了一些固定的sql操作。然后我们通过:
1. sql映射的namespace 使用Dao接口的 全限定名
2.sql映射的 statementId 与Dao接口的方法名一致
3.sql映射的parameterType与Dao中方法的参数类型一致
4.sql映射的resultType与Dao中方法的返回值类型一致
以上四点约定。我们的sql映射实际上已经成为以后Mybatis使用Mapper代理方式时。Spring创建代理的依据; 所以说某种程度上说mapper映射文件中的sql映射部分可以视作是Dao接口的实现类; 这样其实实现了 “实现类”到”映射文件”的转变( java代码----->xml配置的转变)。 将sql语句从java 代码中抽离出来了;
2.配置内容:
<!-- 配置sqlSessionFactoryBean -->
<bean id=\"sqlSessionFactory\" class=\"org.mybatis.spring.SqlSessionFactoryBean\">
<!-- 注入连接池 -->
<property name=\"dataSource\" ref=\"dataSource\"></property>
<!-- 加载mybatis 核心配置文件 -->
<property name=\"configLocation\" value=\"classpath:mybatis/SqlMapConfig.xml\"/>
</bean>
操作数据库简单说明:
我在前面总结持久层框架的时候喜欢将配置分为两部分:
连接数据库 : 得到SessionFactory操作数据库 : 将SessionFactory 与dao融合;
2.配置内容:
<!-- 第一种方式: 通过设置mapper接口 ; 缺点:一次只能配置一个接口-->
<!-- <bean class=\"org.mybatis.spring.mapper.MapperFactoryBean<T>\">
<property name=\"sqlSessionFactory\" ref=\"sqlSessionFactory\"></property>
<property name=\"mapperInterface\" value=\"cn.itheima.mybatis.mapper.UserMapper\"></property>
</bean> -->
<!-- 第二种方式: 通过对mapper包扫描;
优点:1.可以对mapper包下的所有接口完成代理; 2.会自动注入 sqlSessionFactory -->
<bean class=\"org.mybatis.spring.mapper.MapperScannerConfigurer\">
<property name=\"basePackage\" value=\"cn.itheima.mybatis.mapper\"/>
</bean>
上边两种配置都可以完成 sqlSessionFactory 与Dao的融合。但是第二种更有优势: 我们简单分析一下: 它是通过扫描包。然后找到包下的接口。然后读取到包下同名的mapper.xml映射文件。然后根据 接口和映射文件创建 接口实现类对象;
8.持久层框架配置套路总结:
其实:持久层无非做了两件事: 1.连接数据库。 2操作数据库 ;围绕这两件事每个框架都各有千秋。但是它们与Spring整合时的配置应该是有一些套路的:
1 . Spring 的JdbcTemplate
这个类简化了JDBC操作。
它的基本操作: update(sql语句。 参数); query(sql语句。 参数);
操作流程如下:
配置驱动连接池 (可以用默认的。也可以单独配比如C3P0连接池)。属性注入连接参数: driven。 url 。 username 。 password配置 JdbcTemplate我们可以在java类中直接注入 JdbcTemplate 进行CRUD操作;
2.hibernate:
这是一个orm框架。通过映射文件。实现了对数据库表的操作 到 对类的对象操作的转化
操作流程如下:
将基本连接参数分离成一个.properties文件在application.xml中引入.properties文件配置连接池 (注入连接参数)配置 LocalSessionFactory (注入1.连接池; 2.数据库基本配置; 3.映射文件位置 )编写dao 继承 HibernateDaoSupport类 并在配置dao (注入sessionFactory。 本质上是注入到HibernateDaoSupport 中) 这样在dao中就可以获得: hibernateTemplate 模板对象;利用模板对象进行CRUD操作。这个时候操作的。这个模板对象也是简化hibernate数据访问操作;
3.SpringDataJPA
1. 将基本连接参数分离成一个.properties文件
2. 在application.xml中引入.properties文件
3. 配置连接池 (注入连接参数)
4. 配置LocalContainerEntityManagerFactoryBean
注入 : 1.连接池; 2.domain扫描包路径(相当于映射文件配置); 3.持久化提供者; 4.其他数据库基本配置;
<property name=\"dataSource\" ref=\"dataSource\" />
<property name=\"packagesToScan\" value=\"cn.itcast.bos.domain\" />
<property name=\"persistenceProvider\">
<bean class=\"org.hibernate.jpa.HibernatePersistenceProvider\" />
</property>
...
5. Jpa扫描dao; 这一步等价于hibernate中的 dao配置。hibernate中是给dao中注入sessionFactory。使到可以获得hibernateTemplate模板。 这个地方 通过扫描dao的方式。在获得dao对象时通过AOP技术提供给我们默认的dao实现;
<jpa:repositories base-package=\"cn.itcast.bos.dao\" />
4.redis
1.配置连接池 JedisPoolConfig
2.配置连接工厂 JedisConnectionFactory
3.配置模板 RedisTemplate
4.java代码中操作:
redisTemplate.opsForValue().set(model.getTelephone()。 activeCode。 24。 TimeUnit.HOURS);
5.小结:
通过比较持久层这个几个配置。我们可以看出来。Spring在整个各种框架是。基本套路也就是:
配置连接参数。 2.配置连接池。 3.配置连接工厂 4.配置模板;
相比之下: SpringDataJPA做的事情更多一些。它没有直接提供给你一个模板。而是编写了一个默认实现。你只要让你的dao实现了JPARepository / JpaSpecificationExecutor 。 在配上 jpa扫描:
<jpa:repositories base-package=\"cn.itcast.bos.dao\" />
在你注入这个包下的对象时。它就会判断你是否继承了 JPARepository / JpaSpecificationExecutor 这些接口。如果继承了。那么它会利用AOP返给你一个默认实现的类。而在这个类中。已经给我们默认实现了一些CRUD常用操作。所以避免了常用CRUD操作的方法编写;
本文地址:https://gpu.xuandashi.com/77857.html,转载请说明来源于:渲大师
声明:本站部分内容来自网络,如无特殊说明或标注,均为本站原创发布。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。分享目的仅供大家学习与参考,不代表本站立场!