AOP(Aspect Oriented Programming)意为:面向切面编程,体现了横切的思想,意思是在添加某项功能的时候,是以切面插入的方式实现的,对原有的代码不会产生改变。通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生泛型,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的频率。
AOP是基于代理模式实现的,代理模式为另一个对象提供一个替身或占位符以控制对这个对象的访问;用最简单的方式来解释代理模式,那就是房屋中介,客户需要直接去找房东,而是直接访问这个“中介”,有中介代理完成房东的事物。
代理模式的好处是,不仅可以执行被代理对象的所有功能,还可以额外的添加功能,比如房屋中介不仅可以给你租房子,还可以为你其他的房屋信息。
代理模式可以分为两种,静态代理与动态代理。而两个常见的动态代理为JDK实现(反射)与CGLIB实现。
代理模式中三个重要角色
1、抽象角色,声明真实的对象和代理对象的共同的接口。
2、代理角色:代理对象内部含有真实对象的引用,也就是说,代理对象可以操作真实的对象,因此需要实现和真实对象相同的接口,同时代理对象可以在执行真实对象操作的时候,附加上其它的操作,也就是对真实对象的一个简单的封装。
3、真实角色(被代理角色):代理角色所要代表的角色,也就是我们最终想要引用的对象。
提供声明式事务;允许用户自定义切面
切入点(PointCut):说的通俗点,Spring中AOP的切入点就是指一些方法的集合,而这些方法是被代理、被切入的。
连接点(JoinPoint):程序执行时的某个特定的方法,可以理解为可以执行切入点的位置 ,比如方法前后。
通知(Advice):切面必须要完成的工作,通俗点说,就是在切入点的要实现功能,比如日志。
切面(ASPECT):横切关注点被模块化的特殊对象,它是切入点和通知的整合概念,可以理解为切入点的类。
目标(Target):被通知对象。
代理(Proxy):向目标对象应用通知之后创建的对象。
横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点,如日志、安全、缓存、事务等等……
SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice
1、导入AOP依赖包
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
2、建立测试环境
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userService" class="com.nan.service.UserServiceImpl"/>
<bean id="log" class="com.nan.log.Log"/>
<bean id="afterLog" class="com.nan.log.AfterLog"/>
</beans>
3、实行AOP的三种方式
(1)实现Spring的AOP接口,重写前置和后置方法【主要SpringAPI接口实现】
i.前置方法类
public class Log implements MethodBeforeAdvice {
//method:要执行的目标对象的方法
//objects:参数
//taeget:目标对象
public void before(Method method, Object[] objects, Object o) throws Throwable {
System.out.println(o.getClass().getName()+"的"+method.getName()+"被执行了");
}
ii.后置方法类
public class AfterLog implements AfterReturningAdvice {
//o:返回值
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println("执行了"+method.getName()+"返回的结果为:"+o);
}
}
iii.配置文件中配置AOP信息
<bean id="log" class="com.nan.log.Log"/>
<bean id="afterLog" class="com.nan.log.AfterLog"/>
<!--方式一:spring API接口-->
<!--配置aop,需要导入aop的 约束-->
<aop:config>
<!--配置切入点--> <!--定义连接点为com.nan.service下的UserServiceImpl下的所有方法-->
<aop:pointcut id="pointcut" expression="execution(* com.nan.service.UserServiceImpl.*(..))"/>
<!--选取相应的前后置方法-->
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>
(2)方式二:自定义来实现AOP【主要是切面定义】
i.自定义前置后置方法
public class DiyPointCut {
public void before(){
System.out.println("执行前===========================================================");
}
public void after() {
System.out.println("执行后===========================================================");
}
}
ii.配置配置信息
<bean id="diy" class="com.nan.diy.DiyPointCut"/>
<aop:config>
<!--与实现类方式相比多了一个**设置切面**-->
<aop:aspect ref="diy">
<!--设置切入点和连接点-->
<aop:pointcut id="pointcut" expression="execution(* com.nan.service.UserServiceImpl.*(..))"/>
<!--前后插入函数-->
<aop:before method="before" pointcut-ref="pointcut"/>
<aop:after method="after" pointcut-ref="pointcut"/>
</aop:aspect>
</aop:config>
(3)方式三:使用注解实现
i.创建注解方法类
@Aspect
public class AnnotationPointCut {
@Before("execution(* com.nan.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("方法执行前===================================");
}
@After("execution(* com.nan.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("方法执行后===================================");
}
//在环绕增强中,我们可以给定一个参数,代表我们要获取的切入点
@Around("execution(* com.nan.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("环绕前===================================");
Signature signature = jp.getSignature();
Object proceed = jp.proceed();
System.out.println("环绕后");
}
}
ii.配置配置文件
<bean id="annotationPointCut" class="com.nan.diy.AnnotationPointCut"/>
<aop:aspectj-autoproxy/>
(一)编写实体类
package com.nan.pojo;
import lombok.Data;
@Data
public class User {
private int id;
private String name;
private String pwd;
}
(二)编写核心配置文件mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--configuration核心配置文件-->
<configuration>
<typeAliases>
<package name="com.nan.pojo"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper class="com.nan.mapper.UserMapper"/>
</mappers>
</configuration>
(三)编写接口
public interface UserMapper {
public List<User> selectUser();
}
(四)编写Mapper.xml
<?xml version="1.0" encoding="UTF8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--configuration核心配置文件-->
<mapper namespace="com.nan.mapper.UserMapper">
<select id="selectUser" resultType="user">
select * from user;
</select>
</mapper>
(五)编写spring-dao.xml配置文件(关联spring)
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!--DataSource:使用spring的数据源替代mybatis的配置-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<!--SqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="com/nan/mapper/*.xml"/>
</bean>
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
</beans>
(六)使接口编写实现类
public class UserMapperImpl implements UserMapper{
private SqlSessionTemplate sqlSession;
public void setSqlSession(SqlSessionTemplate sqlSession) {
this.sqlSession = sqlSession;
}
public List<User> selectUser() {
return sqlSession.getMapper(UserMapper.class).selectUser();
}
}
(七)将实现类注入Spring中
<bean id="userMapper" class="com.nan.mapper.UserMapperImpl">
<property name="sqlSession" ref="sqlSession"/>
</bean>
(八)测试
public class MyTest {
public static void main(String[] args) throws IOException {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
for (User user : userMapper.selectUser()) {
System.out.println(user);
}
}
}
(九)为Mybatis配置事务(声明式事务,由Aop实现)
2、配置事务类(为哪些方式配置事务)
<!--结合AOP,实现事务的植入-->
<!--配置事务的类-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--给哪些方法配置事务-->
<tx:attributes>
<tx:method name="add" propagation="REQUIRED"/>
<tx:method name="delete" propagation="REQUIRED"/>
<tx:method name="update" propagation="REQUIRED"/>
<tx:method name="query" read-only="true"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
3、 配置事务切入
<!--配置事务切入-->
<aop:config>
<aop:pointcut id="txPointCut" expression="execution(* com.nan.mapper.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>
参考:
[1] 【狂神说Java】Spring5最新完整教程IDEA版通俗易懂