三, Spring核心AOP面向切面学习--实训 2019/10 / 13_关注面切面-程序员宅基地

技术标签: 实训  Spring  

学习入门

在这里插入图片描述
事务管理
https://blog.csdn.net/qq_39088066/article/details/102541023

为什么 AOP会出现?

好多重复的代码,并且当加入越来越多的非业务需求,原有的计算器方法变得膨胀冗长。这里有一件非常痛苦的事情,无法使用原有的编程方式将他们模块化,从核心业务中提取出来。例如日志记录和参数验证,AOP里将他们称为横切关注点
  在使用传统的面向对象的编程方式无法理想化的模块化横切关注点,程序员不能不做的就是将这些横切关注点放置在每一个模块里与核心逻辑交织在一起,这将会导致横切关注点在每一个模块里到处存在。使用非模块化的手段实现横切关注将会导致,代码混乱,代码分散,代码重复。需要一种方式来将横切关注点冲模块中提取出来。

忍无可忍的大牛们提出了AOP,它是一个概念,一个规范,本身并没有设定具体语言的实现,也正是这个特性让它变的非常流行,现在已经有许多开源的AOP实现框架了。

利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

这个字 表达了深入 , 介入 . 而 之前OOP时代 停留在对象层次 无法具体到事物内部. 如:事务管理 ,权限控制 等

OOP引进"抽象"、“封装”、“继承”、"多态"等概念,对万事万物进行抽象和封装,来建立一种对象的层次结构

AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。

AOP 具体如何工作的?

利用一种称为"横切"的技术,能够剖解开封装的对象内部,并将那些影响了多个类并且与具体业务无关的公共行为 封装成一个独立的模块(称为切面), 而且对原来的 程序影响不大.

更重要的是,它又能以巧夺天功的妙手将这些剖开的切面复原,不留痕迹的融入核心业务逻辑中。这样,对于日后横切功能的编辑和重用都能够带来极大
的方便。

AOP所谓面向切面编程,是一种通过预编译和运行期动态代理的方式实现在不修改源代码的情况下给程序动态添加功能的技术。
4.3.1 切入点
程序执行的某个特定位置:如类开始初始化前、类初始化后、类某个方法调用前、调用后、方法抛出异常后。这些代码中的特定点,称为“连接点”。

4.3.2 连接点
每个程序类都拥有多个连接点,如一个拥有两个方法的类,这两个方法都是连接点。但在这为数众多的连接点中,如何定位到某个感兴趣的连接点上呢?AOP通过“切入点”定位特定的连接点。通过数据库查询的概念来理解切点和连接点的关系:连接点相当于数据库中的记录,而切点相当于查询条件。一个切点可以匹配多个连接点。

4.3.3 增强/通知
所谓通知是指拦截到Joinpoint之后所要做的事情就是通知.通知分为前置通知, 后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)

4.3.4 目标对象
增强逻辑的织入目标类。也就是要被通知的对象,也就是真正的业务逻辑,目标业务类只实现那些非横切逻辑的程序逻辑,而性能监视和事务管理等这些横切逻辑则可以使用AOP动态织入到特定的连接点上。

4.3.5 织入
织入是将增强添加对目标类具体连接点上的过程。AOP像一台织布机,将目标类、增强或引介(Introduction为类添加一些属性和方法)通过AOP这台织布机天衣无缝地编织到一起。根据不同的实现技术,AOP有三种织入的方式:
a、编译期织入,这要求使用特殊的Java编译器。
b、类装载期织入,这要求使用特殊的类装载器。
c、动态代理织入,在运行期为目标类添加增强生成子类的方式。
Spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入。

4.3.6 代理
一个类被AOP织入增强后,就产出了一个结果类,它是融合了原类和增强逻辑的代理类。根据不同的代理方式,代理类既可能是和原类具有相同接口的类,也可能就是原类的子类,所以我们可以采用调用原类相同的方式调用代理类。

4.3.7 切面
切面由切点和增强(引介)组成,它既包括了横切逻辑的定义,也包括了连接点的定义,Spring AOP就是负责实施切面的框架,它将切面所定义的横切逻辑织入到切面所指定的连接点中。
4.4 增强处理类型

在这里插入图片描述

AOP在分离出重复代码上做的很不错

关注点形成的类,就叫切面(类)

注: 重复代码就叫做关注点

面向切面编程,就是指对很多功能都有的重复的代码抽取,再在运行的时候往业务 方法上动态植入“切面类代码”。

切入点

执行目标对象方法,动态植入切面代码。

可以通过切入点表达式,指定拦截哪些类的哪些方法; 给指定的类在运行的时候植入 切面类代码。

手动实现AOP编程

AOP 面向切面的编程,AOP可以实现“业务代码”与“关注点代码”分离

public void add(User user) {
      // 保存一个用户
		Session session = null; //聚会
		Transaction trans = null; //交易
		try {
     
			session = HibernateSessionFactoryUtils.getSession();   // 【关注点代码】
			trans = session.beginTransaction();    // 【关注点代码】
			session.save(user);     // 核心业务代码
			trans.commit();     //…【关注点代码】
		} catch (Exception e) {
         
			e.printStackTrace(); 
			if(trans != null){
     
				trans.rollback();   //..【关注点代码】
			} 
		} finally{
     
			HibernateSessionFactoryUtils.closeSession(session);   ..【关注点代码】
		} 
   } 

分析总结:
关注点代码,就是指重复执行的代码。
业务代码与关注点代码分离,好处?
– 关注点代码写一次即可;
-开发者只需要关注核心业务;
-运行时期,执行核心业务代码时候动态植入关注点代码; 【代理】

Dao层与AOP耦合
	IUserDao.java
package cn.atcast.d_myaop;
// 接口
public interface IUserDao {
    
	void save();	
}
	UserDao.java
package cn.atcast.d_myaop;
import javax.annotation.Resource;
import org.springframework.stereotype.Component;
/**
 * 目标对象
*/
@Component   // 加入容器
public class UserDao implements IUserDao{
    
	// 重复执行代码形成的一个类
	@Resource
	private Aop aop;

	@Override
	public void save() {
    
		aop.begin();
	System.out.println("-----核心业务:保存!!!------");
		aop.commite();
	}
}
	Aop.java
package cn.atcast.d_myaop;
import org.springframework.stereotype.Component;
@Component  // 加入IOC容器
public class Aop {
    
	// 重复执行的代码
	public void begin(){
    
		System.out.println("开始事务/异常");
	}
	public void commite(){
    
		System.out.println("提交事务/关闭");
	}
}
	bean.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:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    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">
	<!-- 开启注解扫描 -->
	<context:component-scan base-package="cn.atcast.d_myaop"></context:component-scan>
</beans>        
	App.java
package cn.atcast.d_myaop;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class App {
    
	ApplicationContext ac = 
		new ClassPathXmlApplicationContext("cn/atcast/d_myaop/bean.xml");

	@Test
	public void testApp() {
    
		IUserDao userDao = (IUserDao) ac.getBean("userDao");
		userDao.save();
	}
}

在这里插入图片描述

静态代理虽然保证了业务类只需关注逻辑本身,代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理。再者,如果增加一个方法,除了实现类需要实现这个方法外,所有的代理类也要实现此方法。增加了代码的维护成本。那么要如何解决呢?答案是使用动态代理。

Dao层与AOP解耦
初步实现AOP

反射技术 补充
public class TestClassLoad {
public static void main(String[] args) throws Exception {
Class<?> clz = Class.forName(“A”);
Object o = clz.newInstance();
Method m = clz.getDeclaredMethod(“hello”, null);
m.invoke(o);
}
static class A{
public void hello() {
System.out.println(“hello world”);
}
}
}

public interface Waiter {
    
    //服务方法
    public void server();
}


public class ManWaiter implements Waiter {
    

    @Override
    public void server() {
    
        System.out.println("服务中");
    }
}


public class Demo2 {
    
    @Test
    public void test1() {
    
        Waiter waiter = new ManWaiter();
        waiter.server();
    }

    @Test
    public void test2() {
    
        Waiter manWaiter = new ManWaiter();
        ClassLoader classLoader = this.getClass().getClassLoader();
        Class[] interfaces = {
    Waiter.class};
        InvocationHandler invocationHandler = new WaiterInvocationHandler(manWaiter);
        //得到代理对象,代理对象就是在目标对象的基础上进行了增强的对象
        Waiter waiter = (Waiter) Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
        waiter.server();//前面添加“你好”,后面添加“再见”
    }
}

class WaiterInvocationHandler implements InvocationHandler {
    

    private Waiter waiter;

    WaiterInvocationHandler(Waiter waiter) {
    
        this.waiter = waiter;
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
        System.out.println("你好");
        waiter.server();//调用目标对象的方法
        System.out.println("再见");
        return null;
    }
}

你好
服务中
再见

完善AOP

然后我们添加一个前置增强接口:


/**
 * 前置增强
 */
public interface BeforeAdvice {
    
    public void before();
}
再添加一个后置增强接口:

public interface AfterAdvice {
    
    public void after();
}
/**
 * ProxFactory用来生成代理对象
 * 它需要所有的参数:目标对象,增强,
 * Created by Yifan Jia on 2018/6/5.
 */

/**
 * 1、创建代理工厂
 * 2、给工厂设置目标对象、前置增强、后置增强
 * 3、调用creatProxy()得到代理对象
 * 4、执行代理对象方法时,先执行前置增强,然后是目标方法,最后是后置增强
 */
//其实在Spring中的AOP的动态代理实现的一个织入器也是叫做ProxyFactory 
public class ProxyFactory {
    
    private Object targetObject;//目标对象
    private BeforeAdvice beforeAdvice;//前值增强
    private AfterAdvice afterAdvice;//后置增强

    /**
     * 用来生成代理对象
     * @return
     */
    public Object creatProxy() {
    
        /**
         * 给出三个参数
         */
        ClassLoader classLoader = this.getClass().getClassLoader();
        //获取当前类型所实现的所有接口类型
        Class[] interfaces = targetObject.getClass().getInterfaces();

        InvocationHandler invocationHandler = new InvocationHandler() {
    
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
                /**
                 * 在调用代理对象的方法时,会执行这里的内容
                 */
                if(beforeAdvice != null) {
    
                    beforeAdvice.before();
                }
                Object result = method.invoke(targetObject, args);//调用目标对象的目标方法
                //执行后续增强
                afterAdvice.after();

                //返回目标对象的返回值
                return result;
            }
        };
        /**
         * 2、得到代理对象
         */
        Object proxyObject = Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
        return proxyObject;

    }
//get和set方法略
}

测试

public class Demo3 {
    
    @Test
    public void tset1() {
    

        ProxyFactory proxyFactory = new ProxyFactory();//创建工厂
        proxyFactory.setTargetObject(new ManWaiter());//设置目标对象
        //设置前置增强
        proxyFactory.setBeforeAdvice(new BeforeAdvice() {
    
            @Override
            public void before() {
    
                System.out.println("客户你好");
            }
        });
        //设置后置增强
        proxyFactory.setAfterAdvice(new AfterAdvice() {
    
            @Override
            public void after() {
    
                System.out.println("客户再见");
            }
        });
        Waiter waiter = (Waiter) proxyFactory.creatProxy();
        waiter.server();

    }
}
几个实例

在这里插入图片描述

在这里插入图片描述

注解方式实现AOP编程(1)

	步骤:
	1) 先引入aop相关jar文件    			
	spring-aop-3.2.5.RELEASE.jar   【spring3.2源码】
    aopalliance.jar				  【spring2.5源码/lib/aopalliance】
    aspectjweaver.jar			  【spring2.5源码/lib/aspectj】或【aspectj-1.8.2\lib】
	aspectjrt.jar				  【spring2.5源码/lib/aspectj】或【aspectj-1.8.2\lib】
	注意: 用到spring2.5版本的jar文件,如果用jdk1.7可能会有问题。
    需要升级aspectj组件,即使用aspectj-1.8.2版本中提供jar文件提供。
	2) bean.xml中引入aop名称空间
	3) 开启aop注解
	4) 使用注解
	@Aspect							指定一个类为切面类		
	@Pointcut("execution(* cn.atcast.e_aop_anno.*.*(..))")  指定切入点表达式
	@Before("pointCut_()")				前置通知: 目标方法之前执行
	@After("pointCut_()")					后置通知:目标方法之后执行(始终执行)
	@AfterReturning("pointCut_()")		返回后通知: 执行方法结束前执行
	@AfterThrowing("pointCut_()")			异常通知:  出现异常时候执行
  @Around("pointCut_()")	环绕通知: 在方法执行前后和抛出异常时执行,相当于综合了以上三种通知。
     5)切面声明完毕后 我们用配置文件applicationContext.xml  来 关联到申明的切面类
     将切面类交与Spring容器管理 
 <bean class="advice.CalculationAnnotation"></bean>


  <!--Spring容器初始化 找到这一句 就会找 配置了@Aspect切面注解  使用注解自动生成代理对象 -->
  <aop:aspectj-autoproxy/>

XML方式实现AOP编程(2)

	1) 引入jar文件  【aop 相关jar, 4个】
	2) 引入aop名称空间
	3) aop 配置
	* 配置切面类 (重复执行代码形成的类)
	* aop配置   拦截哪些方法 / 拦截到方法后应用通知代码
补充:切入点表达式
切入点表达式,可以对指定的“方法”进行拦截;从而给指定的方法所在的类生成代	理对象。
execution(* cn.com.dao.impl..*.*(..)) 
第一个*代表任何返回值 
cn.com.dao.impl..*:代表要拦截cn.com.dao.impl包下的以及子包下的所有类 
.*(..):这个代表任意方法,就是说上面那些类的任意方法,()里面的点,
代表任意参数 
比如要拦截add开头的和delete开头的方法?
execution(* add*(..))&& execution(* delete*(..))

基本术语:

AOP技术的具体实现,无非也就是通过动态代理技术或者是在程序编译期间进行静态的"织入"方式。下面是这方面技术的几个基本术语:

1、join point(连接点):是程序执行中的一个精确执行点,例如类中的一个方法。它是一个抽象的概念,在实现AOP时,并不需要去定义一个join point,但Spring只支持方法级的连接点。

2、point cut(切入点):本质上是一个捕获连接点的结构。在AOP中,可以定义一个point cut,来捕获相关方法的调用,这种精准的匹配是由切入点的正则表达式来定义的。

3、advice(通知):是point cut的执行代码,是执行“方面”的具体逻辑以目标方法为参照点,根据放置的地方不同,可分为前置通知(Before)、后置通知(AfterReturning)、异常通知(AfterThrowing)、最终通知(After)与环绕通知(Around)5种。在实际应用中通常是切面类中的一个方法,具体属于哪类通知,同样是在配置中指定的。。

4、aspect(切面):point cut和advice结合起来就是aspect,它类似于OOP中定义的一个类,但它代表的更多是对象间横向的关系。是共有功能的实现 ,配置指定
5 、Weaving 编织:主要是在编译期使用AJC将切面的代码注入到目标中, 并生成出代码混合过的.class的过程.

以下是补充

目标对象(Target):就是那些即将切入切面的对象,也就是那些被通知的对象。这些对象中已经只剩下干干净净的核心业务逻辑代码了,所有的共有功能代码等待AOP容器的切入。

代理对象(Proxy):将通知应用到目标对象之后被动态创建的对象。可以简单地理解为,代理对象的功能等于目标对象的核心业务逻辑功能加上共有功能。代理对象对于使用者而言是透明的,是程序运行过程中的产物。

织入(Weaving):将切面应用到目标对象从而创建一个新的代理对象的过程。这个过程可以发生在编译期、类装载期及运行期,当然不同的发生点有着不同的前提条件。譬如发生在编译期的话,就要求有一个支持这种AOP实现的特殊编译器;发生在类装载期,就要求有一个支持AOP实现的特殊类装载器;只有发生在运行期,则可直接通过Java语言的反射机制与动态代理机制来动态实现。

AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。

AOP与OOP是面向不同领域的两种设计思想。

OOP(面向对象编程)针对业务处理过程的实体及其属性和行为进行抽象封装,以获得更加清晰高效的逻辑单元划分。

AOP则是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。

我们可以单单从上面的字面上来理解AOP和OOP的话,用下面的理解也不为过:

OOP实际上是对对象的属性和行为的封装,而AOP对于这点就无从谈起,但是AOP是处理某个步骤和阶段的,从中进行切面的提取,也就是说,如果几个或更多个逻辑过程中,有重复的操作行为,AOP就可以提取出来,运用动态代理,实现程序功能的统一维护,这么说来可能太含蓄,如果说到权限判断,日志记录等,可能就明白了。如果我们单纯使用OOP,那么权限判断怎么办?在每个操作前都加入权限判断?日志记录怎么办?在每个方法里的开始、结束、异常的地方手动添加日志?所有,如果使用AOP就可以借助代理完成这些重复的操作,就能够在逻辑过程中,降低各部分之间的耦合了。二者扬长补短,互相结合最好。

代码书写 最简单一号即会有问题

Aop类 [切面:由多个关注点组成]

import org.springframework.stereotype.Component;

//切面
@Component
public class Aop {
    
	 //关注点的集合
	
	public void begin(){
    
		System.out.println("事务启动");
	}
	
	public void commit(){
    
		System.out.println("事务提交");
	}
}

修改1–这样子创建对象并实现 aop

动态代理来达到解耦

新增类ProxyFactory 代理工厂

public class ProxyFactory (){
    
     private static Object target;//目标对象
     private static Aop aop;//null
//生成代理对象的方法
public static Object getProxyInstance(Object target_,Aop aop_){
    
     target=target_;
     aop=aop_;
      return  Proxy.newProxyInstance(
                              target.getClass().getClassLoader(),
                              target.getClass.getInterfaces(),
                              new InvocationHandler(){
    
					@Override
					public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
						aop.begin(); //关注点
						//userDao.save()
						Object returnValue=method.invoke(target, args); //业务
						aop.commite();
						return returnValue;
					
                        }
                   });
}

bean.xml

<!--新增  调用工厂类中的静态方法,返回代理对象 -->
<bean id="userDao_proxy" class="cn.atcast.d_myaop1.ProxyFactory" factory-method="getProxyInstance">
		<constructor-arg index="0" ref="userDao"> </constructor-arg>
		<constructor-arg index="1" ref="aop"> </constructor-arg>
	</bean>

第二种修改 通过注解 实现 AOP增强

通过注解的方法实现上面的功能
bean.xml

   <!-- 开启注解扫描 -->
  	<context:component-scan base-package="cn.atcast.e_aop_anno"></context:component-scan>
	
	<!-- 开启aop注解方式 aspectj自动代理-->
	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

Aop类

@Component
@Aspect//切面类
public class Aop {
    
	
	@Pointcut("execution(* cn.atcast.e_aop_anno.UserDao..*(..))")
	public void pointCut(){
    
		
	}
	动态织入 注解写法优化了一下
	 @Before("pointCut()")
	 public void begin(){
    
		 System.out.println("开始事务");
	 }
	 @After("pointCut()")
	 public void after(){
    
		 System.out.println("提交事务");
	 }
 

     //最后执行
	 @AfterReturning("pointCut()")
	 public void afterReturnning(){
    
		 System.out.println("afterreturning");
	 }
	 
	 //异常通知
	 @AfterThrowing("pointCut()")
	 public void afterThrowing(){
    
		 System.out.println("afterthrowing");
	 }
	 
	 //综合的
	 @Around("pointCut()")
	 public void aroudn(ProceedingJoinPoint pjp) throws Throwable{
    
		 System.out.println("开始事务");
		 pjp.proceed();//执行目标方法
		 System.out.println("提交事务");
	 }
	 
}

测试

public class App {
    
	
	ApplicationContext ac = 
		new ClassPathXmlApplicationContext("cn/atcast/e_aop_anno/bean.xml");

	// 目标对象有实现接口,spring会自动选择"JDK代理"
	@Test
	public void testApp() {
    
		IUserDao userDao = (IUserDao) ac.getBean("userDao");
		System.out.println(userDao.getClass());//$Proxy001  
		userDao.save();
	}
	
	// 目标对象没有实现接口, spring会用"cglib代理"
	@Test
	public void testCglib() {
    
		OrderDao orderDao = (OrderDao) ac.getBean("orderDao");
		System.out.println(orderDao.getClass());
		orderDao.save();
	}
}

OrderDao类

/**
 * 目标对象
 *
 */
@Component   // 加入容器
@Scope("prototype")
public class OrderDao{
    

	public void save() {
    
		System.out.println("-----核心业务:订单保存!!!------");
	}
}

XML方式来做 aop 没有注解

1) 引入jar文件 【aop 相关jar, 4个】
2) 引入aop名称空间
3) aop 配置
* 配置切面类 (重复执行代码形成的类)
* aop配置 拦截哪些方法 / 拦截到方法后应用通知代码

// 切面类
public class Aop {
    
	
	public void begin(){
    
		System.out.println("开始事务/异常");
	}
	
	public void after(){
    
		System.out.println("提交事务/关闭");
	}
	
	public void afterReturning() {
    
		System.out.println("afterReturning()");
	}
	
	public void afterThrowing(){
    
		System.out.println("afterThrowing()");
	}
	//前置增强 ,后置增强,异常增强,最终增强
	
	//环绕增强
	public void around(ProceedingJoinPoint pjp) throws Throwable{
    
		System.out.println("环绕前....");
		pjp.proceed();  // 执行目标方法
		System.out.println("环绕后....");
	}
	
}

------------



  • bean.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:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    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">
	
	<!-- dao 实例 -->
	<bean id="userDao" class="cn.atcast.f_aop_xml.UserDao"></bean>
	<bean id="orderDao" class="cn.atcast.f_aop_xml.OrderDao"></bean>
	
	<!-- 切面类 -->
	<bean id="aop" class="cn.atcast.f_aop_xml.Aop"></bean>
	
	<!-- Aop配置 -->
	<aop:config>
		<!-- 定义一个切入点表达式: 拦截哪些方法 -->
		<aop:pointcut expression="execution(* cn.atcast.f_aop_xml.*.*(..))" id="pt"/>
		<!-- 切面 -->
		<aop:aspect ref="aop">
			<!-- 环绕通知 -->
			<aop:around method="around" pointcut-ref="pt"/>
			<!-- 前置通知: 在目标方法调用前执行 -->
			<aop:before method="begin" pointcut-ref="pt"/>
			<!-- 后置通知: -->
			<aop:after method="after" pointcut-ref="pt"/>
			<!-- 返回后通知 -->
			<aop:after-returning method="afterReturning" pointcut-ref="pt"/>
			<!-- 异常通知 -->
			<aop:after-throwing method="afterThrowing" pointcut-ref="pt"/>
			
		</aop:aspect>
	</aop:config>
</beans>      
  • 接口
public interface IUserDao {
    
	void save();
}
  • 目标对象
public class OrderDao{
    

	public void save() {
    
		System.out.println("-----核心业务:保存订单!!!------");
	}

}

-* 目标对象

public class UserDao implements IUserDao{
    

	@Override
	public void save() {
    
		System.out.println("-----核心业务:保存!!!------"); 
	}
}
  • 测试类
public class App {
    
	
	ApplicationContext ac = 
		new ClassPathXmlApplicationContext("cn/atcast/f_aop_xml/bean.xml");

	// 目标对象有实现接口,spring会自动选择“JDK代理”
	@Test
	public void testApp() {
    
		IUserDao userDao = (IUserDao) ac.getBean("userDao");
		System.out.println(userDao.getClass());//$Proxy001  
		userDao.save();
	}
	
	// 目标对象没有实现接口, spring会用“cglib代理”
	@Test
	public void testCglib() {
    
		OrderDao orderDao = (OrderDao) ac.getBean("orderDao");
		System.out.println(orderDao.getClass());
		orderDao.save();
	}
}

补充

pointcut:配置切入点表达式
pointcut-ref:配置切入点引用对象
method:配置切入点执行的通知方法

表达式匹配规则举例:
public * save(entity.User):“*”表示匹配所有类型的返回值。
public void (entity.User):“”表示匹配所有方法名。
public void save (…):“…”表示匹配所有参数个数和类型。

  • service..(…):匹配service 包下所有类的所有方法。
  • service…*( …):匹配service 包及子包下所有类的所有方法。

<!-- Aop相关配置 -->
	<aop:config>
		<!-- 切入点表达式定义 -->
		<aop:pointcut expression="execution(* test.spring_aop_anno.*Dao.*(..))" id="transactionPointcut"/>
		<!-- 切面配置 -->
		<aop:aspect ref="transactionAop">
			<!-- 【环绕通知】 -->
			<aop:around method="arroud" pointcut-ref="transactionPointcut"/>
			<!-- 【前置通知】 在目标方法之前执行 -->
			<aop:before method="beginTransaction" pointcut-ref="transactionPointcut" />
			<!-- 【后置通知】 -->
			<aop:after method="commit" pointcut-ref="transactionPointcut"/>
			<!-- 【返回后通知】 -->
			<aop:after-returning method="afterReturing" pointcut-ref="transactionPointcut"/>
			<!-- 异常通知 -->
			<aop:after-throwing method="afterThrowing" pointcut-ref="transactionPointcut"/>
		</aop:aspect>
	</aop:config>

##  切入点表达式
切入点表达式,可以对指定的“方法”进行拦截;从而给指定的方法所在的类生成代	理对象。
	execution(* cn.com.dao.impl..*.*(..)) 
	第一个*代表任何返回值 
	cn.com.dao.impl..*:代表要拦截cn.com.dao.impl包下的以及子包下的所有类 
	.*(..):这个代表任意方法,就是说上面那些类的任意方法,()里面的点,
	代表任意参数 
	比如要拦截add开头的和delete开头的方法?
	execution(* add*(..))&& execution(* delete*(..))


```csharp
IUserDao.java

package cn.atcast.g_pointcut;
// 接口
public interface IUserDao {
	void save();
}
	UserDao.java
package cn.atcast.g_pointcut;
/**
 * 目标对象
 *
 */
public class UserDao implements IUserDao{
	@Override
	public void save() {
System.out.println("--核心业务:保存!!!userdao---"); 
	}
}
	OrderDao.java
package cn.atcast.g_pointcut;
import org.springframework.stereotype.Component;
/**
 * 目标对象
 */
public class OrderDao{
	public void save() {
		System.out.println("---核心业务:保存orerdao");
	}
}

Aop.java
package cn.atcast.g_pointcut;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

// 切面类
public class Aop {
    
	public void begin(){
    
		System.out.println("开始事务/异常");
	}
	
	public void after(){
    
		System.out.println("提交事务/关闭");
	}
	
	public void afterReturning() {
    
		System.out.println("afterReturning()");
	}
	
	public void afterThrowing(){
    
		System.out.println("afterThrowing()");
	}
	
	public void around(ProceedingJoinPoint pjp) throws Throwable{
    
		System.out.println("环绕前....");
		pjp.proceed();  // 执行目标方法
		System.out.println("环绕后....");
	}
}

bean.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:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    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">
	
	<!-- dao 实例 -->
	<bean id="userDao" class="cn.atcast.g_pointcut.UserDao"></bean>
	<bean id="orderDao" class="cn.atcast.g_pointcut.OrderDao"></bean>
	
	<!-- 切面类 -->
	<bean id="aop" class="cn.atcast.g_pointcut.Aop"></bean>
	
	<!-- Aop配置 -->
	<aop:config>
		
		<!-- 定义一个切入点表达式: 拦截哪些方法 -->
		<!--<aop:pointcut expression="execution(* cn.atcast.g_pointcut.*.*(..))" id="pt"/>-->
		
		<!-- 【拦截所有public方法】 -->
		<!--<aop:pointcut expression="execution(public * *(..))" id="pt"/>-->
		
		<!-- 【拦截所有save开头的方法 】 -->
		<!--<aop:pointcut expression="execution(* save*(..))" id="pt"/>-->
		
		<!-- 【拦截指定类的指定方法, 拦截时候一定要定位到方法】 -->
		<!--<aop:pointcut expression="execution(public * cn.atcast.g_pointcut.OrderDao.save(..))" id="pt"/>-->
		
		<!-- 【拦截指定类的所有方法】 -->
		<!--<aop:pointcut expression="execution(* cn.atcast.g_pointcut.UserDao.*(..))" id="pt"/>-->
		
		<!-- 【拦截指定包,以及其子包下所有类的所有方法】 -->
		<!--<aop:pointcut expression="execution(* cn..*.*(..))" id="pt"/>-->
		
		<!-- 【多个表达式】 -->
		<!--<aop:pointcut expression="execution(* cn.atcast.g_pointcut.UserDao.save()) || execution(* cn.atcast.g_pointcut.OrderDao.save())" id="pt"/>-->
		<!--<aop:pointcut expression="execution(* cn.atcast.g_pointcut.UserDao.save()) or execution(* cn.atcast.g_pointcut.OrderDao.save())" id="pt"/>-->
		
		
		<!-- 【取非值】 -->
		<!--<aop:pointcut expression="!execution(* cn.atcast.g_pointcut.OrderDao.save())" id="pt"/>-->
		<!-- 用not前要一个空格 -->
		<aop:pointcut expression=" not execution(* cn.atcast.g_pointcut.OrderDao.save())" id="pt"/>
		
		<!-- 切面 -->
		<aop:aspect ref="aop">
			<!-- 环绕通知 -->
			<aop:around method="around" pointcut-ref="pt"/>
		</aop:aspect>
	</aop:config>
</beans>        

App.java
package cn.atcast.g_pointcut;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class App {
    
	ApplicationContext ac = 
		new ClassPathXmlApplicationContext("cn/atcast/g_pointcut/bean.xml");
	// 目标对象有实现接口,spring会自动选择“JDK代理”
	@Test
	public void testApp() {
    
		IUserDao userDao = (IUserDao) ac.getBean("userDao");
		System.out.println(userDao.getClass());//$Proxy001  
		userDao.save();
	}
	
	// 目标对象没有实现接口, spring会用“cglib代理”
	@Test
	public void testCglib() {
    
		OrderDao orderDao = (OrderDao) ac.getBean("orderDao");
		System.out.println(orderDao.getClass());
		orderDao.save();
	}
} 
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_39088066/article/details/102566326

智能推荐

分布式光纤传感器的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告_预计2026年中国分布式传感器市场规模有多大-程序员宅基地

文章浏览阅读3.2k次。本文研究全球与中国市场分布式光纤传感器的发展现状及未来发展趋势,分别从生产和消费的角度分析分布式光纤传感器的主要生产地区、主要消费地区以及主要的生产商。重点分析全球与中国市场的主要厂商产品特点、产品规格、不同规格产品的价格、产量、产值及全球和中国市场主要生产商的市场份额。主要生产商包括:FISO TechnologiesBrugg KabelSensor HighwayOmnisensAFL GlobalQinetiQ GroupLockheed MartinOSENSA Innovati_预计2026年中国分布式传感器市场规模有多大

07_08 常用组合逻辑电路结构——为IC设计的延时估计铺垫_基4布斯算法代码-程序员宅基地

文章浏览阅读1.1k次,点赞2次,收藏12次。常用组合逻辑电路结构——为IC设计的延时估计铺垫学习目的:估计模块间的delay,确保写的代码的timing 综合能给到多少HZ,以满足需求!_基4布斯算法代码

OpenAI Manager助手(基于SpringBoot和Vue)_chatgpt网页版-程序员宅基地

文章浏览阅读3.3k次,点赞3次,收藏5次。OpenAI Manager助手(基于SpringBoot和Vue)_chatgpt网页版

关于美国计算机奥赛USACO,你想知道的都在这_usaco可以多次提交吗-程序员宅基地

文章浏览阅读2.2k次。USACO自1992年举办,到目前为止已经举办了27届,目的是为了帮助美国信息学国家队选拔IOI的队员,目前逐渐发展为全球热门的线上赛事,成为美国大学申请条件下,含金量相当高的官方竞赛。USACO的比赛成绩可以助力计算机专业留学,越来越多的学生进入了康奈尔,麻省理工,普林斯顿,哈佛和耶鲁等大学,这些同学的共同点是他们都参加了美国计算机科学竞赛(USACO),并且取得过非常好的成绩。适合参赛人群USACO适合国内在读学生有意向申请美国大学的或者想锻炼自己编程能力的同学,高三学生也可以参加12月的第_usaco可以多次提交吗

MySQL存储过程和自定义函数_mysql自定义函数和存储过程-程序员宅基地

文章浏览阅读394次。1.1 存储程序1.2 创建存储过程1.3 创建自定义函数1.3.1 示例1.4 自定义函数和存储过程的区别1.5 变量的使用1.6 定义条件和处理程序1.6.1 定义条件1.6.1.1 示例1.6.2 定义处理程序1.6.2.1 示例1.7 光标的使用1.7.1 声明光标1.7.2 打开光标1.7.3 使用光标1.7.4 关闭光标1.8 流程控制的使用1.8.1 IF语句1.8.2 CASE语句1.8.3 LOOP语句1.8.4 LEAVE语句1.8.5 ITERATE语句1.8.6 REPEAT语句。_mysql自定义函数和存储过程

半导体基础知识与PN结_本征半导体电流为0-程序员宅基地

文章浏览阅读188次。半导体二极管——集成电路最小组成单元。_本征半导体电流为0

随便推点

【Unity3d Shader】水面和岩浆效果_unity 岩浆shader-程序员宅基地

文章浏览阅读2.8k次,点赞3次,收藏18次。游戏水面特效实现方式太多。咱们这边介绍的是一最简单的UV动画(无顶点位移),整个mesh由4个顶点构成。实现了水面效果(左图),不动代码稍微修改下参数和贴图可以实现岩浆效果(右图)。有要思路是1,uv按时间去做正弦波移动2,在1的基础上加个凹凸图混合uv3,在1、2的基础上加个水流方向4,加上对雾效的支持,如没必要请自行删除雾效代码(把包含fog的几行代码删除)S..._unity 岩浆shader

广义线性模型——Logistic回归模型(1)_广义线性回归模型-程序员宅基地

文章浏览阅读5k次。广义线性模型是线性模型的扩展,它通过连接函数建立响应变量的数学期望值与线性组合的预测变量之间的关系。广义线性模型拟合的形式为:其中g(μY)是条件均值的函数(称为连接函数)。另外,你可放松Y为正态分布的假设,改为Y 服从指数分布族中的一种分布即可。设定好连接函数和概率分布后,便可以通过最大似然估计的多次迭代推导出各参数值。在大部分情况下,线性模型就可以通过一系列连续型或类别型预测变量来预测正态分布的响应变量的工作。但是,有时候我们要进行非正态因变量的分析,例如:(1)类别型.._广义线性回归模型

HTML+CSS大作业 环境网页设计与实现(垃圾分类) web前端开发技术 web课程设计 网页规划与设计_垃圾分类网页设计目标怎么写-程序员宅基地

文章浏览阅读69次。环境保护、 保护地球、 校园环保、垃圾分类、绿色家园、等网站的设计与制作。 总结了一些学生网页制作的经验:一般的网页需要融入以下知识点:div+css布局、浮动、定位、高级css、表格、表单及验证、js轮播图、音频 视频 Flash的应用、ul li、下拉导航栏、鼠标划过效果等知识点,网页的风格主题也很全面:如爱好、风景、校园、美食、动漫、游戏、咖啡、音乐、家乡、电影、名人、商城以及个人主页等主题,学生、新手可参考下方页面的布局和设计和HTML源码(有用点赞△) 一套A+的网_垃圾分类网页设计目标怎么写

C# .Net 发布后,把dll全部放在一个文件夹中,让软件目录更整洁_.net dll 全局目录-程序员宅基地

文章浏览阅读614次,点赞7次,收藏11次。之前找到一个修改 exe 中 DLL地址 的方法, 不太好使,虽然能正确启动, 但无法改变 exe 的工作目录,这就影响了.Net 中很多获取 exe 执行目录来拼接的地址 ( 相对路径 ),比如 wwwroot 和 代码中相对目录还有一些复制到目录的普通文件 等等,它们的地址都会指向原来 exe 的目录, 而不是自定义的 “lib” 目录,根本原因就是没有修改 exe 的工作目录这次来搞一个启动程序,把 .net 的所有东西都放在一个文件夹,在文件夹同级的目录制作一个 exe._.net dll 全局目录

BRIEF特征点描述算法_breif description calculation 特征点-程序员宅基地

文章浏览阅读1.5k次。本文为转载,原博客地址:http://blog.csdn.net/hujingshuang/article/details/46910259简介 BRIEF是2010年的一篇名为《BRIEF:Binary Robust Independent Elementary Features》的文章中提出,BRIEF是对已检测到的特征点进行描述,它是一种二进制编码的描述子,摈弃了利用区域灰度..._breif description calculation 特征点

房屋租赁管理系统的设计和实现,SpringBoot计算机毕业设计论文_基于spring boot的房屋租赁系统论文-程序员宅基地

文章浏览阅读4.1k次,点赞21次,收藏79次。本文是《基于SpringBoot的房屋租赁管理系统》的配套原创说明文档,可以给应届毕业生提供格式撰写参考,也可以给开发类似系统的朋友们提供功能业务设计思路。_基于spring boot的房屋租赁系统论文