开源企业级轻量级框架Spring
概述
Spring框架提供了一个全面的现代java企业应用程序编程和配置模型——在任何类型的部署平台。
Spring是基础设施的一个关键要素支持在应用程序级别:Spring集中在企业应用程序的“管道”,团队可以专注于应用程序的业务逻辑,没有不必要的关系到特定的部署环境。
Spring框架分为模块。应用程序可以选择他们所需要的模块。核心的模块是核心容器,包括配置模型和依赖注入机制。除此之外,Spring框架提供基础支持不同的应用程序架构,包括消息、事务性数据和持久性和网络。它还包括基于servlet的Spring MVC web框架
-
特性
-
框架概览图:Spring框架的各个特性被组织成20个模块。这些模块被分组成Core Container(核心容器), Data Access/Integration(数据访问/集成), Web(网络端), AOP (Aspect Oriented Programming,切面编程), Instrumentation, Messaging(消息),和Test(测试), 以下图片显示的就是Spring的各个模块:
基于spring4上图谈谈整体架构
链接: Spring整体架构和jar包依赖关系详解.
链接:第一章:spring5.0概述
springIOC和DI
spring的约束
spring官方提供了xml配置文件的规范,也就是配置文件(applicationcontext.xml)的约束文件dtd和schema(后缀xsd)
简单解释
Java应用 — 运行着小到各种受限的小程序,大到n层结构的服务器端企业级应用 — 包含着相互协作的对象,从而创建正确的应用。 因此,程序里的对象彼此之间相互依赖。
尽管Java平台提供的很多功能性的应用程序(这个时候比较散比较杂,比如Jar包啊),但是他缺少将这些基础组件组织成一个整体的方法
,最终把这些整合工作交给了架构师或是开发者。你可以使用设计模式,例如工厂,抽象工厂,建造者,装饰和服务定位来将这些不同的类和示例对象组合起来,从而构建一个应用(虽然将某些多的jar或者组件通过各种模式组装,但是毕竟不如成品来的痛快)
。 但是,这些模式仅仅只是:一个被给予名字的最佳实践,说明了该模式做什么,怎样应用,解决了什么问题等等。 模式是形式化的,你必须在你的应用中去实现它。
Spring中的控制反转 (IoC)部分解决了这个问题,通过提供一种有效的方式将各个分开的组件组合成一个完全可供使用的应用。 Spring框架用函数化的形式实现了形式化的设计模式,这样你就可以在你的应用中继承它们。
很多组织个机构正在使用Spring框架来设计健壮的,便于维护的应用。
pring IoC容器设计原理及高级特性
使用IOC容器进行依赖注入简化操作
XML方式
控制反转简单属性
- 对象懒加载与单例
Lazy-init | cope | 对象的创建结果 |
---|---|---|
True | singleton | 单例对象创建懒加载 |
True | prototype 多例对象创建懒加载 | |
Default/false | singleton | 单例对象创建立即加载 |
Default/false | prototype | 多例对象创建懒加载 |
注意 | 懒加载并不是传说的单例模式,他只是在容器中之加载一次名ID的对象 |
- 工厂生产的属性
模式 | 属性 | 用法 |
---|---|---|
静态工厂 | factory-method:指定对应工厂方法的名称,class是静态工厂类的全路径 | 将两个属性书写正确并定义ID后,容器会调用静态工厂方法生成对应实例 |
实力工厂 | factory-method:指定对应工厂方法的名称,factory-bean:对应工厂的实例 | 实例工厂需要在之前将实例工厂的对象加入容器,并不需要定义class(定义了写谁?) |
spring工厂 | class:声明为工厂路径 | 不同的是采用spring工厂的方式,要求工厂类必须实现类:org.springframework.beans.factory.FactoryBean重写方法:getObject() throws Exception、Class getObjectType()、boolean isSingleton()注意:配置不同属性告诉spring你要什么样的模式实现对象的生产及管理 |
下面是一些实例代码
简单注入使用
<!-- setter的属性和对象注入 -->
<!-- 注入时利用元素property -->
<bean id="beanB" class="com.tedu.springDI_01.ClassB">
<property name="id" value="10086"></property>
<property name="name">
<value>kongkongWorld</value>
</property>
<property name="date" >
<bean class="java.util.Date"></bean>
</property>
<property name="date01" ref="ddd"></property>
<property name="ia" ref="beanA"></property>
</bean>
<!-- 构造器可以带参注入 -->
<bean id="beanBC" class="com.tedu.springDI_01.ClassB">
<constructor-arg index="0" value="10010"></constructor-arg>
<constructor-arg name="name" >
<value>张三</value>
</constructor-arg>
<constructor-arg index="2" ref="ddd"></constructor-arg>
<constructor-arg index="3" ref="ddd"></constructor-arg>
<constructor-arg type="com.tedu.springIOC_01.InterfaceA" ref="beanA"></constructor-arg>
</bean>
<!-- autowired ID和class后直接写入属性autowired=default|no|byName|byType|constructor -->
<bean id="cc" class="com.tedu.springDI_01.ClassC"></bean>
<bean id="dd" class="com.tedu.springDI_01.ClassD"></bean>
<bean id="beanET" class="com.tedu.springDI_01.ClassE" autowire="byType"></bean>
<bean id="beanEN" class="com.tedu.springDI_01.ClassE" autowire="byName"></bean>
进阶一些,对list,set,map,property进行注入
<!-- 命名空间注入 相当于在springIOC容器中加载一个set集合对象 -->
<util:set id="hero_set">
<value>set_01</value>
<ref bean="t_1" />
</util:set>
<bean id="hero" class="tedu.ioc.com.BeanTest">
<property name="list">
<list>
<value>list_01</value>
<value>list_02</value>
<ref bean="t_1" />
</list>
</property>
<property name="set" ref="hero_set"></property>
<property name="map">
<map>
<!-- 注意在map注入时时entry -->
<entry key="key_1" value="value_1"></entry>
<entry key="key_2" value-ref="t_1"></entry>
<entry key-ref="t_1" value-ref="t_2"></entry>
</map>
</property>
<property name="prop">
<props><!-- 多了一个props,命名空间注入时不需要此元素 -->
<prop key="公子">宝剑</prop>
<prop key="佳人">折扇</prop>
</props>
</property>
</bean>
注解方式(着重掌握)
开启注解扫描
@ComponentScan:若没有设置basePackages=“包名”,则默认扫描当前包下的所有类,替换XML文件的<Context:Component-Scan base-packages=”包名”>
IOC和Di注解汇总
- 想容器中加载Bean(IOC)(替换了xml里的Bean元素)
名称 | 意义 |
---|---|
@Controller | 修饰在Controller控制器的类上 |
@Serice | 修饰在Service业务类上,可以用@Component替换 |
@Repository | 修饰在Dao数据库访问类上,可以用@Component替换 |
@Component | 修饰在无法归类的类别上,比如xxUtil类 |
@Bean | 遇到第三包而且我们还需要Bean的时候怎么办,比如数据源对象,Spring提供Bean注解,注解在方法上将方法返回值作为对象放置在容器中 |
- 依赖注入(DI)
名称 | 意义 |
---|---|
@Resource | 由JDK提供的若不指定(name=”xxx”)默认按照名称装配,装配名称采取属性名 |
@Autowired | 由spring-Context包提供,默认按照类型装配,若有歧义或者通过有多个实现类的接口实现的时候,采取@Autowired@Qualifier(name=“”)进行精确装配。由于以上两个支持到参数,进行有参构造类的IOC的时候,可将注解放置到构造的参数上完成当前类的依赖注入 |
@Value | 括号里直接值(整数也带双引号,会自己转的) |
@Inject、@Name | 用到第三方jar包。不做解释 |
- 其他注解比如属性配置之类的----未完待续
名称 | 意义 |
---|---|
@Scope(value=“prototype”) | 单例和多例 修饰在类上 |
@Lazy | //代表懒加载 修饰在类上 |
@PostConstruct | 表示Bean初始化操作的自定义方法 修饰在方法上 |
@PreDestroy | 表示销毁Bean时的自定义方法 修饰在方法上 |
@Primary | 自动装配时当出现多个Bean候选者时,被注解为@Primary的Bean将作为首选者,否则将抛出异常 |
@propertySource | 加载配置文件进入SpringIOC容器,属性:name、value、ignoreResourceNotFound(默认false抛异常)、encoding |
@import | 这是在Spring元数据类(相当于一个XML文件)较多时进行添加的操作,比如在一个ApplicationConfig类上。@Import({ApplicationConfig2.class,ApplicationConfig3.class})可以替换在Xml中的 |
@ComponentScan | 若没有设置basePackages=“包名”,则默认扫描当前包下的所有类替换XML文件的<Context:Component-Scan base-packages=”包名”> |
顺道提一嘴
先定义:注意这时候不会初始化,不会初始化,不会初始化
- Resource:定位,根据开发者的定义的内容进行资源定,注解和Xml都是可以的。
- BeanDefinition:的载入,这个时候将Resource定位到的信息保存到BeanDefinition中,此时仍旧没有创建对应实例
- BeanDefinition的注册:将DeanDIFinition的信息注册到SpringIoc容器中,
初始化和依赖注入:注意这个时候要去检查是否lazy-init=”true”
SpringAop
AOP的核心代理设计模式
为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。准确的说,需求对象不能很好的使用或者很好的整合被需求对象,比如对被需求对象做出事务控制的时候,我们不能直接对被需求对象做出直接修改,就需要代理对象做出整合,将需求对象(事务控制)织入被需求对象中。
- 抽象对象:业务的抽象接口。
- 真实对象:被代理对象,需要对其业务进行补充或者访问控制的对象
- 代理对象:对真实对象的问题提供了解决方案,方便使用者即使有效的使用真实对象。
静态代理:代理对象是由程序猿创建的,而且一个Proxy一般就只能代理一类目标对象(实现共同Subject接口),相当于代理类和被代理类关系提前要确定。
动态代理:在实现阶段不用关心代理类的具体实现,而是在运行时才生成代理对象(JAVA中是通过反射实现,具体见下文)平台或者三方库有提供。动态代理的扩展性更高,所以一般一些开源框架都会使用。
动态代理之JDK动态代理
JDK动态代理是JDK的Java.lang.reflect包下提供的代理技术,不需要提供第三方JAR包,到需要被代理对象具有抽象接口。
角色分析:
- 被代理接口:被代理业务的抽象性接口,为具体业务提供抽象声明(面向接口编程,另外没有接口也使用不了JDk的代理技术啊)
- 被代理实现对象:提供业务的具体实现,也是代理对象的真实需要代理的业务逻辑对象
- 代理对象:对需要代理的业务进行扩展和控制的对象。也是我们正在制造的对象
实现逻辑:
- 代理对象实现新业务逻辑的地方:一个实现InvocationHandler接口的实现类,重写其invoke方法。
public Obejct invoke(Object proxy,Method method,Object[] args){ }
//Object 目标方法(被代理的方法)的返回值=method.invoke(老业务对象,目标方法的参数);
//proxy:内部生成的代理类的对象,他将调用这个方法
//method:目标方法的的Method对象,调用>invoke方法。将使用逻辑。
//args:目标方法的参数数组,
- 产生代理对象的方法:Proxy的静态方法newProxyInstance()
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces, InvocationHandler h)throws IllegalArgumentException
//loader是真实对象的类加载器
//interface是被代理接口
//h是继承InvocationHandler的实现类,此参数将新业务或者说新的要求同被代理业务耦合
- 用户使用真实对象:通过产生代理对象的逻辑直接拿到代理对象,并赋予被代理接口引用,最后直接调用就ok啦!!!!
JDK源码原理实现分析
在这里面无非解释了一个问题,JDK动态代理实现的逻辑是,根据那你的在proxy. newProxyInstance里面填入的父接口信息创建一个子实现(包含equals、toString、hashcode和需对应父接口要代理的逻辑的方法实现),所以JDK生成的代理对象(还会继承Proxy类)和被代理对象(真实对象)是兄弟关系。
cglib动态代理
导入第三方包
<dependency>
<groupld>cglib</groupld>
<artifactld>cglib</artifactld>
<version>3.2.10</version>
</dependency>
代理对象实现新业务逻辑的地方
一个实现MethodInterceptor接口的实现类,重写其intercept方法。
public Object intercept(Object proxy,Method method,Object[] args,MethodProxy methodProxy){}
//Object 目标方法(被代理的方法)的返回值=method.invoke(老业务对象,目标方法的参数);
//proxy:cglib生成的代理对象
//method:被代理对象业务方法的对象。
//args:目标方法的参数数组,
//methodProxy:代理方法,使用methodProxy.invoke方法调用原来的业务
产生代理对象的方法:
通过增强类Enhancer产生代理对象
Enhancer enhancer = new Enhancer();
enhancer.setSuperClass(Class cla);//cla是被代理类的class对象
enhancer.setCallback(实现MethodInterceptor接口的对象);
enhancer.create();//返回一个代理对象
//第二步是将代理逻辑绑定到哪一个业务上或者说类上,cglib内部也是通过他来生成其子类,就是此业务的代理类。
//第三部是将代理对象将我们规定的新业务或者说我们希望代理后的业务绑定。
用户使用真实对象:
通过产生代理对象的逻辑直接拿到代理对象,并赋予被代理类的class对象,最后直接调用就ok啦!!!!
cglib源码原理实现分析:
cglib动态代理实现的逻辑是,根据那你的在enhancer.setSuperClass里面填入的类信息创建一个子实现,通过拦截器的等模式实现动态代理,所以cglib代理对象和被代理对象(真实对象)是父子关系。
需要注意的是:Cglib动态代理执行代理方法效率之所以比JDK的高是因为Cglib采用了FastClass机制,它的原理简单来说就是:为代理类和被代理类各生成一个Class,这个Class会为代理类或被代理类的方法分配一个index(int类型)。这个index当做一个入参,FastClass就可以直接定位要调用的方法直接进行调用,这样省去了反射调用,所以调用效率比JDK动态代理通过反射调用高。
若调用method.invoke方法会造成方法循环调用的死循环,
另外cglib不能代理带final的方法的
两种的区别
- JDK动态代理是实现了被代理对象的接口,Cglib是继承了被代理对象。
- JDK和Cglib都是在运行期生成字节码,JDK是直接写Class字节码,Cglib使用ASM框架写Class字节码,Cglib代理实现更复杂,生成代理类比JDK效率低。
- JDK调用代理方法,是通过反射机制调用,Cglib是通过FastClass机制直接调用方法,Cglib执行效率更高。
说完动态代理现在说一下AOP
核心概念
Aop的底层核心是动态代理技术,通过这种模式来掌管各个对象的操作的切面环境,包括事务管理、日志打印等。让我们在可以调用愿业务的同时可以织入我们后期更新维护的逻辑,比如为了安全性而加入事务管理逻辑,甚至新情况发生需求不符合逻辑可以屏蔽原有业务。
并且,在有些流程中,比如数据库事务管理,aop会提供默认的实现逻辑,也会提供一些默认的配置比如spring的声明式事务,大大减少了开发量,提高大吗的可读性和可维护性,让程序员可以更加集中开发在业务上。
基本定义
名称 | 定义 |
---|---|
切面(aspect) | 切面就是我们要织入的业务逻辑岑在的环境,简单可以理解城北插入的新业务方法存在的类,在这里可以定义通知(要织入的逻辑) |
切点(Pointcut) | 这是SpringAop启动拦截的点,一个切点可以有许多的连接点,当需要某个方法前织入一段通知,我们就需要使用切点信息来确定在那些连接点上添加通知。within可具体控制到类,execution具体控制到方法。 |
连接点(joinpoint) | 是程序执行的某个特定的位置,例如:类初始化前后,这就是两个位置。 |
通知(advice) | 通知是切面开启后,切面的方法业务,他根据逻辑在真实业务调用之某个连接点开启通知。before、after、afterRunning、afterThrowing、around。 |
目标对象(target) | 我们需要对他进行业务通知的业务对象 |
引介 | 引介是一种特殊的通知。 它为类添加一些属性和方法。这样,即使一个业务类原本没有实现某个接口,通过AOP的引介功能,我们可以动态的为该业务类添加接口的实现逻辑,让业务类成为这个接口的实现类。 |
使用XMl方式运行AOP
定义切面,规定切点,声明通知(剩下的还用我说嘛)
在xml中可以配置的元素
AOP配置元素|用途|备注
aop:advisor|定义Aop的通知器|方式较老目前很少用
aop:aspect|定义一个切面|
aop:before|前置通知 |
aop:after|后置通知 |
aop:around |环绕通知|
aop:after-returning|返回通知|
aop:after-throwing |异常通知|
aop:config|顶层的aop配置元素|aop的配置是以它开始
aop:declare-parents|给通知引入新的额外的接口增强功能|
aop:pointcut|定义切点|通过expression属性配置要拦截的方法或者说要代理的业务。
具体代码实例
xml配置
<!-- 业务对象 -->
<bean id="userService" class="testspringAOP.service.UserServiceImpl"></bean>
<!-- 代理对象 -->
<bean id="taransaction" class="testspringAOP.transaction.Transaction"></bean>
<!-- spring-aop的配置逻辑 -->
<aop:config proxy-target-class="false">
<!-- 声明切面 谁去代理,要去做横切业务的类是新务类 -->
<aop:aspect id="txAspect" ref="taransaction">
<!-- 声明切点 代理的结果,需要代理的具体业务 -->
<aop:pointcut expression="execution(* testspringAOP.service..*.*(..))" id="txPointCut"/>
<!-- 通知,对业务做的改进更新的业务逻辑,分为五种 -->
<!-- 前置 -->
<aop:before method="before" pointcut-ref="txPointCut"/>
<!-- 后置 -->
<aop:after method="after" pointcut-ref="txPointCut"/>
<!-- 最终 -->
<aop:after-returning method="after_returning" pointcut-ref="txPointCut" returning="returnVa"/>
<!-- 异常 -->
<aop:after-throwing method="after_throwing" pointcut-ref="txPointCut" throwing="ex"/>
<!-- 环绕 -->
<aop:around method="around" pointcut-ref="txPointCut"/>
</aop:aspect>
</aop:config>
切面java书写
public void before(JoinPoint jp) {
System.out.println("before:--------------------------->"+"开启事务/前置通知,老业务方法还没有执行/"+
"目标对象的类"+ jp.getTarget().getClass()+
"目标方法的名称"+ jp.getSignature().getName()+
"目标方法的参数"+ jp.getArgs()[0].getClass());
}
public void after(JoinPoint jp) {
System.out.println("after:--------------------------->"+"提交事务/后置通知,老业务方法执行完毕/");
}
public void after_returning(JoinPoint jp,Object returnVa) {
System.out.println("after_returning:--------------------------->"+"最终通知,老业务方法执行了,不一定执行完毕"+"目标方法的返回值"+ returnVa);
}
public void after_throwing(JoinPoint jp,Throwable ex) {
System.out.println("after_throwing:--------------------------->"+"回滚事务/异常通知,老业务方法执行了,但是执行某语句,报异常:"+"目标方法的异常"+ex);
}
public Object around(ProceedingJoinPoint pjp) {
Object returnValue = null;
System.out.println("around:--------------------------->");
try {
System.out.println("around------------------前置逻辑");
returnValue = pjp.proceed();
System.out.println("around-----------------后置逻辑");
} catch (Throwable e) {
System.out.println("around-----------------异常逻辑");
}finally {
System.out.println("around------------------最终逻辑");
}
return returnValue;
}
注意:
- 在最终和异常通知内,一般情况下,是需要对应参数的。所以在xml一定要配置对应的那两个属性,并且属性值要和代码里的形参名一致
- 多个参数时JoinPoint一定要放在一个,具体应给和内部的原理有关。
- 还有其他使用方式,不再一一介绍
其他使用时候的逻辑不做详细概述了,测试时启动spring容器后,获取业务对象直接调用原有逻辑就好。
使用注解的方式
开启注解
@EnableAspectJAutoProxy
,放在类上或者在XML中<aop:aspect-autoproxy/>
就可以启动AspectJ框架的自动代理,这时候Spring才会自动加载代理对象进而使用Aop
使用注解如下:
注解 | 名称 |
---|---|
@Aspect | 注册切面,应用于类 |
@Before | 前置通知 |
@AfterReturning | 最终通知 |
@AfterThrowing | 异常通知,企业中的异常对象最大也就Exception,有时也需要自定义异常 |
@After | 后置通知 |
@Around | 环绕通知 |
@Pointcut | 声明切点 |
切点的指示器
within和execution
- execution控制粒度细,控制到方法上(修饰符 返回值 报名.类名 方法(参数))
- wethin 控制粒度粗,控制到类
除此之外还有arg()、target()
下面是execution的一些举例 - the execution of any public method:
execution(public * *(..))
- the execution of any method with a name beginning with “set”:
execution(* set*(..))
- the execution of any method defined by the AccountService interface:
execution(* com.xyz.service.AccountService.*(..))
- the execution of any method defined in the service package:
execution(* com.xyz.service.*.*(..))
具体代码实例
@Aspect
public class Transaction_ann {
@Pointcut("execution(* testspringAOP.service..*.*(..))")
public void myPoint() {
}
@Before("execution(* testspringAOP.service..*.*(..))")
public void before(JoinPoint jp) {
System.out.println("before:--------------------------->"+"开启事务/前置通知,老业务方法还没有执行/"+
"目标对象的类"+ jp.getTarget().getClass()+
"目标方法的名称"+ jp.getSignature().getName()+
"目标方法的参数"+ jp.getArgs()[0].getClass());
}
@After("myPoint()")
public void after(JoinPoint jp) {
System.out.println("after:--------------------------->"+"提交事务/后置通知,老业务方法执行完毕/");
}
@AfterReturning( value = "myPoint()",returning = "returnVa")
public void after_returning(JoinPoint jp,Object returnVa) {
System.out.println("after_returning:--------------------------->"+"最终通知,老业务方法执行了,不一定执行完毕"+"目标方法的返回值"+ returnVa);
}
@AfterThrowing(value = "myPoint()",throwing = "ex")
public void after_throwing(JoinPoint jp,Throwable ex) {
System.out.println("after_throwing:--------------------------->"+"回滚事务/异常通知,老业务方法执行了,但是执行某语句,报异常:"+"目标方法的异常"+ex);
}
@Around(value = "myPoint()")
public Object around(ProceedingJoinPoint pjp) {
Object returnValue = null;
System.out.println("around:--------------------------->");
try {
System.out.println("around------------------前置逻辑");
returnValue = pjp.proceed();
System.out.println("around-----------------后置逻辑");
} catch (Throwable e) {
System.out.println("around-----------------异常逻辑");
}finally {
System.out.println("around------------------最终逻辑");
}
return returnValue;
}
}
springJDBC
springJDBC模板类的使用
导入第三方包
mysql驱动包
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>版本</version>
</dependency>
spring-jdbc
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>版本</version>
</dependency>
spring-tx
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>版本</version>
</dependency>
第三方数据源(DBCP,C3P0,druid等)
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>版本</version>
</dependency>
将数据源、模板类等交给spring管里
在spring的元数据里配置(将数据库的数据写在了配置文件中)如下
<!-- 将配置文件加载到容器 -->
<context:property-placeholder location="classpath:conf/mysql.properties"/>
<!-- spring jdbc Template -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.jdbcTemplate">
<property name="dataSource" ref="alibabaDataSource"></property>
</bean>
<!-- 数据库连接池 commons-dbcp ,c3p0,proxool,阿里巴巴druid -->
<bean id="alibabaDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<!-- 数据库连接的4项 -->
<property name="driverClassName">
<value>${jdbc_driverClass}</value>
</property>
<property name="url">
<value>${jdbc_url}</value>
</property>
<property name="username">
<value>${jdbc_userName}</value>
</property>
<property name="password">
<value>${jdbc_userPassword}</value>
</property>
</bean>
使用时将容器加载后,获取模版类实例,提供对应的sql然后直接进行对应的增删查
注意在另个方式中可以使用springJDBC内嵌的数据源,然后在dao类继承org.springframework.jdbc.core.support.JdbcDaoSupport类,在使用时通过super.getJdbcTemplate()
来获取对应的数据操作的模板类对象。进而简单操作数据库。
JdbcTemplate模版类的具体方法调用展示
// jdbc模板
@Resource(name="jdbcTemplate")
private JdbcTemplate jdbcTemplate;
public int addBook(String name, float price) {
return jdbcTemplate.update("insert into book value(?,?)",name,price);
}
public int delBook(String name) {
return jdbcTemplate.update("delete from book where name=?",name);
}
public int updateBook(String name, float price) {
return jdbcTemplate.update("update book set price=? where name=?",price,name);
}
public List<Book> getAllBook() {
return jdbcTemplate.query("select * from book", new BookRowMapper());
}
public Book getBook(String name) {
return jdbcTemplate.queryForObject("select * from book where name=?", new BookRowMapper(),name);
}
public float getPrice(String name) {
return jdbcTemplate.queryForObject("select price from book where name=?", Float.class);
}