学习Spring的整体脉络:
1、IOC控制反转和依赖注入(XML配置和注解)
2、AOP面向切面编程
3、Spring使用JDBC
4、Spring的声明式事务控制
目录
前言
Spring的简单介绍
Spring是轻量级的开源
框架,以IOC
和AOP
为内核
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>
如图:
接下来进入正题:
上面我们提到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代理本身就继承了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
- 设置它的父类,会在虚拟机生成一个父类
- 设置回调函数
- 创建子类对象,作为代理对象
我们知道,使用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"/>
结语
本篇文章是学习笔记,记录下来的方便之后的复习,如果有问题的请指出,不吝赐教。