海创软件组-20200418-AOP

AOP

AOP的常用术语

1)切面
切面( Aspect )是指封装横切到系统功能(例如事务处理)的类。
2)连接点
连接点 (Joinpoint )是指程序运行中的 些时间点,例如方法的调用或异常的抛出。
3)切人点
切入点 (Pointcut )是指需要处理的连接点。在 Spring AOP 中,所有的方法执行都是连接点,而切入点是一个描述信息,它修饰的是连接点,通过切入点确定哪些连接点需要被处理。
4)通知
通知( Advice )是由切面添加到特定的连接点(满足切入点规则)的一段代码,即在定义好的切入点处所要执行的程序代码,可以将其理解为切面开启后切面的方法,因此通知是切面的具体实现。
5)引人
引入( Introduction )允许在现有的实现类中添加自定义的方法和属性。
6)目标对象
目标对象( Target Object )是指所有被通知的对象。如果 AOP 框架使用运行时代理的方式(动态的 AOP )来实现切面,那么通知对象总是 个代理对象。
7)代理
代理( Proxy )是通知应用到目标对象之后被动态创建的对象。
8)织人
织入( Weaving )是将切面代码插入到目标对象上,从而生成代理对象的过程。根据不同的实现技术, AOP 织入有三种方式:编译期织入, 需要有特殊 Java 编译器;类装载期织入,需要有特殊的类装载器;动态代理织入,在运行期为目标类添加通知生成子类的方式。 SpringAOP 框架默认采用动态代理织入,而 AspectJ采用编译期织入和类装载期织入。

通知类型

根据 Spring 通知在目标类方法中的连接点位置,通知可以分为6类型
1)环绕通知
环绕通知( org .aopalliance.intercept.Methodlnterceptor )是在目标方法执行前和执行后实施增强,可应用于日志记录、 事务处理等功能.

public interface ISomeService {
	 void doFirst();
	 String doSecond();
}



public class SomeServiceImpl implements ISomeService{

	public void doFirst(){
		System.out.println("执行dofirst");
}
	public String doSecond(){
		System.out.println("执行doSecond");

		return "abcde";
		
		
	}
}
public class methodInterceptor implements MethodInterceptor {

	@Override
	public Object invoke(MethodInvocation invocation) throws Throwable {
		System.out.println("目标方法执行之前:");
		//调用了目标方法
		Object result=invocation.proceed();
		System.out.println("目标方法执行之后:");
		//返回执行方法的结果
		if(result!=null) {
			result =((String)result).toUpperCase();
			
		}
		return result;
	}

}
<?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:tx="http://www.springframework.org/schema/tx"
    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
        ">
    <!-- 注册目标对象 -->    
    <bean id="someService" class="com.spring.aop04.SomeServiceImpl"/>
        <!-- 注册切面:通知 -->    
    <bean id="myAdice" class="com.spring.aop04.methodInterceptor"/>
        <!-- 生成代理对象 -->    
    <bean id="ServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
    	<property name="target" ref="someService"/>
    	<property name="interceptorNames" value="myAdice"/>
    
    	
    </bean>

        
</beans>
@Test
	public void test01() {
	ApplicationContext ctx=new ClassPathXmlApplicationContext("com/spring/aop04/applicationContext.xml");
		      ISomeService hello=(ISomeService) ctx.getBean("ServiceProxy");
		      hello.doFirst();
				System.out.println("=============");
		     String result= hello.doSecond();
				System.out.println(result);
					}}

海创软件组-20200418-AOP_第1张图片
2)前置通知
前置通知( org springframework.aop.Met odBeforeAdvice 是在 目标方法执行前实施增
强,可应用于权限管理等功能。

public interface ISomeService {
	 void doFirst();
	 void doSecond();
}
public class SomeServiceImpl implements ISomeService{

	public void doFirst(){
		System.out.println("执行dofirst");
}
	public void doSecond(){
		System.out.println("执行doSecond");

	}
}
public class MyMethodBeforeAdvice implements MethodBeforeAdvice {
    //当前方法在目标方法执行之前执行
	//method:目标方法
	//args:目标方法的参数列表
	//target:目标对象
	@Override
	public void before(Method arg0, Object[] arg1, Object arg2) 
			throws Throwable {
		//对于目标方法的增强就应该写在这里
	System.out.println("执行前置通知方法")
}

}
<?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:tx="http://www.springframework.org/schema/tx"
    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">
        
      <!-- 注册目标对象 -->    
    <bean id="someService" class="com.spring.aop01.SomeServiceImpl"/>
        <!-- 注册切面:通知 -->    
    <bean id="myAdice" class="com.spring.aop01.MyMethodBeforeAdvice"/>
        <!-- 生成代理对象 ,代理工程bean-->    
    <bean id="ServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
    	<!-- <property name="targetName" value="someService"/> 
    	通过target就可以获取到全部的接口
    	-->
    	<property name="target" ref="someService"/>
    	<!-- 拦截器,底层的织入用拦截器的,指定切面 -->
    	<property name="interceptorNames" value="myAdice"/>
    	<!-- 目标类实现的接口    	<property name="interfaces" value="myAdice"/> -->
    	
            	
    </bean>
</beans>
 @Test
 public void test01() {
 ApplicationContext ctx=new ClassPathXmlApplicationContext("com/spring/aop01/applicationContext.xml");
        ISomeService hello=(ISomeService) ctx.getBean("ServiceProxy");
        hello.doFirst();
    System.out.println("=============");
       hello.doSecond()}}

结果:
海创软件组-20200418-AOP_第2张图片
3)后置返回通知
后置返回通知( org spring:framework.aop.AfterReturningAdvice )是在目标方法成功执行
后实施增强,可应用于关闭流、删除临时文件等功能。(在动态代理反射原有对象方法或执行环绕通知后正常返回(无异常)执行的通知方法)

//后置通知可以获取目标方法的返回结果,但无法改变目标方法的结果
public class MyAfterReturningAdvice implements AfterReturningAdvice {

	//在目标方法执行之后
	//returnValue:目标方法的返回值
	@Override
	public void afterReturning(Object returnValue,
			Method method, Object[] args, Object target) throws Throwable {
       //但这里改变不了目标方法的返回值,因为这个方法没有返回值
		System.out.println("执行后置方法 returnValue=" + returnValue);
	}
}
//xml文件
    <bean id="someService" class="com.spring.aop02.SomeServiceImpl"/>
    <bean id="myAdice" class="com.spring.aop02.MyAfterReturningAdvice"/>
    
    <bean id="ServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
    	<property name="target" ref="someService"/>
    	<property name="interceptorNames" value="myAdice"/>
    	 	
    </bean>

海创软件组-20200418-AOP_第3张图片
若想要只是增强某一个方法

<bean id="someService" class="com.spring.aop08.SomeServiceImpl"/>
         <!-- 注册切面:通知 -->
    <bean id="myAdice" class="com.spring.aop08.MyAfterReturningAdvice"/>
       <!-- 注册切面:顾问 -->
    <bean id="myAdisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
           <property name="advice" ref="myAdice"/>
                  <!-- 写指定切入点,这里匹配的对象时简单方法名-->
           <property name="mappedName" value="doFirst"></property>
     </bean>
    <bean id="ServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
    	<property name="target" ref="someService"/>
    	<property name="interceptorNames" value="myAdisor"/>
    	 	
    </bean>

海创软件组-20200418-AOP_第4张图片
4)后置(最终)通知
后置通知( org.springframework.aop AfterAdvice )是在目标方法执行后实施增强,与后置返回通知不同的是 不管是否发生异常都要执行该类通知,该类通知应用于释放资源。
5)异常通知
常通知 org.springframework.aop. ThrowsAdvice 是在方法抛出异常后实施增强,
应用于处理异常、记录日志等功能。

public class throwAdvice implements ThrowsAdvice {

	public void afterThrowing(Exception ex) {		
	​System.out.println("执行异常通知=" + ex.getMessage());}
}
public class SomeServiceImpl implements ISomeService{

	public void doFirst(){
		
		System.out.println("执行dofirst"+ 3/0);

		System.out.println("执行dofirst");
}
	public String doSecond(){
		System.out.println("执行doSecond");

		return "abcde"}
}

在这里插入图片描述
异常通知的补充:
1)运行时异常,不进行处理也可以通过编译,
若一个类继承自RunTimeException,则该异常是运行时异常
2)编译时异常,受查异常,不进行处理,将无法通过编译

public class UserException extends Exception {

	public UserException() {
		super();
	}

	public UserException(String message) {
		super(message);
	}

	
}
public class passwordExecption extends UserException {

	public passwordExecption() {
		super();
		// TODO Auto-generated constructor stub
	}
	public passwordExecption(String message) {
		super(message);
		// TODO Auto-generated constructor stub
	}	
}

//异常通知public class throwAdvice implements ThrowsAdvice {


	public void afterThrowing(UsernameException ex) {
		
	         System.out.println("发生用户名异常="+ ex.getMessage());
	}
	public void afterThrowing(passwordExecption ex) {
			
		System.out.println("发生密码异常="+ ex.getMessage());
		}
}
public class SomeServiceImpl implements ISomeService{

	@Override
	public boolean login(String username, String password) throws UserException {

		if(!"jiayou".equals(username)) {
			throw new UsernameException("用户名输入错了");
		}
	
		if(!"111".equals(password)) {
			throw new passwordExecption("密码输入错了");
		}
		return true;
	}	
}
@Test
	public void test01()  {
	ApplicationContext ctx=new ClassPathXmlApplicationContext("com/spring/aop06/applicationContext.xml");
		      ISomeService hello=(ISomeService) ctx.getBean("ServiceProxy");
		    
		      try {
				hello.login("jiaybou", "111");
			} catch (UserException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
}}

在这里插入图片描述

@Test
	public void test01() throws UserException  {
	ApplicationContext ctx=new ClassPathXmlApplicationContext("com/spring/aop06/applicationContext.xml");
		      ISomeService hello=(ISomeService) ctx.getBean("ServiceProxy");
		    
				hello.login("jiaybou", "111");}

在这里插入图片描述对于try catch和throw的区别:
…throw 关键字通常用于方法体中,并且抛出一个异常对象。程序在执行到 throw 语句时立即终止,它后面的语句都不执行。通过 throw 抛出异常后,如果想在上一级代码中来捕获并处理异常,则需要在抛出异常的方法中使用 throws 关键字在方法的声明中指明要抛出的异常:如果要捕捉throw 抛出的异常,则必须使用try-catch语句块。
…出现异常的代码用try-catch 语句块进行处理,当try代码块中的语句发生异常时,程序就会调转到catch代码块中执行,执行完catch 代码块中的程序代码后,将继续执行 catch 代码块后的其他代码,而不会执行try代码块中发生异常语句后面的代码。由此可知,Java的异常处理是结构化的,不会因为一个异常影响整个程序的执行。
6)引入通知
引入通知( org.springframework.aop Introductionlnterceptor )是在目标类中添加一些新的
方法和属性,可应用于修改目标类(增强类)。

ProxyFactoryBean

ProxyFactoryBean( org.springframework.beans.factory FactoryBean )接口的实现类,
FactoryBean负责实例化Bean 实例, ProxyFactoryBean 负责为其他 Bean 实例创建代理
实例。

属性 描述
target 代理的目标对象
proxyInterfaces 代理需要实现的接口列 ,如果是多个接口,可以使用下侧的格式赋值:
< list > < value > …< /value> < list>
interceptorNames 需要织入目标的advice
proxyTargetClass 是否对类代理而不是接口,默认为false 使用JDK动态; true 时, 使用CGLIB动态代理
singleton 返回的代理实例是否单例,默认为true
optimize 当设为 true时强制使用CGLIB动态代理
切入点

对于切入点的指定有两种方法
第一种:名称匹配法

<bean id="someService" class="com.spring.aop08.SomeServiceImpl"/>
    <!-- 注册切面:通知 -->
    <bean id="myAdice" class="com.spring.aop08.MyAfterReturningAdvice"/>
    
    <!-- 注册切面:顾问 -->
    <bean id="myAdisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
       <property name="advice" ref="myAdice"/>
        
        <!-- 写指定切入点,这里匹配的对象时简单方法名-->
       <property name="mappedName" value="doFirst"></property>
     </bean>
    <bean id="ServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
    	<property name="target" ref="someService"/>
    	<property name="interceptorNames" value="myAdisor"/>
    	 
    </bean>

第二种正则表达式

<bean id="someService" class="com.spring.aop09.SomeServiceImpl"/>
    <!-- 注册切面:通知 -->
    <bean id="myAdice" class="com.spring.aop09.MyAfterReturningAdvice"/>
       <!-- 注册切面:顾问 -->
    <bean id="myAdisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
           <property name="advice" ref="myAdice"/>
                  <!-- 写指定切入点,正则表达式和全限方法名进行匹对-->
           <property name="pattern" value=".*doFirst"/>
     </bean>
    <bean id="ServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
    	<property name="target" ref="someService"/>
    	<property name="interceptorNames" value="myAdisor"/>
    	 	<property name="optimize" value="true"/>
    </bean>

测试类

@Test
	public void test01() {  
	String resource="com/spring/aop08/applicationContext.xml";
	ApplicationContext ctx=new ClassPathXmlApplicationContext(resource);
	ISomeService hello=(ISomeService) ctx.getBean("ServiceProxy");
               hello.doFirst();
               System.out.println("=============");
               hello.doSecond();
               System.out.println("=============");
               hello.doThird();

运行结果:
海创软件组-20200418-AOP_第5张图片
两则的差别: 正则表达式和全限方法名进行匹对、而名称匹配法匹配的对象时简单方法名。

运算符 意义
. 表示任意单个字符
+ 表示前一个字符出现一次或者多次
* 表示前一个字符出现0次或者多次

上述代码的问题
1)若存在多个目标对象,就需要使用多次ProxyFactoryBean来创建多个代理对象,这会不便于管理。
2)现在真正在调用的是代理对象,而不是目标方法

<bean id="Service" class="com.spring.aop10.SomeServiceImpl"/>
        <bean id="Service2" class="com.spring.aop10.SomeServiceImpl"/>
    
    <!-- 注册切面:通知 -->
    <bean id="myAdice" class="com.spring.aop10.MyAfterReturningAdvice"/>
       <!-- 注册切面:顾问 -->
    <bean id="myAdisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
           <property name="advice" ref="myAdice"/>
                  <!-- 写指定切入点,这里匹配的对象时简单方法名-->
           <property name="mappedName" value="doFirst"></property>
     </bean>
     <!-- 注册自动代理生成器 -->
    <bean id="ServiceProxy" class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator">
    	
    	    </bean>
@Test
	public void test01() {
		String resource="com/spring/aop10/applicationContext.xml";
	ApplicationContext ctx=new ClassPathXmlApplicationContext(resource);
	ISomeService hello=(ISomeService) ctx.getBean("Service");
     hello.doFirst();
     System.out.println("=============");
     hello.doThird();
     System.out.println("--------------------------------=====");
     ISomeService hello2=(ISomeService) ctx.getBean("Service2");
     hello2.doFirst();
     System.out.println("=============");
     hello2.doThird();

海创软件组-20200418-AOP_第6张图片
当前代码的问题
1)不能选择目标对象
2)不能选择切面类型,切面只能是advisor
3)不能选择advisor,所以advisor均将被作为切面织入到目标方法

<bean id="Service" class="com.spring.aop11.SomeServiceImpl"/>
    <bean id="Service2" class="com.spring.aop11.SomeServiceImpl"/>
    
    <!-- 注册切面:通知 -->
    <bean id="myAdice" class="com.spring.aop10.MyAfterReturningAdvice"/>
     
       <!-- 注册切面:顾问 -->
    <bean id="myAdisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
    <property name="advice" ref="myAdice"/>
<!-- 写指定切入点,这里匹配的对象时简单方法名-->
     <property name="mappedName" value="doFirst"></property>
     </bean>
     <!-- 注册自动代理生成器 -->
    <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
    	<property name="beanNames" value="Service"></property>
    	<property name="interceptorNames" value="myAdice"></property>
    	
    	    </bean>
@Test
	public void test01() {
		String resource="com/spring/aop11/applicationContext.xml";
	ApplicationContext ctx=new ClassPathXmlApplicationContext(resource);
	ISomeService hello=(ISomeService) ctx.getBean("Service");
     hello.doFirst();
     System.out.println("=============");
     
     hello.doThird();
     System.out.println("--------------------------------=====");

     ISomeService hello2=(ISomeService) ctx.getBean("Service2");
     hello2.doFirst();
     System.out.println("=============");
    
     hello2.doThird();
		 
}}

海创软件组-20200418-AOP_第7张图片

你可能感兴趣的:(海创软件组)