spring快速入门

常用技巧

类上注解

@Component(value=”userDao”)
@Component取代 <bean class="">
@Component(“id”) 取代 <bean id="" class="">


web开发,提供3个@Component注解衍生注解(功能一样)取代<bean class="">
@Repository :dao层
@Service:service层
@Controller:web层

Bean 的作用范围

@Scope(scopeName=”prototype”)
@Scope(“prototype”) 多例

单例(singleton):在整个应用中,只创建bean的一个实例。
原型(prototype):每次注入或者通过Spring应用上下文获取的时候,都会创建一个新的bean实例。
会话(session):在Web应用中,为每个会话创建一个bean实例。
请求(rquest):在Web应用中,为每个请求创建一个bean实例。
globalsession – 应用在Web项目中,多服务器间的session

依赖注入

给私有字段设置,也可以给setter方法设置

@Value("tom")
private String name;
@Value(value="man")
private String sex;

//@Autowired默认是按照类型装配注入的,如果想按照名称来转配注入,则需要结合@Qualifier一起使用
@Autowired
private Car car;

//按照名称来注入
@Qualifier("bus")
private Bus bus;

//@Resource默认是按照名称来装配注入的,只有当找不到与名称匹配的bean才会按照类型来装配注入
//@Resource(name=”userService”)
@Resource("fly")
private Plane plane;
//和在属性上赋值是一样的效果
@Value("tom")
public void setName(String name){
    this.name=name;
}

生命周期

初始化:@PostConstruct
销毁:@PreDestroy

class user{

@PostConstruct //在对象被创建后调用.init-method
public void init(){
        System.out.println("我是初始化方法!");
    }

@PreDestroy //在销毁之前调用.destory-method
public void destory(){
        System.out.println("我是销毁方法!");
    }
}

spring测试

//帮我们创建容器
@RunWith(SpringJUnit4ClassRunner.class)
//指定配置文件路径
@ContextConfiguration("classpath:applicationContext.xml")
public class Demo2 {

    @Resource(name="userService")
    private  UserService UserService;

    @Test
    public void run1(){

        UserService.sayHell();
    }
}

spring简介

Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。Spring的核心是控制反转(IoC)和面向切面(AOP)。Spring就是一个大工厂(容器),可以将所有对象创建和依赖关系维护,交给Spring管理,spring工厂是用于生成bean。

为了降低Java开发的复杂性,Spring采取了以下4种关键策略:
基于POJO的轻量级和最小侵入性编程;
通过依赖注入和面向接口实现松耦合;
基于切面和惯例进行声明式编程;
通过切面和模板减少样板式代码。

Spring会避免自己的API入侵你的程序,最坏的情况是在你的类中使用注解。Spring有两个核心,DI和AOP。
AOP 可以在不修改源代码的前提下,对程序进行增强!!

Spring框架结构

这里写图片描述

 Spring核心容器

容器是Spring框架最核心的部分,它管理着Spring应用中bean的创建、配置和管理。在该模块中,包括了Spring bean工厂,它为Spring提供了DI的功能。基于bean工厂,我们还会发现有多种Spring应用上下文的实现,每一种都提供了配置Spring的不同方式。所有的Spring模块都构建于核心容器之上。当你配置应用时,其实你隐式地使用了这些类。

Spring的AOP模块

在AOP模块中,Spring对面向切面编程提供了丰富的支持。这个模块是Spring应用系统中开发切面的基础。与DI一样,AOP可以帮助应用对象解耦。借助于AOP,可以将遍布系统的关注点(例如事务和安全)从它们所应用的对象中解耦出来。

数据访问与集成

使用JDBC编写代码通常会导致大量的样板式代码,例如获得数据库连接、创建语句、处理结果集到最后关闭数据库连接。Spring的JDBC和DAO(Data Access Object)模块抽象了这些样板式代码,使我们的数据库代码变得简单明了,还可以避免因为关闭数据库资源失败而引发的问题。该模块在多种数据库服务的错误信息之上构建了一个语义丰富的异常层,以后我们再也不需要解释那些隐晦专有的SQL错误信息了!
对于那些更喜欢ORM(Object-Relational Mapping)工具而不愿意直接使用JDBC的开发者,Spring提供了ORM模块。Spring的ORM模块建立在对DAO的支持之上,并为多个ORM框架提供了一种构建DAO的简便方式。Spring没有尝试去创建自己的ORM解决方案,而是对许多流行的ORM框架进行了集成,包括Hibernate、Java Persisternce API、Java Data Object和iBATIS SQL Maps。Spring的事务管理支持所有的ORM框架以及JDBC。
除此之外,本模块会使用Spring AOP模块为Spring应用中的对象提供事务管理服务。

Web与远程调用

MVC(Model-View-Controller)模式是一种普遍被接受的构建Web应用的方法,它可以帮助用户将界面逻辑与应用逻辑分离。Java从来不缺少MVC框架,Apache的Struts、JSF、WebWork和Tapestry都是可选的最流行的MVC框架。虽然Spring能够与多种流行的MVC框架进行集成,但它的Web和远程调用模块自带了一个强大的MVC框架,有助于在Web层提升应用的松耦合水平。
除了面向用户的Web应用,该模块还提供了多种构建与其他应用交互的远程调用方案。Spring远程调用功能集成了RMI(Remote Method Invocation)、Hessian、Burlap、JAX-WS,同时Spring还自带了一个远程调用框架:HTTP invoker。Spring还提供了暴露和使用REST API的良好支持。

Instrumentation

Spring的Instrumentation模块提供了为JVM添加代理(agent)的功能。具体来讲,它为Tomcat提供了一个织入代理,能够为Tomcat传递类文件,就像这些文件是被类加载器加载的一样。
如果这听起来有点难以理解,不必对此过于担心。这个模块所提供的Instrumentation使用场景非常有限

测试

鉴于开发者自测的重要性,Spring提供了测试模块以致力于Spring应用的测试。
通过该模块,你会发现Spring为使用JNDI、Servlet和Portlet编写单元测试提供了一系列的mock对象实现。对于集成测试,该模块为加载Spring应用上下文中的bean集合以及与Spring上下文中的bean进行交互提供了支持。

Spring的核心

IoC控制反转

之前开发中,直接new一个对象即可。学习spring之后,将由Spring创建对象实例–> IoC 控制反转(Inverse of Control)之后需要实例对象时,从spring工厂(容器)中获得,需要将实现类的全限定名称配置到xml文件中,或者使用注解代替xml。spring的配置文件约定的名字是applicationContext.xml

public interface UserService {

    public void addUser();

}

//---------------------------------------------------
public class UserServiceImpl implements UserService {

    @Override
    public void addUser() {
        System.out.println(" add user");
    }

}
//----------------------配置文件xml----------------------

<?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.xsd">
    <!-- 配置service 
        <bean> 配置需要创建的对象
            id :用于之后从spring容器获得实例时使用的,可以随便取名字,但是最好和类名统一。
            class :需要创建实例的全限定类名
    -->
<bean id="userService" class="com.hero.show.UserServiceImpl"></bean>
</beans>
//----------------------------------------------------
@Test
    public void demo02(){
        //从spring容器获得
        //1 获得容器
        String xmlPath = "com/hero/show/applicationContext.xml";
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
        //2获得内容 --不需要自己new,都是从spring容器获得
        UserService userService = (UserService) applicationContext.getBean("userService");
        userService.addUser();

    }

以上就是spring的IOC,把创建bean实例的权利反转给spring,就叫控制反转。上面的程序实际开发中并不会用到。用来了解原理。

DI依赖注入

DI: Dependency Injection ,依赖注入
is a :是一个,Teacher继承person,老师是一个人。
has a:有一个,成员变量,依赖。五年级三班有一个老师。班级类依赖成员变量 Teacher teacher。

你的程序需要一个猫对象,你如果自己new一个黑猫,白猫,这样耦合太高。使用spring的DI,让Spring来帮助你创建对象,把黑猫的对象注入到你的程序中,如果需要改变对象,只需要把声明的黑猫变成白猫,而不需要改变你的 代码。
class B {
private A a; //B类依赖A类
}
依赖:一个对象需要使用另一个对象
注入:如果你有setter方法,spring会根据你set方法的名称来注入属性

装配Bean

自动装配Bean

Spring从两个角度来实现自动化装配:

1.组件扫描(component scanning):Spring会自动发现应用上下文中所创建的bean。
2.自动装配(autowiring):Spring自动满足bean之间的依赖。
组件扫描和自动装配组合在一起就能发挥出强大的威力,它们能够将你的显式配置降低到最少。

高级装配

bean的作用域

在默认情况下,Spring应用上下文中所有bean都是作为以单例(singleton)的形式创建的。也就是说,不管给定的一个bean被注入到其他bean多少次,每次所注入的都是同一个实例。
有时候,可能会发现,你所使用的类是易变的(mutable),它们会保持一些状态,因此重用是不安全的。在这种情况下,将class声明为单例的bean就不是什么好主意了,因为对象会被污染,稍后重用的时候会出现意想不到的问题。


Spring定义了多种作用域,可以基于这些作用域创建bean,包括:
单例(Singleton):在整个应用中,只创建bean的一个实例。
原型(Prototype):每次注入或者通过Spring应用上下文获取的时候,都会创建一个新的bean实例。
会话(Session):在Web应用中,为每个会话创建一个bean实例。
请求(Rquest):在Web应用中,为每个请求创建一个bean实例。
globalsession – 应用在Web项目中,多服务器间的session
单例是默认的作用域,但是正如之前所述,对于易变的类型,这并不合适。如果选择其他的作用域,要使用@Scope注解,它可以与@Component或@Bean一起使用。


使用会话和请求作用域
在Web应用中,如果能够实例化在会话和请求范围内共享的bean,那将是非常有价值的事情。例如,在典型的电子商务应用中,可能会有一个bean代表用户的购物车。如果购物车是单例的话,那么将会导致所有的用户都会向同一个购物车中添加商品。另一方面,如果购物车是原型作用域的,那么在应用中某一个地方往购物车中添加商品,在应用的另外一个地方可能就不可用了,因为在这里注入的是另外一个原型作用域的购物车。就购物车bean来说,会话作用域是最为合适的,因为它与给定的用户关联性最大。要指定会话作用域,我们可以使用@Scope注解,它的使用方式与指定原型作用域是相同的;

运行时值注入

有些属性的数值,需要程序运行的时候,从配置文件中读取注入。

Spring提供了两
种在运行时求值的方式:
1.属性占位符(Property placeholder)。
2.Spring表达式语言(SpEL)。不常用。

AOP

什么是AOP

在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP(面向对象编程)的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码.
经典应用:事务管理、性能监视、安全检查、缓存 、日志等.
Spring AOP使用纯Java实现,不需要专门的编译过程和类加载器,在运行期通过代理方式向目标类织入增强代码.
AspectJ是一个基于Java语言的AOP框架,Spring2.0开始,Spring AOP引入对Aspect的支持,AspectJ扩展了Java语言,提供了一个专门的编译器,在编译时提供横向代码的织入.

AOP实现原理

Srping框架的AOP技术底层也是采用的代理技术,代理的方式提供了两种

1.基于JDK的动态代理

必须是面向接口的,只有实现了具体接口的类才能生成代理对象

2.基于CGLIB动态代理

对于没有实现了接口的类,也可以产生代理,产生这个类的子类的方式


Spring的传统AOP中根据类是否实现接口,来采用不同的代理方式

1.如果实现类接口,使用JDK动态代理完成AOP
2.如果没有实现接口,采用CGLIB动态代理完成AOP

JDK的动态代理(代码了解,理解原理)
1. 使用Proxy类来生成代理对象的一些代码如下:
    /**
     * 使用JDK的方式生成代理对象
     * @author Administrator
     */
    public class MyProxyUtils {
        public static UserDao getProxy(final UserDao dao) {
            // 使用Proxy类生成代理对象
            UserDao proxy = (UserDao) Proxy.newProxyInstance(dao.getClass().getClassLoader(),
                    dao.getClass().getInterfaces(), new InvocationHandler() {

                        // 代理对象方法一直线,invoke方法就会执行一次
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            if("save".equals(method.getName())){
                                System.out.println("记录日志...");
                                // 开启事务
                            }
                            // 提交事务
                            // 让dao类的save或者update方法正常的执行下去
                            return method.invoke(dao, args);
                        }
                    });
            // 返回代理对象
            return proxy;
        }
    }
CGLIB的代理技术(代码了解)
1. 引入CBLIB的开发包
    * 如果想使用CGLIB的技术来生成代理对象,那么需要引入CGLIB的开发的jar包,在Spring框架核心包中已经引入了CGLIB的开发包了。所以直接引入Spring核心开发包即可!

2. 编写相关的代码
    public static OrderDaoImpl getProxy(){
        // 创建CGLIB核心的类
        Enhancer enhancer = new Enhancer();
        // 设置父类
        enhancer.setSuperclass(OrderDaoImpl.class);
        // 设置回调函数
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object obj, Method method, Object[] args,
                    MethodProxy methodProxy) throws Throwable {
                if("save".equals(method.getName())){
                    // 记录日志
                    System.out.println("记录日志了...");
                }
                return methodProxy.invokeSuper(obj, args);
            }
        });
        // 生成代理对象
        OrderDaoImpl proxy = (OrderDaoImpl) enhancer.create();
        return proxy;
    }

什么是面向切面

如果要重用通用功能的话,最常见的面向对象技术是继(inheritance)或委托(delegation)。但是,如果在整个应用中都使用相同的基类,继承往往会导致一个脆弱的对象体系;而使用委托可能需要对委托对象进行复杂的调用。
切面提供了取代继承和委托的另一种可选方案,而且在很多场景下更清晰简洁。在使用面向切面编程时,我们仍然在一个地方定义通用功能,但是可以通过声明的方式定义这个功能要以何种方式在何处应用,而无需修改受影响的类。横切关注点可以被模块化为特殊的类,这些类被称为切面(aspect)。这样做有两个好处:首先,现在每个关注点都集中于一个地方,而不是分散到多处代码中;其次,服务模块更简洁,因为它们只包含主要关注点(或核心功能)的代码,而次要关注点的代码被转移到切面中了。

这里写图片描述


通知(Advice)

Advice(通知/增强) – 所谓通知是指拦截到Joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)
当抄表员出现在我们家门口时,他们要登记用电量并回去向电力公司报告。显然,他们必须有一份需要抄表的住户清单,他们所汇报的信息也很重要,但记录用电量才是抄表员的主要工作。类似地,切面也有目标——它必须要完成的工作。在AOP术语中,切面的工作被称为通知。
通知定义了切面是什么以及何时使用。除了描述切面要完成的工作,通知还解决了何时执行这个工作的问题。它应该应用在某个方法被调用之前?之后?之前和之后都调用?还是只在方法抛出异常时调用?
Spring切面可以应用5种类型的通知:
前置通知(Before):在目标方法被调用之前调用通知功能;
后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么;
返回通知(After-returning):在目标方法成功执行之后调用通知;
异常通知(After-throwing):在目标方法抛出异常后调用通知;
环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。

连接点(Join point) 

Joinpoint(连接点) – 所谓连接点是指那些被拦截到的点。在spring中,这些点指的是类中的方法(所有的方法都可以叫连接点),因为spring只支持方法类型的连接点
电力公司为多个住户提供服务,甚至可能是整个城市。每家都有一个电表,这些电表上的数字都需要读取,因此每家都是抄表员的潜在目标。抄表员也许能够读取各种类型的设备,但是为了完成他的工作,他的目标应该房屋内所安装的电表。
同样,我们的应用可能也有数以千计的时机应用通知。这些时机被称为连接点。连接点是在应用执行过程中能够插入切面的一个点。这个点可以是调用方法时、抛出异常时、甚至修改一个字段时。切面代码可以利用这些点插入到应用的正常流程之中,并添加新的行为。

切点(Poincut) 

Pointcut(切入点) – 所谓切入点是指我们要对哪些Joinpoint进行拦截的定义。对部分连接点进行切入,进行增强,被增强的方法叫切入点
如果让一位抄表员访问电力公司所服务的所有住户,那肯定是不现实的。实际上,电力公司为每一个抄表员都分别指定某一块区域的住户。类似地,一个切面并不需要通知应用的所有连接点。切点有助于缩小切面所通知的连接点的范围。
如果说通知定义了切面的“什么”和“何时”的话,那么切点就定义了“何处”。切点的定义会匹配通知所要织入的一个或多个连接点。我们通常使用明确的类和方法名称,或是利用正则表达式定义所匹配的类和方法名称来指定这些切点。有些AOP框架允许我们创建动态的切点,可以根据运行时的决策(比如方法的参数值)来决定是否应用通知。

切面(Aspect) 

Aspect(切面) – 是切入点和通知的结合,以后咱们自己来编写和配置的
当抄表员开始一天的工作时,他知道自己要做的事情(报告用电量)和从哪些房屋收集信息。因此,他知道要完成工作所需要的一切东西。
切面是通知和切点的结合。通知和切点共同定义了切面的全部内容——它是什么,在何时和何处完成其功能。

引入(Introduction) 

Introduction(引介) – 引介是一种特殊的通知在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field
引入允许我们向现有的类添加新方法或属性。例如,我们可以创建一个Auditable通知类,该类记录了对象最后一次修改时的状态。这很简单,只需一个方法,setLastModified(Date),和一个实例变量来保存这个状态。然后,这个新方法和实例变量就可以被引入到现有的类中,从而可以在无需修改这些现有的类的情况下,让它们具有新的行为和状态。

织入(Weaving) 

Weaving(织入) – 是指把增强应用到目标对象来创建新的代理对象的过程
织入是把切面应用到目标对象并创建新的代理对象的过程。切面在指定的连接点被织入到目标对象中。在目标对象的生命周期里有多个点可以进行织入:
编译期:切面在目标类编译时被织入。这种方式需要特殊的编译器。AspectJ的织入编译器就是以这种方式织入切面的。
类加载期:切面在目标类加载到JVM时被织入。这种方式需要特殊的类加载器(ClassLoader),它可以在目标类被引入应用之前增强该目标类的字节码。AspectJ 5的加载时织入(load-time weaving,LTW)就支持以这种方式织入切面。
运行期:切面在应用运行的某个时刻被织入。一般情况下,在织入切面时,AOP容器会为目标对象动态地创建一个代理对象。SpringAOP就是以这种方式织入切面的。


Target(目标对象) – 代理的目标对象

Proxy(代理) – 一个类被AOP织入增强后,就产生一个结果代理类

通知包含了需要用于多个应用对象的横切行为;连接点是程序执行过程中能够应用通知的所有点;切点定义了通知被应用的具体位置(在哪些连接点)。其中关键的概念是切点定义了哪些连接点会得到
通知。

<aop:config>
        <!-- 引入切面类 -->
        <aop:aspect ref="myAspectXml">
            <!-- 定义通知类型:切面类的方法和切入点的表达式 -->
            <aop:before method="log" pointcut="execution(public * com.itheima.demo3.CustomerDaoImpl.save(..))"/>
        </aop:aspect>
    </aop:config>
切入点的表达式

再配置切入点的时候,需要定义表达式,重点的格式如下:execution(public * *(..)),具体展开如下:

切入点表达式的格式如下:

execution([修饰符] 返回值类型 包名.类名.方法名(参数))
修饰符可以省略不写,不是必须要出现的。

返回值类型是不能省略不写的,根据你的方法来编写返回值。可以使用 * 代替。
包名例如:com.itheima.demo3.BookDaoImpl

首先com是不能省略不写的,但是可以使用 * 代替
中间的包名可以使用 * 号代替
如果想省略中间的包名可以使用 ..
类名也可以使用 * 号代替,也有类似的写法:*DaoImpl

方法也可以使用 * 号代替
参数如果是一个参数可以使用 * 号代替,如果想代表任意参数使用


1.execution() 用于描述方法 【掌握】
语法:execution(修饰符 返回值 包.类.方法名(参数) throws异常)
a.修饰符,一般省略
public 公共方法
* 任意
b.返回值,不能省略
void 返回没有值
String 返回值字符串
* 任意
c.包,可以省略
com.hero.show 固定包
com.hero.show.crm.*.service crm包下面子包任意 (例如:com.hero.show.staff.service)
com.hero.show.. crm包下面的所有子包(含自己)
com.hero.show.*.service.. crm包下面任意子包,固定目录service,service目录任意包
d.类,可以省略
UserServiceImpl 指定类
*Impl 以Impl结尾
User* 以User开头
* 任意
e.方法名,不能省略
addUser 固定方法
add* 以add开头
*Do 以Do结尾
* 任意
f.(参数)
() 无参
(int) 一个整型
(int ,int) 两个
(..) 参数任意
g.throws ,可省略,一般不写。

综合1
execution(* com.hero.show..service...*(..))
综合2:||表示两个一起匹配

<aop:pointcut expression="execution(* com.hero.*WithCommit.*(..)) || 
                          execution(* com.hero.*Service.*(..))" id="myPointCut"/>

AOP的通知类型

1.前置通知

在目标类的方法执行之前执行。
配置文件信息:
应用:可以对方法的参数来做校验



2.最终通知

在目标类的方法执行之后执行,如果程序出现了异常,最终通知也会执行。
在配置文件中编写具体的配置:
应用:例如像释放资源


3.后置通知

方法正常执行后的通知。
在配置文件中编写具体的配置:
应用:可以修改方法的返回值
异常抛出通知


4.在抛出异常后通知
在配置文件中编写具体的配置:
应用:包装异常的信息


5.环绕通知

方法的执行前后执行。
在配置文件中编写具体的配置:
要注意:目标的方法默认不执行,需要使用ProceedingJoinPoint对来让目标对象的方法执行

/**
     * 环绕通知:方法执行之前和方法执行之后进行通知,默认的情况下,目标对象的方法不能执行的。需要手动让目标对象的方法执行,形参是固定的这么写ProceedingJoinPoint joinPoint
     */
    public void around(ProceedingJoinPoint joinPoint){
        System.out.println("环绕通知1...");
        try {
            // 手动让目标对象的方法去执行
            joinPoint.proceed();
        } catch (Throwable e) {
            e.printStackTrace();
        }
        System.out.println("环绕通知2...");
    }

Spring对AOP的支持

并不是所有的AOP框架都是相同的,它们在连接点模型上可能有强弱之分。有些允许在字段修饰符级别应用通知,而另一些只支持与方法调用相关的连接点。它们织入切面的方式和时机也有所不同。但是无论如何,创建切点来定义切面所织入的连接点是AOP框架的基本功能。
Spring和AspectJ项目之间有大量的协作,Spring对AOP的支持在很多方面借鉴了AspectJ项目。
Spring提供了4种类型的AOP支持:
1.基于代理的经典Spring AOP;
2.纯POJO切面;
3.@AspectJ注解驱动的切面;
4.注入式AspectJ切面(适用于Spring各版本)。
前三种都是Spring AOP实现的变体,Spring AOP构建在动态代理基础之上,因此,Spring对AOP的支持局限于方法拦截。

Spring所创建的通知都是用标准的Java类编写的。这样的话,我们就可以使用与普通Java开发一样的集成开发环境(IDE)来开发切面。而且,定义通知所应用的切点通常会使用注解或在Spring配置文件里采用XML来编写,这两种语法对于Java开发者来说都是相当熟悉的。
AspectJ与之相反。虽然AspectJ现在支持基于注解的切面,但AspectJ最初是以Java语言扩展的方式实现的。这种方式有优点也有缺点。通过特有的AOP语言,我们可以获得更强大和细粒度的控制,以及更丰富的AOP工具集,但是我们需要额外学习新的工具和语法。


这里写图片描述

通过在代理类中包裹切面,Spring在运行期把切面织入到Spring管理的bean中。如图4.3所示,代理类封装了目标类,并拦截被通知方法的调用,再把调用转发给真正的目标bean。当代理拦截到方法调用时,在调用目标bean方法之前,会执行切面逻辑。
直到应用需要被代理的bean时,Spring才创建代理对象。如果使用的是ApplicationContext的话,在ApplicationContext从BeanFactory中加载所有bean的时候,Spring才会创建被代理的对象。因为Spring运行时才创建代理对象,所以我们不需要特殊的编译器来织入Spring AOP的切面。

正如前面所探讨过的,通过使用各种AOP方案可以支持多种连接点模型。因为Spring基于动态代理,所以Spring只支持方法连接点。这与一些其他的AOP框架是不同的,例如AspectJ和JBoss,除了方法切点,它们还提供了字段和构造器接入点。Spring缺少对字段连接点的支持,无法让我们创建细粒度的通知,例如拦截对象字段的修改。而且它不支持构造器连接点,我们就无法在bean创建时应用通知。
但是方法拦截可以满足绝大部分的需求。如果需要方法拦截之外的连接点拦截功能,那么我们可以利用Aspect来补充Spring AOP的功能。

通过切点来选择连接点  

在开发中,要使用AspectJ的切点表达式语言来定义切点。
关于Spring AOP的AspectJ切点,最重要的一点就是Spring仅支持AspectJ切点指示器(pointcut designator)的一个子集。让我们回顾下,Spring是基于代理的,而某些切点表达式是与基于代理的AOP无关的。表4.1列出了Spring AOP所支持的AspectJ切点指示器。
这里写图片描述

当我们查看如上所展示的这些Spring支持的指示器时,注意只execution指示器是实际执行匹配的,而其他的指示器都是用来限制匹配的。这说明execution指示器是我们在编写切点定义时最主要使用的指示器。在此基础上,我们使用其他指示器来限制所匹配的切点。

使用注解创建切面

@Component
@Aspect//替换 <aop:aspect ref="myAspect">
public class MyAspect { 
//<aop:around method="myAround" pointcut-ref="myPointCut"/>
    @Around(value = "myPointCut()")
    public Object myAround(ProceedingJoinPoint joinPoint) throws Throwable{
        System.out.println("前");
        //手动执行目标方法
        Object obj = joinPoint.proceed();

        System.out.println("后");
        return obj;
    }
}

@Service("userServiceId")
public class UserServiceImpl implements UserService {

管理事务

PlatformTransactionManager:平台事务管理器,这是接口,别的框架会实现这个接口,例如下面的两个。

org.springframework.jdbc.datasource.DataSourceTransactionManager
使用 Spring JDBC 或 iBatis 进行持久化数据时使用

org.springframework.orm.hibernate3.HibernateTransactionManager
使用Hibernate 版本进行持久化数据时使用

事务的传播行为

如果a程序调用了b程序,会出现a或者b没有开启事务的情况,这种情况怎么处理?
大多数使用PROPAGATION_REQUIRED,如果你没开事务,就开启。
如果是查询的话,可以使用PROPAGATION_SUPPORTS,反正查询对事务也没有要求。
read-only控制是否只读。

  • 保证同一个事务中
    PROPAGATION_REQUIRED 支持当前事务,如果不存在 就新建一个(默认)
    PROPAGATION_SUPPORTS 支持当前事务,如果不存在,就不使用事务
    PROPAGATION_MANDATORY 支持当前事务,如果不存在,抛出异常

  • 保证没有在同一个事务中
    PROPAGATION_REQUIRES_NEW 如果有事务存在,挂起当前事务,创建一个新的事务
    PROPAGATION_NOT_SUPPORTED 以非事务方式运行,如果有事务存在,挂起当前事务
    PROPAGATION_NEVER 以非事务方式运行,如果有事务存在,抛出异常
    PROPAGATION_NESTED 如果当前事务存在,则嵌套事务执行

xml配置事务

<!-- 事务管理器 -->
    <bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- 数据源 -->
        <property name="dataSource" ref="dataSource" />
    </bean>
    <!-- 通知 -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <!-- 传播行为 -->
            <tx:method name="save*" propagation="REQUIRED" />
            <tx:method name="insert*" propagation="REQUIRED" />
            <tx:method name="add*" propagation="REQUIRED" />
            <tx:method name="create*" propagation="REQUIRED" />
            <tx:method name="delete*" propagation="REQUIRED" />
            <tx:method name="update*" propagation="REQUIRED" />
            <tx:method name="find*" propagation="SUPPORTS" read-only="true" />
            <tx:method name="select*" propagation="SUPPORTS" read-only="true" />
            <tx:method name="get*" propagation="SUPPORTS" read-only="true" />
        </tx:attributes>
    </tx:advice>
    <!-- 切面 -->
    <aop:config>
        <aop:advisor advice-ref="txAdvice"
            pointcut="execution(* com.taotao.service.*.*(..))" />
    </aop:config>

注解配置事务

<!-- 开启使用注解管理aop事务 -->
<tx:annotation-driven/>
@Transactional(isolation=Isolation.REPEATABLE_READ,propagation=Propagation.REQUIRED,readOnly=true)
public class AccountServiceImpl implements AccountService {

    private AccountDao ad ;
    private TransactionTemplate tt;

    @Override
    @Transactional(isolation=Isolation.REPEATABLE_READ,propagation=Propagation.REQUIRED,readOnly=false)
    public void transfer(final Integer from,final Integer to,final Double money) {
                //减钱
                ad.decreaseMoney(from, money);
                int i = 1/0;
                //加钱
                ad.increaseMoney(to, money);
    }



}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值