用注解来向Spring容器注册Bean。需要在applicationContext.xml中注册<context:component-scan base-package=”pagkage1[,pagkage2,…,pagkageN]”/>。
如:在base-package指明一个包
1 <context:component-scan base-package="cn.gacl.java"/>
表明cn.gacl.java包及其子包中,如果某个类的头上带有特定的注解【@Component/@Repository/@Service/@Controller】,就会将这个对象作为Bean注册进Spring容器。也可以在<context:component-scan base-package=” ”/>中指定多个包,如:
1 <context:component-scan base-package="cn.gacl.dao.impl,cn.gacl.service.impl,cn.gacl.action"/>
多个包逗号隔开。
1、@Component
@Component
是所有受Spring 管理组件的通用形式,@Component注解可以放在类的头上,@Component不推荐使用。
2、@Controller
@Controller对应表现层的Bean,也就是Action,例如:
1 @Controller
2 @Scope("prototype")
3 public class UserAction extends BaseAction<User>{
4 ……
5 }
使用@Controller注解标识UserAction之后,就表示要把UserAction交给Spring容器管理,在Spring容器中会存在一个名字为"userAction"的action,这个名字是根据UserAction类名来取的。注意:如果@Controller不指定其value【@Controller】,则默认的bean名字为这个类的类名首字母小写,如果指定value【@Controller(value="UserAction")】或者【@Controller("UserAction")】,则使用value作为bean的名字。
这里的UserAction还使用了@Scope注解,@Scope("prototype")表示将Action的范围声明为原型,可以利用容器的scope="prototype"来保证每一个请求有一个单独的Action来处理,避免struts中Action的线程安全问题。spring 默认scope是单例模式(scope="singleton"),这样只会创建一个Action对象,每次访问都是同一Action对象,数据不安全,struts2 是要求每次次访问都对应不同的Action,scope="prototype" 可以保证当有请求的时候都创建一个Action对象
3、@ Service
@Service对应的是业务层Bean,例如:
1 @Service("userService")
2 public class UserServiceImpl implements UserService {
3 ………
4 }
@Service("userService")注解是告诉Spring,当Spring要创建UserServiceImpl的的实例时,bean的名字必须叫做"userService",这样当Action需要使用UserServiceImpl的的实例时,就可以由Spring创建好的"userService",然后注入给Action:在Action只需要声明一个名字叫“userService”的变量来接收由Spring注入的"userService"即可,具体代码如下:
1 // 注入userService
2 @Resource(name = "userService")
3 private UserService userService;
注意:在Action声明的“userService”变量的类型必须是“UserServiceImpl”或者是其父类“UserService”,否则由于类型不一致而无法注入,由于Action中的声明的“userService”变量使用了@Resource注解去标注,并且指明了其name = "userService",这就等于告诉Spring,说我Action要实例化一个“userService”,你Spring快点帮我实例化好,然后给我,当Spring看到userService变量上的@Resource的注解时,根据其指明的name属性可以知道,Action中需要用到一个UserServiceImpl的实例,此时Spring就会把自己创建好的名字叫做"userService"的UserServiceImpl的实例注入给Action中的“userService”变量,帮助Action完成userService的实例化,这样在Action中就不用通过“UserService userService = new UserServiceImpl();”这种最原始的方式去实例化userService了。如果没有Spring,那么当Action需要使用UserServiceImpl时,必须通过“UserService userService = new UserServiceImpl();”主动去创建实例对象,但使用了Spring之后,Action要使用UserServiceImpl时,就不用主动去创建UserServiceImpl的实例了,创建UserServiceImpl实例已经交给Spring来做了,Spring把创建好的UserServiceImpl实例给Action,Action拿到就可以直接用了。Action由原来的主动创建UserServiceImpl实例后就可以马上使用,变成了被动等待由Spring创建好UserServiceImpl实例之后再注入给Action,Action才能够使用。这说明Action对“UserServiceImpl”类的“控制权”已经被“反转”了,原来主动权在自己手上,自己要使用“UserServiceImpl”类的实例,自己主动去new一个出来马上就可以使用了,但现在自己不能主动去new“UserServiceImpl”类的实例,new“UserServiceImpl”类的实例的权力已经被Spring拿走了,只有Spring才能够new“UserServiceImpl”类的实例,而Action只能等Spring创建好“UserServiceImpl”类的实例后,再“恳求”Spring把创建好的“UserServiceImpl”类的实例给他,这样他才能够使用“UserServiceImpl”,这就是Spring核心思想“控制反转”,也叫“依赖注入”,“依赖注入”也很好理解,Action需要使用UserServiceImpl干活,那么就是对UserServiceImpl产生了依赖,Spring把Acion需要依赖的UserServiceImpl注入(也就是“给”)给Action,这就是所谓的“依赖注入”。对Action而言,Action依赖什么东西,就请求Spring注入给他,对Spring而言,Action需要什么,Spring就主动注入给他。
4、@ Repository
@Repository对应数据访问层Bean ,例如:
1 @Repository(value="userDao")
2 public class UserDaoImpl extends BaseDaoImpl<User> {
3 ………
4 }
@Repository(value="userDao")注解是告诉Spring,让Spring创建一个名字叫“userDao”的UserDaoImpl实例。
当Service需要使用Spring创建的名字叫“userDao”的UserDaoImpl实例时,就可以使用@Resource(name = "userDao")注解告诉Spring,Spring把创建好的userDao注入给Service即可。
1 // 注入userDao,从数据库中根据用户Id取出指定用户时需要用到
2 @Resource(name = "userDao")
3 private BaseDao<User> userDao;
为何要使用Spring
至少在我看来,在项目中引入spring立即可以带来下面的好处
降低组件之间的耦合度,实现软件各层之间的解耦。
可以使用容器提供的众多服务,如:事务管理服务、消息服务等等。当我们使用容器管理事务时,开发人员就不再需要手工控制事务.也不需处理复杂的事务传播。
容器提供单例模式支持,开发人员不再需要自己编写实现代码。
容器提供了AOP技术,利用它很容易实现如权限拦截、运行期监控等功能。
容器提供的众多辅作类,使用这些类能够加快应用的开发,如: JdbcTemplate、 HibernateTemplate。
Spring对于主流的应用框架提供了集成支持,如:集成Hibernate、JPA、Struts等,这样更便于应用的开发。
使用Spring的好处
当使用spring时,我们可以使用容器提供的众多服务
如果使用Spring, 我们就不再需要手工控制事务
Hibernate的事务操作:
- public void save(){
- Session session = sessionFactory.getCurrentSession();
- session.beginTransaction();
- Info info = new Info("传智播客");
- info.setContent("国内实力最强的java培训机构");
- session.save(info );
- session.getTransaction().commit();
- }
JDBC的事务操作:
- Connection conn = null;
- try {
- .......
- conn.setAutoCommit(false);
- Statement stmt = conn.createStatement();
- stmt.executeUpdate("update person where name='叶天'");
- conn.commit();
- .....
- } catch (Exception e) {
- conn.rollback();
- } finally{
- conn.close();
- }
另外,如果使用spring, 我们也不需要处理复杂的事务传播行为
- public void payment(){
- Bean1.update();
- Bean2.save();
- }
如果我们不使用Spring,针对下面这两种业务需求,我们该如何做?
第1种可能的业务需求:要求Bean1.update()和Bean2.save()在同一个事务中执行。
第2种可能的业务需求:要求不管Bean1.update() 的事务是否成功,都需要记录操作日志。
- public class Bean1 {
- public void update(){
- Connection conn = null;
- conn.setAutoCommit(false);
- Statement.executeUpdate("update account set amount=? where id=?");
- }
- }
- public class Bean2 {
- public void save(){
- Connection conn = null;
- conn.setAutoCommit(false);
- Statement.executeUpdate("insert into Log (content) values (?)");
- }
- }
使用Spring,不再需要我们处理复杂的事务传播行为
使用Spring,我们只需要通过声明式的事务属性配置就可以轻松地实现这两种业务需求
1.要求Bean1.update()和Bean2.save()的在同一个事务中执行
2.要求不管Bean1.update() 的事务是否成功,都需要记录日志。
- @Transactional(propagation=Propagation.Required)
- public void payment(){
- Bean1.update();
- Bean2.save();
- }
- public class Bean1 {
- @Transactional(propagation=Propagation.Required)
- public void update(){
- executeUpdate("update account set amount=? where id=?");
- }
- }
- public class Bean2 {
- @Transactional(propagation=Propagation.RequiresNew)
- public void save(){
- executeUpdate("insert into Log (content) values (?)");
- }
- }
轻量级与重量级概念的划分
经常会有同学问到spring属于轻量级框架,还是重量框架?其实划分一个应用是否属于轻量级还是重量级,主要看它使用了多少服务.使用的服务越多,容器要为普通java对象做的工作就越多,必然会影响到应用的发布时间或者是运行性能.
对于spring容器,它提供了很多服务,但这些服务并不是默认为应用打开的,应用需要某种服务,还需要指明使用该服务,如果应用使用的服务很少,如:只使用了spring核心服务,那么我们可以认为此时应用属于轻量级的,如果应用使用了spring提供的大部分服务,这时应用就属于重量级。目前EJB容器就因为它默认为应用提供了EJB规范中所有的功能,所以它属于重量级。
使用Spring需要的jar
到http://www.springsource.org/download下载spring,然后进行解压缩,在解压目录中找到下面jar文件,拷贝到类路径下
dist\spring.jar
lib\jakarta-commons\commons-logging.jar
如果使用了切面编程(AOP),还需要下列jar文件
lib/aspectj/aspectjweaver.jar和aspectjrt.jar
lib/cglib/cglib-nodep-2.1_3.jar
如果使用了JSR-250中的注解,如@Resource/@PostConstruct/@PreDestroy,还需要下列jar文件
lib\j2ee\common-annotations.jar
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"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
- .....
- </beans>
该配置模版可以从spring的参考手册或spring的例子中得到。配置文件的取名可以任意,文件可以存放在任何目录下,但考虑到通用性,一般放在类路径下。
编写spring配置文件时,不能出现帮助信息
由于spring的schema文件位于网络上,如果机器不能连接到网络,那么在编写配置信息时候就无法出现提示信息,解决方法有两种:
1。让机器上网,eclipse会自动从网络上下载schema文件并缓存在硬盘上。
2。手动添加schema文件,方法如下:
windwos->preferences->myeclipse->files and editors->xml->xmlcatalog
点"add",在出现的窗口中的Key Type中选择URI,在location中选"File system",然后在spring解压目录的dist/resources目录中选择spring-beans-2.5.xsd,回到设置窗口的时候不要急着关闭窗口,应把窗口中的Key Type改为Schema location,Key改为http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
实例化spring容器
实例化Spring容器常用的两种方式:
方法一:
在类路径下寻找配置文件来实例化容器
- ApplicationContext ctx = new ClassPathXmlApplicationContext(new String[]{"beans.xml"});
方法二:
在文件系统路径下寻找配置文件来实例化容器
- ApplicationContext ctx = new FileSystemXmlApplicationContext(new String[]{“d:\\beans.xml“});
Spring的配置文件可以指定多个,可以通过String数组传入。
从spring容器中得到bean
当spring容器启动后,因为spring容器可以管理bean对象的创建,销毁等生命周期,所以我们只需从容器直接获取Bean对象就行,而不用编写一句代码来创建bean对象。从容器获取bean对象的代码如下:
- ApplicationContext ctx = new ClassPathXmlApplicationContext(“beans.xml”);
- OrderService service = (OrderService)ctx.getBean("personService");
使用dom4j读取spring配置文件
- public class ItcastClassPathXmlApplicationContext {
- private List<BeanDefinition> beanDefines = new ArrayList<BeanDefinition>();
- public ItcastApplicationContext(String filename){
- init(filename);
- }
- private void init(String filename){
- SAXReader saxReader = new SAXReader();
- Document document=null;
- try{
- URL xmlpath = this.getClass().getClassLoader().getResource(filename);
- document = saxReader.read(xmlpath);
- Map<String,String> nsMap = new HashMap<String,String>();
- nsMap.put("ns","http://www.springframework.org/schema/beans");//加入命名空间
- XPath xsub = document.createXPath("//ns:beans/ns:bean");//创建beans/bean查询路径
- xsub.setNamespaceURIs(nsMap);
- List<Element> beans = xsub.selectNodes(document);
- for(Element element: beans){
- String id = element.attributeValue("id");
- String clazz = element.attributeValue("class");
- BeanDefinition beanDefine = new BeanDefinition(id, clazz);
- beanDefines.add(beanDefine);
- }
- }catch(Exception e){
- e.printStackTrace();
- }
- }
- }
三种实例化bean的方式
1.使用类构造器实例化
- <bean id=“orderService" class="cn.itcast.OrderServiceBean"/>
2.使用静态工厂方法实例化
- <bean id="personService" class="cn.itcast.service.OrderFactory" factory-method="createOrder"/>
- public class OrderFactory {
- public static OrderServiceBean createOrder(){
- return new OrderServiceBean();
- }
- }
3.使用实例工厂方法实例化:
- <bean id="personServiceFactory" class="cn.itcast.service.OrderFactory"/>
- <bean id="personService" factory-bean="personServiceFactory" factory-method="createOrder"/>
- public class OrderFactory {
- public OrderServiceBean createOrder(){
- return new OrderServiceBean();
- }
- }
Bean的作用域
.singleton
在每个Spring IoC容器中一个bean定义只有一个对象实例。默认情况下会在容器启动时初始化bean,但我们可以指定Bean节点的lazy-init=“true”来延迟初始化bean,这时候,只有第一次获取bean会才初始化bean。如:
<bean id="xxx" class="cn.itcast.OrderServiceBean" lazy-init="true"/>
如果想对所有bean都应用延迟初始化,可以在根节点beans设置default-lazy-init=“true“,如下:
<beans default-lazy-init="true“ ...>
.prototype
每次从容器获取bean都是新的对象。
.request
.session
.global session
后面三个域范围是在J2EE中使用到的
指定Bean的初始化方法和销毁方法
- <bean id="xxx" class="cn.itcast.OrderServiceBean" init-method="init" destroy-method="close"/>
注入依赖对象
基本类型对象注入:
- <bean id="orderService" class="cn.itcast.service.OrderServiceBean">
- <constructor-arg index=“0” type=“java.lang.String” value=“xxx”/>//构造器注入
- <property name=“name” value=“zhao/>//属性setter方法注入
- </bean>
注入其他bean:
方式一
- <bean id="orderDao" class="cn.itcast.service.OrderDaoBean"/>
- <bean id="orderService" class="cn.itcast.service.OrderServiceBean">
- <property name="orderDao" ref="orderDao"/>
- </bean>
方式二(使用内部bean,但该bean不能被其他bean使用)
- <bean id="orderService" class="cn.itcast.service.OrderServiceBean">
- <property name="orderDao">
- <bean class="cn.itcast.service.OrderDaoBean"/>
- </property>
- </bean>
集合类型的装配
- public class OrderServiceBean {
- private Set<String> sets = new HashSet<String>();
- private List<String> lists = new ArrayList<String>();
- private Properties properties = new Properties();
- private Map<String, String> maps = new HashMap<String, String>();
- ....//这里省略属性的getter和setter方法
- }
集合类型的装配
- <bean id="order" class="cn.itcast.service.OrderServiceBean">
- <property name="lists">
- <list>
- <value>lihuoming</value>
- </list>
- </property>
- <property name="sets">
- <set>
- <value>set</value>
- </set>
- </property>
- <property name="maps">
- <map>
- <entry key="lihuoming" value="28"/>
- </map>
- </property>
- <property name="properties">
- <props>
- <prop key="12">sss</prop>
- </props>
- </property>
- </bean>
依赖注入
使用构造器注入
使用属性setter方法注入
使用Field注入(用于注解方式)
注入依赖对象可以采用手工装配或自动装配,在实际应用中建议使用手工装配,因为自动装配会产生未知情况,开发人员无法预见最终的装配结果。
1.手工装配依赖对象
2.自动装配依赖对象
依赖注入--手工装配
手工装配依赖对象,在这种方式中又有两种编程方式
1. 在xml配置文件中,通过在bean节点下配置,如
- <bean id="orderService" class="cn.itcast.service.OrderServiceBean">
- <constructor-arg index=“0” type=“java.lang.String” value=“xxx”/>//构造器注入
- <property name=“name” value=“zhao/>//属性setter方法注入
- </bean>
2. 在java代码中使用@Autowired或@Resource注解方式进行装配。但我们需要在xml配置文件中配置以下信息:
- <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-2.5.xsd
- http://www.springframework.org/schema/context
- http://www.springframework.org/schema/context/spring-context-2.5.xsd">
- <context:annotation-config/>
- </beans>
这个配置隐式注册了多个对注释进行解析处理的处理器:AutowiredAnnotationBeanPostProcessor,CommonAnnotationBeanPostProcessor,PersistenceAnnotationBeanPostProcessor,RequiredAnnotationBeanPostProcessor
注: @Resource注解在spring安装目录的lib\j2ee\common-annotations.jar
依赖注入--手工装配
在java代码中使用@Autowired或@Resource注解方式进行装配,这两个注解的区别是:@Autowired 默认按类型装配,@Resource默认按名称装配,当找不到与名称匹配的bean才会按类型装配。
- @Autowired
- private PersonDao personDao;
- @Autowired
- public void setOrderDao(OrderDao orderDao) {
- this.orderDao = orderDao;
- }
@Autowired注解是按类型装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它required属性为false。如果我们想使用按名称装配,可以结合@Qualifier注解一起使用。如下:
- @Autowired @Qualifier("personDaoBean")
- private PersonDao personDao;
@Resource注解和@Autowired一样,也可以标注在字段或属性的setter方法上,但它默认按名称装配。名称可以通过@Resource的name属性指定,如果没有指定name属性,当注解标注在字段上,即默认取字段的名称作为bean名称寻找依赖对象,当注解标注在属性的setter方法上,即默认取属性名作为bean名称寻找依赖对象。
- @Resource(name=“personDaoBean”)
- private PersonDao personDao;
注意:如果没有指定name属性,并且按照默认的名称仍然找不到依赖对象时, @Resource注解会回退到按类型装配。但一旦指定了name属性,就只能按名称装配了。
依赖注入--自动装配依赖对象
对于自动装配,大家了解一下就可以了,实在不推荐大家使用。例子:
<bean id="..." class="..." autowire="byType"/>
autowire属性取值如下:
byType:按类型装配,可以根据属性的类型,在容器中寻找跟该类型匹配的bean。如果发现多个,那么将会抛出异常。如果没有找到,即属性值为null。
byName:按名称装配,可以根据属性的名称,在容器中寻找跟该属性名相同的bean,如果没有找到,即属性值为null。
constructor与byType的方式类似,不同之处在于它应用于构造器参数。如果在容器中没有找到与构造器参数类型一致的bean,那么将会抛出异常。
autodetect:通过bean类的自省机制(introspection)来决定是使用constructor还是byType方式进行自动装配。如果发现默认的构造器,那么将使用byType方式。
通过在classpath自动扫描方式把组件纳入spring容器中管理
前面的例子我们都是使用XML的bean定义来配置组件。在一个稍大的项目中,通常会有上百个组件,如果这些这组件采用xml的bean定义来配置,显然会增加配置文件的体积,查找及维护起来也不太方便。spring2.5为我们引入了组件自动扫描机制,他可以在类路径底下寻找标注了@Component、@Service、@Controller、@Repository注解的类,并把这些类纳入进spring容器中管理。它的作用和在xml文件中使用bean节点配置组件是一样的。要使用自动扫描机制,我们需要打开以下配置信息:
- <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:
- http:
- http:
- http:
- <context:component-scan base-package="cn.itcast"/>
- </beans>
其中base-package为需要扫描的包(含子包)。
@Service用于标注业务层组件
@Controller用于标注控制层组件(如struts中的action)
@Repository用于标注数据访问组件,即DAO组件
@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注
JDK动态代理
- public class JDKProxy implements InvocationHandler {
- private Object targetObject;
- public Object createProxyInstance(Object targetObject){
- this.targetObject = targetObject;
-
-
-
-
-
- return Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(),
- this.targetObject.getClass().getInterfaces(), this);
- }
- public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {
- return method.invoke(this.targetObject, args);
- }
- }
当目标类实现了接口,我们可以使用jdk的Proxy来生成代理对象。
使用CGLIB生成代理
- public class CGLIBProxy implements MethodInterceptor {
- private Object targetObject;
- public Object createProxyInstance(Object targetObject){
- this.targetObject = targetObject;
- Enhancer enhancer = new Enhancer();
- enhancer.setSuperclass(this.targetObject.getClass());
- enhancer.setCallback(this);
- return enhancer.create();
- }
- public Object intercept(Object proxy, Method method, Object[] args,
- MethodProxy methodProxy) throws Throwable {
- return methodProxy.invoke(this.targetObject, args);
- }
- }
CGLIB可以生成目标类的子类,并重写父类非final修饰符的方法。
AOP中的概念
Aspect(切面):指横切性关注点的抽象即为切面,它与类相似,只是两者的关注点不一样,类是对物体特征的抽象,而切面横切性关注点的抽象.
joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点,实际上joinpoint还可以是field或类构造器)
Pointcut(切入点):所谓切入点是指我们要对那些joinpoint进行拦截的定义.
Advice(通知):所谓通知是指拦截到joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知
Target(目标对象):代理的目标对象
Weave(织入):指将aspects应用到target对象并导致proxy对象创建的过程称为织入.
Introduction(引入):在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field.
使用Spring进行面向切面(AOP)编程
要进行AOP编程,首先我们要在spring的配置文件中引入aop命名空间:
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:aop="http://www.springframework.org/schema/aop"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
- http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
- </beans>
Spring提供了两种切面声明方式,实际工作中我们可以选用其中一种:
基于XML配置方式声明切面。
基于注解方式声明切面。
基于注解方式声明切面
首先启动对@AspectJ注解的支持:
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:aop="http://www.springframework.org/schema/aop"
- xsi:schemaLocation="http://www.springframework.org/schema/beans
- http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
- http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
- <aop:aspectj-autoproxy/>
- <bean id="orderservice" class="cn.itcast.service.OrderServiceBean"/>
- <bean id="log" class="cn.itcast.service.LogPrint"/>
- </beans>
基于注解方式声明切面
- @Aspect
- public class LogPrint{
-
- @Pointcut("execution(* cn.itcast.service..*.*(..))")
- private void anyMethod() {}
-
- @Before("anyMethod()&& args(userName)")
- publicvoid doAccessCheck(StringuserName) {
- }
-
- @AfterReturning(pointcut="anyMethod()",returning="revalue")
- publicvoid doReturnCheck(Stringrevalue) {
- }
-
- @AfterThrowing(pointcut="anyMethod()",throwing="ex")
- publicvoid doExceptionAction(Exceptionex) {
- }
-
- @After("anyMethod()")
- publicvoid doReleaseAction(){
- }
-
- @Around("anyMethod()")
- publicObject doBasicProfiling(ProceedingJoinPointpjp)throws Throwable {
- return pjp.proceed();
- }
- }
基于基于XML配置方式声明切面
- public class LogPrint {
- public void doAccessCheck() {}定义前置通知
- public void doReturnCheck() {}定义后置通知
- public void doExceptionAction() {}定义例外通知
- public void doReleaseAction() {}定义最终通知
- public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
- return pjp.proceed();环绕通知
- }
- }
基于XML配置方式声明切面
- <bean id="orderservice" class="cn.itcast.service.OrderServiceBean"/>
- <bean id="log" class="cn.itcast.service.LogPrint"/>
- <aop:config>
- <aop:aspect id="myaop" ref="log">
- <aop:pointcut id="mycut" expression="execution(* cn.itcast.service..*.*(..))"/>
- <aop:before pointcut-ref="mycut" method="doAccessCheck"/>
- <aop:after-returning pointcut-ref="mycut" method="doReturnCheck "/>
- <aop:after-throwing pointcut-ref="mycut" method="doExceptionAction"/>
- <aop:after pointcut-ref="mycut" method=“doReleaseAction"/>
- <aop:around pointcut-ref="mycut" method="doBasicProfiling"/>
- </aop:aspect>
- </aop:config>
Spring+JDBC组合开发
使用Spring+JDBC集成步骤如下:
配置数据源,如:
- <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
- <property name="driverClassName" value="org.gjt.mm.mysql.Driver"/>
- <property name="url" value="jdbc:mysql://localhost:3306/itcast?useUnicode=true&characterEncoding=UTF-8"/>
- <property name="username" value="root"/>
- <property name="password" value="123456"/>
- .....略
- </bean>
配置事务。配置事务时,需要在xml配置文件中引入用于声明事务的tx命名空间(见下页),事务的配置方式有两种:注解方式和基于XML配置方式。
在spring配置文件中引入用于声明事务的tx命名空间
- <beans xmlns="http://www.springframework.org/schema/beans"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xmlns:aop="http://www.springframework.org/schema/aop"
- 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-2.5.xsd
- http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
- http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
- http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">
- </beans>
配置数据源
- <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
- <property name="driverClassName" value="org.gjt.mm.mysql.Driver"/>
- <property name="url" value="jdbc:mysql://localhost:3306/itcast?useUnicode=true&characterEncoding=UTF-8"/>
- <property name="username" value="root"/>
- <property name="password" value="123456"/>
-
- <property name="initialSize" value="1"/>
-
- <property name="maxActive" value="500"/>
-
- <property name="maxIdle" value="2"/>
-
- <property name="minIdle" value="1"/>
- </bean>
使用<context:property-placeholder location=“jdbc.properties”/>属性占位符
使用属性占位符方式配置数据源
- <context:property-placeholder location=“jdbc.properties”/>
- <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
- <property name="driverClassName" value="${driverClassName}"/>
- <property name="url" value="${url}"/>
- <property name="username" value="${username}"/>
- <property name="password" value="${password}"/>
-
- <property name="initialSize" value="${initialSize}"/>
-
- <property name="maxActive" value="${maxActive}"/>
-
- <property name="maxIdle" value="${maxIdle}"/>
-
- <property name="minIdle" value="${minIdle}"/>
- </bean>
采用注解方式配置事务
采用注解方式
- <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
- <property name="dataSource" ref="dataSource"/>
- </bean>
- <!– 采用@Transactional注解方式使用事务 -->
- <tx:annotation-driven transaction-manager="txManager"/>
- @Service @Transactional
- public class PersonServiceBean implements PersonService {
- }
采用基于XML方式配置事务
- <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
- <property name="dataSource" ref="dataSource"/>
- </bean>
- <aop:config>
- <aop:pointcut id="transactionPointcut" expression="execution(* cn.itcast.service..*.*(..))"/>
- <aop:advisor advice-ref="txAdvice" pointcut-ref="transactionPointcut"/>
- </aop:config>
- <tx:advice id="txAdvice" transaction-manager="txManager">
- <tx:attributes>
- <tx:method name="get*" read-only="true" propagation="NOT_SUPPORTED"/>
- <tx:method name="*"/>
- </tx:attributes>
- </tx:advice>
使用JdbcTemplate进行insert/update/delete操作
- @Service @Transactional
- public class PersonServiceBean implements PersonService {
- private JdbcTemplate jdbcTemplate;
- @Resource
- public void setDataSource(DataSource dataSource) {
- this.jdbcTemplate = new JdbcTemplate(dataSource);
- }
-
- public void save(Person person) throws Exception{
- jdbcTemplate.update("insert into person (name) values(?)",
- new Object[]{person.getName()}, new int[]{java.sql.Types.VARCHAR});
- }
- }
使用JdbcTemplate获取一条记录
- @Service @Transactional
- public class PersonServiceBean implements PersonService {
- private JdbcTemplate jdbcTemplate;
- @Resource
- public void setDataSource(DataSource dataSource) {
- this.jdbcTemplate = new JdbcTemplate(dataSource);
- }
- public Person getPerson(Integer id){
- RowMapper rowMapper = new RowMapper(){
- public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
- Person person = new Person();
- person.setId(rs.getInt("id"));
- person.setName(rs.getString("name"));
- return person;
- }
- };
- return (Person)jdbcTemplate.queryForObject("select * from person where id=?",
- new Object[]{id}, new int[]{java.sql.Types.INTEGER}, rowMapper);
- }
- }
使用JdbcTemplate获取多条记录
- @Service @Transactional
- public class PersonServiceBean implements PersonService {
- private JdbcTemplate jdbcTemplate;
- @Resource
- public void setDataSource(DataSource dataSource) {
- this.jdbcTemplate = new JdbcTemplate(dataSource);
- }
- public List<Person> getPersons(){
- RowMapper rowMapper = new RowMapper(){
- public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
- Person person = new Person();
- person.setId(rs.getInt("id"));
- person.setName(rs.getString("name"));
- return person;
- }
- };
- return jdbcTemplate.query("select * from person", rowMapper);
- }
- }
事务传播属性
REQUIRED:业务方法需要在一个事务中运行。如果方法运行时,已经处在一个事务中,那么加入到该事务,否则为自己创建一个新的事务。
NOT_SUPPORTED:声明方法不需要事务。如果方法没有关联到一个事务,容器不会为它开启事务。如果方法在一个事务中被调用,该事务会被挂起,在方法调用结束后,原先的事务便会恢复执行。
REQUIRESNEW:属性表明不管是否存在事务,业务方法总会为自己发起一个新的事务。如果方法已经运行在一个事务中,则原有事务会被挂起,新的事务会被创建,直到方法执行结束,新事务才算结束,原先的事务才会恢复执行。
MANDATORY:该属性指定业务方法只能在一个已经存在的事务中执行,业务方法不能发起自己的事务。如果业务方法在没有事务的环境下调用,容器就会抛出例外。
SUPPORTS:这一事务属性表明,如果业务方法在某个事务范围内被调用,则方法成为该事务的一部分。如果业务方法在事务范围外被调用,则方法在没有事务的环境下执行。
Never:指定业务方法绝对不能在事务范围内执行。如果业务方法在某个事务中执行,容器会抛出例外,只有业务方法没有关联到任何事务,才能正常执行。
NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按REQUIRED属性执行.它使用了一个单独的事务, 这个事务拥有多个可以回滚的保存点。内部事务的回滚不会对外部事务造成影响。它只对DataSourceTransactionManager事务管理器起效
- Connection conn = null;
- try {
- conn.setAutoCommit(false);
- Statement stmt = conn.createStatement();
- stmt.executeUpdate("update person set name='888' where id=1");
- Savepoint savepoint = conn.setSavepoint();
- try{
- conn.createStatement().executeUpdate("update person set name='222' where sid=2");
- }catch(Exception ex){
- conn.rollback(savepoint);
- }
- stmt.executeUpdate("delete from person where id=9");
- conn.commit();
- stmt.close();
- } catch (Exception e) {
- conn.rollback();
- }finally{
- try {
- if(null!=conn && !conn.isClosed()) conn.close();
- } catch (SQLException e) { e.printStackTrace(); }
- }
- }
数据库系统提供了四种事务隔离级
数据库系统提供了四种事务隔离级别供用户选择。不同的隔离级别采用不同的锁类型来实现,在四种隔离级别中,Serializable的隔离级别最高,Read Uncommited的隔离级别最低。大多数据库默认的隔离级别为Read Commited,如SqlServer,当然也有少部分数据库默认的隔离级别为Repeatable Read ,如Mysql
Read Uncommited:读未提交数据(会出现脏读,不可重复读和幻读)。
Read Commited:读已提交数据(会出现不可重复读和幻读)
Repeatable Read:可重复读(会出现幻读)
Serializable:串行化
脏读:一个事务读取到另一事务未提交的更新新据。
不可重复读:在同一事务中,多次读取同一数据返回的结果有所不同。换句话说就是,后续读取可以读到另一事务已提交的更新数据。相反,“可重复读”在同一事务中多次读取数据时,能够保证所读数据一样,也就是,后续读取不能读到另一事务已提交的更新数据。
幻读:一个事务读取到另一事务已提交的insert数据。