SpringAOP的使用

本文详细介绍了如何在Spring框架中配置AOP,包括引入aop约束、自定义切面、使用注解方式切入、声明式事务配置,以及结合MyBatis的编程式事务管理。通过实例展示了切点表达式和事务通知的使用,确保了数据库操作的完整性和一致性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

SpringAOP的使用
1.在配置文件中引入对aop的约束,并在POM文件中加入织入包的依赖
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    //上面两个是基础IOC的约束,必备
    xmlns:context="http://www.springframework.org/schema/context"
    //上面一个是开启注解管理Bean对象的约束
    xmlns:aop="http://www.springframework.org/schema/aop"
    //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
    //上面两个是基础IOC的约束,必备
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context.xsd
    //上面一个是开启注解管理Bean对象的约束
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd
    //aop的注解约束
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx.xsd">
    //事务的约束
</beans>

		<dependency>
		    <groupId>org.aspectj</groupId>
		    <artifactId>aspectjweaver</artifactId>
		    <version>1.9.6</version>
		</dependency>
		<!-- 在POM文件中加入织入包的依赖https://mvnrepository.com/artifact/org.aspectj/aspectjrt -->
		<dependency>
		    <groupId>org.aspectj</groupId>
		    <artifactId>aspectjrt</artifactId>
		    <version>1.9.6</version>
		</dependency>

定义一个UserService接口和其实现类UserServiceImpl

public interface UserService {
	void add();
	void update();
	void delete();
	void query();
}
public class UserServiceImpl implements UserService{
	public void add() {
		System.out.println("添加了一个用户");	
	}
	public void update() {
		System.out.println("更新了一个用户");
	}
	public void delete() {
		System.out.println("删除了一个用户");
	}
	public void query() {
		System.out.println("查询了用户");	
	}
}

自定义一个切面

public class TestAspect {
	
	public void BeforeMyAdvice() {
		System.out.println("这是一个前置通知");
	}
	public void AfterMyAdvice() {
		System.out.println("这是一个后置通知");
	}
	public void AfterReturnMyAdvice() {
		System.out.println("这是一个执行完成后的通知");
	}
	public Object AroundMyAdvice(ProceedingJoinPoint jp) {
		Object result = null;
        try {
            System.out.println("环绕通知开始 日志记录");
            long start = System.currentTimeMillis();

            //有返回参数 则需返回值
            result =  jp.proceed();

            long end = System.currentTimeMillis();
            System.out.println("总共执行时长" + (end - start) + " 毫秒");
            System.out.println("环绕通知结束 日志记录");
        } catch (Throwable t) {
            System.out.println("出现错误");
        }
        return result;
		
	}
}

2.配置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: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">
    
    <bean id="UserService" class="com.lihan.Service.UserServiceImpl"></bean>  <!--测试用的UserService-->
    <bean id="IdentityAspect" class="com.lihan.Service.TestAspect"></bean>  <!--自定义的切面-->
    <aop:config>
    	<aop:aspect ref="IdentityAspect">                                             <!--使用Execute表达式-->
    		<aop:pointcut expression="execution(* com.lihan.Service.*.*(..))" id="mypoint"/> <!--选择切点-->
    		<aop:before pointcut-ref="mypoint" method="BeforeMyAdvice"/>
    		<aop:after pointcut-ref="mypoint" method="AfterMyAdvice"/>
    	</aop:aspect>
    </aop:config>
    
</beans>

请添加图片描述

3.使用注解方式进行切入
<aop:aspectj-autoproxy /><!--开启织入注解-->

编写切面类

@Aspect                               //切面注解
public class AnnoTestAspect {
	
	@Before("execution(* com.lihan.Service.*.*(..))")       //用Execution表达式指定一个Pointcut(切入点)
	public void MyBefore() {
		System.out.println("这是一个前置通知----@anno");
	}
	@After("execution(* com.lihan.Service.*.*(..))")
	public void MyAfter() {
		System.out.println("这是一个后置通知----@anno");
	}
	
	@Around("execution(* com.lihan.Service.*.*(..))")
	public Object MyAround(ProceedingJoinPoint joinpoint) {
		Object result = null;
        try {
            System.out.println("环绕通知开始 日志记录");
            long start = System.currentTimeMillis();

            //有返回参数 则需返回值
            result =  joinpoint.proceed();

            long end = System.currentTimeMillis();
            System.out.println("总共执行时长" + (end - start) + " 毫秒");
            System.out.println("环绕通知结束 日志记录");
        } catch (Throwable t) {
            System.out.println("出现错误");
        }
        return result;
		
	}

}

请添加图片描述

4.Execution表达式
execution(* * *)
    //表达式组成:修饰符(public,private..) 返回类型  包名.类名.方法名(类型,不填就是没有)

    //在此有几个必填  返回类型(构造器可无) 名称(可以全填,也可以只填包名,类名,甚至方法名) 参数模式(没有就填())
execution(<修饰符模式>?<返回类型模式><方法名模式>(<参数模式>)<异常模式>?)
       
 execution(* com.baobaotao.*(..))   //匹配com.baobaotao包下所有类的所有方法;
 execution(* com.baobaotao..*(..))  //匹配com.baobaotao包、子孙包下所有类的所有方法,如com.baobaotao.dao,com.baobaotao.servier以及com.baobaotao.dao.user包下的所有类的所有方法都匹配。".."出现在类名中时,后面必须跟"*",表示包、子孙包下的所有类
 execution(* com..*.*Dao.find*(..)) //匹配包名前缀为com的任何包下类名后缀为Dao的方法,方法名必须以find为前缀。如com.baobaotao.UserDao#findByUserId()、com.baobaotao.dao.ForumDao#findById()的方法都匹配切点。
    
 execution(* joke(String,int)))  
     //匹配匹配joke(String,int)方法,且joke()方法的第一个入参是String,第二个入参是int。它匹配NaughtyWaiter#joke(String,int)方法。如果方法中的入参类型是Java.lang包下的类,可以直接使用类名,否则必须使用全限定类名,如joke(java.util.List,int);
 execution(* joke(String,*)))
  //匹配目标类中的joke()方法,该方法第一个入参为String,第二个入参可以是任意类型,如joke(Strings1,String s2)和joke(String s1,double d2)都匹配,但joke(String s1,doubled2,String s3)则不匹配
 args(com.baobaotao.Waiter)

  //表示运行时入参是Waiter类型的方法,它和execution(**(com.baobaotao.Waiter))区别在于后者是针对类方法的签名而言的,而前者则针对运行时的入参类型而言。如args(com.baobaotao.Waiter)既匹配于addWaiter(Waiterwaiter),也匹配于addNaiveWaiter(NaiveWaiter naiveWaiter),而execution(**(com.baobaotao.Waiter))只匹配addWaiter(Waiterwaiter)方法;实际上,args(com.baobaotao.Waiter)等价于execution(**(com.baobaotao.Waiter+)),当然也等价于args(com.baobaotao.Waiter+)。
     @args()

  //该函数接受一个注解类的类名,当方法的运行时入参对象标注发指定的注解时,方法匹配切点。

5.声明式事务和使用(整合Mybatis)

因为事务的引用必须配置数据源,所以这里采取mybatis的方法进行操作

<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>                              <!--引入Mybatis和其与spring的整合的依赖-->
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.7</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>2.0.6</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.microsoft.sqlserver/mssql-jdbc -->
<dependency>
    <groupId>com.microsoft.sqlserver</groupId>  <!--引入数据库对应jdbc连接驱动-->
    <artifactId>mssql-jdbc</artifactId>
    <version>8.2.2.jre8</version>
</dependency>
<dependency>                                   <!--引入spring对JDBC的支持-->
	    <groupId>org.springframework</groupId>
	    <artifactId>spring-jdbc</artifactId>
	    <version>5.3.9</version>
</dependency>



为配置文件添加事务的约束

xmlns:tx="http://www.springframework.org/schema/tx"              //加入事务的约束
http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx.xsd

添加dao层的UserDao,并创建它对应的Mapper.xml,再来一个实现类UserDaoImpl

public interface UserDao {
	List<User> queryAllUser();
	void insert(User user);
	void update(User user);
	void delete(Integer ID);
	
}
<?xml version="1.0" encoding="UTF-8" ?>															
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.lihan.dao.UserDao">          <!--绑定的mapper.xml-->
  <select id="queryAllUser" resultType="com.lihan.pojo.User">
  		select * from t_User;
  </select>
  <insert id="insert" parameterType="com.lihan.pojo.User">
  		insert into t_User values(#{user.Name},#{user.Address},#{user.Phone});
  </insert>
  <update id="update"> <!--故意写错sql语句,测试事务时用-->
      update tt_User set Name = #{user.Name},Address = #{user.Address},#{user.Phone} where ID = #{user.ID}
  </update>
  <delete id="delete" parameterType="Integer">
  			delete from t_User where ID = #{ID}
  </delete>
</mapper>

多加的实现类UserDaoImpl

public class UserDaoImpl implements UserDao {//不要用UserDao接口去配置Bean(其实也可以),用它的实现类UserDaoImpl

	private SqlSession sqlSession;             //spring-mybatis官方推荐用SqlSession直接进行数据库操作
	
	public SqlSession getSqlSession() {        //提供sqlSession的Getter和Setter方法以便在XML中配置它的Bean
		return sqlSession;
	}

	public void setSqlSession(SqlSession sqlSession) {
		this.sqlSession = sqlSession;
	}

	public List<User> queryAllUser() {
		UserDao userdao = sqlSession.getMapper(UserDao.class);  //我个人习惯用得到Mapper来执行方法
		userdao.delete(7);              //这个删除操作为了触发事务的异常(违反设置的只读属性)
		return userdao.queryAllUser();
		
	}

	public void insert(User user) {
		UserDao userdao = sqlSession.getMapper(UserDao.class);
         userdao.insert(user);
		userdao.update(user);          //调用写错sql语句的update方法,查看事务执行情况
		
	}

	public void update(User user) {
		UserDao userdao = sqlSession.getMapper(UserDao.class);
		userdao.update(user);
	}

	public void delete(Integer ID) {
		UserDao userdao = sqlSession.getMapper(UserDao.class);
		userdao.delete(ID);
	
	}
}

添加spring-mybatis.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">
    
    <aop:config proxy-target-class="true"></aop:config> 
    <!--默认动态代理是jdk动态代理,而jdk动态代理不支持类注入也就是依赖注入的对象不能是类,只能是接口
解决方法:proxy-target-class
proxy-target-class属性值决定是基于接口的还是基于类的代理被创建。首先说明下proxy-target-class="true"和proxy-target-class="false"的区别,为true则是基于类的代理将起作用(需要cglib库),为false或者省略这个属性,则标准的JDK 基于接口的代理将起作用。
-->
    
    
    
    <bean id="propertyConfigurer"  													
      class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">  						
      <property name="location" value="classpath:jdbc.properties" />  <!--通过jdbc.properties设置连接信息-->
   </bean>  
    
    <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource">
      <property name="driverClassName" value="${driver}" />  <!--配置数据源,使用DBCP2连接池-->
      <property name="url" value="${url}" />  <!--读取连接信息-->	<!--须要额外导入依赖-->	
      <property name="username" value="${username}" />  												
      <property name="password" value="${password}" />  						
      <property name="initialSize" value="${initialSize}"></property>										
      <property name="maxIdle" value="${maxIdle}"></property>  											
      <property name="minIdle" value="${minIdle}"></property>  											
    </bean>
    
     											
 
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"><!--通过引用数据源-->
	  <property name="dataSource" ref="dataSource" />            <!--创建sqlSessionFactory,并扫mapper包-->
	  <property name="mapperLocations" value="classpath:com/lihan/Mapper/*.xml"></property> 
        <!--以下可以配置原来mabatis-configer.xml的属性,如别名什么的,IDEA会有更多提示-->
        <!--<properties name="typealias" value="com.lihan.pojo" />-->
	</bean>												
													
  									
													
   	<tx:jta-transaction-manager /><!--Spring 应该被设置为使用 JtaTransactionManager 或由容器指定的一个子类作为事务管理器。最简单的方式是使用 Spring 的事务命名空间-->	
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  		 <property name="dataSource" ref="dataSource" />  	<!--配置spring的事务管理器-->
  	</bean>  
   	<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">  <!--配置sqlsession的bean-->
  		<constructor-arg index="0" ref="sqlSessionFactory" />            <!--构造函数注入Factory的引用-->
	</bean>
	<bean id="userDao" class="com.lihan.dao.UserDaoImpl">       <!--创建实现类UserDaoImpl的Bean-->
  		<property name="sqlSession" ref="sqlSession" />         <!--注入sqlsession的引用-->
	</bean>
    

再数据库中创建对应的数据表,再插入若干条数据

请添加图片描述

编写测试类

public static void main(String[] args) {
		ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
		UserService userService = (UserService) app.getBean("UserService");
		userService.add();
		UserDao dao = app.getBean("userDao", UserDaoImpl.class);//拿到xml中配置的bean
		List<User> users = dao.queryAllUser();                  //执行查询拿到查询到的集合
		for(User u:users) {
			System.out.println(u.getName()+u.getAddress()+u.getPhone());
		}	
	}

请添加图片描述

PS这里使用了DBCP2的连接池,需要额外添加DBCP2的依赖

<tx:jta-transaction-manager />  <!--开启事务-->

在SqlSessionFactory的bean中添加内部bean的TransactionManage

 <property name="transactionFactory">
    <bean class="org.apache.ibatis.transaction.managed.ManagedTransactionFactory" />
  </property>

添加事务管理器的配置到spring-mybatis的配置文件中

<!-- 结合AOP实现事务的织入 -->
	<!-- 配置事务的通知 -->
	<tx:advice id="txAdvice" transaction-manager="transactionManager">
		<!-- 给哪些方法设置事务 -->
		<tx:attributes>
			<tx:method name="insert" propagation="REQUIRED"/>
			<tx:method name="update" propagation="REQUIRED"/>
			<tx:method name="delete" propagation="REQUIRED"/>
			<tx:method name="queryAllUser" read-only="true"/>
		</tx:attributes>
	</tx:advice>
	<aop:config> <!-- 定义织入点 -->
		<aop:pointcut expression="exection(* com.lihan.dao.*.*(..))" id="TXpointCut"/>
		<aop:advisor advice-ref="txAdvice" pointcut-ref="TXpointCut"/>  <!--aop配置事务通知及其切入点-->
	</aop:config>

定义一个方法进行测试

public static void main(String[] args) {
		ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
		UserService userService = (UserService) app.getBean("UserService");
		userService.add();
		UserDao dao = app.getBean("userDao", UserDaoImpl.class);
		List<User> users = dao.queryAllUser();  //调用查询方法,等待事务异常抛出
		for(User u:users) {
			System.out.println(u.getName()+u.getAddress()+u.getPhone());
		}
}	
	

请添加图片描述

再写一个测试类

public static void main(String[] args) {
		ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
		UserService userService = (UserService) app.getBean("UserService");
		userService.add();
		UserDao dao = app.getBean("userDao", UserDaoImpl.class);
//		List<User> users = dao.queryAllUser();
//		for(User u:users) {
//			System.out.println(u.getName()+u.getAddress()+u.getPhone());
//		}
		User newone = new User();
		newone.setName("默罕默德");
		newone.setAddress("伊斯坦布尔");
		newone.setPhone("1453");
		dao.insert(newone);
		
	}

请添加图片描述

触发了事务异常,查看以下数据库是否插入了新数据

请添加图片描述

并没有插入测试方法里的新对象,说明事务的配置生效了

6.编程式事务管理和使用

先注释掉之前的xml的声明式配置

然后在持久层中添加事务管理器

public class UserDaoImpl implements UserDao {   

	@Autowired
	private PlatformTransactionManager pManager;//看过它的源码,是一个接口,实现类是不同的数据源的事务管理器,
	                                            //比如HibernateTransactionManager,之前配的
                                                //DataSourceTransactionManager是它的抽象实现类		                                                             //AbstractPlatformTransactionManager的子类,所以可以可以直接注入
	private SqlSession sqlSession;
	
	public SqlSession getSqlSession() {    //提供Getter和Setter方法以便注入sqlSession
		return sqlSession;
	}
	public void setSqlSession(SqlSession sqlSession) {
		this.sqlSession = sqlSession;
	}

	public List<User> queryAllUser() {
		UserDao userdao = sqlSession.getMapper(UserDao.class);
		List<User> list = new ArrayList<User>();
		TransactionTemplate template = new TransactionTemplate(pManager);
		template.setReadOnly(true);
		template.execute(new TransactionCallbackWithoutResult() {
			
			@Override
			protected void doInTransactionWithoutResult(TransactionStatus status) {
				DefaultTransactionDefinition dd = new DefaultTransactionDefinition();
				dd.setReadOnly(true);
				status = pManager.getTransaction(dd);
				try {
					List<User> temp = userdao.queryAllUser();
					User user = new User();user.setName("穆拉德");user.setAddress("伊斯特本");user.setPhone("1453");                 //人为制造异常
					userdao.insert(user);
					userdao.update(new User());
				} catch (Exception e) {
					System.out.println("出现了事务的异常");
					pManager.rollback(status);  //回滚
					throw e;
				}
				pManager.commot(status);     //提交
			}
		});
		
		return userdao.queryAllUser();
	}

	public void insert(User user) {
		UserDao userdao = sqlSession.getMapper(UserDao.class);
		userdao.insert(user);
	}

	public void update(User user) {
		UserDao userdao = sqlSession.getMapper(UserDao.class);
		userdao.update(user);
	}

	public void delete(Integer ID) {
		UserDao userdao = sqlSession.getMapper(UserDao.class);
		userdao.delete(ID);
	
	}
}

请添加图片描述

编写测试类

public static void main(String[] args) {
		ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
		UserService userService = (UserService) app.getBean("UserService");
		userService.add();
		UserDao dao = app.getBean("userDao", UserDaoImpl.class);
		List<User> users = dao.queryAllUser();    //调用
		for(User u:users) {
			System.out.println(u.getName()+u.getAddress()+u.getPhone());
		}
		
	}

请添加图片描述

再看看数据库里有没有插入方法中的新的User对象,

请添加图片描述

并没有插入新的对象

Spring AOP 是一个用于实现面向切面编程的框架。它可以通过配置来实现横切关注点的模块化,并将其应用到程序的不同部分。Spring AOP 使用 AspectJ 切入点指示符来定义切入点表达式,用于匹配方法执行连接点。Spring AOP 支持以下 AspectJ 切入点指示符: 1. execution:用于匹配方法执行连接点。这是使用 Spring AOP 时要使用的主要切入点指示符。 2. within:限制匹配以连接某些类型中的点(使用 Spring AOP 时在匹配类型中声明的方法的执行)。 3. this:限制匹配到连接点(使用 Spring AOP 时方法的执行),其中 Bean 引用(Spring AOP 代理)是给定类型的实例。 4. target:限制匹配到连接点(使用 Spring AOP 时方法的执行),其中目标对象(正在代理的应用程序对象)是给定类型的实例。 5. args:限制匹配到连接点(使用 Spring AOP 时方法的执行),其中参数是给定类型的实例。 6. @target:限制匹配到连接点(使用 Spring AOP 时方法的执行),其中执行对象的类具有给定类型的注释。 7. @args:限制匹配到连接点(使用 Spring AOP 时方法的执行),其中传递的实际参数的运行时类型具有给定类型的注释。 8. @within:限制匹配以连接具有给定注释的类型中的点(使用 Spring AOP 时在具有给定注释的类型中声明的方法的执行)。 9. @annotation:限制匹配到连接点的主题(在 Spring AOP 中运行的方法)具有给定注释的连接点。 在使用 Spring AOP 时,需要引入 Spring AOP 和 Spring Context 相关的包,并在配置文件中进行相应的配置。可以通过 Maven 或其他构建工具来引入相关依赖。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

罗马苏丹默罕默德

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值