Spring-Aop定义及案例<二>

本文详细介绍了Spring AOP的定义,包括切面、通知、目标类、切入点和织入的概念,强调了其松耦合的优势。通过execution表达式展示了如何精确匹配方法。此外,解释了Spring AOP的加载过程,以及代理对象的创建。文中还探讨了不同类型的通知,如前置、后置、最终和异常通知,并强调了环绕通知的控制能力。最后,提供了将Spring AOP与Hibernate整合的案例分析。

问题?SpringAop的定义及使用

Aop定义:

aop:

   1、切面

        事务、日志、安全性框架、权限等都是切面

   2、通知

      切面中的方法就是通知

   3、目标类

   4、切入点

         只有符合切入点,才能让通知和目标方法结合在一起

   5、织入:

         形成代理对象的方法的过程

         

 

好处:

   事务、日志、安全性框架、权限、目标方法之间完全是松耦合的



execution详解:



executionpublic * *..)) 所有的公共方法

execution(*set*..)) 以set开头的任意方法

execution(*com.xyz.service.AccountService.*..))com.xyz.service.AccountService类中的所有的方法

execution(*com.xyz.service.*.*..)) com.xyz.service包中的所有的类的所有的方法

execution(*com.xyz.service..*.*..))com.xyz.service包及子包中所有的类的所有的方法

execution(*cn.itcast.spring.sh..*.*(String,?,Integer)) cn.itcast.spring.sh包及子包中所有的类的有三个参数

                                                           第一个参数为String,第二个参数为任意类型,

                                                           第三个参数为Integer类型的方法


加载过程:

springAOP的具体加载步骤:

   1、当spring容器启动的时候,加载了spring的配置文件

   2、为配置文件中所有的bean创建对象

   3spring容器会解析aop:config的配置

       1、解析切入点表达式,用切入点表达式和纳入spring容器中的bean做匹配

            如果匹配成功,则会为该bean创建代理对象,代理对象的方法=目标方法+通知

            如果匹配不成功,不会创建代理对象

   4、在客户端利用context.getBean获取对象时,如果该对象有代理对象则返回代理对象,如果代理对象,则返回目标对象

说明:如果目标类没有实现接口,则spring容器会采用cglib的方式产生代理对象,如果实现了接口,会采用jdk的方式


通知:

通知:

   1、前置通知

      1、在目标方法执行之前执行

      2、无论目标方法是否抛出异常,都执行,因为在执行前置通知的时候,目标方法还没有执行,还没有遇到异常

   2、后置通知

      1、在目标方法执行之后执行

      2、当目标方法遇到异常,后置通知将不再执行

      3、后置通知可以接受目标方法的返回值,但是必须注意:

               后置通知的参数的名称和配置文件中returning="var"的值是一致的

   3、最终通知:

      1、在目标方法执行之后执行

      2、无论目标方法是否抛出异常,都执行,因为相当于finally

   4、异常通知

      1、接受目标方法抛出的异常信息

      2、步骤

           在异常通知方法中有一个参数Throwable  ex

           在配置文件中

              <aop:after-throwingmethod="throwingMethod" pointcut-ref="perform"throwing="ex"/>

   5、环绕通知

       1、如果不在环绕通知中调用ProceedingJoinPointproceed,目标方法不会执行

       2、环绕通知可以控制目标方法的执行


联合hibernate+spring的案例:

hibernate工具类:

package cn.itcast.spring.sh.aop;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;

public class HibernateUtils {
	public static SessionFactory sessionFactory;
	static{
		Configuration configuration = new Configuration();
		configuration.configure("cn/itcast/spring/sh/aop/hibernate.cfg.xml");
		sessionFactory = configuration.buildSessionFactory();
//		Session session = sessionFactory.openSession();
//		Transaction transaction = session.beginTransaction();
//		transaction.commit();
	}
}


持久化类:

package cn.itcast.spring.sh.aop;

import java.io.Serializable;

public class Person implements Serializable{
	private Long pid;
	private String pname;
	private String psex;
	
	public Person(){}
	
	public Person(String pname){
		this.pname = pname;
	}
	
	public Long getPid() {
		return pid;
	}
	public void setPid(Long pid) {
		this.pid = pid;
	}
	public String getPname() {
		return pname;
	}
	public void setPname(String pname) {
		this.pname = pname;
	}
	public String getPsex() {
		return psex;
	}
	public void setPsex(String psex) {
		this.psex = psex;
	}
	
}

映射文件:Person.hbm.xml

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
	<!-- 
		用来描述一个持久化类
		name  类的全名
	 	table 可以不写  默认值和类名一样 
	 	catalog  数据库的名称  一般不写
	 -->
	<class name="cn.itcast.spring.sh.aop.Person">
		<!-- 
			标示属性  和数据库中的主键对应
			name  属性的名称
			column 列的名称
		 -->
		<id name="pid" column="pid" length="200" type="java.lang.Long">
			<!-- 
				主键的产生器
				  就该告诉hibernate容器用什么样的方式产生主键
			 -->
			<generator class="increment"></generator>
		</id>
		<!-- 
			描述一般属性
		 -->
		<property name="pname" column="pname" length="20" type="string"></property>
		
		<property name="psex" column="psex" length="10" type="java.lang.String"></property>
	</class>
</hibernate-mapping>


hibernate.cfg.xml配置文件

<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
	<!-- 
		一个session-factory只能连接一个数据库
	-->
<session-factory>
	<!-- 
		数据库的用户名
	-->
	<property name="connection.username">root</property>
	<!-- 
		密码
	-->
	<property name="connection.password">root</property>
	<!-- 
		url
	-->
	<property name="connection.url">
		jdbc:mysql://localhost:3306/Use_Aop
	</property>
	<!-- 
		作用:根据持久化类和映射文件生成表
		validate
		create-drop
		create
		update
	-->
	<property name="hbm2ddl.auto">update</property>
	<!-- 
		显示hibernate内部生成的sql语句
	-->
	<property name="show_sql">true</property>
	<property name="current_session_context_class">thread</property>
	<!-- 添加映射文件 -->
	<mapping resource="cn/itcast/spring/sh/aop/Person.hbm.xml" />

</session-factory>
</hibernate-configuration>

dao层接口:

package cn.itcast.spring.sh.aop;

public interface PersonDao {
	public String savePerson(Person person);
}

dao实现层

package cn.itcast.spring.sh.aop;

import java.lang.reflect.Method;

import org.junit.Test;

public class PersonDaoImpl extends HibernateUtils implements PersonDao  {

	public String savePerson(Person person) {
		//在这里首先得开启事务才能crud操作,Springaop配置已经准备好动态代理了
		//int a=1/0;
		sessionFactory.getCurrentSession().save(person);
		System.out.println("执行保存操作了。");
		return "aaa";
	}
	


}

事务层

package cn.itcast.spring.sh.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.hibernate.Transaction;

public class MyTransaction extends HibernateUtils {
	private Transaction transaction;
	
	/**
	 * 通过一些参数可以获取目标方法的相关信息,报告目标方法名称、返回值参数、异常信息等
	 * 不过相应的也要在配置文件中进行设置(通过实验,好像只配置后置及异常就可以只用了),不然就获取不了。
	 * 注:环绕通知不可以跟前置通知和后置通知一起用,因为环绕通知已经把两个通知的东西做了,它可以调用目标方法
	 * 
	 * */
	
	public void beginTransaction(JoinPoint joinPoint){
		//可以通过该参数得到目标方法名称(导的是这个包:import org.aspectj.lang.JoinPoint;)
		System.out.println(joinPoint.getSignature().getName());
		//开启事务
		System.out.println("开启事务");
		this.transaction = sessionFactory.getCurrentSession().beginTransaction();
		
	}
	//这里的var参数是后置通知,得到目标方法的返回值
	public void commit(Object var){
		//提交事务
		this.transaction.commit();
		System.out.println(var);
		System.out.println("关闭事务");
	}
	
	//测试最终通知
//	public void finallFun(){
//		System.out.println("最终通知发生了");
//	}
	
	//测试异常通知
	//throwing="ag":表示异常信息参数,和配置文件中是相对应的
//	public void exception(Throwable ag){
//		System.out.println("异常信息:"+ag);
//	}
	
	//测试环绕异常通知
//	public void aroundException(ProceedingJoinPoint joitPoint) throws Throwable{
//		//joitPoint.proceed();//调用目标方法,不然目标方法不能执行,这个时候前置和后置都已经不用了
//		System.out.println("环绕通知发生");
//	}
}

spring配置文件(applicationContext.xml):

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:aop="http://www.springframework.org/schema/aop" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">

	<!-- SpringAop案例
		使用条件:必须有
		xmlns:aop="http://www.springframework.org/schema/aop" 
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd" 
	-->
	
	<!-- 导入目标类 -->
	<bean id="personDao" class="cn.itcast.spring.sh.aop.PersonDaoImpl"></bean>
	<bean id="myTransaction" class="cn.itcast.spring.sh.aop.MyTransaction"></bean>
	<!-- aop配置 -->
	<aop:config>
	  <!-- 
      	切入点表达式
      -->
      <aop:pointcut expression="execution(* cn.itcast.spring.sh.aop.PersonDaoImpl.*(..))" id="perform"/>
      <!--
      aop:aspect:引入切面
     前置通知:aop:before:目标方法没执行前执行该方法
      后置通知:aop:after-returning:目标方法执行后执行该方法。目标方法有异常,不会执行了,
      			这里的var是后置通知的参数,可以得到目标方法的返回值,并且和方法中所带参数(Object var)一致
  最终通知::aop:after :表示目标方法执行后才执行,不管目标方法有没有异常,都会执行。
        异常通知:aop:after-throwing:当目标方法出现错误了,就会执行该方法,throwing="ag":表示异常信息参数,在方法中带参数(Throwable ag)(可以使用
   环绕通知:aop:around 可以控制目标方法的执行(注:环绕与前置、后置不能一起用.)
   
      pointcut-ref: 引入切入点
        returning="var":代表接受目标类方法中的返回值
       -->
      <aop:aspect ref="myTransaction">
      		<aop:before method="beginTransaction" pointcut-ref="perform"/>
      		<aop:after-returning method="commit" pointcut-ref="perform" returning="var"/>
      		<!-- <aop:after method="finallFun" pointcut-ref="perform"/>
      		<aop:after-throwing method="exception" throwing="ag" pointcut-ref="perform"/> -->
      		<!-- <aop:around method="aroundException" pointcut-ref="perform"/> -->
      </aop:aspect>
	<!--
	通知类型:

	前置通知(Before advice):在某连接点之前执行的通知,
							但这个通知不能阻止连接点之前的执行流程(除非它抛出一个异常)。 

	后置通知(After returning advice):在某连接点正常完成后执行的通知:
									例如,一个方法没有抛出任何异常,正常返回。 

	异常通知(After throwing advice):在方法抛出异常退出时执行的通知。 

	最终通知(After (finally) advice):当某连接点退出的时候执行的通知
									(不论是正常返回还是异常退出)。 

	环绕通知(Around Advice):包围一个连接点的通知,如方法调用。
							这是最强大的一种通知类型。环绕通知可以在方法调用前后完成自定义的行为。
							它也会选择是否继续执行连接点或直接返回它自己的返回值或抛出异常来结束执行。 
	 
	 -->
</beans>

测试类:

package cn.itcast.spring.sh.aop;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class PersonTest {
	@Test
	public void test(){
		ApplicationContext applicationContext = new  ClassPathXmlApplicationContext("cn/itcast/spring/sh/aop/applicationContext.xml");
		PersonDao personDao = (PersonDao)applicationContext.getBean("personDao");
		Person person1 = new Person();
		person1.setPname("李大鹏");
		person1.setPsex("男");
		personDao.savePerson(person1);
	}
}

结果:

spring动态代理:



异常信息的执行结果:



注意:继承接口的是cglib动态代理方式,而在getbean的时候产生的不是接口的对象而是动态代理的对象








<think>嗯,用户导入了Spring Context和AspectJ Weaver的依赖,但没出现Spring Config的问题。首先,我需要理清楚这两个依赖的作用。Spring Context是Spring框架的核心,提供IoC和依赖注入功能,而AspectJ Weaver用于支持AOP,比如切面编程。 用户提到“没有出现Spring Config”,可能是指在IDE里没有自动生成配置,或者在项目结构中找不到相关配置。常见的情况是,用户可能期望Spring自动生成配置文件,但实际上需要手动创建。比如,Spring的XML配置文件(如applicationContext.xml)或Java配置类(带@Configuration的类)需要用户自己创建,Spring不会自动生成。 另外,检查依赖是否正确引入。用户提供的Maven依赖版本是5.1.11.RELEASE和AspectJ 1.9.6,可能存在版本兼容性问题吗?不过这两个版本通常是兼容的,但需要确认是否有冲突。用户是否执行了Maven的重新导入?有时候依赖没下载完全会导致问题。 还有,是否缺少必要的配置?比如,如果使用注解驱动,需要在配置类上添加@EnableAspectJAutoProxy,或者在XML中配置<aop:aspectj-autoproxy/>。如果用户没有正确配置这些,即使有AspectJ Weaver的依赖,AOP也不会生效,导致Spring Config不出现。 另外,项目结构是否正确?比如,配置文件是否放在src/main/resources目录下,或者配置类是否在组件扫描的包路径下。如果配置类不在扫描范围内,Spring不会加载它,导致配置无效。 用户可能混淆了依赖引入和配置创建。添加依赖只是提供了库的支持,但具体的配置需要用户自行设置。比如,使用Java配置或XML配置定义Bean和切面。如果用户没有创建这些配置Spring自然不会自动生成。 还有可能用户期望IDE的某些功能,比如Spring Tools Suite的仪表盘或配置支持,但普通项目可能需要手动启用Spring插件或配置。或者,用户可能没有正确设置Spring Facet,导致IDE没有识别到Spring项目结构。 最后,是否需要检查构建工具的输出,确认依赖是否被正确下载,没有出现错误或冲突。比如,Maven的pom.xml是否正确,依赖是否下载到本地仓库,是否存在网络问题导致依赖下载失败。</think>你遇到的问题可能有以下几个原因,以下从依赖作用和配置方式逐步分析: 1. **依赖作用分析** - `spring-context`: 提供Spring核心容器功能(IoC/DI)和基础注解支持 $$ \text{Spring Context} \supset \{ \text{@Component, @Autowired, @Configuration} \} $$ - `aspectjweaver`: 提供AspectJ切面织入能力(配合`@Aspect`使用) 2. **未出现Spring Config的可能原因** **情况一:未主动创建配置文件** - 需要手动创建以下两种配置之一: - **XML配置**:`src/main/resources/applicationContext.xml` ```xml <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" xsi:schemaLocation="..."> <context:component-scan base-package="com.example"/> <aop:aspectj-autoproxy/> </beans> ``` - **Java配置类**:`@Configuration`标注的类 ```java @Configuration @ComponentScan("com.example") @EnableAspectJAutoProxy public class AppConfig {} ``` **情况配置未被正确加载** - **XML配置需要初始化容器**: ```java ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); ``` - **Java配置需要初始化容器**: ```java AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class); ``` 3. **验证步骤** ```mermaid graph TD A[检查依赖] -->|mvn dependency:tree| B[确认spring-conject存在] B --> C[检查配置文件位置] C --> D[确认是否启用组件扫描] D --> E[验证AOP代理是否启用] ``` 4. **典型成功案例** ```java // 定义切面 @Aspect @Component public class LoggingAspect { @Before("execution(* com.example.service.*.*(..))") public void logBefore(JoinPoint joinPoint) { System.out.println("方法执行: " + joinPoint.getSignature()); } } // 主程序 public class MainApp { public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class); MyService service = ctx.getBean(MyService.class); service.execute(); } } ``` 5. **版本兼容性建议** - Spring 5.1.x 推荐配合 AspectJ 1.9.x 使用 - 若需使用最新特性,可升级至: ```xml <version>5.3.27</version> <!-- spring-context --> <version>1.9.19</version> <!-- aspectjweaver --> ``` 请根据实际项目结构检查配置文件位置和加载方式,若仍无法解决建议提供具体错误日志。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值