转载请注明出处:
http://blog.csdn.net/gane_cheng/article/details/52759099
http://www.ganecheng.tech/blog/52759099.html (浏览效果更好)
Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson创建。简单来说,Spring是一个开源的控制反转(Inversion of Control ,IoC)和面向切面(AOP)的容器框架。它的主要目得是简化企业开发。
Spring是潜在地一站式解决方案,定位于与典型应用相关的大部分基础结构。它也涉及到其他framework没有考虑到的内容。
Spring致力于J2EE应用的各层的解决方案,而不是仅仅专注于某一层的方案。可以说Spring是企业应用开发的“一站式”选择,并贯穿表现层、业务层及持久层。然而,Spring并不想取代那些已有的框架,而是与它们无缝地整合。
1.方便解耦,简化开发
通过Spring提供的IoC容器,我们可以将对象之间的依赖关系交由Spring进行控制,避免硬编码所造成的过度程序耦合。有了Spring,用户不必再为单实例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。
2.AOP编程的支持
通过Spring提供的AOP功能,方便进行面向切面的编程,许多不容易用传统OOP实现的功能可以通过AOP轻松应付。
3.声明式事务的支持
在Spring中,我们可以从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活地进行事务的管理,提高开发效率和质量。
4.方便程序的测试
可以用非容器依赖的编程方式进行几乎所有的测试工作,在Spring里,测试不再是昂贵的操作,而是随手可做的事情。例如:Spring对Junit4支持,可以通过注解方便的测试Spring程序。
5.方便集成各种优秀框架
Spring不排斥各种优秀的开源框架,相反,Spring可以降低各种框架的使用难度,Spring提供了对各种优秀框架(如Struts,Hibernate、Hessian、Quartz)等的直接支持。
6.降低Java EE API的使用难度
Spring对很多难用的Java EE API(如JDBC,JavaMail,远程调用等)提供了一个薄薄的封装层,通过Spring的简易封装,这些Java EE API的使用难度大为降低。
7.Java 源码是经典学习范例
Spring的源码设计精妙、结构清晰、匠心独运,处处体现着大师对Java设计模式灵活运用以及对Java技术的高深造诣。Spring框架源码无疑是Java技术的最佳实践范例。如果想在短时间内迅速提高自己的Java技术水平和应用开发水平,学习和研究Spring源码将会使你收到意想不到的效果。
Spring 框架是一个分层架构,由 7 个定义良好的模块组成。Spring 模块构建在核心容器之上,核心容器定义了创建、配置和管理 bean 的方式。
组成 Spring 框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现。每个模块的功能如下:
核心容器:核心容器提供 Spring 框架的基本功能。核心容器的主要组件是 BeanFactory,它是工厂模式的实现。BeanFactory 使用控制反转 (IOC) 模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。
Spring 上下文:Spring 上下文是一个配置文件,向 Spring 框架提供上下文信息。Spring 上下文包括企业服务,例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。
Spring AOP:通过配置管理特性,Spring AOP 模块直接将面向方面的编程功能集成到了 Spring 框架中。所以,可以很容易地使 Spring 框架管理的任何对象支持 AOP。Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖 EJB 组件,就可以将声明性事务管理集成到应用程序中。
Spring DAO:JDBC DAO 抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量(例如打开和关闭连接)。Spring DAO 的面向 JDBC 的异常遵从通用的 DAO 异常层次结构。
Spring ORM:Spring 框架插入了若干个 ORM 框架,从而提供了 ORM 的对象关系工具,其中包括 JDO、Hibernate 和 MyBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。
Spring Web 模块:Web 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。所以,Spring 框架支持与 Jakarta Struts 的集成。Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。
Spring MVC 框架:MVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口,MVC 框架变成为高度可配置的,MVC 容纳了大量视图技术,其中包括 JSP、Velocity、Tiles、iText 和 POI。
IoC—Inversion of Control,控制反转 ,在 Spring 里的实现是 Dependency Injection ( 依赖注入 ),就是说对象之间的依赖关系在后期通过配置文件(典型为 XML 文件)生成, Spring 里实现了两种注入方式:构造函数注入、 Setter 方法注入。我们可以这样理解这种技术带来的好处,前期我们只需要关注单个对象(组件)的功能实现,具体的业务实现是通过后期配置出来的,不同的配置可以产生不同的业务功能。
AOP—Aspect-oriented programming,面向切面编程。 AOP 大大降低了对象之间的耦合程度,与 IoC 一样,能够通过后期的配置动态为对象增加新的特性,甚至能够为对象动态增加方法。在 Spring 下, AOP 的实现不需要借助专门的 AOP 定义语言,只需要普通的 Java 对象和 XML 配置文件即可。
public class PersonServiceBean {
private PersonDao personDao = new PersonDaoBean();
public void save(Person person){
personDao.save(person);
}
}
PersonDaoBean 是在应用内部创建及维护的。所谓控制反转就是应用本身不负责依赖对象的创建及维护,依赖对象的创建及维护是由外部容器负责的。这样控制权就由应用转移到了外部容器,控制权的转移就是所谓反转。
当我们把依赖对象交给外部容器负责创建,那么PersonServiceBean 类可以改成如下:
public class PersonServiceBean
{
private PersonDao personDao ;
//通过构造器参数,让容器把创建好的依赖对象注入进PersonServiceBean,当然也可以使用setter方法进行注入。
public PersonServiceBean(PersonDao personDao)
{
this.personDao=personDao;
}
public void save(Person person)
{
personDao.save(person);
}
}
所谓依赖注入就是指:在运行期,由外部容器动态地将依赖对象注入到组件中。
如果使用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容器常用的两种方式:
方法一:
在类路径下寻找配置文件来实例化容器
ApplicationContext ctx = new ClassPathXmlApplicationContext(new String[]{"beans.xml"});
方法二:
在文件系统路径下寻找配置文件来实例化容器
ApplicationContext ctx = new FileSystemXmlApplicationContext(new String[]{"d:\\beans.xml"});
Spring的配置文件可以指定多个,可以通过String数组传入。
当spring容器启动后,因为spring容器可以管理bean对象的创建,销毁等生命周期,所以我们只需从容器直接获取Bean对象就行,而不用编写一句代码来创建bean对象。从容器获取bean对象的代码如下:
ApplicationContext ctx = new ClassPathXmlApplicationContext(“beans.xml”);
OrderService service = (OrderService)ctx.getBean("personService");
①使用Set方法实例化
set方式注入的属性一定要加set方法
/**通过set方法注入示例*/
public class IoC_By_Set {
/**注入Integer类型参数*/
private Integer id;
/**注入String类型参数*/
private String name;
/**注入实体Bean*/
private User user;
/**注入数组*/
private Object[] array;
/**注入List集合*/
private List
applicationContext.xml配置
<bean id="ioC_By_Set" class="com.bc.ioc.demo01.IoC_By_Set">
<property name="id" value="1"/>
<property name="name">
<value>value>
property>
<property name="user">
<bean class="com.bc.pojo.User">
<property name="id" value="1"/>
<property name="userName" value="内部Bean"/>
<property name="passWord" value="233"/>
bean>
property>
<property name="array">
<array>
<value>array01value>
<value>array02value>
<value>array03value>
array>
property>
<property name="list">
<list>
<value>list01value>
<value>list02value>
<value>list03value>
list>
property>
<property name="set">
<set>
<value>set01value>
<value>set02value>
<value>set03value>
set>
property>
<property name="map">
<map>
<entry>
<key>
<value>mapKey01value>
key>
<value>mapValue01value>
entry>
<entry>
<key>
<value>mapKey02value>
key>
<value>mapValue02value>
entry>
map>
property>
<property name="properties">
<props>
<prop key="propKey1">propValue1prop>
<prop key="propKey2">propValue2prop>
props>
property>
<property name="emptyValue">
<value>value>
property>
<property name="nullValue">
<null/>
property>
bean>
测试代码
public class IoC_Test {
private ApplicationContext ctx;
@Before
public void load() {
//读取applicationContext.xml配置文件
ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
}
@Test
public void SetTest() {
IoC_By_Set ioc = (IoC_By_Set) ctx.getBean("ioC_By_Set");
ioc.checkAttr();
}
}
②通过构造方法注入各种类型属性
注意:使用JDK1.8版本请将spring相关jar包升级到4.x版本以上,否则不兼容构造方法注入
/** 通过构造方法注入示例 */
public class IoC_By_Constructor {
private Integer id;
private String name;
private User user;
private List list;
public IoC_By_Constructor() {
}
public IoC_By_Constructor(Integer id, String name, User user,
List list) {
this.id = id;
this.name = name;
this.user = user;
this.list = list;
}
/**检查是否注入成功*/
public boolean checkAttr() {
if(id == null) {
return false;
}else {
System.out.println("id:" + id);
}
System.out.println("----------------------------");
if(name == null) {
return false;
}else {
System.out.println("name:" + name);
}
System.out.println("----------------------------");
if(user == null) {
return false;
}else {
System.out.println("user:" + user.getId() + "|" +
user.getUserName() + "|" + user.getPassWord());
}
System.out.println("----------------------------");
if(list == null) {
return false;
}else {
System.out.println("list:");
for (Object object : list) {
System.out.println(object.toString());
}
}
System.out.println("----------------------------");
System.out.println("全部正确!!!");
return true;
}
}
applicationContext.xml配置
<bean id="ioC_By_Constructor" class="com.bc.ioc.demo02.IoC_By_Constructor">
<constructor-arg index="0" value="1" type="java.lang.Integer"/>
<constructor-arg value="P&G"/>
<constructor-arg>
<bean class="com.bc.pojo.User">
<constructor-arg value="1"/>
<constructor-arg value="构造内部Bean"/>
<constructor-arg value="666"/>
bean>
constructor-arg>
<constructor-arg>
<list>
<value>list01value>
<value>list02value>
<value>list03value>
list>
constructor-arg>
bean>
测试代码:
public class IoC_Test {
private ApplicationContext ctx;
@Before
public void load() {
//读取applicationContext.xml配置文件
ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
}
@Test
public void constructorTest() {
IoC_By_Constructor ioc = (IoC_By_Constructor) ctx.getBean("ioC_By_Constructor");
ioc.checkAttr();
}
}
③自动注入(自动装配)
/**自动装配注入*/
public class IoC_By_Auto {
private User user;
/**检查是否注入成功*/
public boolean checkAttr() {
if(user == null) {
return false;
}else {
System.out.println("user:" + user.getId() + "|" +
user.getUserName() + "|" + user.getPassWord());
}
System.out.println("正确!!!");
return true;
}
/**自动装配的属性需要设置set方法*/
public void setUser(User user) {
this.user = user;
}
}
applicationContext.xml配置
<bean id="user" class="com.bc.pojo.User">
<property name="id" value="1"/>
<property name="userName" value="自动装配"/>
<property name="passWord" value="233"/>
bean>
<bean id="ioC_By_Auto" class="com.bc.ioc.demo03.IoC_By_Auto" autowire="byName">bean>
测试代码
public class IoC_Test {
private ApplicationContext ctx;
@Before
public void load() {
//读取applicationContext.xml配置文件
ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
}
@Test
public void AutoTest() {
IoC_By_Auto ioc = (IoC_By_Auto) ctx.getBean("ioC_By_Auto");
ioc.checkAttr();
}
}
④使用P命名空间注入属性
使用 < property > 和命名空间P是等价的。但是使用命名空间P可以省去很多的尖括号。
使用 < property >的效果如下。
id="user2" class="com.bc.pojo.User">
<property name="id" value="1"/>
<property name="userName" value="P"/>
<property name="passWord" value="233"/>
使用命名空间P的效果如下。
id="ioC_By_P" class="com.bc.ioc.demo04.IoC_By_P" p:id="1"
p:name="命名空间" p:user-ref="user2">
/**使用P命名空间注入*/
public class IoC_By_P {
private Integer id;
private String name;
private User user;
/**检查是否注入成功*/
public boolean checkAttr() {
if(id == null) {
return false;
}else {
System.out.println("id:" + id);
}
System.out.println("----------------------------");
if(name == null) {
return false;
}else {
System.out.println("name:" + name);
}
System.out.println("----------------------------");
if(user == null) {
return false;
}else {
System.out.println("user:" + user.getId() + "|" +
user.getUserName() + "|" + user.getPassWord());
}
System.out.println("----------------------------");
System.out.println("全部正确!!!");
return true;
}
//使用P命名空间注入属性需要设置set方法
public void setId(Integer id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setUser(User user) {
this.user = user;
}
}
applicationContext.xml配置
<bean id="user2" class="com.bc.pojo.User">
<property name="id" value="1"/>
<property name="userName" value="P"/>
<property name="passWord" value="233"/>
bean>
<bean id="ioC_By_P" class="com.bc.ioc.demo04.IoC_By_P" p:id="1"
p:name="命名空间" p:user-ref="user2">bean>
测试代码
public class IoC_Test {
private ApplicationContext ctx;
@Before
public void load() {
//读取applicationContext.xml配置文件
ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
}
@Test
public void PTest() {
IoC_By_P ioc = (IoC_By_P) ctx.getBean("ioC_By_P");
ioc.checkAttr();
}
}
⑤ 使用注解方式注入
Spring在2.5以后,提供了基于Annotation(注解)的注入。
1.@Autowired -对成员变量、方法和构造函数进行标注,来完成自动装配的工作,不推荐使用
@Autowired
private PersonDao personDao;//用于字段上
@Autowired
public void setOrderDao(OrderDao orderDao) {//用于属性的setter方法上
this.orderDao = orderDao;
}
2.@Qualifier -配合@Autowired来解决装配多个同类型的bean
@Autowired @Qualifier("personDaoBean")
private PersonDao personDao;
3.@Resource -JSR-250标准注解,作用相当于@Autowired,只不过@Autowired按byType自动注入,而@Resource默认按byName自动注入 ,当找不到与名称匹配的bean才会按类型装配。
@Resource注解和@Autowired一样,也可以标注在字段或属性的setter方法上,但它默认按名称装配。名称可以通过@Resource的name属性指定,如果没有指定name属性,当注解标注在字段上,即默认取字段的名称作为bean名称寻找依赖对象,当注解标注在属性的setter方法上,即默认取属性名作为bean名称寻找依赖对象。
@Resource(name=“personDaoBean”)
private PersonDao personDao;//用于字段上
注意:如果没有指定name属性,并且按照默认的名称仍然找不到依赖对象时, @Resource注解会回退到按类型装配。但一旦指定了name属性,就只能按名称装配了。
4.@PostConstruct -在方法上加上注解@PostConstruct,这个方法就会在Bean初始化之后被Spring容器执行
5.@PreDestroy -在方法上加上注解@PreDestroy,这个方法就会在Bean初始化之后被Spring容器执行
6.@Component -只需要在对应的类上加上一个@Component注解,就将该类定义为一个Bean,不推荐使用,推荐使用更加细化的三种:@Repository、@Service、@Controller
@Repository 存储层Bean
@Service 业务层Bean
@Controller 控制器层Bean
7.@Scope -定义Bean的作用范围
singleton
在每个Spring IoC容器中一个bean定义只有一个对象实例。默认情况下会在容器启动时初始化bean,但我们可以指定Bean节点的lazy-init=“true”来延迟初始化bean,这时候,只有第一次获取bean才会初始化bean。如:
"xxx" class="cn.itcast.OrderServiceBean" lazy-init="true"/>
如果想对所有bean都应用延迟初始化,可以在根节点beans设置default-lazy-init=“true“,如下:
"true" ...> 作用域为protoype时 也是延迟初始化
prototype
每次从容器获取bean都是新的对象。
request
session
global session
首先配置applicationContext.xml开启注解,开启扫描。
<context:component-scan base-package="com.bc.ioc.demo05"/>
实体Bean加注解
@Repository
public class User {
private Integer id = 1;
private String userName = "注解注入";
private String passWord = "233";
public User() {
super();
}
public User(Integer id, String userName, String passWord) {
super();
this.id = id;
this.userName = userName;
this.passWord = passWord;
}
public Integer getId() {
return id;
}
public String getUserName() {
return userName;
}
public String getPassWord() {
return passWord;
}
public void setId(Integer id) {
this.id = id;
}
public void setUserName(String userName) {
this.userName = userName;
}
public void setPassWord(String passWord) {
this.passWord = passWord;
}
}
测试类代码加注解
/**使用注解注入属性*/
@Service("ioC_By_Annotation")
public class IoC_By_Annotation {
@Resource
private User user;
public void setUser(User user) {
this.user = user;
}
/**检查是否注入成功*/
public boolean checkAttr() {
if(user == null) {
return false;
}else {
System.out.println("user:" + user.getId() + "|" +
user.getUserName() + "|" + user.getPassWord());
}
System.out.println("正确!!!");
return true;
}
}
测试代码
public class IoC_Test {
private ApplicationContext ctx;
@Before
public void load() {
//读取applicationContext.xml配置文件
ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
}
@Test
public void annotationTest() {
IoC_By_Annotation ioc = (IoC_By_Annotation) ctx.getBean("ioC_By_Annotation");
ioc.checkAttr();
}
}
⑥ 通过配置静态工厂方法Bean注入
静态工厂代码
/**静态工厂*/
public class StaticFactory {
public static Integer getId() {
return 1;
}
public static String getName() {
return "静态工厂";
}
public static User getUser() {
return new User(1, "工厂User", "666");
}
}
测试类代码
/** 通过静态工厂方式注入 */
public class IoC_By_StaticFactory {
private Integer id;
private String name;
private User user;
/** 检查是否注入成功 */
public boolean checkAttr() {
if (id == null) {
return false;
} else {
System.out.println("id:" + id);
}
System.out.println("----------------------------");
if (name == null) {
return false;
} else {
System.out.println("name:" + name);
}
System.out.println("----------------------------");
if (user == null) {
return false;
} else {
System.out.println("user:" + user.getId() + "|"
+ user.getUserName() + "|" + user.getPassWord());
}
System.out.println("----------------------------");
System.out.println("全部正确!!!");
return true;
}
/**需要为需要注入的属性设置set方法*/
public void setId(Integer id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setUser(User user) {
this.user = user;
}
}
applicationContext.xml配置
<bean id="factory_id" class="com.bc.ioc.demo06.StaticFactory" factory-method="getId"/>
<bean id="factory_name" class="com.bc.ioc.demo06.StaticFactory" factory-method="getName"/>
<bean id="factory_user" class="com.bc.ioc.demo06.StaticFactory" factory-method="getUser"/>
<bean id="ioC_By_StaticFactory" class="com.bc.ioc.demo06.IoC_By_StaticFactory">
<property name="id" ref="factory_id"/>
<property name="name" ref="factory_name"/>
<property name="user" ref="factory_user"/>
bean>
测试代码
public class IoC_Test {
private ApplicationContext ctx;
@Before
public void load() {
//读取applicationContext.xml配置文件
ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
}
@Test
public void staticFactoryTest() {
IoC_By_StaticFactory ioc = (IoC_By_StaticFactory) ctx.getBean("ioC_By_StaticFactory");
ioc.checkAttr();
}
}
⑦ 通过实例工厂方法注入
与静态工厂区别在于实例工厂不是静态的,需要先new 一个实例工厂对象,才可以配置其方法,而new 的这个对象也由spring来管理
工厂代码
/**实例工厂*/
public class Factory {
public Integer getId() {
return 1;
}
public String getName() {
return "实例工厂";
}
public User getUser() {
return new User(1, "实例工厂User", "233");
}
}
测试类代码
/**实例工厂注入*/
public class IoC_By_Factory {
private Integer id;
private String name;
private User user;
/** 检查是否注入成功 */
public boolean checkAttr() {
if (id == null) {
return false;
} else {
System.out.println("id:" + id);
}
System.out.println("----------------------------");
if (name == null) {
return false;
} else {
System.out.println("name:" + name);
}
System.out.println("----------------------------");
if (user == null) {
return false;
} else {
System.out.println("user:" + user.getId() + "|"
+ user.getUserName() + "|" + user.getPassWord());
}
System.out.println("----------------------------");
System.out.println("全部正确!!!");
return true;
}
/**需要为需要注入的属性设置set方法*/
public void setId(Integer id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setUser(User user) {
this.user = user;
}
}
applicationContext.xml配置
<bean id="factory" class="com.bc.ioc.demo07.Factory"/>
<bean id="f_id" factory-bean="factory" factory-method="getId"/>
<bean id="f_name" factory-bean="factory" factory-method="getName"/>
<bean id="f_user" factory-bean="factory" factory-method="getUser"/>
<bean id="ioC_By_Factory" class="com.bc.ioc.demo07.IoC_By_Factory">
<property name="id" ref="f_id"/>
<property name="name" ref="f_name"/>
<property name="user" ref="f_user"/>
bean>
测试类代码
public class IoC_Test {
private ApplicationContext ctx;
@Before
public void load() {
//读取applicationContext.xml配置文件
ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
}
@Test
public void factoryTest() {
IoC_By_Factory ioc = (IoC_By_Factory) ctx.getBean("ioC_By_Factory");
ioc.checkAttr();
}
}
*如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
*如果目标对象实现了接口,可以强制使用CGLIB实现AOP
*如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换
如何强制使用CGLIB实现AOP?
JDK动态代理和CGLIB字节码生成的区别?
public class JDKProxy implements InvocationHandler
{
private Object targetObject;//代理的目标对象
public Object createProxyInstance(Object targetObject)
{
this.targetObject = targetObject;
/*
* 第一个参数设置代码使用的类装载器,一般采用跟目标类相同的类装载器
* 第二个参数设置代理类实现的接口
* 第三个参数设置回调对象,当代理对象的方法被调用时,会委派给该参数指定对象的invoke方法
*/
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);//把方法调用委派给目标对象
}
}
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);
}
}
AOP中的概念
Aspect(切面):指横切性关注点的抽象即为切面,它与类相似,只是两者的关注点不一样,类是对物体特征的抽象,而切面横切性关注点的抽象.
joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点,实际上joinpoint还可以是field或类构造器)
Pointcut(切入点):所谓切入点是指我们要对那些joinpoint进行拦截的定义.
Advice(通知):所谓通知是指拦截到joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知
Interception Around环绕通知:JointPoint前后调用
Before前置通知:JointPoint前调用
After Returning后置通知:JointPoint后调用
Throw异常通知:JoinPoint抛出异常时调用
Introduction最终通知:JointPoint调用完毕后调用
Target(目标对象):代理的目标对象
Weave(织入):指将aspects应用到target对象并导致proxy对象创建的过程称为织入.
Introduction(引入):在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field.
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)")//定义前置通知
public void doAccessCheck(String userName) { }
@AfterReturning(pointcut="anyMethod()",returning="revalue")//定义后置通知
public void doReturnCheck(String revalue) { }
@AfterThrowing(pointcut="anyMethod()", throwing="ex")//定义例外通知
public void doExceptionAction(Exception ex) { }
@After("anyMethod()")//定义最终通知
public void doReleaseAction() { }
@Around("anyMethod()")//环绕通知
public Object doBasicProfiling(ProceedingJoinPoint pjp) 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();
}
}
id="orderservice" class="cn.itcast.service.OrderServiceBean"/>
id="log" class="cn.itcast.service.LogPrint"/>
id="myaop" ref="log">
id="mycut" expression="execution(* cn.itcast.service..*.*(..))"/>
before pointcut-ref="mycut" method="doAccessCheck"/>
after-returning pointcut-ref="mycut" method="doReturnCheck "/>
after-throwing pointcut-ref="mycut" method="doExceptionAction"/>
after pointcut-ref="mycut" method=“doReleaseAction"/>
" method=" doBasicProfiling"/>
采用注解方式配置事务
id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
-->
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>
传播行为 | 含义 |
---|---|
PROPAGATION_REQUIRED | 支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。 |
PROPAGATION_SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行。 |
PROPAGATION_MANDATORY | 支持当前事务,如果当前没有事务,就抛出异常。 |
PROPAGATION_REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起。 |
PROPAGATION_NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
PROPAGATION_NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
PROPAGATION_NESTED | 如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。 |
① PROPAGATION_REQUIRED
假如当前正要执行的事务不在另外一个事务里,那么就起一个新的事务
比如说,ServiceB.methodB的事务级别定义为PROPAGATION_REQUIRED, 那么由于执行ServiceA.methodA的时候,ServiceA.methodA已经起了事务,这时调用ServiceB.methodB,ServiceB.methodB看到自己已经运行在ServiceA.methodA的事务内部,就不再起新的事务。而假如ServiceA.methodA运行的时候发现自己没有在事务中,他就会为自己分配一个事务。这样,在ServiceA.methodA或者在ServiceB.methodB内的任何地方出现异常,事务都会被回滚。即使ServiceB.methodB的事务已经被提交,但是ServiceA.methodA在接下来fail要回滚,ServiceB.methodB也要回滚。
② PROPAGATION_SUPPORTS
如果当前在事务中,即以事务的形式运行,如果当前不再一个事务中,那么就以非事务的形式运行
③ PROPAGATION_MANDATORY
必须在一个事务中运行。也就是说,他只能被一个父事务调用。否则,他就要抛出异常。
④ PROPAGATION_REQUIRES_NEW
这个就比较绕口了。 比如我们设计ServiceA.methodA的事务级别为PROPAGATION_REQUIRED,ServiceB.methodB的事务级别为PROPAGATION_REQUIRES_NEW,那么当执行到ServiceB.methodB的时候,ServiceA.methodA所在的事务就会挂起,ServiceB.methodB会起一个新的事务,等待ServiceB.methodB的事务完成以后,他才继续执行。他与PROPAGATION_REQUIRED 的事务区别在于事务的回滚程度了。因为ServiceB.methodB是新起一个事务,那么就是存在两个不同的事务。如果ServiceB.methodB已经提交,那么ServiceA.methodA失败回滚,ServiceB.methodB是不会回滚的。如果ServiceB.methodB失败回滚,如果他抛出的异常被ServiceA.methodA捕获,ServiceA.methodA事务仍然可能提交。
⑤ PROPAGATION_NOT_SUPPORTED
当前不支持事务。比如ServiceA.methodA的事务级别是PROPAGATION_REQUIRED ,而ServiceB.methodB的事务级别是PROPAGATION_NOT_SUPPORTED ,那么当执行到ServiceB.methodB时,ServiceA.methodA的事务挂起,而他以非事务的状态运行完,再继续ServiceA.methodA的事务。
⑥ PROPAGATION_NEVER
不能在事务中运行。假设ServiceA.methodA的事务级别是PROPAGATION_REQUIRED, 而ServiceB.methodB的事务级别是PROPAGATION_NEVER ,那么ServiceB.methodB就要抛出异常了。
⑦ PROPAGATION_NESTED
理解Nested的关键是savepoint。他与PROPAGATION_REQUIRES_NEW的区别是,PROPAGATION_REQUIRES_NEW另起一个事务,将会与他的父事务相互独立,而Nested的事务和他的父事务是相依的,他的提交是要等和他的父事务一块提交的。也就是说,如果父事务最后回滚,他也要回滚的。
而Nested事务的好处是他有一个savepoint。
隔离级别 | 含义 |
---|---|
ISOLATION_DEFAULT | 这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别.另外四个与JDBC的隔离级别相对应 |
ISOLATION_READ_UNCOMMITTED | 这是事务最低的隔离级别,它充许别外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻像读 |
ISOLATION_READ_COMMITTED | 保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。这种事务隔离级别可以避免脏读出现,但是可能会出现不可重复读和幻像读。 |
ISOLATION_REPEATABLE_READ | 这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。 |
ISOLATION_SERIALIZABLE | 这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。除了防止脏读,不可重复读外,还避免了幻像读。 |
脏读:一个事务读取到另一事务未提交的更新新据。
不可重复读:在同一事务中,多次读取同一数据返回的结果有所不同。换句话说就是,后续读取可以读到另一事务已提交的更新数据。相反,“可重复读”在同一事务中多次读取数据时,能够保证所读数据一样,也就是,后续读取不能读到另一事务已提交的更新数据。
幻读:一个事务读取到另一事务已提交的insert数据。
http://baike.baidu.com/item/spring
http://v.itcast.cn/course/41.html
http://blog.csdn.net/voyage_mh1987/article/details/5755729
http://blog.csdn.net/xiaohai0504/article/details/6832990
http://blog.csdn.net/qq_32588349/article/details/51554379
http://blog.csdn.net/u011285162/article/details/19247711
http://blog.csdn.net/paincupid/article/details/48185597
http://blog.csdn.net/it_wangxiangpan/article/details/24180085
http://docs.spring.io/spring-framework/docs/current/spring-framework-reference/html/overview.html