Spring框架

Spring框架

1.spring介绍

spring是一个非常活跃的开源框架。基于IOC和aop来构架多层javaee系统,以帮助分离项目组件之间的依赖关系(解耦)
底层:工厂模式+xml

2.spring作用

Spring ioc能帮我们根据配置文件创建及组装对象之间的依赖关系
Spring aop能帮助我们无耦合的实现日志记录,性能统计,安全控制
Spring能非常简单的帮助我们管理数据库事务
Spring提供了与第三方数据访问框架无缝连接,比如Hibernate,mybatis,而且自己也提供了一套jdbc模板用来数据的访问
Spring还提供了与第三方web框架的无缝连接,比如Structs,并且自己也提供了一套springMVC框架,来方便web层搭建

3.spring框架组成

在这里插入图片描述

IOC和DI

1.IOC-控制反转

是降低对象之间的耦合关系的设计思想
通过IOC,开发人员不需要关心对象的创建过程,交给Spring容器完成,程序读取Spring配置文件,获取需要创建的bean对象,通过反射机制创建对象的实例

2.DI-依赖注入

Spring容器负责被调用者实例,实例创建后又负责将该实例注入调用者

入门程序

1.创建一个Maven项目

2.添加Spring相关jar包

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-core</artifactId>
  <version>4.3.18.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-beans</artifactId>
  <version>4.3.18.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>4.3.18.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context-support</artifactId>
  <version>4.3.18.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-expression</artifactId>
  <version>4.3.18.RELEASE</version>
</dependency>

3.创建java类

main文件夹新建一个java文件夹,设置文件夹的功能(make directory as)

4.1创建配置文件

main文件夹新建一个resources文件夹,设置文件夹的属性

4.2新建spring配置文件

在这里插入图片描述

5.测试

在src文件夹下,创建与main同级的文件夹test,设置test文件夹的的功能
在这里插入图片描述

bean标签的各种属性的使用

1. id 和 name属性

<!--同时添加id和name-->
<bean id="drink_01" name="drink_02"
    class="com.test.pojo.Drink" />

java代码

//创建一个IOC容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/applicationContext.xml");
//从容器中获取bean对象
//根据id来获取对象
Drink drink_01=applicationContext.getBean("drink_01",Drink.class);
//根据name获取对象,两种方式在单例模式下效果一样
Drink drink_02=applicationContext.getBean("drink_02",Drink.class);

2. scope属性

<!--scope:设置对象的生成方式
singleton:单例 默认 大多数场景都是单例模式
<bean id="drink_01" name="drink_02" scope="singleton"
      class="com.test.pojo.Drink" />

测试

//创建一个IOC容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/applicationContext.xml");
//从容器中获取bean对象
//根据id来获取对象
Drink drink_01=applicationContext.getBean("drink_01",Drink.class);
//根据name获取对象
Drink drink_02=applicationContext.getBean("drink_02",Drink.class);
System.out.println(drink_01==drink_02);//返回true
<!--scope:设置对象的生成方式
prototype:多例模式-->
<bean id="drink_01" name="drink_02" scope="prototype"
      class="com.test.pojo.Drink" />

测试

//创建一个IOC容器
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/applicationContext.xml");
//从容器中获取bean对象
//根据id来获取对象
Drink drink_01=applicationContext.getBean("drink_01",Drink.class);
//根据name获取对象
Drink drink_02=applicationContext.getBean("drink_02",Drink.class);
System.out.println(drink_01==drink_02);//返回false

3. laze-init属性

<!--设置懒加载的前提是单例模式
laze-init:是否懒加载
默认是false 即在创建IOC容器的时候 就创建出对象
true:在获取对象的时候(调用getBean()方法)才创建出对象-->

配置

<bean id="drink_01" name="drink_02" scope="singleton"
      lazy-init="false" 
      class="com.test.pojo.Drink" />

lazy-init=“false”

 public void test(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/applicationContext.xml");
        System.out.println("在获取对象之前");
        Drink drink_01=applicationContext.getBean("drink_01",Drink.class);
        Drink drink_02=applicationContext.getBean("drink_02",Drink.class);
        System.out.println("在获取对象之后");
        //测试结果:false 不延迟创建,在创建ApplicationContext时就创建了对象

    }

lazy-init=“true”

 public void test(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/applicationContext.xml");
        System.out.println("在获取对象之前");
        Drink drink_01=applicationContext.getBean("drink_01",Drink.class);
        Drink drink_02=applicationContext.getBean("drink_02",Drink.class);
        System.out.println("在获取对象之后");
        //测试结果:true 延迟创建,只在获取的时候创建

    } 

4. init-method(初始化) 和destroy-method(销毁)

<bean id="drink_01" name="drink_02" scope="singleton"
      lazy-init="true" init-method="init" destroy-method="destroy"
      class="com.test.pojo.Drink" />

创建对象的三种方式

1.通过构造方法创建(默认调用的是无参的构造方法)

<bean id="drink_01" class="com.test.pojo.Drink" />

2.通过静态工厂创建

在整合第三方框架时,需要创建第三方框架中的类的对象,而这个类的的构造方法并没有提供,只提供了静态工厂和工厂中的创建这个对象的方法,这个时候只能通过静态工厂来创建对象。

<!--根据静态工厂创建对象-->
<bean id="drink_staticFactory_create" class="com.test.factory.StaticFactory" factory-method="getDrink"/>

测试通过静态工厂创建对象

@Test
public void testStaticFactory(){

    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/applicationContext.xml");
   Drink drink = applicationContext.getBean("drink_staticFactory_create",Drink.class);
   System.out.println(drink);

}

3.通过非静态工厂创建

<!--根据非静态工厂创建对象-->
<!--先创建工厂对象-->
<bean id="factoryBean" class="com.test.factory.Factory"/>
<!--调用工厂对象中的getDrink方法来获得drink-->
<bean id="drink_factory_create" factory-bean="factoryBean" factory-method="getDrink"/>

测试通过非静态工厂创建对象

@Test
public void testFactory(){

    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/applicationContext.xml");
    Drink drink = applicationContext.getBean("drink_factory_create",Drink.class);
    System.out.println(drink);

}

依赖注入

1.通过属性标签property注入

property标签
name:属性名 value:属性值 ref:依赖的对象id

public class Drink {
    private String name;
    private String sugar;
    private float price;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSugar() {
        return sugar;
    }

    public void setSugar(String sugar) {
        this.sugar = sugar;
    }

    public float getPrice() {
        return price;
    }

    public void setPrice(float price) {
        this.price = price;
    }

    public Drink() {
        System.out.println("创建一杯饮料");
    }

    public Drink(String name) {
        this.name = name;
    }

    public Drink(String name, String sugar) {
        this.name = name;
        this.sugar = sugar;
    }

    public Drink(String name, float sugar) {
        this.name = name;
        this.price = sugar;
    }
    @Override
    public String toString() {
        return "Drink{" +
                "name='" + name + '\'' +
                ", sugar='" + sugar + '\'' +
                ", price=" + price +
                '}';
    }
}
<!--如果属性是普通值 则用value-->
<bean id="drink_01" class="com.test.pojo.Drink">
    <property name="name" value="柠檬水"/>
    <property name="sugar" value="少糖"/>
    <property name="price" value="12"/>
</bean>

<!--如果属性是对象 则用ref-->
<bean id="drinkDao" class="com.test.dao.impl.MysqlDrinkDao"/>

<bean id="drinkService" class="com.test.service.DrinkService">
    <property name="drinkDao" ref="drinkDao"/>
</bean>

2.通过构造函数注入

constructor-arg标签:
name:构造函数的参数名
value:传进去的值
ref:参数依赖的对象

<bean id="drink_02" class="com.test.pojo.Drink">
    <constructor-arg name="name" value="蜂蜜柚子茶"/>
</bean>

<bean id="drink_03" class="com.test.pojo.Drink">
    <constructor-arg name="name" value="可乐"/>
    <constructor-arg name="sugar" value="15" type="float"/>
</bean>

3.通过p空间注入

引入p空间:xmlns:p=“http://www.springframework.org/schema/p”

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

p:属性名=属性值

<bean id="drink_p" class="com.test.pojo.Drink"
      p:name="酸梅汁" p:sugar="少糖" p:price="6"/>

4.复杂类型注入

package com.test.pojo;

import java.util.List;
import java.util.Map;
import java.util.Properties;

public class AA {
    private int arr[];
    private Drink drinkArr[];
    private List<String> stringList;
    private List<Drink> drinkList;
    private Map<String,Drink> drinkMap;
    private Properties properties;


}
<bean id="drink_01" class="com.test.pojo.Drink"/>
<bean id="drink_02" class="com.test.pojo.Drink"/>
<bean id="drink_03" class="com.test.pojo.Drink"/>
<!--数组-->
<bean id="aa" class="com.test.pojo.AA">
    <property name="arr">
        <array>
            <value>1</value>
            <value>2</value>
            <value>3</value>
        </array>
    </property>
    
    <property name="drinkArr">
        <array>
            <ref bean="drink_01"/>
            <ref bean="drink_02"/>
            <ref bean="drink_03"/>
        </array>
    </property>
<!--列表-->
    <property name="stringList">
        <list>
            <value>name</value>
            <value>sex</value>
            <value>age</value>
        </list>
    </property>

    <property name="drinkList">
        <list>
            <ref bean="drink_01"/>
            <ref bean="drink_02"/>
            <ref bean="drink_03"/>
        </list>
    </property>

    <property name="drinkMap">
        <map>
            <entry key="first" value-ref="drink_01"/>
            <entry key="second" value-ref="drink_02"/>
            <entry key="third" value-ref="drink_03"/>
        </map>
    </property>

    <property name="properties">
        <props>
            <prop key="driver">com.mysql.jdbc.Driver</prop>
            <prop key="url">jdbc:mysql://localhost:3306/baidu</prop>
        </props>
    </property>

    
</bean>

5.spel表达式注入

需要jar包

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-expression</artifactId>
  <version>4.3.18.RELEASE</version>
</dependency>
import com.test.pojo.Drink;

import java.util.ArrayList;
import java.util.List;

public class TestSpel {
    public List<Drink> getDrinkList(){

        List<Drink> list = new ArrayList<Drink>();
        list.add(new Drink("酸奶"));
        list.add(new Drink("橙汁"));
        return  list;
    }


    public static List<Drink> getDrinkList2(){

        List<Drink> list = new ArrayList<Drink>();
        list.add(new Drink("酸奶2"));
        list.add(new Drink("橙汁2"));
        return  list;
    }
}
<bean id="testSpel" class="com.test.spel.TestSpel"></bean>

<bean id="aa" class="com.test.pojo.AA">
        <property name="drinkList" value="#{testSpel.getDrinkList()}">
        </property>
</bean>

<bean id="aa2" class="com.test.pojo.AA">
        <property name="drinkList" value="#{T(com.test.spel.TestSpel).getDrinkList2()}"/>
</bean>

用注解的方式实现IOC

1.配置注解扫描

<?xml version="1.0" encoding="UTF-8"?>
<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.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
        <!--开启注解扫描
        扫描com.test包以及所有子包中的文件
        -->
    <context:component-scan
            base-package="com.test"/>
</beans>

2.常用的注解

2.1用来创建对象的注解
@Component
创建一个对象(组件)
@Service
创建Service层对象
@Controller
创建控制层对象

//创建一个Service层对象,对象名为drinkService
@Service
//或者
@Service("自己设定的名字")
public class DrinkService implements IDrinkService {
    }

2.2用来注入的注解
2.2.1注入对象的方式1
@Autowired 根据类型注入
@Qualifier(“对象名”)
指定注入对象(如果满足注入对象有多个的时候)
2.2.2注入对象方式2
Resource(name=“对象名”)
根据对象名注入,相当于@Autowired+@Qualifie
2.2.3注入普通值
@Value(“数值”)

//    自动注入
    @Autowired
//    指定注入对象(如果满足注入对象有多个的时候)
    @Qualifier("oracleDrinkDao")*/
//自动注入方式2 ,相当于上面2个的结合
    @Resource(name="oracleDrinkDao")

2.3其他注解
@Scope
设置对象的作用域,默认是单例,可以设置为多例
@Scope(scopeName=“prototype”)
@Laze
设置为懒加载 前提是单例模式

//设置是否是懒加载
@Lazy
//可以设置对象的作用域 默认是单例
//@Scope(scopeName = "prototype")
@Component
public class Drink {
    @Value("橙汁")
    private String name;
    @Value("多糖")
    private String sugar;
    @Value("15")
    private float price;
    }

Spring JDBC数据访问

Spring JDBC 是Spring所提供的持久层技术,它的主要目标是降低使用JDBC API的门槛,以一种更直接、更方便的方式使用JDBC API,在Spring JDBC里,仅需做那些与业务相关的DML操作,而其他工作交给Spring JDBC.

1.JDBC Template的使用

导入jar包
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>5.1.38</version>
</dependency>
<dependency>
  <groupId>com.mchange</groupId>
  <artifactId>c3p0</artifactId>
  <version>0.9.5.2</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-jdbc</artifactId>
  <version>4.3.18.RELEASE</version>
</dependency>
 @Test
    public void test(){
//        创建一个连接池
        ComboPooledDataSource comboPooledDataSource =new ComboPooledDataSource();
        try {
            comboPooledDataSource.setDriverClass("com.mysql.jdbc.Driver");
            comboPooledDataSource.setJdbcUrl("jdbc:mysql://localhost:3306/ssm");
            comboPooledDataSource.setUser("root");
            comboPooledDataSource.setPassword("123456");
        } catch (PropertyVetoException e) {
            e.printStackTrace();
        }
//        创建一个JDBC Template对象
        JdbcTemplate jdbcTemplate =new JdbcTemplate(comboPooledDataSource);
        
//        执行sql语句
        String sql="insert into users values(null,?,?)";
        jdbcTemplate.update(sql,"timi","123456");
    }

2、用Spring创建ComboPooledDataSource对象和JdbcTemplate对象

applicationContext.xml配置文件
 <context:component-scan base-package="com.test"></context:component-scan>

    <!--导入db.properties-->
    <context:property-placeholder location="db.properties"/>
<!--    创建数据源-->
    <bean id="comboPooledDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${driverClass}"/>
        <property name="jdbcUrl" value="${jdbcUrl}"/>
        <property name="user" value="${user}"/>
        <property name="password" value="${password}"/>
    </bean>

<!-- 创建JDBCTemplate 对象  -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <constructor-arg name="dataSource" ref="comboPooledDataSource"/>
    </bean>

db.properties文件

driverClass=com.mysql.jdbc.Driver
jdbcUrl=jdbc:mysql://localhost:3306/ssm
user=root
password=123456

3、JDBCTemplate的查询方法

//    查询 返回单个对象
    public  int selectUsersCounts(){
        String sql = "select count(*) from users";
        int num = jdbcTemplate.queryForObject(sql,Integer.class);
        return num;

    }
//    查询 返回集合
    public List<Users> selectAllUsers(){
        String sql = "select * from users";
        List<Users> usersList =jdbcTemplate.query(sql, new RowMapper<Users>() {
//            遍历结果集,将结果集中的每条数据组装成Users对象
            @Override
            public Users mapRow(ResultSet resultSet, int i) throws SQLException {
                return rowMapperHander(resultSet);
            }
        });
                return usersList;
    }
    public Users rowMapperHander(ResultSet resultSet){
        Users users =new Users();
        try {
            users.setUid(resultSet.getInt("uid"));
            users.setUname(resultSet.getString("uname"));
            users.setPassword(resultSet.getString("password"));

        } catch (SQLException e) {
            e.printStackTrace();
        }
        return users;

    }
@Test
public void test2(){
    ApplicationContext applicationContext=new ClassPathXmlApplicationContext("/applicationContext.xml");
    UsersDao usersDao=applicationContext.getBean("usersDao", UsersDao.class);

   int num= usersDao.selectUsersCounts();
   System.out.println(num);


}
@Test
public void test3(){
    ApplicationContext applicationContext=new ClassPathXmlApplicationContext("/applicationContext.xml");
    UsersDao usersDao=applicationContext.getBean("usersDao", UsersDao.class);

    List<Users> users=usersDao.selectAllUsers();
    System.out.println(users);

}

Spring中AOP的开发

一、AOP介绍

1.AOP介绍
AOP(Aspect Oriented Programming ),即面向切面编程。AOP技术利用一种横切技术,剖开封装对象的内部,并将那些影响了多个类的公众行为封装到一个可重用的模块,即切面。AOP把软件系统分为两部分:核心关注点和切面关注点。业务处理的主要流程是核心关注点,与之关注不大的是部分是横切关注点。横切关注点的一个特点是,它们经常发生在核心关注点的多处,而各处基本相似。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开。
2、AOP核心概念

. 连接点
程序执行的某一个特定位置,如类开始初始化之前,类初始化后,类中某一个方法调用前或后,方法抛出异常后,一个类或一段程序代码拥有一些具有边界性质的特定点,即称为连接点,spring仅支持方法连接点,即仅能在方法调用前,方法调用后,方法抛出异常,以及方法调用前后这些程序执行点织入增强。
. 切点
特定的连接点
.增强
增强是织入目标类连接点上的一段程序代码,在spring中,增强不仅可以描述程序代码,还拥有和另一个连接点相关的信息,即执行点的方位,结合执行点的方位信息和切点信息,就可以找到特定的连接。
.目标对象
增强逻辑的织入目标类
.织入
将增强添加到目标类具体的连接点上的过程。
AOP有三种织入方式:
1. 编译期织入,需要使用特殊的java编译器
2.类装载期织入,要求使用特殊的类装载器
3.动态代理织入,在运行期为目标类添加增强生成子类的方式。
.代理
一个类被AOP织入增强后,就产生了一个结果类,他是融合了原类和增强逻辑的代理类。
.切面
切面由切点和增强组成,它既包括横切逻辑的定义,也包括链接点的定义。
AOP的工作重心在于如何将增强应用于目标对象的连接点上,包含两项工作:
1.如何通过切点和增强定位到链接点上
2.如何在增强中编写切面代码
.通知类型
1.前置通知:目标方法运行之前调用
2.成功返回通知:在目标方法运行之后调用
3.环绕通知:在目标方法之前和之后都调用
4.异常拦截通知:如果出现异常,就会调用
5.后置通知:在目标方法运行之后调用

3.Spring AOP基础知识
两种代理机制:

  • JDK的动态代理:针对实现了接口的类产生代理
  • CGlib的动态代理:针对没有实现接口的类产生代理

二、代理模式

1.静态代理
原理:创建一个代理类实现目标类的接口,在代理类中实现目标类中的对象
创建一个接口

public interface IUserService {
    public void insert();
}

创建一个目标类

//目标类
public class UserService implements IUserService{

    @Override
    public void insert() {
        System.out.println("添加用户");
    }
}

创建一个代理类

public class UserServiceProxy implements IUserService{
//    在代理类中存放一个目标类的对象
   private IUserService userService =new UserService();
    @Override
    public void insert() {
        System.out.println("添加开始前:"+new Date());
        userService.insert();
        System.out.println("添加结束前:"+new Date());

    }
}

测试

public class TestStaticProxy {
    @Test
    public void test(){
        IUserService userService =new UserServiceProxy();
        userService.insert();
    }
}

2.动态代理
可以代理不同的对象
1.jdk自带的代理方式
必须有接口
创建一个接口

public interface IUserService {
    public void insert();
}

创建目标类

//目标类
public class UserService implements IUserService {

    @Override
    public void insert() {
        System.out.println("添加用户");
    }
}

创建一个代理类生成器

//代理类生成器
public class Handler implements InvocationHandler {
//    存放目标对象
        Object object;
//    传进目标对象
    public Handler(Object object) {
        this.object = object;
    }
//    根据传递进来的目标对象生产代理对象 返回
    public static Object blind(Object object) {
        return Proxy.newProxyInstance(Handler.class.getClassLoader(),
                object.getClass().getInterfaces(),
                new Handler(object));
    }

    //实现代理类中重写接口中的方法
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("添加开始前:"+new Date());
//        调用目标对象的方法
        Object subject=method.invoke(object,args);
        System.out.println("添加开始前:"+new Date());
        return subject;
    }
}
测试
public class TestJdkProxy {
    @Test
    public void test(){
       IUserService userService=(IUserService) Handler.blind(new UserService());
       userService.insert();
    }
}

2.Cglib方式
原理:通过字节码技术,创建一个目标类的子类,作为代理对象,子啊子类中拦截目标类中的方法,对方法做一个加强处理。
创建目标类

public class UserService implements IUserService{

    @Override
    public void insert() {
        System.out.println("添加用户");
    }
}

导入jar包

<dependency>
  <groupId>cglib</groupId>
  <artifactId>cglib</artifactId>
  <version>3.2.4</version>
</dependency>

创建一个代理类生成器

package com.test.cglibProxy;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import sun.reflect.MethodAccessor;

import java.lang.reflect.Method;
import java.util.Date;

//代理对象生成器
public class CglibProxy implements MethodInterceptor {
//创建目标对象的代理对象 并且拦截目标对象中的方法 使之执行代理对象的方法
    Enhancer enhancer = new Enhancer();

    public Object getProxy(Class clazz){
//        设置目标对象的字节码文件 为代理对象的父类
        enhancer.setSuperclass(clazz);
//        设置回调函数 目的使调用intercept()方法
        enhancer.setCallback(this);
//        根据传递过去的参数,创建一个代理对象
        return enhancer.create();
    }


//重写 拦截了目标对象的方法 所做的事情
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("方法执行前:"+new Date());
        Object result=methodProxy.invokeSuper(o,objects);
        System.out.println("方法执行后:"+new Date());
        return null;
    }
}

测试

public class TestCglibProxy {
    @Test
    public void test(){
        CglibProxy cglibProxy = new CglibProxy();
       UserService userService=(UserService)cglibProxy.getProxy(UserService.class);
       userService.insert();
    }
}

三、AOP的使用

1.通过配置文件织入
导入jar包

<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjweaver</artifactId>
  <version>1.8.10</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-aspects</artifactId>
  <version>4.3.18.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-aop</artifactId>
  <version>4.3.18.RELEASE</version>
</dependency>
配置文件
<?xml version="1.0" encoding="UTF-8"?>
<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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
    <context:component-scan base-package="com.test"/>
    <!--创建增强类对象-->
    <bean id="myAdvice" class="com.test.advice.MyAdvice"/>
    <!--织入-->
    <aop:config>
        <!--定义切点-->
        <aop:pointcut id="pc" expression=" execution(* com.test.service.impl.UserService.add()) "/>
        <!--切入-->
        <aop:aspect ref="myAdvice">
            <!--将增强类对象中的before方法切入到pc对应的切点-->
            <aop:before method="before" pointcut-ref="pc"></aop:before>
            
            <aop:around method="around" pointcut-ref="pc"/>

            <aop:after method="after" pointcut-ref="pc"/>

            <aop:after-returning method="afterReturning" pointcut-ref="pc"/>

            <aop:after-throwing method="afterException" pointcut-ref="pc"/>
        </aop:aspect>
    </aop:config>
</beans>

创建一个接口

public interface IUserService {
    public void add();

}

创建目标类

import com.test.service.IUserService;
import org.springframework.stereotype.Service;

@Service
public class UserService implements IUserService {

    @Override
    public void add() {

    }

}

创建代理类

public class MyAdvice {
    public void before(){
        System.out.println("添加在用户之前:");
    }
    //目标方法执行后(不管是出异常还是成功执行)
    public void after(){
        System.out.println("添加在用户之后:");
    }
//环绕通知  ,用这个增强代码替换掉目标代码
    public void around(ProceedingJoinPoint point) throws Throwable {
        System.out.println("执行目标方法之前");
        point.proceed();//放行异常处的方法

    }
//目标方法成功执行后
    public void afterReturning(){
        System.out.println("目标方法成功执行之后");
    }
//目标代码出现异常
    public void afterException(){
        System.out.println("目标方法出现异常之后才执行");
    }
}

测试

public class TestAop {
    @Test
    public void test(){
        ApplicationContext applicationContext =new ClassPathXmlApplicationContext("applicationContext.xml");
        IUserService userService=applicationContext.getBean("userService", IUserService.class);
        userService.add();
    }
}

2、通过注解方式织入
切面类

//切面类
@Aspect
public class MyAdvice {
//    定义切点表达式
    @Pointcut("execution(* com.test.service.impl.*.add())")
    public void pc(){

    }
    @Before("MyAdvice.pc()")
    public void before(){
        System.out.println("添加在用户之前:"+new Date());
    }
    @After("MyAdvice.pc()")
//目标方法执行后(不管是出异常还是成功执行)
    public void after(){
        System.out.println("添加在用户之后:");
    }
    @Around("MyAdvice.pc()")
//环绕通知  ,用这个增强代码替换掉目标代码
    public void around(ProceedingJoinPoint point) throws Throwable {
        System.out.println("执行目标方法之前");
        point.proceed();//放行异常处的方法

    }
    @AfterReturning("MyAdvice.pc()")
//目标方法成功执行后
    public void afterReturning(){
        System.out.println("目标方法成功执行之后");
    }
    @AfterThrowing("MyAdvice.pc()")
//目标代码出现异常
    public void afterException(){
        System.out.println("目标方法出现异常之后才执行");
    }

}

配置文件

<context-component:component-scan base-package="com.test"/>
<!--创建切面类对象-->
<bean id="myAdvice" class="com.test.advice.MyAdvice"/>

<aop:aspectj-autoproxy>
    <!--开启自动代理-->
</aop:aspectj-autoproxy>

四、切点表达式

1.切点表达式:对指定的方法进行拦截,并且生成代理表达式

表达式语法:
execution([修饰符] 返回值类型 包名.类名.方法名(参数))
execution(public void com.test.service.impl.UserService())

2、表达式的不同写法
1.匹配指定方法

<aop:pointcut id="pc" expression=" execution(public void com.test.service.impl.UserService.add()) "/>

2、默认public可以省略

<aop:pointcut id="pc" expression=" execution(void com.test.service.impl.UserService.add()) "/>

3.匹配任何返回值

<aop:pointcut id="pc" expression=" execution(* com.test.service.impl.UserService.add()) "/>

4.参数列表可以使用*,表示可以是任何的数据类型,但是必须有参数

<aop:pointcut id="pc" expression=" execution(* com.test.service.impl.UserService.add(*)) "/>

5.参数列表可以使用…表示有无参数均可,有参数可以是任意类型

<aop:pointcut id="pc" expression=" execution(* com.test.service.impl.UserService.add(..)) "/>

6.使用…表示当前包及其子包

<aop:pointcut id="pc" expression=" execution(* com..add()) "/>

7.类名可以使用*号,表示任意类

<aop:pointcut id="pc" expression=" execution(* com..*.add()) "/>

Spring事务管理

1.什么是事务

是并发控制的单元,是用户定义的一个操作序列,是一个不可分割的工作单位。通过事务,sql可以将逻辑相关的一组操作绑定在一起,以便服务器保持数据的完整性。事务通常是以begin/start transaction开始,以commit或rollback结束。Commit表示提交,即提交事务的所有操作。rollback表示回滚,即事务在运行的过程中发生了某种故障,事务不能继续运行,系统将事务中对数据库的所有已完成的操作全部撤销,滚回到事务开始的状态。

2.事务的特性

ACID

  • 原子性 事务对于其数据的修改,要么全部执行,要么全部不执行
  • 一致性 事务在完成时,必须所有的数据都保持一致状态
  • 隔离性 一个事务的执行不能被其他事务所影响
  • 持久性 一个事务一旦提交,事务的操作便永久的保存在DB中,即使此时在执行回滚操作也不能撤销所做的更改

3.事务的并发问题

  • 脏读 一个事务读取到另一个事务未提交的数据操作结果
  • 不可重复读(虚读)一个事务对同一行数据重复读了两次,但是却得到了不同的结果。(如事务A读取某一个数据后,事务B对其做了修改,当事务A再读该数据的时候就会得到与前一次不同的值)
  • 幻读 事务在操作过程中进行两次查询,第二次查询的结果包含第一次查询中未出现的数据或者缺少了第一次查询中出现的数据,这是因为在两次查询过程中有另一个事务插入数据造成的

4.事务的隔离级别

  • 读未提交 最低级别,以上情况均无法保证
  • 读已提交(只允许读已提交的事务) 可避免脏读的发生
  • 可重复读(保证两次读取的数据一样,即把所要读的记录锁住,不允许修改)可避免脏读、不可重复读的发生,不可避免幻读
  • 串行化读Serializable(锁住整张表,添加删除都不允许) 事务只能一个一个的执行,避免脏读、不可重复读、幻读。
    5.事务的传播行为
    事务方法被另一个事务方法调用时,必须指定事务是如何传播的。
    1.PROPALGATION_REQUIRED 表示当前方法必须运行在事务中,如果当前事务存在,方法会在改事务中运行,否则,会启动一个新事务
    2.PROPALGATION_SUPPORTS
    表示当前方法不需要事务上下文,但是如果存在当前事务的话,那么该方法会在这个事务中运行
    3.PROPALGATION_MANDATOY 表示当前方法必须在事务中运行,如果当前事务不存在,则会抛出一个异常
    4.PROPALGATION_REQUIRED_NEW
    表示当前方法必须运行在它自己的事务中,一个新的事务将被启动。如果存在当前事务,在该方法执行期间,当前事务会被挂起。
    5.PROPALGATION_NOT_SUPPORTED
    表示该方法不应该运行在事务中。如果存在当前事务,在该方法运行期间,当前事务将被挂起。
    6.PROPALGATION_NEVER
    表示当前方法不应该运行在事务上下文。如果当前正有一个事务在运行,则会抛出异常
    7.PROPALGATION_NESTED
    表示如果当前已经存在一个事务,那么该方法会嵌套在事务中运行。嵌套的事务可独立于当前事务进行单独的提交或回滚,如果当前事务不存在,那么其行为与PROPALGATION_REQUIRED 一样

5、Spring中事务的三种实现方式

1.编程式声明管理(一般不用
2.声明式事务管理
2.1基于TransactionProxyFactoryBean的声明式事务管理
(1)、创建异常类

public class MyException extends Exception{
    public MyException() {
    }

    public MyException(String message) {
        super(message);
    }
}

(2)、在service的中的转账方法中模拟出异常效果

@Override
public void transfer(int from, int to, float money) throws Exception {
    cardInfoDao.decrease(from,money);
        if(true)
            throw new Exception("转账异常");//Exception 所有异常的父类
    cardInfoDao.increase(to,money);
}

(3)、在配置文件中创建事务对象及其代理对象

<!--声明一个事务方法-->
<bean id="cardInfoServiceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
        <property name="transactionManager" ref="txManager"/>
        <property name="target" ref="cardInfoService"/>
        <property name="transactionAttributes">
                <props>
                        <!--隔离级别 传播行为 回滚或者提交  +遇到指定异常提交 -遇到指定异常回滚-->
                       <prop key="transfer">ISOLATION_DEFAULT,PROPAGATION_REQUIRED,-MyException</prop>
                </props>
        </property>
</bean>

(4)、测试

@Test
public void test(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
   ICardInfoService iCardInfoService= applicationContext.getBean("cardInfoServiceProxy",ICardInfoService.class);
    try {
        iCardInfoService.transfer(1,2,500);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

2.1基于@Transactional注解的声明式事务管理
(1)、修改配置文件

<!--开启事务注解-->
<tx:annotation-driven transaction-manager="txManager"/>

(2)、在service层转账方法中添加注解

    @Override
//    通过注解的方法 生命事务方法  隔离级别 传播行为 回滚条件
    @Transactional(isolation = Isolation.DEFAULT,propagation = Propagation.REQUIRED,rollbackFor = MyException.class)
    public void transfer(int from, int to, float money) throws Exception {
        cardInfoDao.decrease(from,money);
            if(true)
                throw new Exception("转账异常");//Exception 所有异常的父类
        cardInfoDao.increase(to,money);
    }
}

3.基于AOP的事务管理

<?xml version="1.0" encoding="UTF-8"?>
<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" xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
        <!--开启注解扫描-->
        <context:component-scan base-package="com.test"/>
        <!--创建数据源-->
        <context:property-placeholder location="db.properties"/>

        <bean id="comboPooledDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
                <property name="driverClass" value="${driverClass}"/>
                <property name="jdbcUrl" value="${jdbcUrl}"/>
                <property name="user" value="${user}"/>
                <property name="password" value="${password}"/>
        </bean>

        <!--创建JDBCTemplate对象-->
        <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
                <constructor-arg name="dataSource" ref="comboPooledDataSource"/>
        </bean>
        <!--创建一个事务管理器-->
        <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
                <property name="dataSource" ref="comboPooledDataSource"/>
        </bean>
        <!--定义事务方法(增强方法)-->
        <tx:advice id="txAdvice" transaction-manager="txManager">
                <tx:attributes><!--事务的相关的属性-->
                        <tx:method name="transfer" isolation="DEFAULT" propagation="REQUIRED" rollback-for="MyException"/>
                </tx:attributes>
        </tx:advice>



        <aop:config>
                <!--定义一个切点-->
                <aop:pointcut id="pc" expression="execution(* com.test.service.impl.CardInfoService.transfer(..))"/>
                <!--织入-->
                <aop:advisor advice-ref="txAdvice" pointcut-ref="pc"></aop:advisor>
        </aop:config>
        
       


</beans>
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值