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

智能推荐

使用UmcFramework和unimrcpclient.xml连接多个SIP设置的配置指南及C代码示例

在多媒体通信领域,MRCP(Media Resource Control Protocol)协议被广泛用于控制语音识别和合成等媒体资源。UniMRCP是一个开源的MRCP实现,提供了客户端和服务端的库。UmcFramework是一个基于UniMRCP客户端库的示例应用程序框架,它帮助开发者快速集成和测试MRCP客户端功能。本文将详细介绍如何使用UmcFramework和unimrcpclient.xml配置文件连接到多个SIP设置,以及如何用C代码进行示例说明。

java.net.ProtocolException: Server redirected too many times (20)-程序员宅基地

文章浏览阅读3k次。报错:java.net.ProtocolException: Server redirected too many times (20)1.没有检查到cookie,一直循环重定向。解决:CookieHandler.setDefault(new CookieManager(null, CookiePolicy.ACCEPT_ALL));URL url = new URL(url); ..._java.net.protocolexception: server redirected too many times (20)

springboot启动报错 Failed to scan *****/derbyLocale_ja_JP.jar from classloader hierarchy_failed to scan from classloader hierarchy-程序员宅基地

文章浏览阅读4.1k次。问题这是部分报错信息2019-07-11 14:03:34.283 WARN [restartedMain][DirectJDKLog.java:175] - Failed to scan [file:/D:/repo/org/apache/derby/derby/10.14.2.0/derbyLocale_ja_JP.jar] from classloader hierarchyjava...._failed to scan from classloader hierarchy

MATLAB-ones函数_matlab中ones函数-程序员宅基地

文章浏览阅读2.8k次,点赞3次,收藏7次。在MATLAB中,ones函数用于创建一个指定大小的由1组成的矩阵或数组。_matlab中ones函数

解决PS等软件出现应用程序无法正常启动(0xc000007b)_photoshop应用程序无法正常启动0xc000007b。请单击“确认”关闭应用程序。-程序员宅基地

文章浏览阅读3.9w次,点赞2次,收藏9次。  在使用电脑办公过程中,安装应用程序时难免遇到无法安装或者无法正常启动的问题,这对我们使用电脑带来了诸多不便。那遇到应用程序无法正常启动的问题要如何解决呢?相信大家肯定都是十分疑问的,每次都是只能忍痛重新安装软件。今天,小编就和大家探讨下应用程序无法正常启动的解决方法,帮助大家排忧解难。0xc000007b电脑图解1  第一种方案:SFC检查系统完整性来尝试修复丢失文件  1、打开电脑搜索输入cmd.exe,选择以管理员身份运行,跳出提示框时选择继续。0xc000007b电脑图解2_photoshop应用程序无法正常启动0xc000007b。请单击“确认”关闭应用程序。

oracle介质恢复和实例恢复的异同-程序员宅基地

文章浏览阅读396次。1、概念 REDO LOG是Oracle为确保已经提交的事务不会丢失而建立的一个机制。实际上REDO LOG的存在是为两种场景准备的:实例恢复(INSTANCE RECOVERY);介质恢复(MEDIA RECOVERY)。 实例恢复的目的是在数据库发生故障时,确保BUFFER CACHE中的数据不会丢失,不会造成数据库的..._oracle 实例恢复和介质恢复

随便推点

轻松搭建CAS 5.x系列(5)-增加密码找回和密码修改功能-程序员宅基地

文章浏览阅读418次。概述说明CAS内置了密码找回和密码修改的功能; 密码找回功能是,系统会吧密码重置的连接通过邮件或短信方式发送给用户,用户点击链接后就可以重置密码,cas还支持预留密码重置的问题,只有回答对了,才可以重置密码;系统可配置密码重置后,是否自动登录; 密码修改功能是,用户登录后输入新密码即可完成密码修改。安装步骤`1. 首先,搭建好cas sso server您需要按..._修改cas默认用户密码

springcloud(七) feign + Hystrix 整合 、-程序员宅基地

文章浏览阅读141次。之前几章演示的熔断,降级 都是 RestTemplate + Ribbon 和RestTemplate + Hystrix ,但是在实际开发并不是这样,实际开发中都是 Feign 远程接口调用。Feign + Hystrix 演示:  eruka(略)order 服务工程:  pom.xml<?xml version="1.0" encoding="U..._this is order 服务工程

YOLOv7如何提高目标检测的速度和精度,基于优化算法提高目标检测速度-程序员宅基地

文章浏览阅读3.4k次,点赞35次,收藏43次。学习率是影响目标检测精度和速度的重要因素之一。合适的学习率调度策略可以加速模型的收敛和提高模型的精度。在YOLOv7算法中,可以使用基于余弦函数的学习率调度策略(Cosine Annealing Learning Rate Schedule)来调整学习率。

linux中进程退出函数:exit()和_exit()的区别_linux结束进程可以用哪些函数,它们之间有何区别?-程序员宅基地

文章浏览阅读4k次,点赞4次,收藏9次。 linux中进程退出函数:exit()和_exit()的区别(1)_exit()执行后立即返回给内核,而exit()要先执行一些清除操作,然后将控制权交给内核。(2)调用_exit函数时,其会关闭进程所有的文件描述符,清理内存以及其他一些内核清理函数,但不会刷新流(stdin, stdout, stderr ...). exit函数是在_exit..._linux结束进程可以用哪些函数,它们之间有何区别?

sqlserver55555_sqlserver把小数点后面多余的0去掉-程序员宅基地

文章浏览阅读134次。select 5000/10000.0 --想变成0.5select 5500/10000.0 --想变成0.55select 5550/10000.0 --想变成0.555select 5555/10000.0 --想变成0.5555其结果分别为:0.5000000 0.5500000 0.5550000 0.5555000一、如果想去掉数字5后面多余的0 ,需要转化一下:selec..._sql server 去小数 0

Angular6 和 RXJS6 的一些改动_angular6,requestoptions改成了什么-程序员宅基地

文章浏览阅读3.1k次。例一:import { Injectable } from '@angular/core';import { Observable } from 'rxjs';import { User } from "./model/User";import { map } from 'rxjs/operators';import { Http, Response, Headers, RequestOp..._angular6,requestoptions改成了什么

推荐文章

热门文章

相关标签