Spring框架总结

学习Spring的整体脉络:
  1、IOC控制反转和依赖注入(XML配置和注解)
  2、AOP面向切面编程
  3、Spring使用JDBC
  4、Spring的声明式事务控制


前言

Spring的简单介绍

Spring是轻量级的开源框架,以IOCAOP为内核

Spring的优势(了解):
  1、方便解藕,简化开发
  2、AOP编程支持
  3、声明式事务支持
  4、方便程序测试
  5、方便集成各种优秀的框架

什么是IOC

IOC是指控制反转,是将对象的创建控制权交给Spring容器处理,而不是直接创建,实现IOC的方式有两种,一种是通过配置文件进行注入,另一种是通过注解的方式进行注入。

什么是AOP

AOP指的是面向切面编程,底层使用的是动态代理。Spring的AOP由JDK动态代理(针对接口)和cglib动态代理(针对类)这两个实现

pom.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>Spring</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.0.5.RELEASE</version>
        </dependency>
        <!-- c3p0连接池 -->
        <dependency>
            <groupId>c3p0</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.1.2</version>
        </dependency>
        <!-- druid -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.10</version>
        </dependency>
        <!--mysql驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.38</version>
        </dependency>
        <!-- Junit测试 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <!--Spring集成Junit导入的依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.0.15.RELEASE</version>
        </dependency>
        <!--Spring的Aop依赖,推荐使用-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.6</version>
        </dependency>
        <!--Spring集成Junit的依赖-->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>RELEASE</version>
            <scope>compile</scope>
        </dependency>
        <!--Spring的事务依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.3.11</version>
        </dependency>
    </dependencies>
</project>

一、XML配置

首先,我们先新建一个配置文件,名字可以随便取,但为了方便后面的阅读,建议名字和我一样:applicationContext.xml
创建的位置在resources目录下

下面我先把约定的俗称列出,把下面内容复制到你的文件内容里,之后的内容都会用到!

<?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:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       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/aop  http://www.springframework.org/schema/aop/spring-aop.xsd
                            http://www.springframework.org/schema/tx   http://www.springframework.org/schema/tx/spring-tx.xsd">

</beans>

如图:
xml配置
接下来进入正题:
上面我们提到IOC是将类的创建交给容器,以下我们通过XML配置的方式使用,首先是定义一个实体类,然后在xml文件定义bean标签,然后填写对应的属性值

实体类:

//在com.entry包下新建一个实体类
package com.entry;
//新建实体类,类名为User
public class User {
	//设置属性
    private String name;
    //设置map域的属性
    private Map<String,String> map;
    //设置list域的属性
    private List<String> list;
    //设置数组域的属性
    private String array[]=new String[10];
    //设置Set无序集合的属性
    private Set<String> set;
    //有参和无参构造
    public User() {}
    public User(String name, String password, Map<String, String> map, List<String> list) {
        this.name = name;
        this.password = password;
        this.map = map;
        this.list = list;
    }
	//设置所有属性的get和set方法
	   public String[] getArray() {
        return array;
    }
    public void setArray(String[] array) {
        this.array = array;
    }
    public Set<String> getSet() {
        return set;
    }
    public void setSet(Set<String> set) {
        this.set = set;
    }
    //设置构造函数
    public List<String> getList() {
        return list;
    }
    public void setList(List<String> list) {
        this.list = list;
    }
    public String getName() {
        return name;
    }
    public Map<String, String> getMap() {
        return map;
    }
    public void setMap(Map<String, String> map) {
        this.map = map;
    }
    public void setName(String name) {
        this.name = name;
    }
}

applicationContext.xml配置文件

<!--在<beans>标签下写入一个新的<bean>标签 id是给bean设定唯一标识,class是指定User类的路径-->
<!--方式一、通过属性注入,scope指的是作用域,分为单例(默认),原型和request域和session域-->
	<!--com.entry.User指的是com.entry包下的User类-->
    <bean id="user" class="com.entry.User" scope="singleton">
    	<!---->
        <property name="name" value="小庄"/>
        <!--对Map属性赋值-->
        <property name="map">
            <map>
                <entry key="name" value="小庄"/>
            </map>
        </property>
        <!--对List赋值-->
        <property name="list">
            <list>
                <value>111</value>
                <value>222</value>
            </list>
        </property>
                <!--对Array数组赋值-->
        <property name="a">
            <array>
                <value>1</value>
                <value>2</value>
            </array>
        </property>
        <!--对Set集合注入-->
        <property name="set">
            <set>
                <value>1</value>
                <value>2</value>
            </set>
        </property>
 </bean>
<!--方式二、通过构造器注入-->
 <bean id="user2" class="com.entry.User">
        <constructor-arg name="name" value="小庄"/>
        <!--对list属性注入,name指的是属性名,value指的是属性值-->
        <constructor-arg name="list">
            <list>
                <value>111</value>
                <value>222</value>
            </list>
        </constructor-arg>
        <!--对map属性注入,key指的是键,value指的是值,在<entry>标签里-->
        <constructor-arg name="map">
            <map>
                <entry key="name" value="小庄"></entry>
            </map>
        </constructor-arg>
 </bean>

测试:

import com.entry.User;
import org.junit.jupiter.api.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
 * @Author:小庄
 */
public class UserDemo {
    @Test
    public void test(){
    	//获取上下文
        ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");
        User user = (User) app.getBean("user");
        System.out.println(user.getName());
    }
}

对相关标签的介绍

标签作用
<bean>用于定义一个类
<property>属性标识
<list>标识list的属性
<value>属性值
<map>标识map的属性
<entry>map中的键值对标识
<constructor-arg>构造函数标签标识
<import>导入其他的Spring的分文件

属性的介绍

标签属性作用
id属性在容器中Bean实例的唯一标识,不允许重复
class属性要实例化的Bean的全限定名
scope属性Bean的作用范围,常用的是Singleton(默认)和prototype
name属性属性名称
value属性属性值
ref属性引用外部bean,类型需要跟属性名name保持一致,否则无法引用

二、Spring的JDBC

步骤:

1、导入mysql驱动的坐标,和数据池坐标,如果已经导入,则不需要导入

<!-- c3p0连接池 -->
<dependency>
    <groupId>c3p0</groupId>
    <artifactId>c3p0</artifactId>
    <version>0.9.1.2</version>
</dependency>
<!-- druid连接池-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.10</version>
</dependency>
<!--mysql驱动-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.38</version>
</dependency>

2、方式一:直接使用

@Test
public void test() throws PropertyVetoException, SQLException {
	//C3P0的连接池
    ComboPooledDataSource dataSource=new ComboPooledDataSource();
    //给连接池赋值
    dataSource.setDriverClass("com.mysql.jdbc.Driver");
    dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/数据库名");
    dataSource.setUser("数据库用户名");
    dataSource.setPassword("数据库用户密码");
    //获取连接
    Connection connection = dataSource.getConnection();
    //打印测试
    System.out.println(connection);
    //关闭连接
    connection.close();
}

3、方式二:使用xml直接配置

<!--数据源配置-->
<!--以下的class中的内容是c3p0依赖中的连接池类的路径-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="driverClass" value="com.mysql.jdbc.Driver"/>
    <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/数据库名"/>
    <property name="user" value="数据库用户名"/>
    <property name="password" value="数据库用户密码"/>
</bean>

4、方式三:使用xml加外部文件配置

外部文件:jdbc.properties

jdbc.driver=com.mysql.jdbc.Driver
#端口号默认是3306
jdbc.url=jdbc:mysql://localhost:端口号/数据库名称
jdbc.user=数据库用户名
jdbc.password=数据库密码

xml配置

<!--在<beans>标签内部-->
<!--加载外部的properties-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--数据源配置-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="driverClass" value="${jdbc.driver}"/>
    <property name="jdbcUrl" value="${jdbc.url}"/>
    <property name="user" value="${jdbc.user}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>
 
<!--配置组件扫描,下面的意思是扫描com包下的所有文件-->
   <context:component-scan base-package="com"/>
</beans>

具体使用

@Test
//测试Spring容器产生数据源对象
public void test4() throws Exception {
    //加载指定的xml文件
    ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");
    //获取数据源对象
    DataSource dataSource = app.getBean(DataSource.class);
    //获取连接
    Connection connection = dataSource.getConnection();
    System.out.println(connection);
    //关闭连接
    connection.close();
}

5、方式四:使用注解配置+外部文件配置

外部文件:jdbc.properties

#数据库驱动
jdbc.driver=com.mysql.jdbc.Driver
#端口号默认是3306
jdbc.url=jdbc:mysql://localhost:端口号/数据库名称
jdbc.user=数据库用户名
jdbc.password=数据库密码

配置文件类

//配置文件核心注解
@Configuration
// <!--配置组件扫描,下面的意思是扫描com包下的所有文件-->
//    <context:component-scan base-package="com"/>
@ComponentScan("com")
//<!--加载外部的properties-->
//<context:property-placeholder location="classpath:jdbc.properties"/>
@PropertySource("classpath:jdbc.properties")
public class SpringConfig {
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.user}")
    private String user;
    @Value("${jdbc.password}")
    private String password;
    @Bean("dataSource")
    public DataSource getDataSource() throws Exception {
        ComboPooledDataSource dataSource=new ComboPooledDataSource();
        dataSource.setDriverClass(driver);
        dataSource.setJdbcUrl(url);
        dataSource.setUser(user);
        dataSource.setPassword(password);
        return dataSource;
    }
}

具体使用

 @Test
    public void test5() throws SQLException {
        ApplicationContext app=new AnnotationConfigApplicationContext(SpringConfig.class);
        DataSource dataSource = app.getBean(DataSource.class);
        Connection connection = dataSource.getConnection();
        //打印连接
        System.out.println(connection);
        connection.close();
    }

三、注解开发

原始注解:主要替代<brean>配置
其中@Controller、@Service、@Repository这三个标签的作用和@Component一样,只是为了赋予了不同的语义化概念。

注解说明
@Component使用在类上用于实例化Bean
@Controller使用在Web层类上用于实例化Bean
@Service使用在service层类上用于实例化Bean
@Repository使用在dao层类上用于实例化Bean
@Autowired使用在字段上用于根据类型依赖注入
@Qualifier结合@Autowired一起使用,用于根据名称进行依赖注入,按照id值从容器中进行匹配的
@Resource相当于@Autowired+@Qualifier,按照名称进行注入
@Value注入普通属性
@Scope标注Bean的作用范围
@PostConstruct使用在方法上标注该方法是Bean的初始化方法
@PreDestroy使用在方法上标注该方法是Bean的销毁方法

步骤1:写入注解
步骤2:注册组件,这样注解才能生效
也就是在xml配置文件添加以下代码

<!--配置组件扫描,下面的意思是扫描com包下的所有文件-->
<context:component-scan base-package="com"/>

如下代码:

新注解

注解说明
@Configuration用于指定当前类是一个Spring配置类,当创建容器时会从该类上加载注解
@ComponentScan用于指定Spring在初始化容器时要扫描的包。作用和在Spring的xml配置文件中的<context:component-scan base-package=“com”/>一样
@Bean使用在方法上,标注将该方法的返回值存储到Spring容器中
@PropertySource用于加载.properties文件中的配置
@import用于导入其他配置类

四、Spring集成Junit

1、导入spring集成Junit的坐标

<!-- Junit测试 -->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>
<!--Spring集成Junit导入的依赖-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.0.15.RELEASE</version>
</dependency>

2、使用@Runwith注解替换原来的运行期

@RunWith(SpringJUnit4ClassRunner.class)

3、使用@ContextConfiguration指定配置文件或配置类

/**
配置文件方式,applicationContext.xml是自定义的配置文件,存放路径在resources
@ContextConfiguration("classpath:applicationContext.xml")
全注解的方式,SpringConfig.class是Java写的注解文件类
**/

@ContextConfiguration(classes = {SpringConfig.class})

4、使用@Autowired注入需要测试的对象

//使用Autowired注解将UserService注入
@Autowired
private UserService userService;

5、创建测试方法进行测试

//编写测试类,调用对象,查看是否可用
@Test
public void test(){
    userService.save();
}

五、SpringAOP

1、AOP的作用及其优势

作用:在程序运行期间,在不修改源代码的情况下对方法进行功能增强

优势:减少重复代码,提高开发效率,并且便于维护

2、AOP的底层实现

通过Spring提供的动态代理技术实现的,在运行期间,Spring通过动态代理技术动态的生成代理对象,代理对象方法执行时进行增强功能的介入,在去调用目标对象的方法,从而完成功能的增强。

3、AOP的具体实现

  • JDK代理:基于接口的动态代理技术
  • cglib代理:基于父类的动态代理技术

1、JDK动态代理技术

JDK代理
jdk代理主要通过反射的方式进行代理,JDK代理本身就继承了Proxy类,由于Java不支持多继承,所以不支持对类的代理,只支持对接口的代理。
具体案例如下:

//接口1
interface IBoard {
	void draw();
}
//接口2
interface IComputer {
	void open();
	void complete();
}
//接口1的实现类
class Board implements IBoard{
	@Override
	public void draw() {
		System.out.println("被代理类画画操作执行");
	}
}
//接口2的实现类
class Computer implements IComputer{
	@Override
	public void open() {
		System.out.println("电脑正在启动....");	
	}
	@Override
	public void complete() {
		System.out.println("电脑启动完成");
	}
}
//代理类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

class JDKProxy {
	//设置被代理对象
	private Object target;
	//通过构造函数设置被代理对象
	public JDKProxy(Object target) {
	 this.target=target;
	}
	//被代理对象生成
	public Object getProxyInstance() {
		//返回一个JDK代理类对象
		return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),new InvocationHandler() {

			@Override
			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
				System.out.println("=================");
				System.out.println("代理开始");
				Object object=method.invoke(target, args);
				System.out.println("被代理的方法名:"+method.getName());
				if("open"==method.getName()) {
					Method complete=target.getClass().getMethod("complete");
					if(null!=complete){
						System.out.println("complete方法被代理对象自动调用");
						Thread.sleep(1000);
						complete.invoke(target, args);
					}
				}
				System.out.println("代理结束");
				System.out.println("=================");
				System.out.println();
				return object;
			}
		});
	}
}
//使用者
public class Client {
	public static void main(String[] args) {
		//代理画板对象
		JDKProxy board=new JDKProxy(new Board());
		IBoard iBoard=(IBoard)board.getProxyInstance();
		//调用画板的draw()方法
		iBoard.draw();
		//代理电脑对象
		JDKProxy computer=new JDKProxy(new Computer());
		IComputer iComputer=(IComputer) computer.getProxyInstance();
		//这里只调用了open()方法,但是complete会被代理对象自动调用
		iComputer.open();
	}
}

2、cglib动态代理技术

实现代理的关键:

  • 创建工具类Enhancer
  • 设置它的父类,会在虚拟机生成一个父类
  • 设置回调函数
  • 创建子类对象,作为代理对象

Cgilb代理
我们知道,使用Jdk代理的不足之处是不能对类进行代理,而Cglib代理刚好解决了这个问题。
Cglib可以对无接口的类进行代理,需要实现MethodInterceptor接口

具体案例:

//没有实现接口的类1
class Board{
	public void draw() {
		System.out.println("被代理类画画操作执行");
	}
}
//没有实现接口的类2
class Computer{
	public void open() {
		System.out.println("电脑正在启动....");
	}
	public void complete() {
		System.out.println("电脑启动完成");	
	}
}
//Cglib代理
import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
//实现MethodInterceptor接口,重写intercept方法(实现代理功能)
class CglibProxy implements MethodInterceptor{
	//设置被代理类
	public Object target;
	//通过构造函数进行设置
	public CglibProxy(Object target) {
		this.target=target;
	}
	public Object getProxyInstance() {
		//创建工具类
		Enhancer e=new Enhancer();
		//设置父类
		e.setSuperclass(target.getClass());
		//设置回调函数
		e.setCallback(this);
		//创建子类对象,作为代理对象
		return e.create();
	}
	@Override
	public Object intercept(Object arg0, Method method, Object[] args, MethodProxy arg3) throws Throwable {
		System.out.println("===============");
		//代理模式底层使用反射
		System.out.println("Cglib代理模式开始");
		//invoke激活,和jdk代理功能类似
		Object object=method.invoke(target, args);
		System.out.println("被代理的方法名:"+method.getName());
		//在代理类实现自己的功能
		if("open"==method.getName()) {
			Method complete=target.getClass().getMethod("complete");
			if(null!=complete){
				System.out.println("complete方法被代理对象自动调用");
				Thread.sleep(1000);
				complete.invoke(target, args);
			}
		}
		System.out.println("代理结束");
		System.out.println("=================");
		System.out.println();
		return object;
	}
}

另一种方式:不需要类去实现MethodInterceptor接口,是在回调函数的实现这个接口

//没有实现接口的类1
class Board{
	public void draw() {
		System.out.println("被代理类画画操作执行");
	}
}
//没有实现接口的类2
class Computer{
	public void open() {
		System.out.println("电脑正在启动....");
	}
	public void complete() {
		System.out.println("电脑启动完成");	
	}
}
//Cglib代理
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
//实现MethodInterceptor接口,重写intercept方法(实现代理功能)
class CglibProxy2{
	//设置被代理类
	public Object target;
	//通过构造函数进行设置
	public CglibProxy2(Object target) {
		this.target=target;
	}
	
	public Object getProxyInstance() {
		//创建工具类
		Enhancer e=new Enhancer();
		//设置父类
		e.setSuperclass(target.getClass());
		//设置回调函数
		e.setCallback(new MethodInterceptor() {

			@Override
			public Object intercept(Object arg0, Method method, Object[] args, MethodProxy proxy) throws Throwable {
				System.out.println("===============");
				//代理模式底层使用反射
				System.out.println("Cglib代理模式开始");
				//invoke激活,和jdk代理功能类似
				Object object=method.invoke(target, args);
				System.out.println("被代理的方法名:"+method.getName());
				//在代理类实现自己的功能
				if("open"==method.getName()) {
					Method complete=target.getClass().getMethod("complete");
					if(null!=complete){
						System.out.println("complete方法被代理对象自动调用");
						Thread.sleep(1000);
						complete.invoke(target, args);
					}
				}
				System.out.println("代理结束");
				System.out.println("=================");
				System.out.println();
				return object;
			}
		});
		//创建子类对象,作为代理对象
		return e.create();
	}
}

想看更多的设计模式请进:Java设计模式总结篇

3、AOP的相关概念

常用的术语如下:

  • Target(目标对象):代理的目标对象
  • Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类
  • Joinpoint(连接点):所谓连接点是指那些被拦截到的点,在Spring中,这些点指的是方法类型的连接点
  • Pointcut(切入点):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义
  • Advice(通知/增强):所谓通知是指拦截到Joinpoint之后所要做的事情就是通知
  • Aspect(切面):是切入点和通知(引介)的结合
  • Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程。Spring采用动态代理织入,而Aspect采用编译期织入和类装载期织入。

4、XML配置AOP详解

1、切入表达式的写法

表达式语法:

execution([修饰符] 返回值类型 包名.类名.方法名(参数类型))
  • 访问修饰符可以省略
  • 返回值类型、包名、类名、方法名可以使用星号*代表任意
  • 包名与类名之间一个点.代表当前包下的类,两个点..表示当前包及其子包下的类
  • 参数列表可以使用两个点..表示任意个数、任意类型的参数列表
//method()指方法(切点),Target指类名(切面),com.aop值包名
execution(public void com.aop.Target.method())
//方式二,采用*号和一个.
execution(* com.aop.*.*())
//方式三,采用*号和一个.和两个. 比较常用
execution(* com..*.*(..))
//方式四 基本不会用上
execution(* *..*.(..))

2、通知种类

通知的配置语法:

<aop:通知类型 method="切面类中的方法" pointcut="切点表达式" />
名称标签说明
前置通知<aop:before>用于配置前置通知,指定增强方法在切入点方法之前执行
后置通知<aop:after-returning>用于配置后置通知,指定增强方法在切入点方法之后执行
环绕通知<aop:around>用于配置环绕通知,指定增强方法在切入点方法之前和之后执行
异常抛出通知<aop:throwing>用于配置异常抛出通知,指定增强方法在出现异常时执行
最终通知<aop:after>用于配置最终通知,无论增强方法执行是否有异常都会执行

代码展示:
在com.aop包中新建一个MyAspect类,Target类和TargetInterface接口

package com.aop;
/**
 * @Author:小庄
 */
//切面
public class MyAspect {
	//切点
    public void before(){
        System.out.println("前置增强");
    }
    public void afterRetuning(){
        System.out.println("后置增强");
    }
}
//接口
interface TargetInterface {
    public void save();
}
//目标方法
public class Target implements TargetInterface{
    public void save(){
        System.out.println("Save.........");
    }
}

在xml文件进行配置AOP

<!--先把bean注入Spring容器中-->
<bean id="target" class="com.aop.Target"/>
<bean id="myAspect" class="com.aop.MyAspect"/>
<!--aop配置开始-->
<aop:config>
<!--        <aop:pointcut id="myPointcut" expression="execution(* com..*.*(..))"/>-->
	<!--配置切面-->
	<aop:aspect ref="myAspect">
		<!--配置切点,method是方法名:pointcut切入点指定生效路径-->
    	<aop:before method="before" pointcut="execution(* com..*.*(..))"/>
        </aop:aspect>
    </aop:config>

使用Junit测试看看

import com.aop.TargetInterface;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

/**
 * @Author:小庄
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AopTest {
    @Autowired
   private TargetInterface target;
   @Test
    public void test(){
       target.save();
   }
}

5、注解配置AOP详解

注解AOP开发步骤:

  • 使用@Aspect标注切面类
  • 使用@通知注解标注通知方法
  • 在配置文件中配置aop自动代理<aop:aspectj-autoproxy/>让注解生效

通知注解的类型:

名称标签说明
前置通知@Before用于配置前置通知,指定增强方法在切入点方法之前执行
后置通知@AfterReturning用于配置后置通知,指定增强方法在切入点方法之后执行
环绕通知@Around用于配置环绕通知,指定增强方法在切入点方法之前和之后执行
异常抛出通知@AfterThrowing用于配置异常抛出通知,指定增强方法在出现异常时执行
最终通知@After用于配置最终通知,无论增强方法执行是否有异常都会执行
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
 * @Author:小庄
 */
@Component("myAspect")
@Aspect//告诉Spring容器Myspect是一个切面
public class MyAspect {
    @Before("execution(* com..*.*(..))")
    public void before(){
        System.out.println("前置增强");
    }
    @AfterReturning
    public void afterRetuning(){
        System.out.println("后置增强");
    }
}

六、Spring的事务控制

知识要点:

平台事务管理器配置

事务通知配置

事务aop的织入配置

基于xml的声明式事务控制

通过转账业务进行测试

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"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       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/aop  http://www.springframework.org/schema/aop/spring-aop.xsd
                            http://www.springframework.org/schema/tx   http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!--加载外部的properties-->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <!--数据源配置-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}"/>
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="user" value="${jdbc.user}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
    <!--jdbcTemplate注入-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <bean id="accountDao" class="com.dao.impl.AccountDaoImpl">
        <property name="jdbcTemplate" ref="jdbcTemplate"/>
    </bean>

    <!--目标对象,内部的方法就是切点-->
    <bean id="accountService" class="com.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"/>
    </bean>
    <!--选择jdbc数据源驱动-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--通知 事务的配置-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <!--
            name:切点方法名称
            isolation:事务隔离级别
            propagation:事务的传播行为
            timeout:超时时间
            read-only:是否只读
            -->
            <tx:method name="transfer"/>
        </tx:attributes>
    </tx:advice>
    <!--aop配置事务织入-->
    <aop:config>
        <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.service.impl.AccountServiceImpl.transfer(..))"/>
    </aop:config>
</beans>

实体类

/**
 * @Author:小庄
 * 金额表实体类
 */
public class Account {
    private String name;
    private double money;
    //设置方法
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public double getMoney() {
        return money;
    }
    public void setMoney(double money) {
        this.money = money;
    }
}

Service层

转账业务接口

/**
 * @Author:小庄
 */
public interface AccountService {
    public void transfer(String outMan,String inMan,double money);
}

Service的实现类

/**
 * @Author:小庄
 */

public class AccountServiceImpl implements AccountService {
    private AccountDao accountDao;

    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }
    @Transactional
    public void transfer(String outMan, String inMan, double money) {
        //支出人,金额
        accountDao.out(outMan,money);
        //收款人,金额
        accountDao.in(inMan,money);
    }
}

Dao层
转账数据访问层接口

/**
 * @Author:小庄
 */
public interface AccountDao {
    public void out(String outMan,double money);
    public void in(String inMan,double money);
}

Dao的实现类

/**
 * @Author:小庄
 */
public class AccountDaoImpl implements AccountDao {
    private JdbcTemplate jdbcTemplate;

    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    public void out(String outMan, double money) {
        String sql="select money from account where name='"+outMan+"'";

        Integer sql_money = jdbcTemplate.queryForObject(sql, Integer.class);
        if (sql_money>=money){
            jdbcTemplate.update("update account set money=money-? where name=? ",money,outMan);
        }else {
            System.out.println("对不起!"+outMan+"。您的的金额不足,无法进行交易!");
            //退出程序
            System.exit(0);
        }
    }
    //存钱
    public void in(String inMan, double money) {
        jdbcTemplate.update("update account set money=money+? where name=?",money,inMan);
    }
}

Controller层

/**
 * @Author:小庄
 */
public class AccountController {
    public static void main(String[] args) {
        ApplicationContext app=new ClassPathXmlApplicationContext("applicationContext.xml");
        AccountService accountService = app.getBean(AccountService.class);
        accountService.transfer("zhangsan","lisi",50);
    }
}

基于注解的声明式事务控制

1、使用@Transactional在需要进行事务控制的类或是方法上修饰,注解可用的属性同xml配置方式,例如隔离级别、传播行为等。

2、注解使用在类上,那么该类下的所有方法都使用同一套注解参数配置。

3、使用在方法上,不同的方法可以采用不同的事务参数配置。

4、xml配置文件中要开启事务的注解驱动<tx:annotation-driven>

实现步骤如下:

修改xml配置文件(applicationContext)

<?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:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       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/aop  http://www.springframework.org/schema/aop/spring-aop.xsd
                            http://www.springframework.org/schema/tx   http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!--加载外部的properties-->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <!--组件扫描-->
    <context:component-scan base-package="com" />
    <!--数据源配置-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driver}"/>
        <property name="jdbcUrl" value="${jdbc.url}"/>
        <property name="user" value="${jdbc.user}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!--选择jdbc事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
	<!--使得事务配置有效-->
    <tx:annotation-driven transaction-manager="transactionManager"/>
</beans>

修改Dao层的类(AccountDaoImpl 类)

/**
 * @Author:小庄
 */
@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;
	//取钱
    public void out(String outMan, double money) {
    	//定义sql语句
        String sql="select money from account where name='"+outMan+"'";
		//执行sql语句
        Integer sql_money = jdbcTemplate.queryForObject(sql, Integer.class);
        //逻辑判断
        if (sql_money>=money){
            jdbcTemplate.update("update account set money=money-? where name=? ",money,outMan);
        }else {
            System.out.println("对不起!"+outMan+"。您的的金额不足,无法进行交易!");
            //退出程序
            System.exit(0);
        }
    }
    //存钱
    public void in(String inMan, double money) {
        jdbcTemplate.update("update account set money=money+? where name=?",money,inMan);
    }
}

修改Service的类(AccountServiceImpl 类)

/**
 * @Author:小庄
 */
@Service("accountService")
public class AccountServiceImpl implements AccountService {
    @Autowired
    private AccountDao accountDao;
    //可以加参数,可以把该注解放到类外头,表示统一的数据库参数配置
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public void transfer(String outMan, String inMan, double money) {
        //支出人,金额
        accountDao.out(outMan,money);
        //收款人,金额
        accountDao.in(inMan,money);
    }
}

事务控制小结

使用xml的方式
  首先需要配置通知事务的增强,然后配置事务织入,如:

<!--通知 事务的配置-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <!--
            name:切点方法名称
            isolation:事务隔离级别
            propagation:事务的传播行为
            timeout:超时时间
            read-only:是否只读
            -->
            <tx:method name="transfer"/>
        </tx:attributes>
    </tx:advice>
    <!--aop配置事务织入-->
    <aop:config>
        <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.service.impl.AccountServiceImpl.transfer(..))"/>
    </aop:config>

使用注解的方式更加简单
  只需要在需要声明事务的地方添加@Transactional注解,并配置相关的参数和在xml配置文件中要开启事务的注解驱动<tx:annotation-driven manager="配置的事务管理器">,开启组件扫描,
xml的配置,再加上@Transactional

 	<!--组件扫描:com表示包-->
    <context:component-scan base-package="com" />
	<!--使得事务配置有效-->
    <tx:annotation-driven transaction-manager="transactionManager"/>

结语

本篇文章是学习笔记,记录下来的方便之后的复习,如果有问题的请指出,不吝赐教。

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值