Spring 知识点——认识、springIOC的使用方式、事务管理(AOP)
Spring 总结
一、认识Spring
Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson 在其著作Expert One-On-One J2EE Development and Design中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。(可以增加辅助理解:让代码更加趋于“高内聚、低耦合”)框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 J2EE 应用程序开发提供集成的框架。Spring使用基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。Spring的核心是控制反转(IoC)和面向切面(AOP)。简单来说,Spring是一个分层的JavaSE/EE full-stack(一站式) 轻量级开源框架。
Spring之前是引用EJB的,简单对EJB了解一下:
小结:a.EJB实现原理: 就是把原来放到客户端实现的代码放到服务器端,并依靠RMI进行通信。
b.RMI实现原理 :就是通过Java对象可序列化机制实现分布计算。
c.服务器集群: 就是通过RMI的通信,连接不同功能模块的服务器,以实现一个完整的功能。
EJB组件:可以理解为:把你编写的软件中那些需要执行制定的任务的类,不放到客户端软件上了,而是给他打成包放到一个服务器上了
EJB 就是将那些"类"放到一个服务器上,用C/S 形式的软件客户端对服务器上的"类"进行调用。
理解RMI 之前,需要理解两个名词:
对象的序列化
分布式计算与RPC
对象的序列化概念:对象的序列化过程就是将对象状态转换成字节流和从字节流恢复对象。相对于将对象转换为0与1的字节流汇编语言;
RPC是 “”Remote Procedure Call“的缩写,也就是远程过程调用;简单理解就是调用远程计算机上的一个函数;”
二者结合就是RMI
RMI中文名称是远程方法调用,它就是利用Java 对象序列化的机制实现分布式计算,实现远程类对象的实例化以及调用的方法。说的更清楚些,就是利用对象序列化来实现远程调用,也就是上面两个概念的结合体,利用这个方法来调用远程的类的时候,就不需要编写Socket 程序了,也不需要把对象进行序列化操作,直接调用就行了非常方便。
优点:这种机制给分布计算的系统设计、编程都带来了极大的方便。只要按照RMI 规则设计程序,可以不必再过问在RMI 之下的网络细节了。也就是说,可以将类似Java 哈西表这样的复杂类型作为一个参数进行传递。
缺点 :如果是较为简单的方法调用,其执行效率也许会比本地执行慢很多,即使和远程Socket机制的简单数据返回的应用相比,也会慢一些,原因是,其在网络间需要传递的信息不仅仅包含该函数的返回值信息,还会包含该对象序列化后的字节内容。
EJB 是以RMI 为基础的
EJB 所谓的"服务群集"的概念。就是将原来在一个计算机上运算的几个类,分别放到其他计算机上去运行,以便分担运行这几个类所需要占用的CPU 和内存资源。同时,也可以将不同的软件功能模块放到不同的服务器上,当需要修改某些功能的时候直接修改这些服务器上的类就行了,修改以后所有客户端的软件都被修改了。
二、Spring的优点
方便解耦,简化开发 (高内聚低耦合)
Spring就是一个大工厂(容器),可以将所有对象创建和依赖关系维护,交给Spring管理
spring工厂是用于生成bean
AOP编程的支持
Spring提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能
声明式事务的支持
只需要通过配置就可以完成对事务的管理,而无需手动编程
方便程序的测试
Spring对Junit4支持,可以通过注解方便的测试Spring程序
方便集成各种优秀框架
Spring不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如:Struts、Hibernate、MyBatis、Quartz等)的直接支持
降低JavaEE API的使用难度
Spring 对JavaEE开发中非常难用的一些API(JDBC、JavaMail、远程调用等),都提供了封装,使这些API应用难度大大降低
为什么要用Spring?像上面说的,高内聚低耦合;自己的理解是通过IOC模式可以彻底解决这种耦合,它把耦合统一放入到xml中,通过一个容器在别的类需要的时候把这个依赖关系形成;即把需要的借口实现注入到需要它的类中,是对“依赖注入”的一种理解。还可以这样去理解,(个人理解)把IOC模式类似看做工厂模式的升级版,看成一个大工厂;即:大工厂要生产的对象都是在XML文件中给出定义,然后利用java的反射编程,再根据XML中给出的类名生成相应的对象:提高灵活性、可维护性;
优点:对象生成放入XML中,需要实现的子类变得很简单;
缺点:对象生成是使用反射编程,在效率上有些损耗;
缺少IDE重构操作,如果在Eclipse或者其它的开发工具里面对类改名字,则需要手动改XML文件;
其中:IOC最大改变不是代码上,而是思想上,“主从换位”,使得程序的整个体系变得灵活;也可以理解为:IOC的实现必须依赖抽象,而不是依赖实现;对象不写死;
Spring控制反转是解决对象创建问题(对象创建交给别人);依赖注入是对象关系的处理(通过set方法依赖注入,设置了value值)
三:Spring技术
1、 Spring的体系结构(借鉴图片)
2、 导入jar包
4 + 1 : 4个核心(beans、core、context、expression) + 1个依赖(commons-loggins…jar)必须要引入的5个!如果缺少,会报错:找不到bean对象;
(具体的运用参考例子代码)
3、依赖注入装配Bean 基于注解
这是从容器中获取Bean的方式之一;注解一个类,使用@注解去取代 xml配置文件。
@Component("id") 取代 <bean id="" class="">
在web开发中,@Component注解提供3种(@功能是一样的)
@Repository :dao层
@Service: service层
@Controller: web层
用set方法注入,可以用:@Value(" 引用值"),里面的引用值可以按照 类型、名称 去注入;
4.生命周期(个人觉得容易忽略的点)
初始化:@PostConstruct 销毁:@PreDestroy
5.作注解使用前提
要添加命名空间,让spring扫描含有注解类;相对于组件扫描,扫描含有注解的类
<!-- 组件扫描,扫描含有注解的类 -->
<context:component-scan basepackage="包名">
</context:component-scan>
四、AOP(面向切面编程)
1、什么是Aop?
AOP是OOP(面向对象编程)的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码
经典应用:事务管理、性能监视、安全检查、缓存 、日志等
Spring AOP使用纯Java实现,不需要专门的编译过程和类加载器,在运行期通过代理方式向目标类织入增强代码
AspectJ是一个基于Java语言的AOP框架,Spring2.0开始,Spring AOP引入对Aspect的支持,AspectJ扩展了Java语言,提供了一个专门的编译器,在编译时提供横向代码的织入;
2、AOP实现原理
aop底层将采用代理机制进行实现。
接口 + 实现类 :spring采用 jdk 的动态代理Proxy。
实现类:spring 采用 cglib字节码增强。
3 AOP术语【掌握】
1.target:目标类,需要被代理的类。例如:UserService
2.Joinpoint(连接点):所谓连接点是指那些可能被拦截到的方法。例如:所有的方法
3.PointCut 切入点:已经被增强的连接点。例如:addUser()
4.advice 通知/增强,增强代码。例如:after、before
5. Weaving(织入):是指把增强advice应用到目标对象target来创建新的代理对象proxy的过程.
6.proxy 代理类
7. Aspect(切面): 是切入点pointcut和通知advice的结合
一个线是一个特殊的面。 一个切入点和一个通知,组成成一个特殊的面。
(具体的理解看例子代码)
五、代码例子及理解
1、概述:
Spring框架,可以解决对象创建以及对象之间依赖关系的一种框架。且可以和其他框架一起使用;Spring与Struts, Spring与hibernate(起到整合(粘合)作用的一个框架)
Spring提供了一站式解决方案:
1) Spring Core spring的核心功能: IOC容器, 解决对象创建及依赖关系
2) Spring Web Spring对web模块的支持。
- 可以与struts整合,让struts的action创建交给spring
- spring mvc模式
3) Spring DAO Spring 对jdbc操作的支持 【JdbcTemplate模板工具类】
4) Spring ORM spring对orm的支持:
既可以与hibernate整合,【session】
也可以使用spring的对hibernate操作的封装
5)Spring AOP 切面编程
6)SpringEE spring 对javaEE其他模块的支持
2、核心配置文件: applicationContext.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" 4 xmlns:context="http://www.springframework.org/schema/context" 5 xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" 6 xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 7 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd 8 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd 9 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> 10 <!--此处添加其它标签--> 11 </beans>
3、获取IOC容器对象的方法如下:(核心代码)
从IOC容器中获取对象有两种方法:@注解 和 <bean> 标签
1 public class TestApp { 2 //一般使用第二种方法,第一中方法了解一下; 3 // 1. 通过工厂类得到IOC容器创建的对象 4 @Test 5 public void testIOC(){ 6 // 原先把拿到另外一个类的对象,一般的方法是:User user = new User(); 7 8 // 现在,把对象的创建交给spring的IOC容器 9 Resource resource = new ClassPathResource("applicationContext.xml"); 10 // 然后,创建容器对象(Bean的工厂),可以理解为: IOC容器 = 工厂类 + applicationContext.xml 11 BeanFactory factory = new XmlBeanFactory(resource); 12 // 得到容器创建的对象,下面以获取User类中的对象为例子; 13 User user = (User) factory.getBean("user"); 14 // 再user去调用方法,此处省略 15 16 } 17 18 //2. (方便)直接得到IOC容器对象 19 @Test 20 public void test2(){ 21 // 得到IOC容器对象 22 ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml"); 23 // 从容器中获取bean,也是获取User类中对象的例子 24 User user = (User) ac.getBean("user"); 25 // 再user去调用方法,此处省略 26 } 27 }
4、bean对象创建的细节(代码中解释,具体结合下面的 5 、 spring IOC 容器去理解)
1 /** 2 * 1) 对象创建: 单例/多例 3 * scope="singleton", 默认值, 即 默认是单例 【service/dao/工具类】 4 * scope="prototype", 多例; 【Action对象】 5 * 6 * 2) 什么时候创建? 7 * scope="prototype" 在用到对象的时候,才创建对象。 8 * scope="singleton" 在启动(容器初始化之前), 就已经创建了bean,且整个应用只有一个。 9 * 3)是否延迟创建 10 * lazy-init="false" 默认为false, 不延迟创建,即在启动时候就创建对象 11 * lazy-init="true" 延迟初始化, 在用到对象的时候才创建对象 12 * (只对单例有效) 13 * 4) 创建对象之后,初始化/销毁 14 * init-method="xx" 【对应对象的xx方法,在对象创建爱之后执行 】 15 * destroy-method="xx" 【在调用容器对象的xx方法时候执行,(容器用实现类)】 16 */ 17 @Test 18 public void testIOC2(){ 19 // 得到IOC容器对象 【用实现类,因为要调用销毁的方法】 20 ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml"); 21 //容器创建,从容器中获取bean;一样以User类为例子; 22 23 User user1 = (User) ac.getBean("user"); 24 User user2 = (User) ac.getBean("user"); 25 26 System.out.println(user1); 27 System.out.println(user2); 28 29 // 销毁容器对象 30 ac.destroy(); 31 }
5、SpringIOC容器
SpringIOC容器是spring核心内容,它的作用主要解决对象的创建和处理对象的依赖关系
创建对象, 有几种方式:
1) 调用无参数构造器
2) 带参数构造器
3) 工厂创建对象
工厂类,静态方法创建对象
工厂类,非静态方法创建对象
理解如下:(工厂的实例方法简单来说就是调用的思想)
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:p="http://www.springframework.org/schema/p" 5 xmlns:context="http://www.springframework.org/schema/context" 6 xsi:schemaLocation=" 7 http://www.springframework.org/schema/beans 8 http://www.springframework.org/schema/beans/spring-beans.xsd 9 http://www.springframework.org/schema/context 10 http://www.springframework.org/schema/context/spring-context.xsd"> 11 12 <!--对象创建的方法如下:(结合上面的获取IOC容器中对象的代码一起看,以User类对象被获取为例) --> 13 14 <!-- 1. 默认无参数构造器 15 <bean id="user1" class="包名.User"></bean> 16 --> 17 18 <!-- 2. 带参数构造器 --> 19 <!-- index可以来控制value输出的顺序 --> 20 <bean id="user2" class="包名.User"> 21 <constructor-arg index="0" type="int" value="100"></constructor-arg> 22 <constructor-arg index="1" type="java.lang.String" value="Jack"></constructor-arg> 23 </bean> 24 25 <!-- 创建字符串,值是"Jack",让其它bean区调用 --> 26 <bean id="str" class="java.lang.String"> 27 <constructor-arg value="Jack"></constructor-arg> 28 </bean> 29 <bean id="user3" class="包名.User"> 30 <constructor-arg index="0" type="int" value="100"></constructor-arg> 31 <constructor-arg index="1" type="java.lang.String" ref="str"></constructor-arg> 32 </bean> 33 34 35 <!-- 3. 工厂类创建对象 --> 36 <!-- # 3.1 工厂类,实例方法(用工厂去调用方法) --> 37 <!-- 先创建工厂 --> 38 <bean id="factory" class="包名.类名"></bean> 39 <!-- 在创建user对象,用factory方的实例方法 --> 40 <bean id="user4" factory-bean="factory" factory-method="实例方法名"></bean> 41 42 43 <!-- # 3.2 工厂类: 静态方法 --> 44 <!-- 45 class 引用工厂对象 46 factory-method 一定是工厂里面的“静态方法” 47 --> 48 <bean id="user" class="包名.类名" factory-method="静态方法名"></bean>
5.1 对象依赖关系
Spring中,如何给对象的属性赋值? 【DI, 依赖注入】
a、 通过构造函数
b、 通过set方法给属性注入值
c、 p名称空间
d、自动装配(了解)
e、 注解
(常用)Set方法注入值
1 <!-- 2 构造函数 3 <bean id="xx" class="包名.类名"> 4 <constructor-arg value="xx"></constructor-arg> 5 </bean> 6 --> 7 <!-- 8 set方法注入值 9 <bean id="xx" class="包名.类名"> 10 <property name="xx" value="xx"></property> 11 </bean> 12 --> 13 <!--对象的注入: name是对象 ref去引用,实例化对象 --> 14 <!-- dao 插入 --> 15 <bean id="xxdao" class="包名. UserDao"></bean> 16 17 <!-- service 插入 --> 18 <bean id="xxService" class="包名.UserService"> 19 <property name="userDao" ref="xxDao"></property> 20 </bean> 21 22 <!-- action 插入 --> 23 <bean id="xxAction" class="包名.UserAction"> 24 <property name="userService" ref="xxService"></property> 25 </bean>
或者另外一种方式去注入对象:内部bean方法
1 <bean id="xx" class="包名.类名"> 2 <property name="xxService"> 3 <bean class="包名.类名"> 4 <property name="xxDao"> 5 <bean class="包名.类名"></bean> 6 </property> 7 </bean> 8 </property> 9 </bean>
p 名称空间注入属性值
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:p="http://www.springframework.org/schema/p" 5 xmlns:context="http://www.springframework.org/schema/context" 6 xsi:schemaLocation=" 7 http://www.springframework.org/schema/beans 8 http://www.springframework.org/schema/beans/spring-beans.xsd 9 http://www.springframework.org/schema/context 10 http://www.springframework.org/schema/context/spring-context.xsd"> 11 12 13 <!-- 14 给对象属性注入值:p 名称空间(方式) 15 (spring3.0以上版本才支持) 16 --> 17 <bean id="xx" class="包名.类名"></bean> 18 19 <bean id="xx2" class="包名.类名" p:name-ref="xx"></bean> 20 21 <bean id="xx3" class="包名.类名" p:name-ref="xx2"></bean> 22 23 24 <!-- 传统的注入: 25 <bean id="xx" class="包名.类名" > 26 <property name="name" value="xxx"></property> 27 </bean> 28 --> 29 <!-- p名称空间优化后 --> 30 <bean id="xx" class="包名.类名" p:name="xx"></bean> 31 32 </beans>
自动装配(一般不使用)
根据名称自动装配:autowire="byName"
- 自动去IOC容器中找与属性名同名的引用的对象,并自动注入
也可以定义到全局, 这样就不用每个bean节点都去写autowire=”byName”
根据类型自动装配:autowire="byType"
必须确保改类型在IOC容器中只有一个对象;否则报错。
总结:
Spring提供的自动装配主要是为了简化配置; 但是不利于后期的维护。(一般不推荐使用)
<!-- 自动装配 --> <bean id="xxDao" class="包名.类名"></bean> <bean id="xxService" class="包名.类名" autowire="byName"></bean> <!-- 根据“名称”自动装配: userAction注入的属性,会去Ioc容器中自动查找与属性同名的对象 --> <bean id="xxAction" class="包名.类名" autowire="byName"></bean>
1 <!--定义全局,就不用每个bean节点写上 autowire="byName"--> 2 <?xml version="1.0" encoding="UTF-8"?> 3 <beans xmlns="http://www.springframework.org/schema/beans" 4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 5 xmlns:p="http://www.springframework.org/schema/p" 6 xmlns:context="http://www.springframework.org/schema/context" 7 xsi:schemaLocation=" 8 http://www.springframework.org/schema/beans 9 http://www.springframework.org/schema/beans/spring-beans.xsd 10 http://www.springframework.org/schema/context 11 http://www.springframework.org/schema/context/spring-context.xsd" default-autowire="byName"> 12 <!--根据名称自动装配(全局)--> 13 <!-- 自动装配 --> 14 <bean id="xx" class="包名.类名"></bean> 15 <bean id="xx2" class="包名.类名"></bean> 16 <bean id="xx3" class="包名.类名"></bean> 17 </beans>
1 <!--必须确保改类型在IOC容器中只有一个对象;否则报错--> 2 <?xml version="1.0" encoding="UTF-8"?> 3 <beans xmlns="http://www.springframework.org/schema/beans" 4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 5 xmlns:p="http://www.springframework.org/schema/p" 6 xmlns:context="http://www.springframework.org/schema/context" 7 xsi:schemaLocation=" 8 http://www.springframework.org/schema/beans 9 http://www.springframework.org/schema/beans/spring-beans.xsd 10 http://www.springframework.org/schema/context 11 http://www.springframework.org/schema/context/spring-context.xsd" default-autowire="byType"> 12 <!-- 自动装配 --> 13 14 <!-- 如果根据类型自动装配: 必须确保IOC容器中只有一个该类型的对象 --> 15 <bean id="xx" class="包名.类名"></bean> 16 17 18 <!-- 报错: 因为上面已经有一个该类型的对象,且使用了根据类型自动装配 19 <bean id="xx" class="包名.类名" autowire="byType"></bean> 20 --> 21 </beans>
注:相对于id不同,有相同的class;
6、注解(从IOC容器中获取对象的另外一种方式:@注解)(推荐使用,方便些,简化容器的配置)
使用注解步骤:
a、先引入context名称空间
xmlns:context="http://www.springframework.org/schema/context"
b、开启注解扫描
<context:component-scan base-package="包名"></context:component-scan>
c、使用注解
通过注解的方式,把对象加入Ioc容器。
创建对象以及处理对象依赖关系,相关的注解:
@Component 指定把一个对象加入IOC容器
@Repository : 在持久层使用
@Service 作用同:在业务逻辑层使用
@Controller 作用同:在控制层使用
@Resource 属性注入
总结:
1、使用注解,可以简化配置,且可以把对象加入IOC容器,及处理依赖关系(DI)
2、注解可以和XML配置一起使用。
六、AOP(事务管理)相关知识点
1、Java的代理模式(3种)(Spring中是动态代理对象)
理解:代理对象与目标对象的关系,可以看做代理对象是对目标对象的扩展,并调用出目标对象;关系如下图:
1.1 静态代理
使用静态代理时,代理对象和被代理对象需要实现相同的接口,反过来理解就是需要先定义接口或者父类;代码例子如下:
接口:Dao.java
1 /** 2 * 接口 3 */ 4 public interface Dao { 5 void save(); 6 }
目标对象:sonDao.java
1 /** 2 * 接口实现 3 * 目标对象 4 */ 5 public class sonDao implements Dao { 6 public void save() { 7 System.out.println("----接口的实现----"); 8 } 9 }
代理对象:DaoProxy.java
1 /** 2 * 代理对象,静态代理 3 */ 4 public class DaoProxy implements Dao{ 5 //接收保存目标对象 6 private Dao target; 7 public DaoProxy(Dao target){ 8 this.target=target; 9 } 10 11 public void save() { 12 System.out.println("开始事务..."); 13 target.save();//执行目标对象的方法 14 System.out.println("提交事务..."); 15 } 16 }
测试类:Test.java
1 /** 2 * 测试类 3 */ 4 public class Test { 5 public static void main(String[] args) { 6 //目标对象 7 sonDao target = new sonDao(); 8 9 //代理对象,把目标对象传给代理对象,建立代理关系 10 DaoProxy proxy = new DaoProxy(target); 11 12 proxy.save();//执行的是代理的方法 13 } 14 }
小结:优点就是在不修改目标对象的基础上对目标功能的一些扩展;
缺点是代理对象和目标对象都要写一样的接口,后续可能会造成代理类太多;
2.2 动态代理
也叫JDK代理,接口代理;其中代理对象,不需要实现接口,它的生成,是利用JDK的API,在动态的内存中构建代理对象;
理解:
首先JDK的proxy方式实现的动态代理,目标对象必须有接口;
代理对象:
1 package XX; 2 3 4 import java.lang.reflect.InvocationHandler; 5 import java.lang.reflect.Method; 6 import java.lang.reflect.Proxy; 7 8 public class ProxyTest2 { 9 10 public static void main(String[] args) { 11 12 final Target target = new Target(); 13 14 //动态创建代理对象 15 //JDK的API中存在一个生成动态代理的方法:newProxyInstance 16 公共接口类 proxy = (公共接口类) Proxy.newProxyInstance( 17 //参数:loader 18 target.getClass().getClassLoader(), 19 //目标对象实现所有接口字节码数组:interfaces 20 target.getClass().getInterfaces(), 21 //具体代理接口: InvocationHandler 22 new InvocationHandler() { 23 @Override 24 //被执行几次?------- 看代理对象调用方法几次 25 //代理对象调用接口相应方法 都是调用invoke 26 /* 27 * proxy:是代理对象 28 * method:代表的是目标方法的字节码对象 29 * args:代表是调用目标方法时参数 30 */ 31 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 32 //反射知识点 33 Object invoke = method.invoke(target, args);//目标对象的相应方法 34 //retrun返回的值给代理对象 35 return invoke; 36 } 37 } 38 ); 39 40 //调用invoke---Method:目标对象的method1方法 args:null 返回值null 41 proxy.method1(); 42 //调用invoke---Method:目标对象的method2方法 args:null 返回值method2 43 String method2 = proxy.method2(); 44 //调用invoke-----Method:目标对象的method3方法 args:Object[]{100} 返回值100 45 int method3 = proxy.method3(100); 46 47 System.out.println(method2); 48 System.out.println(method3); 49 50 } 51 52 }
代码还可以加上一个增强方法的过滤器
1 package XX; 2 3 4 import java.io.IOException; 5 import java.io.UnsupportedEncodingException; 6 import java.lang.reflect.InvocationHandler; 7 import java.lang.reflect.Method; 8 import java.lang.reflect.Proxy; 9 10 import javax.servlet.Filter; 11 import javax.servlet.FilterChain; 12 import javax.servlet.FilterConfig; 13 import javax.servlet.ServletException; 14 import javax.servlet.ServletRequest; 15 import javax.servlet.ServletResponse; 16 import javax.servlet.http.HttpServletRequest; 17 import javax.servlet.http.HttpServletRequestWrapper; 18 19 import com.sun.corba.se.impl.protocol.giopmsgheaders.ReplyMessage_1_0; 20 21 public class XX implements Filter{ 22 23 @Override 24 public void init(FilterConfig filterConfig) throws ServletException { 25 // TODO Auto-generated method stub 26 27 } 28 29 @Override 30 public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 31 throws IOException, ServletException { 32 HttpServletRequest req = (HttpServletRequest) request; 33 34 HttpServletRequest enhanceRequset = (HttpServletRequest) Proxy.newProxyInstance( 35 req.getClass().getClassLoader(), 36 req.getClass().getInterfaces(), 37 new InvocationHandler() { 38 39 @Override 40 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 41 //对getParameter进行增强 42 if(method.getName().equals("getParameter")){ 43 String invoke = (String) method.invoke(req, args);//乱码 44 invoke = new String(invoke.getBytes("iso8859-1"),"UTF-8");//转码 45 return invoke; 46 } 47 return method.invoke(req, args); 48 } 49 }); 50 chain.doFilter(enhanceRequset, response);//放行 51 } 52 53 @Override 54 public void destroy() { 55 // TODO Auto-generated method stub 56 57 } 58 59 60 }
其中还有一个静态方法:
Class<?>[ ] interfaces :目标对象实现的接口的类型,使用泛型方式确认类型
测试类 App.java
1 /** 2 * 测试类 3 */ 4 public class App { 5 public static void main(String[] args) { 6 // 目标对象 7 接口类 target = new 目标对象类(); 8 // 【原始的类型 class 包名.类名】 9 System.out.println(target.getClass()); 10 11 // 给目标对象,创建代理对象 12 接口类 proxy = (接口类) new ProxyFactory(target).getProxyInstance(); 13 // class $Proxy0 内存中动态生成的代理对象 14 System.out.println(proxy.getClass()); 15 16 // 执行方法 【代理对象】 17 proxy.save(); 18 } 19 }
查看生产的代理类(代码):
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true" );
1.3 Cglib 代理
实现的方法是继承,其中要2个jar包:asm-x.x.jar 、 cglib-x.x.x.jar
相对于静态和动态代理比较,它们都需要实现一个接口的目标对象,如果对象只是一个单独的对象,其中没有任何接口的实现,那么这种情况就可以用目标子类的方式去实现代理,这种方式也叫Cglib代理;简称子类代理,从内存中构建子类对象从而实现对目标对象的功能扩展;
Cglib是一个强大的高性能的代码生成包,它的底层是通过使用一个小而块的字节码处理框架ASM来转换字节码并生成新的类。(一般不使用ASM,因为要对JVM内部结构掌握精通)它能在运行期扩展java类与实现java接口;如:Spring AOP和synaop,为他们提供方法的interception(拦截);
Cglib子类代理的实现
先要引入jar包,如:spring-core-3.2.5.jar , 然后在内存中动态构建子类;其中代理的类不能为final,否则报错;目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法;方法是final/static则无法进行代理。
目标对象类(没有接口)
1 /** 2 * 目标对象,没有实现任何接口 3 */ 4 public class XX { 5 6 public void save() { 7 System.out.println("XXXXXX"); 8 } 9 }
代理工厂类
1 /** 2 * Cglib子类代理工厂 3 * 在内存中动态构建一个子类对象 4 */ 5 public class XX implements MethodInterceptor{ 6 //维护目标对象 7 private Object target; 8 9 public XX(Object target) { 10 this.target = target; 11 } 12 13 //给目标对象创建一个代理对象 14 public Object getProxyInstance(){ 15 //1.工具类 16 Enhancer en = new Enhancer(); 17 //2.设置父类 18 en.setSuperclass(target.getClass()); 19 //3.设置回调函数 20 en.setCallback(this); 21 //4.创建子类(代理对象) 22 return en.create(); 23 24 } 25 26 @Override 27 public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { 28 System.out.println("开始事务..."); 29 30 //执行目标对象的方法 31 Object returnValue = method.invoke(target, args); 32 33 System.out.println("提交事务..."); 34 35 return returnValue; 36 } 37 }
测试类
1 /** 2 * 测试类 3 */ 4 public class Test{ 5 6 @Test 7 public void test(){ 8 //目标对象 9 XX target = new XX(); 10 11 //代理对象 12 XX proxy = (XX)new 代理工厂类(target).getProxyInstance(); 13 14 //执行代理对象的方法 15 proxy.save(); 16 } 17 }
DefaultAopProxyFactory的源码(有兴趣可看)
1 // Source code recreated from a .class file by IntelliJ IDEA 2 // (powered by Fernflower decompiler) 3 4 package org.springframework.aop.framework; 5 6 import java.io.Serializable; 7 import java.lang.reflect.Proxy; 8 import org.springframework.aop.SpringProxy; 9 10 public class DefaultAopProxyFactory implements AopProxyFactory, Serializable { 11 public DefaultAopProxyFactory() { 12 } 13 14 public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException { 15 if (!config.isOptimize() && !config.isProxyTargetClass() && !this.hasNoUserSuppliedProxyInterfaces(config)) { 16 return new JdkDynamicAopProxy(config); 17 } else { 18 Class<?> targetClass = config.getTargetClass(); 19 if (targetClass == null) { 20 throw new AopConfigException("TargetSource cannot determine target class: Either an interface or a target is required for proxy creation."); 21 } else { 22 return (AopProxy)(!targetClass.isInterface() && !Proxy.isProxyClass(targetClass) ? new ObjenesisCglibAopProxy(config) : new JdkDynamicAopProxy(config)); 23 } 24 } 25 } 26 27 private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) { 28 Class<?>[] ifcs = config.getProxiedInterfaces(); 29 return ifcs.length == 0 || ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0]); 30 } 31 }
小结:加入容器的目标对象有实现接口,用JDK代理
目标对象没有实现接口,用Cglib代理
目标对象实现了接口,且强制使用cglib代理,则会使用cglib代理。
2、程序中事务控制
2.1 流程
一般是先用户访问,然后Action到Service到Dao;如果调用service执行成功,则说明业务是成功的,因为事务应该在service层统一控制;
2.2 事务控制的初步了解
编程式事务控制:自己手动控制的事务;
Jdbc代码:Conn.setAutoCommite(false); // 设置手动控制事务
Hibernate代码:Session.beginTransaction(); // 开启一个事务
【细粒度的事务控制: 可以对指定的方法、指定的方法的某几行添加事务控制】
(比较灵活,但开发起来比较繁琐: 每次都要开启、提交、回滚.)
声明式事务控制
声明式事务管理就是Spring提供了对事务的管理。
Spring提供了对事务控制的实现。用户如果想用Spring的声明式事务管理,只需要在配置文件中配置即可; 不想使用时直接移除配置。这个实现了对事务控制的最大程度的解耦。
Spring声明式事务管理的核心实现就是基于Aop。
【粗粒度的事务控制: 只能给整个方法应用事务,不可以对方法的某几行应用事务。】(因为aop拦截的是方法。)
Spring声明式事务管理器类:
Jdbc技术:DataSourceTransactionManager
Hibernate技术:HibernateTransactionManager
3、声明式事务管理的实现
首先要引入spring-aop相关的4个jar文件,再引入aop名称空间,因为XML配置方法需要引入,最后引入tx名称空间(事务方式必须引入);
XML去实现
Dao.java类
1 public class Dao { 2 3 // 容器注入JdbcTemplate对象 4 private JdbcTemplate jdbcTemplate; 5 public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { 6 this.jdbcTemplate = jdbcTemplate; 7 } 8 9 public void save(Dept dept){ 10 String sql = "insert into t_dept (deptName) values(?)"; 11 jdbcTemplate.update(sql,dept.getDeptName()); 12 } 13 }
service类
1 public class Service { 2 3 // 容器注入dao对象 4 private Dao deptDao; 5 public void setDao(Dao deptDao) { 6 this.deptDao = deptDao; 7 } 8 9 /* 10 * 事务控制 11 */ 12 public void save(Dept dept){ 13 // 第一次调用 14 deptDao.save(dept); 15 16 int i = 1/0; // 异常: 整个Service.save()执行成功的要回滚 17 18 // 第二次调用 19 deptDao.save(dept); 20 } 21 }
测试类
1 @Test 2 public void test() throws Exception { 3 //容器对象 4 ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml"); 5 6 // 模拟数据 7 Dept dept = new Dept(); 8 dept.setDeptName("测试"); 9 10 Service Service = (Service) ac.getBean("deptService"); 11 Service.save(dept); 12 13 }
bean.xmk (Spring管理配置)
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:p="http://www.springframework.org/schema/p" 5 xmlns:context="http://www.springframework.org/schema/context" 6 xmlns:aop="http://www.springframework.org/schema/aop" 7 xmlns:tx="http://www.springframework.org/schema/tx" 8 xsi:schemaLocation="http://www.springframework.org/schema/beans 9 http://www.springframework.org/schema/beans/spring-beans.xsd 10 http://www.springframework.org/schema/context 11 http://www.springframework.org/schema/context/spring-context.xsd 12 http://www.springframework.org/schema/aop 13 http://www.springframework.org/schema/aop/spring-aop.xsd 14 http://www.springframework.org/schema/tx 15 http://www.springframework.org/schema/tx/spring-tx.xsd"> 16 17 18 <!-- 数据源对象: C3P0连接池 --> 19 <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> 20 <property name="XX" value="com.mysql.jdbc.Driver"></property> 21 <property name="jdbcUrl" value="jdbc:mysql:XX"></property> 22 <property name="user" value="root"></property> 23 <property name="password" value="root"></property> 24 <property name="initialPoolSize" value="XX"></property> 25 <property name="maxPoolSize" value="XX"></property> 26 <property name="maxStatements" value="XX"></property> 27 <property name="acquireIncrement" value="XX"></property> 28 </bean> 29 30 <!-- JdbcTemplate工具类实例 --> 31 <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> 32 <property name="dataSource" ref="dataSource"></property> 33 </bean> 34 35 <!-- dao实例 --> 36 <bean id="deptDao" class="XX"> 37 <property name="jdbcTemplate" ref="jdbcTemplate"></property> 38 </bean> 39 40 <!-- service实例 --> 41 <bean id="deptService" class="XX"> 42 <property name="deptDao" ref="deptDao"></property> 43 </bean> 44 45 <!-- Spring声明式事务管理配置:配置事务管理器类 --> 46 47 <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 48 <property name="dataSource" ref="dataSource"></property> 49 </bean> 50 51 <!-- 配置事务增强 --> 52 <tx:advice id="txAdvice" transaction-manager="txManager"> 53 <tx:attributes> 54 <tx:method name="get*" read-only="true"/> 55 <tx:method name="find*" read-only="true"/> 56 <tx:method name="*" read-only="false"/> 57 </tx:attributes> 58 </tx:advice> 59 60 <!--Aop配置: 拦截哪些方法(切入点表表达式) + 应用上面的事务增强配置 --> 61 <aop:config> 62 <aop:pointcut expression="execution(* cn.itcast.a_tx.DeptService.*())" id="pt"/> 63 <aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/> 64 </aop:config> 65 66 </beans>
还有一种方法:注解(推荐使用,更简单)
先引入Aop相关的jar包文件,再在 bean.xml中指定注解方式实现声明式事务管理及管理器,最后在需要添加事务控制的地方写上:@Transactional ;例如:
应用事务的注解
定义到方法上: 当前方法应用spring的声明式事务
定义到类上: 当前类的所有的方法都应用Spring声明式事务管理;
定义到父类上: 当执行父类的方法时候应用事务。
bean.xml(核心代码)
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:p="http://www.springframework.org/schema/p" 5 xmlns:context="http://www.springframework.org/schema/context" 6 xmlns:aop="http://www.springframework.org/schema/aop" 7 xmlns:tx="http://www.springframework.org/schema/tx" 8 xsi:schemaLocation="http://www.springframework.org/schema/beans 9 http://www.springframework.org/schema/beans/spring-beans.xsd 10 http://www.springframework.org/schema/context 11 http://www.springframework.org/schema/context/spring-context.xsd 12 http://www.springframework.org/schema/aop 13 http://www.springframework.org/schema/aop/spring-aop.xsd 14 http://www.springframework.org/schema/tx 15 http://www.springframework.org/schema/tx/spring-tx.xsd"> 16 17 18 <!-- C3P0连接池 --> 19 <bean id="dataSource" class="xx"> 20 <property name="driverClass" value="com.mysql.jdbc.Driver"></property> 21 <property name="jdbcUrl" value="jdbc:mysql:xx"></property> 22 <property name="user" value="root"></property> 23 <property name="password" value="root"></property> 24 <property name="initialPoolSize" value="xx"></property> 25 <property name="maxPoolSize" value="xx"></property> 26 <property name="maxStatements" value="xx"></property> 27 <property name="acquireIncrement" value="xx"></property> 28 </bean> 29 30 <!--JdbcTemplate工具类实例 --> 31 <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> 32 <property name="dataSource" ref="dataSource"></property> 33 </bean> 34 35 <!-- 事务管理器类 --> 36 <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> 37 <property name="dataSource" ref="dataSource"></property> 38 </bean> 39 40 <!-- 开启注解扫描 --> 41 <context:component-scan base-package="cn.itcast.b_anno"></context:component-scan> 42 43 <!-- 注解方式实现事务: 指定注解方式实现事务 --> 44 <tx:annotation-driven transaction-manager="txManager"/> 45 </beans>
事务传播行为
Propagation.REQUIRED:指定当前的方法必须在事务的环境下执行,如果当前运行的方法,已经存在事务, 就会加入当前的事务;
Propagation.REQUIRED_NEW:必须在事务的环境下执行的方法,如果当前运行的方法,已经存在事务: 事务会挂起; 会始终开启一个新的事务,执行完后; 刚才挂起的事务才继续运行。
Class Log{ Propagation.REQUIRED insertLog(); } Propagation.REQUIRED Void saveDept(){ insertLog(); // 加入当前事务 .. 异常, 会回滚 saveDept(); } Class Log{ Propagation.REQUIRED_NEW insertLog(); } Propagation.REQUIRED Void saveDept(){ insertLog(); // 始终开启事务 .. 异常, 日志不会回滚 saveDept(); }
end...