三, 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

智能推荐

从零开始搭建Hadoop_创建一个hadoop项目-程序员宅基地

文章浏览阅读331次。第一部分:准备工作1 安装虚拟机2 安装centos73 安装JDK以上三步是准备工作,至此已经完成一台已安装JDK的主机第二部分:准备3台虚拟机以下所有工作最好都在root权限下操作1 克隆上面已经有一台虚拟机了,现在对master进行克隆,克隆出另外2台子机;1.1 进行克隆21.2 下一步1.3 下一步1.4 下一步1.5 根据子机需要,命名和安装路径1.6 ..._创建一个hadoop项目

心脏滴血漏洞HeartBleed CVE-2014-0160深入代码层面的分析_heartbleed代码分析-程序员宅基地

文章浏览阅读1.7k次。心脏滴血漏洞HeartBleed CVE-2014-0160 是由heartbeat功能引入的,本文从深入码层面的分析该漏洞产生的原因_heartbleed代码分析

java读取ofd文档内容_ofd电子文档内容分析工具(分析文档、签章和证书)-程序员宅基地

文章浏览阅读1.4k次。前言ofd是国家文档标准,其对标的文档格式是pdf。ofd文档是容器格式文件,ofd其实就是压缩包。将ofd文件后缀改为.zip,解压后可看到文件包含的内容。ofd文件分析工具下载:点我下载。ofd文件解压后,可以看到如下内容: 对于xml文件,可以用文本工具查看。但是对于印章文件(Seal.esl)、签名文件(SignedValue.dat)就无法查看其内容了。本人开发一款ofd内容查看器,..._signedvalue.dat

基于FPGA的数据采集系统(一)_基于fpga的信息采集-程序员宅基地

文章浏览阅读1.8w次,点赞29次,收藏313次。整体系统设计本设计主要是对ADC和DAC的使用,主要实现功能流程为:首先通过串口向FPGA发送控制信号,控制DAC芯片tlv5618进行DA装换,转换的数据存在ROM中,转换开始时读取ROM中数据进行读取转换。其次用按键控制adc128s052进行模数转换100次,模数转换数据存储到FIFO中,再从FIFO中读取数据通过串口输出显示在pc上。其整体系统框图如下:图1:FPGA数据采集系统框图从图中可以看出,该系统主要包括9个模块:串口接收模块、按键消抖模块、按键控制模块、ROM模块、D.._基于fpga的信息采集

微服务 spring cloud zuul com.netflix.zuul.exception.ZuulException GENERAL-程序员宅基地

文章浏览阅读2.5w次。1.背景错误信息:-- [http-nio-9904-exec-5] o.s.c.n.z.filters.post.SendErrorFilter : Error during filteringcom.netflix.zuul.exception.ZuulException: Forwarding error at org.springframework.cloud..._com.netflix.zuul.exception.zuulexception

邻接矩阵-建立图-程序员宅基地

文章浏览阅读358次。1.介绍图的相关概念  图是由顶点的有穷非空集和一个描述顶点之间关系-边(或者弧)的集合组成。通常,图中的数据元素被称为顶点,顶点间的关系用边表示,图通常用字母G表示,图的顶点通常用字母V表示,所以图可以定义为:  G=(V,E)其中,V(G)是图中顶点的有穷非空集合,E(G)是V(G)中顶点的边的有穷集合1.1 无向图:图中任意两个顶点构成的边是没有方向的1.2 有向图:图中..._给定一个邻接矩阵未必能够造出一个图

随便推点

MDT2012部署系列之11 WDS安装与配置-程序员宅基地

文章浏览阅读321次。(十二)、WDS服务器安装通过前面的测试我们会发现,每次安装的时候需要加域光盘映像,这是一个比较麻烦的事情,试想一个上万个的公司,你天天带着一个光盘与光驱去给别人装系统,这将是一个多么痛苦的事情啊,有什么方法可以解决这个问题了?答案是肯定的,下面我们就来简单说一下。WDS服务器,它是Windows自带的一个免费的基于系统本身角色的一个功能,它主要提供一种简单、安全的通过网络快速、远程将Window..._doc server2012上通过wds+mdt无人值守部署win11系统.doc

python--xlrd/xlwt/xlutils_xlutils模块可以读xlsx吗-程序员宅基地

文章浏览阅读219次。python–xlrd/xlwt/xlutilsxlrd只能读取,不能改,支持 xlsx和xls 格式xlwt只能改,不能读xlwt只能保存为.xls格式xlutils能将xlrd.Book转为xlwt.Workbook,从而得以在现有xls的基础上修改数据,并创建一个新的xls,实现修改xlrd打开文件import xlrdexcel=xlrd.open_workbook('E:/test.xlsx') 返回值为xlrd.book.Book对象,不能修改获取sheett_xlutils模块可以读xlsx吗

关于新版本selenium定位元素报错:‘WebDriver‘ object has no attribute ‘find_element_by_id‘等问题_unresolved attribute reference 'find_element_by_id-程序员宅基地

文章浏览阅读8.2w次,点赞267次,收藏656次。运行Selenium出现'WebDriver' object has no attribute 'find_element_by_id'或AttributeError: 'WebDriver' object has no attribute 'find_element_by_xpath'等定位元素代码错误,是因为selenium更新到了新的版本,以前的一些语法经过改动。..............._unresolved attribute reference 'find_element_by_id' for class 'webdriver

DOM对象转换成jQuery对象转换与子页面获取父页面DOM对象-程序员宅基地

文章浏览阅读198次。一:模态窗口//父页面JSwindow.showModalDialog(ifrmehref, window, 'dialogWidth:550px;dialogHeight:150px;help:no;resizable:no;status:no');//子页面获取父页面DOM对象//window.showModalDialog的DOM对象var v=parentWin..._jquery获取父window下的dom对象

什么是算法?-程序员宅基地

文章浏览阅读1.7w次,点赞15次,收藏129次。算法(algorithm)是解决一系列问题的清晰指令,也就是,能对一定规范的输入,在有限的时间内获得所要求的输出。 简单来说,算法就是解决一个问题的具体方法和步骤。算法是程序的灵 魂。二、算法的特征1.可行性 算法中执行的任何计算步骤都可以分解为基本可执行的操作步,即每个计算步都可以在有限时间里完成(也称之为有效性) 算法的每一步都要有确切的意义,不能有二义性。例如“增加x的值”,并没有说增加多少,计算机就无法执行明确的运算。 _算法

【网络安全】网络安全的标准和规范_网络安全标准规范-程序员宅基地

文章浏览阅读1.5k次,点赞18次,收藏26次。网络安全的标准和规范是网络安全领域的重要组成部分。它们为网络安全提供了技术依据,规定了网络安全的技术要求和操作方式,帮助我们构建安全的网络环境。下面,我们将详细介绍一些主要的网络安全标准和规范,以及它们在实际操作中的应用。_网络安全标准规范