Spring 是一个开源的 Java 企业级应用开发框架,它的核心目标是简化企业级应用开发的复杂性,提供一站式的解决方案。

在Spring这部分内容中主要包含IOC/DI和AOP,在后面也会基于这几个部分进行讲解。
概念:
IOC (Inversion of Control) 控制反转:对象的创建控制权由程序转移到外部,这种思想称为控制反转。
即控制权从应用程序代码转移到框架容器,由容器负责管理对象生命周期和依赖关系。
Bean:在Spring中提供了IOC容器,而用于充当外部,负责对象的创建、初始化等工作,被创建或被管理的对象在IOC容器中的统称为Bean。
DI(Dependency Injection)依赖注入:在容器中建立bean与bean之间的依赖关系的整个过程,称为依赖注入
IOC主要是控制反转思想,在这里分为bean基础配置,bean实例化,bean的生命周期讲解,分别采用非注解,注解的写法。
同时讲解核心容器,也就是ApplicationContext。

id:bean的id,使用容器可以通过id值获取对应的bean,在一个容器中id值唯一。 class:bean的类型,即配置的bean的全路径类名 name:bean的别名配置,别名可以有多个,使用逗号、分号、空格分隔 scope:负责控制bean的作用范围,有单例和非单例
前置工作:创建Maven项目,添加Spring对应jar包和所需类 使用思路:添加Spring配置文件,完成配置,进行使用
Spring配置文件示例(在resources下建立)
resources是 Java 项目(尤其是 Maven/Gradle 等构建工具管理的项目)中专门用于存放非 Java 源代码的资源文件的标准目录
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<!--文档前面内容省略-->
<bean id="bookDao" class="com.Coolipa.Dao.Impl.BookDaoImpl" scope="singleton" name="bookDao2 bookDao"/>
<bean id="bookService" class="com.Coolipa.Service.Impl.BookServiceImpl" scope="prototype"/>
</beans>启动类通过配置文件中的xml接口完成IOC容器创建,并通过id和class调用内部的方法
配置文件关键点:
<bean id="bookDao" class="com.Coolipa.Dao.Impl.BookDaoImpl" />启动类:
public class App {
public static void main(String[] args) {
ApplicationContext app = new ClassPathXmlApplicationContext("ApplicationContext.xml");
BookDao bookDao= (BookDao) app.getBean("bookDao");
bookDao.save();
}
}id可能会由于命名习惯而产生分歧,可以利用别名去解决这个问题
配置文件关键点:
<bean id="bookDao" class="com.Coolipa.Dao.Impl.BookDaoImpl" name="bookDao2"/>启动类:
public class App {
public static void main(String[] args) {
ApplicationContext app = new ClassPathXmlApplicationContext("ApplicationContext.xml");
BookDao bookDao= (BookDao) app.getBean("bookDao2");
bookDao.save();
}
}拓充:
可以利用bean依赖注入的ref属性指定bean,必须在容器中存在,不存在将会报错NoSuchBeanDefinitionException
<bean id ="bookService" name="service service2" class="com.Coolipa.Dao.Impl.BookDaoImpl">
<property name="bookDao" ref="dao"/>
</bean>
在默认情况下,Spring创建的Bean对象都是单例了,所以需要手动更改
配置文件关键点:
<bean id="bookDao" class="com.Coolipa.Dao.Impl.BookDaoImpl"
scope="singleton"/>其中单例为singleton,非单例为prototype
拓充:
为什么bean默认为单例?
单例指在IOC容器中只会有该类的一个对象,bean对象只有一个就避免了对象的频繁创建与销毁,达到了bean对象的复用,性能高
bean在容器中是单例的,会不会产生线程安全问题?
如果对象有成员变量可以用来存储数据,由于线程共用一个bean对象,会存在线程安全问题。 如果对象没有成员变量进行数据存储,则方法中的局部变量在方法调用完毕后会被销毁,不会存在线程安全问题
知识点1:@Component等
名称 | @Component/@Controller/@Service/@Repository |
|---|---|
类型 | 类注解 |
位置 | 类定义上方 |
作用 | 设置该类为spring管理的bean |
属性 | value(默认):定义bean的id |
知识点2:@Configuration
名称 | @Configuration |
|---|---|
类型 | 类注解 |
位置 | 类定义上方 |
作用 | 设置该类为spring配置类 |
属性 | value(默认):定义bean的id |
知识点3:@ComponentScan
名称 | @ComponentScan |
|---|---|
类型 | 类注解 |
位置 | 类定义上方 |
作用 | 设置spring配置类扫描路径,用于加载使用注解格式定义的bean |
属性 | value(默认):扫描路径,此路径可以逐层向下扫描 |
知识点4:@Scope
名称 | @Scope |
|---|---|
类型 | 类注解 |
位置 | 类定义上方 |
作用 | 设置该类创建对象的作用范围可用于设置创建出的bean是否为单例对象 |
属性 | value(默认):定义bean作用范围,默认值singleton(单例),可选值prototype(非单例) |
创建java的配置类,用@configuration注解替换配置类,用@ComponentScan替换指定class文件
配置类:
@Configuration
@Scope("singleton")
@ComponentScan("com.Coolipa")
public class SpringConfig {
}被替换部分:
<bean id="bookDao" class="com.Coolipa.Dao.Impl.BookDaoImpl" scope="singleton" name="bookDao2"/>
<bean id="bookService" class="com.Coolipa.Service.Impl.BookServiceImpl" scope="prototype"/>启动类:
public class App {
public static void main(String[] args) {
ApplicationContext app = new AnnotationConfigApplicationContext(SpringConfig.class); //名称和字节码都可以
BookDao bookDao= (BookDao) app.getBean("bookDao");
bookDao.save();
BookService bookService =app.getBean(BookService.class);
bookService.save();
}
}被替换部分对比:
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);ClassPathXmlApplicationContext是加载XML配置文件AnnotationConfigApplicationContext是加载配置类
这里主要为实例化bean的方式
bean是由构造方法创建的
Spring的IOC实例化对象的三种方式分别为构造方法,静态方法,实例方法,主要掌握构造方法和实例方法中的FactoryBean。
需要注意的一点是,构造方法在类中默认会提供,但是如果重写了构造方法,默认的就会消失,在使用的过程中需要注意,如果需要重写构造方法,最好把默认的构造方法也重写下。
前置工作:准备创建的类,配置类,启动类
在类中添加无参构造函数即可
public class BookDaoImpl implements BookDao {
public BookDaoImpl() {
System.out.println("book dao constructor is running ....");
}
public void save() {
System.out.println("book dao save ...");
}
}Spring容器在创建对象时用的就是构造函数(无参构造),同时Spring底层用的是反射(可以访问私有构造方法)
配置类添加内容
class:工厂类全名 factory-method:具体工厂类中创建对象的方法名
<bean id="orderDao" class="com.itheima.factory.OrderDaoFactory" factory-method="getOrderDao"/>编写运行类运行
public class AppForInstanceOrder {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
OrderDao orderDao = (OrderDao) ctx.getBean("orderDao");
orderDao.save();
}
}在工厂静态方法,可以作其他的业务操作
实例工厂:
配置类内容
<bean id="userFactory" class="com.itheima.factory.UserDaoFactory"/>
<bean id="userDao" factory-method="getUserDao" factory-bean="userFactory"/>factory-bean:工厂的实例对象 factory-method:工厂对象中的具体创建对象的方法名
编写运行类,在类中通过工厂获取对象
public class AppForInstanceUser {
public static void main(String[] args) {
ApplicationContext ctx = new
ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = (UserDao) ctx.getBean("userDao");
userDao.save();
}
}FactoryBean:
创建类,实现FactoryBean接口,重写方法
public class UserDaoFactoryBean implements FactoryBean<UserDao> {
//代替原始实例工厂中创建对象的方法
public UserDao getObject() throws Exception {
return new UserDaoImpl();
}
//返回所创建类的Class对象
public Class<?> getObjectType() {
return UserDao.class;
}
}FactoryBean接口有三个方法:
getObject():返回由FactoryBean创建的对象实例getObjectType():返回创建对象的类型isSingleton():默认返回true,表示创建的bean是单例的。可以通过重写此方法返回false来创建原型(prototype)bean验证方法:可以通过在Spring容器中多次获取同一个FactoryBean创建的bean,观察是否为同一个实例来验证单例行为。
T getObject() throws Exception;
Class<?> getObjectType();
default boolean isSingleton() {
return true;
}这里主要知识点为bean生命周期
关于Spring中对bean生命周期提供两种方式
init-method和destroy-method属性
InitializingBean与DisposableBean接口,这种方式了解下即可。
关闭容器分为close()和registerShutdownHook()两个方法
前置工作:准备创建的类,配置类,启动类
添加初始化和销毁方法
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println("book dao save ...");
}
//表示bean初始化对应的操作
public void init(){
System.out.println("init...");
}
//表示bean销毁前对应的操作
public void destory(){
System.out.println("destory...");
}
}在配置文件添加配置,如下:
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" init-method="init" destroy-method="destory"/>由于在普通Java应用中,JVM退出时不会自动调用bean的销毁方法,可以通过以下两种方式触发:
显式关闭:使用ClassPathXmlApplicationContext的close()方法
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
// 使用bean...
ctx.close(); // 显式关闭容器,触发销毁方法注册关闭钩子:使用registerShutdownHook()方法,在JVM关闭时自动调用销毁方法
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
ctx.registerShutdownHook(); // 注册JVM关闭钩子区别:
close():需要手动调用,立即触发销毁registerShutdownHook():自动在JVM关闭时触发,更适合生产环境在实现类添加两个接口InitializingBean, DisposableBean并实现接口中的两个方法afterPropertiesSet和destroy
public class BookServiceImpl implements BookService, InitializingBean, DisposableBean {
private BookDao bookDao;
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
public void save() {
System.out.println("book service save ...");
bookDao.save();
}
public void destroy() throws Exception {
System.out.println("service destroy");
}
public void afterPropertiesSet() throws Exception {
System.out.println("service init");
}
}知识点1:@PostConstruct
名称 | @PostConstruct |
|---|---|
类型 | 方法注解 |
位置 | 方法上 |
作用 | 设置该方法为初始化方法 |
属性 | 无 |
知识点2:@PreDestroy
名称 | @PreDestroy |
|---|---|
类型 | 方法注解 |
位置 | 方法上 |
作用 | 设置该方法为销毁方法 |
使用@PostConstruct、@PreDestroy定义bean生命周期
@Repository
//@Scope设置bean的作用范围
@Scope("singleton")
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println("book dao save ...");
}
//@PostConstruct设置bean的初始化方法
@PostConstruct
public void init() {
System.out.println("init ...");
}
//@PreDestroy设置bean的销毁方法
@PreDestroy
public void destroy() {
System.out.println("destroy ...");
}
}核心容器可以简单理解为ApplicationContext
上述三种方式,各有各的优缺点,用哪个都可以。
bean常用属性

依赖注入常用属性

分为类路径下的XML配置文件和文件系统下的XML配置文件
类路径下的XML配置文件
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");文件系统下的XML配置文件
ApplicationContext ctx = new FileSystemXmlApplicationContext("D:\\workspace\\spring\\spring_10_container\\src\\main\\resources\\applicationContext.xml");方式一:
BookDao bookDao = (BookDao) ctx.getBean("bookDao");缺点:需要强转
方式二:
BookDao bookDao = ctx.getBean("bookDao",BookDao.class);缺点:需要多加一个参数
方式三:
BookDao bookDao = ctx.getBean(BookDao.class);缺点:IOC容器中对应的bean对象只能有一个
public class AppForBeanFactory {
public static void main(String[] args) {
Resource resources = new ClassPathResource("applicationContext.xml");
BeanFactory bf = new XmlBeanFactory(resources);
BookDao bookDao = bf.getBean(BookDao.class);
bookDao.save();
}
}主要围绕引用类型和简单类型的注入方式进行讲解
通过set方法进行注入
引用类型
在配置类中使用propety和ref注入引用类型对象
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<!--property标签:设置注入属性-->
<!--name属性:设置注入的属性名,实际是set方法对应的名称-->
<!--ref属性:设置注入引用类型bean的id或name-->
<property name="bookDao" ref="bookDao"/>
</bean>在bean中定义引用数据属性并提供set方法即可
public class BookServiceImpl implements BookService{
private BookDao bookDao;
//setter注入需要提供要注入对象的set方法
public void setBookDao(BookDao bookDao) {
this.bookDao = bookDao;
}
}简单类型
在配置类中使用propety和value属性注入简单类型数据
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<!--property标签:设置注入属性-->
<!--name属性:设置注入的属性名,实际是set方法对应的名称-->
<!--value属性:设置注入简单类型数据值-->
<property name="connectionNum" value="100"/>
<property name="databaseName" value="mysql"/>
</bean>通过将set方法改为构造方法传参,进行注入
引用注入
配置类使用constructor-arg标签ref属性注入引用类型对象
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<!-- 根据构造方法参数名称注入-->
<constructor-arg name="userDao" ref="userDao"/>
<constructor-arg name="bookDao" ref="bookDao"/>
</bean>这里的ref指向的是参数,如果改成BookServiceImpl(BookDao bookDao1, UserDao userDao1),则配置中name指向userDao1和bookDao1
实现类中定义引用类型属性,并提供构造方法
public class BookServiceImpl implements BookService{
private BookDao bookDao;
private UserDao userDao;
public BookServiceImpl(BookDao bookDao, UserDao userDao) {
this.bookDao = bookDao;
this.userDao = userDao;
}
public void save() {
System.out.println("book service save ...");
bookDao.save();
userDao.save();
}
}简单类型
在配置类中使用constructor-arg标签和value属性注入简单类型数据
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
<constructor-arg name="databaseName" value="mysql"/>
<constructor-arg name="connectionNum" value="666"/>
</bean>
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
<constructor-arg name="bookDao" ref="bookDao"/>
<constructor-arg name="userDao" ref="userDao"/>
</bean>在实体类添加构造方法
public class BookDaoImpl implements BookDao {
private String databaseName;
private int connectionNum;
public BookDaoImpl(String databaseName, int connectionNum) {
this.databaseName = databaseName;
this.connectionNum = connectionNum;
}
public void save() {
System.out.println("book dao save ..."+databaseName+","+connectionNum);
}
}在配置类中,可以添加type属性,按照类型注入,也可以添加index属性,按照索引下标注入
在配置文件中,可以直接添加autowire属性进行注入
<bean class="com.itheima.dao.impl.BookDaoImpl"/>
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl" autowire="byType"/>autowire属性可以按照类型(byType),可以按照名称(byName) 使用按类型装配时(byType)必须保障容器中相同类型的bean唯一,推荐使用 使用按名称装配时(byName)必须保障容器中具有指定名称的bean,因变量名与配置耦合,不推荐使用 自动装配优先级低于setter注入与构造器注入,同时出现时自动装配配置失效
在配置类中直接注入即可
注入数组类型数据
<property name="array">
<array>
<value>100</value>
<value>200</value>
<value>300</value>
</array>
</property>注入List类型数据
<property name="list">
<list>
<value>itcast</value>
<value>itheima</value>
<value>boxuegu</value>
<value>chuanzhihui</value>
</list>
</property>注入Set类型数据
<property name="set">
<set>
<value>itcast</value>
<value>itheima</value>
<value>boxuegu</value>
<value>boxuegu</value>
</set>
</property>注入Map类型数据
<property name="map">
<map>
<entry key="country" value="china"/>
<entry key="province" value="henan"/>
<entry key="city" value="kaifeng"/>
</map>
</property>注入Properties类型数据
<property name="properties">
<props>
<prop key="country">china</prop>
<prop key="province">henan</prop>
<prop key="city">kaifeng</prop>
</props>
</property>实现类
public class BookDaoImpl implements BookDao {
private int[] array;
private List<String> list;
private Set<String> set;
private Map<String,String> map;
private Properties properties;
public void save() {
System.out.println("book dao save ...");
System.out.println("遍历数组:" + Arrays.toString(array));
System.out.println("遍历List" + list);
System.out.println("遍历Set" + set);
System.out.println("遍历Map" + map);
System.out.println("遍历Properties" + properties);
}
}知识点1:@Autowired
名称 | @Autowired |
|---|---|
类型 | 属性注解 或 方法注解(了解) 或 方法形参注解(了解) |
位置 | 构造方法、setter方法、字段、配置方法上 或 方法形参前面 |
作用 | 为引用类型属性设置值 |
属性 | required:true/false,定义该属性是否允许为null |
知识点2:@Qualifier
名称 | @Qualifier |
|---|---|
类型 | 属性注解 或 方法注解(了解) |
位置 | 属性定义上方 或 标准set方法上方 或 类set方法上方 |
作用 | 为引用类型属性指定注入的beanId |
属性 | value(默认):设置注入的beanId |
知识点3:@Value
名称 | @Value |
|---|---|
类型 | 属性注解 或 方法注解(了解) |
位置 | 属性定义上方 或 标准set方法上方 或 类set方法上方 |
作用 | 为 基本数据类型 或 字符串类型 属性设置值 |
属性 | value(默认):要注入的属性值 |
知识点4:@PropertySource
名称 | @PropertySource |
|---|---|
类型 | 类注解 |
位置 | 类定义上方 |
作用 | 加载properties文件中的属性值 |
属性 | value(默认):设置加载的properties文件对应的文件名或文件名组成的数组 |
通过使用注解,可以按照类型、名称、数据类型,甚至可以读取properties配置文件
按照类型注入
使用@Autowired注解开启自动装配模式
@Service
public class BookServiceImpl implements BookService{
@Autowired
private BookDao bookDao;
public void save(){
System.out.println("save");
bookDao.save();
}
}基于反射设计常见对象,无需提供setter方法,建议使用无参构造方法构建对象
按照名称注入
使用@Qualifier注解开启指定名称装配bean
@Service
public class BookServiceImpl implements BookService{
@Autowired
@Qualifier("bookDao")
private BookDao bookDao;
}@Qualifier注解无法单独使用,必须配合@Autowired注解使用简单数据类型注入
使用@Value实现简单类型注入
@Repository("bookDao")
public class BookDaoImpl implements BookDao{
@Value("100")
private String connectionNum;
}注解读取properties配置文件
对于如@Qualifier、@Value这种注解,为了防止写死,可以放到properties文件内
使用方法:
使用@PropertySource注解加载properties文件
@Configuration
@ComponentScan("com.itheima")
@PropertySource("classpath:jdbc.properties")
public class SpringConfig{
}注意:
如果读取的properties配置文件有多个,可以使用@PropertySource的属性来指定多个
@PropertySource({"jdbc.properties","xxx.properties"})@PropertySource注解属性中不支持使用通配符*,运行会报错
@PropertySource({"*.properties"})@PropertySource注解属性中可以把classpath:加上,代表从当前项目的根路径找文件
@PropertySource({"classpath:jdbc.properties"})AOP(Aspect Oriented Programming)面向切面编程,一种编程范式,指导开发者如何组织程序结构,它可以在不惊动原始设计的基础上为其进行功能增强。
在这里主要学习AOP核心概念和AOP作用,如AOP工作流程、AOP配置管理、AOP事务管理
概念:AOP(Aspect Oriented Programming)面向切面编程,一种编程范式
作用:在不惊动原始设计的基础上为方法进行功能增强
核心概念:
目标对象是要增强的类对应的对象,也叫原始对象。
SpringAOP中式不改变原有代码前提进行增强的,采用的是代理模式,因此对原始对象进行增强,需要对原始方法创建代理对象,在代理对象中的方法把通知加进去,实现了增强,这就是代理。
知识点1:@EnableAspectJAutoProxy
名称 | @EnableAspectJAutoProxy |
|---|---|
类型 | 配置类注解 |
位置 | 配置类定义上方 |
作用 | 开启注解格式AOP功能 |
知识点2:@Aspect
名称 | @Aspect |
|---|---|
类型 | 类注解 |
位置 | 切面类定义上方 |
作用 | 设置当前类为AOP切面类 |
知识点3:@Pointcut
名称 | @Pointcut |
|---|---|
类型 | 方法注解 |
位置 | 切入点方法定义上方 |
作用 | 设置切入点方法 |
属性 | value(默认):切入点表达式 |
知识点4:@Before
名称 | @Before |
|---|---|
类型 | 方法注解 |
位置 | 通知方法定义上方 |
作用 | 设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法前运行 |
准备工作:创建Maven项目,添加依赖,实现类,配置类,运行类
思路分析:添加依赖,定义接口与实现类,定义通知类和通知,定义切入点,制作切面,将通知类配给容器,并标识为切面类,开启注解格式AOP功能
添加依赖
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>在
spring-context中自动导入了spring-aop,这里是AspectJ,是AOP思想的具体实现
接口与实现类
public interface BookDao {
public void save();
public void update();
}
@Repository
public class BookDaoImpl implements BookDao {
public void save() {
System.out.println(System.currentTimeMillis());
System.out.println("book dao save ...");
}
public void update(){
System.out.println("book dao update ...");
}
}定义通知类和通知
public class MyAdvice {
public void method(){
System.out.println(System.currentTimeMillis());
}
}定义切入点(增强update方法)
public class MyAdvice {
@Pointcut("execution(void com.Coolipa.dao.BookDao.update())")
private void pt(){}
public void method(){
System.out.println(System.currentTimeMillis());
}
}制作切面
public class MyAdvice {
@Pointcut("execution(void com.itheima.dao.BookDao.update())")
private void pt(){}
@Before("pt()")
public void method(){
System.out.println(System.currentTimeMillis());
}
}将通知类配给容器并表示为切面类
@Component
@Aspect
public class MyAdvice {
@Pointcut("execution(void com.itheima.dao.BookDao.update())")
private void pt(){}
@Before("pt()")
public void method(){
System.out.println(System.currentTimeMillis());
}
}开启注解AOP功能
@Configuration
@ComponentScan("com.itheima")
@EnableAspectJAutoProxy
public class SpringConfig {
}运行程序即可
这里主要是切入点表达式、AOP通知类型、书写技巧
切入点表达式格式:execution([修饰符] 返回类型 [声明类型].方法名(参数) [throws 异常])
[] 表示可选部分*匹配任意类型..匹配多级包*通配符()表示无参数,(..)表示任意参数execution(public User com.example.service.UserService.findById(int))*:通配符,可以单独出现,可以作为前后缀..:通配符,多个连续的任意符号,用于简化包名与参数书写+:通配符,匹配子类类型注意事项:
示例:
execution(void com.itheima.dao.BookDao.update())
匹配接口,能匹配到
execution(void com.itheima.dao.impl.BookDaoImpl.update())
匹配实现类,能匹配到
execution(* com.itheima.dao.impl.BookDaoImpl.update())
返回值任意,能匹配到
execution(* com.itheima.dao.impl.BookDaoImpl.update(*))
返回值任意,但是update方法必须要有一个参数,无法匹配,要想匹配需要在update接口和实现类添加参数
execution(void com.*.*.*.*.update())
返回值为void,com包下的任意包三层包下的任意类的update方法,匹配到的是实现类,能匹配
execution(void com.*.*.*.update())
返回值为void,com包下的任意两层包下的任意类的update方法,匹配到的是接口,能匹配
execution(void *..update())
返回值为void,方法名是update的任意包下的任意类,能匹配
execution(* *..*(..))
匹配项目中任意类的任意方法,能匹配,但是不建议使用这种方式,影响范围广
execution(* *..u*(..))
匹配项目中任意包任意类下只要以u开头的方法,update方法能满足,能匹配
execution(* *..*e(..))
匹配项目中任意包任意类下只要以e结尾的方法,update和save方法能满足,能匹配
execution(void com..*())
返回值为void,com包下的任意包任意类任意方法,能匹配,*代表的是方法
execution(* com.itheima.*.*Service.find*(..))
将项目中所有业务层方法的以find开头的方法匹配
execution(* com.itheima.*.*Service.save*(..))
将项目中所有业务层方法的以save开头的方法匹配知识点1:@After
名称 | @After |
|---|---|
类型 | 方法注解 |
位置 | 通知方法定义上方 |
作用 | 设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法后运行 |
知识点2:@AfterReturning
名称 | @AfterReturning |
|---|---|
类型 | 方法注解 |
位置 | 通知方法定义上方 |
作用 | 设置当前通知方法与切入点之间绑定关系,当前通知方法在原始切入点方法正常执行完毕后执行 |
知识点3:@AfterThrowing
名称 | @AfterThrowing |
|---|---|
类型 | 方法注解 |
位置 | 通知方法定义上方 |
作用 | 设置当前通知方法与切入点之间绑定关系,当前通知方法在原始切入点方法运行抛出异常后执行 |
知识点4:@Around
名称 | @Around |
|---|---|
类型 | 方法注解 |
位置 | 通知方法定义上方 |
作用 | 设置当前通知方法与切入点之间的绑定关系,当前通知方法在原始切入点方法前后运行 |
修改MyAdvice,在方法上加上对应注解即可,前置@Before、后置@After、环绕@Around、返回后通知@AfterReturning、异常后通知@AfterThrowing
前置通知示例:除了环绕通知,其他基本如下
@Component
@Aspect
public class MyAdvice {
@Pointcut("execution(void com.itheima.dao.BookDao.update())")
private void pt(){}
@Before("pt()")
//此处也可以写成 @Before("MyAdvice.pt()"),不建议
public void before() {
System.out.println("before advice ...");
}
}环绕通知:
@Around("pt2()")
public Object aroundSelect(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("around before advice ...");
//表示对原始操作的调用
Object ret = pjp.proceed();
System.out.println("around after advice ...");
return ret;
}环绕通知注意事项
ProceedingJoinPoint参数才能调用原始方法pjp.proceed(),原始方法将被跳过Object类型以兼容各种返回值Throwable异常,因为无法预知原始方法是否会抛出异常示例:
@Around("pt()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
// 前置逻辑
Object result = pjp.proceed(); // 调用原始方法
// 后置逻辑
return result;
}准备工作:Maven项目,依赖,实现类,配置类,通知类
思路分析:通过JoinPoint获得参数,通过getArgs()从环绕通知获得参数,通过AfterReturning和Around获得返回值,通过AfterThrowing和Around获得异常
非环绕通知获取方式
此方法适用于前置、后置、返回后、抛出异常后通知。
@Before("pt()")
public void before(JoinPoint jp){
Object[] args = jp.getArgs();
System.out.println(Arrays.toString(args));
}环绕通知获取方式
通过getArgs()方法获取
@Around("pt()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
Object[] args = pjp.getArgs();
System.out.println(Arrays.toString(args));
Object ret = pjp.proceed();
return ret;
}只有返回后AfterReturing和环绕Around这两个通知类型可以获取
环绕通知获取返回值
ret是方法的返回值
@Around("pt()")
public Object around(ProceedingJoinPoint pjp) throws Throwable{
Object[] args = pjp.getArgs();
System.out.println(Arrays.toString(args));
args[0] = 666;
Object ret = pjp.proceed(args);
return ret;
}返回后通知获取返回值
@AfterReturning(value = "pt()",returning = "ret")
public void afterReturning(Object ret) {
System.out.println("afterReturning advice ..."+ret);
}只有抛出异常后AfterThrowing和环绕Around这两个通知类型可以获取
环绕通知获取异常
将异常捕获即可
@Around("pt()")
public Object around(ProceedingJoinPoint pjp){
Object[] args = pjp.getArgs();
System.out.println(Arrays.toString(args));
args[0] = 666;
Object ret = null;
try{
ret = pjp.proceed(args);
}catch(Throwable throwable){
t.printStackTrace();
}
return ret;
}获取异常
@AfterThrowing(value = "pt()",throwing = "t")
public void afterThrowing(Throwable t) {
System.out.println("afterThrowing advice ..."+t);
}事务:在数据层或业务层保障一系列的数据库操作同成功失败
通过@Transactional注解实现,可拓展属性

在Spring中,提供了PlatformTransactionManager平台事务管理器
在方法上添加@Transactional注解
@Transactional
public void transfer(String out,String in ,Double money) {
accountDao.outMoney(out,money);
int i = 1/0;
accountDao.inMoney(in,money);
}
上面这些属性都可以在@Transactional注解的参数上进行设置。
//配置事务管理器,mybatis使用的是jdbc事务
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource){
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(dataSource);
transactionManager.setDataSource(dataSource);
return transactionManager;
}//开启注解式事务驱动
@EnableTransactionManagement
public class SpringConfig {
}@Transactional可以写在接口类上、接口方法上、实现类上和实现类方法上
如果这篇文章对你有帮助,希望可以给一个推荐,收藏和评论