Spring-AOP

	问题:
		在学习了SpringIOC之后,我们可以非常方便的使用IOC来帮助我们
		创建和管理对象,实现责任链上的层与层之间对象的解耦,便于我们
		对对象的替换和升级。但是,很多时候,我们会有这样的需求:
			不是去替换对象,而是将对象中的方法进行功能的扩展。
		传统方式直接去修改要扩展的功能方法的源码即可。但是很多时候
		我们是没有源码文件的,或者说该方法不是我们自己编写的。
		我们希望在不修改原有方法的源码的基础上完成功能的扩展。
	解决:
		比如我们要对A类的test方法进行扩展,创建一个B类
		在B中也创建一个test方法,并在B类的test方法中调用
		A类的test方法,同时在B类的test方法中完成其他的功能代码的编写
		然后将以前调用A类的test方法的代码替换成调用B类的test方法即可
	流程:
		不需要扩展的流程:
			1.A--->配置成bean
			2.获取Spring容器中的A对象
			3.调用A对象的test方法
		不修改源码的基础上完成test方法的功能扩展
			1.创建一个实现了和A相同接口的B类
			2.在B类的test方法中调用A类的test方法,并完成其他功能代码的编写
			3.将Spring配置文件中A的bean的class修改为B的全限定路径
	问题二:
		我们真正要做的事情是要要扩展的功能代码,其他的动作只不过是为了不修改原来的
		代码的基础上把扩展代码加上。那么,能不能我只写功能扩展代码,其他的动作交由
		框架实现。
	实现:
		AOP
	概念:
		面向切面的编程:其实说白了就是不改变源码的基础进行功能扩展
		扩展:在不改变原有功能代码的基础上增加新的功能代码
		切点:要进行功能扩展的方法称为切点
		织入:前置通知和后置通知和切点形成切面的过程
		切面:前置通知和后置通知和切点形成的横向执行的面
		前置通知:在切点执行之前执行的扩展代码
		后置通知:在切点执行之后执行的扩展代码
	作用:
		1.可以让我们在不修改功能方法源码的基础上完成功能的扩展
		2.提升了开发效率
	总结:
		其实所谓AOP就是由我们自己创建代理对象并在代理对象中创建代理方法,变成了由Spring容器
		帮动态创建代理对象并动态的在代理对象中生成代理方法,然后将代理对象返回给我们使用
	使用:
		1.提供真实对象
		2.提供扩展代码所隶属的对象
		3.提供切点
		4.声明组装方式
	实现:
		基于Schema-Based方式
		基于Aspectj方式

AOP介绍

  面向切面编程,就是将交叉业务逻辑封装成切面,利用AOP的功能将切面织入到主业务逻辑中。
  所谓交叉业务逻辑是指,通用的、与主业务逻辑无关的代码,如安全检查、事务、日志等。若不使用AOP,则会出现代码纠缠,即交叉业务逻辑与主业务逻辑混合在一起。
  这样,会使主业务逻辑变的混杂不清

  AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。
  面向切面是面向对象中的一种方式而已。在代码执行过程中,动态嵌入其他代码,叫做面向切面编程。常见的使用场景:

  • 日志
  • 事务
  • 数据库操作

面向切面编程的几个核心概念

概念说明
IOC/DI本质是就是Java反射+XML解析
AOP本质上Java动态代理
切点要添加代码的地方称作切点
切面切点+通知
通知(增强)向切点插入的代码称为通知Advice
连接点切点的定义

AOP术语介绍

术语说明
切面切面泛指交叉业务逻辑。比如事务处理、日志处理就可以理解为切面。常用的切面有通知与顾问。实际就是对主业务逻辑的一种增强
织入织入是指将切面代码插入到目标对象的过程
连接点连接点指切面可以织入的位置
切入点切入点指切面具体织入的位置
通知(Advice)通知是切面的一种实现,可以完成简单织入功能(织入功能就是在这里完成的)。通知定义了增强代码切入到目标代码的时间点,是目标方法执行之前执行,还是之后执行等。通知类型不同,切入时间不同
顾问(Advisor)顾问是切面的另一种实现,能够将通知以更为复杂的方式织入到目标对象中,是将通知包装为更复杂切面的装配器。 不仅指定了切入时间点,还可以指定具体的切入点

AOP的实现方式

通知类型说明
前置通知(MethodBeforeAdvice)目标方法执行之前调用
后置通知(AfterReturningAdvice)目标方法执行完成之后调用
环绕通知(MethodInterceptor)目标方法执行前后都会调用方法,且能增强结果
异常处理通知(ThrowsAdvice)目标方法出现异常调用

基于Schema-based方式实现

需要添加的jar包
在这里插入图片描述
前置通知
1.创建目标接口和实现类

public interface UserService {
	String doSome();
	String say();
}
public class UserServiceImpl implements UserService{
	@Override
	public String doSome() {
		System.out.println("doSome...");
		return "hello";
	}

	@Override
	public String say() {
		System.out.println("say...");	
		return null;
	}
}

2.创建切面类

//前置通知
public class MyBeforeAdvice implements MethodBeforeAdvice{
	/**
	 * 	method 目标方法
	 * 	args   目标方法参数列表
	 * 	target 目标对象
	 */
	@Override
	public void before(Method method, Object[] args, Object target) throws Throwable {
		System.out.println("前置通知执行啦");
	}
}

3.配置文件中配置

<?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:c="http://www.springframework.org/schema/c"
	xmlns:p="http://www.springframework.org/schema/p"
	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/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
		
		<!-- 注册目标对象 -->
		<bean id="UserServiceImpl" class="com.i.service.impl.UserServiceImpl"/>
		<!-- 注册前置通知 -->
		<bean id="myBeforeAdvice" class="com.i.advice.MyBeforeAdvice"/>
		
		<!-- 注册代理对象 -->				   
		<bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
			<!-- 指定目标对象 -->
			<property name="target" ref="UserServiceImpl"/>
			<!-- 指定目标类实现的所有接口 -->
			<property name="interfaces" value="com.i.service.UserService"/>
			<!-- 指定切面 -->
			<property name="interceptorNames">
				<list>
					<value>myBeforeAdvice</value>
				</list>
			</property>
		</bean>
</beans>

测试

public class test {
	public static void main(String[] args) {
		ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
		//注意通过getBean获取增强的代理类
		UserService user = ac.getBean("proxyFactoryBean",UserService.class);
		user.doSome();
		user.say();
	}
}

在这里插入图片描述

后置通知

1.接口和之前的一样

2.创建后置通知切面类

public class MyAfterRunningAdvice implements AfterReturningAdvice{

	/**
	 *  returnValue 目标方法返回值
	 *  method		目标方法
	 *  args		目标方法参数
	 *  target		目标对象
	 */
	@Override
	public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
		System.out.println("后置方法执行了.."+returnValue);
	}
}

3.配置文件中修改
在这里插入图片描述测试方法一样
在这里插入图片描述

环绕通知

1.创建环绕通知切面类

public class MyMethodInterceptor implements MethodInterceptor {
	//invocation 方法调用器
	@Override
	public Object invoke(MethodInvocation invocation) throws Throwable {
		System.out.println("环绕通知...前");
		Object ic = invocation.proceed();
		if(ic != null){
			//增强返回结果
			ic = ((String)ic).toUpperCase();
		}
		System.out.println("环绕通知...后");
		return ic;
	}
}

2.配置文件注册
在这里插入图片描述测试方法一样
在这里插入图片描述
异常通知

1.创建异常通知切面类

//异常通知切面类
public class MyThrowsAdvice implements ThrowsAdvice{
	public void afterThrowing(Exception ex){
		System.out.println("出bug啦!!!");
	}
}

ThrowsAdvice接口没有定义方法,是个标志接口,在注释中有提示
在这里插入图片描述2.配置文件配置
在这里插入图片描述制造异常
在这里插入图片描述
测试
在这里插入图片描述

基于aspectJ方式实现

    对于AOP这种编程思想,很多框架都进行了实现。Spring就是其中之一,可以完成面向切面编程。然而,AspectJ也实现了AOP的功能,且其实现方式更为简捷,使用更为方便,而且还支持注解式开发。
    所以,Spring又将AspectJ的对于AOP的实现也引入到了自己的框架中。
    在Spring中使用AOP开发时,一般使用AspectJ的实现方式

aspectJ中的通知类型

通知类型说明
前置通知目标方法执行之前调用
目标方法执行之前调用目标方法执行完成之后调用
目标方法执行完成之后调用目标方法执行完成之后调用
异常处理通知异常处理通知
异常处理通知无论程序执行是否正常,该通知都会执行。类似于try…catch中finally代码块

AspectJ的切入点表达式

在这里插入图片描述    execution是个关键字
    切入点表达式要匹配的对象就是目标方法的方法名。
    所以,execution表达式中明显就是方法的签名。
    注意,表达式中加[ ]的部分表示可省略部分,各部分间用空格分开。在其中可以使用以下符号

符号符号
*0至多个字符
. .方法参数中表示任意多个参数,用在包名后表示当前包及其子包路径
+用在类名后表示当前类及子类,用在接口后表示接口及实现类

举例:

execution(public * (. .))
指定切入点为:任意公共方法。
execution(
set (. .))
指定切入点为:任何一个以“set”开始的方法。
execution(
com.xyz.service..(. .))
指定切入点为:定义在service包里的任意类的任意方法。
execution(* com.xyz.service. ..(. .))
指定切入点为:定义在service包或者子包里的任意类的任意方法。“…”出现在类名中时,
后面必须跟“”,表示包、子包下的所有类。
execution(
.service..(. .))
指定只有一级包下的serivce子包下所有类(接口)中的所有方法为切入点
execution(
. .service..*(. .))
指定所有包下的serivce子包下所有类(接口)中的所有方法为切入点

AspectJ对于AOP的实现有两种方式

    注解与配置文件

引入jar包
在这里插入图片描述

注解方式

注意:
		注解的配置是基于AspectJ的,也就是我们可以使用注解来替换
		Aspectj在applicationcontext.xml中的配置信息。
使用:
		1.需要在applicationcontext.xml文件中声明注解扫描
		   设置代理模式为cglib
			作用:告诉Spring容器那些位置使用了注解。
		2.在真实对象上使用注解
			@Compent:在类上声明,相当于bean标签
			@Pointcut:在切面方法上声明,表名该方法为切点
		3.在通知类上使用注解
			@Compent:在类上声明,相当于bean标签
			@Aspect:在类上声明,表名该类的对象为通知对象。用来进行功能扩展的
			@Before:在前置通知方法上使用
			@After:在后置通知方法上使用
			@Around:在环绕通知方法上使用
			@AfterThrowing:在异常通知方法上使用

前置通知

1.创建接口和实现类

public interface UserService {
	String doSome();
	String say();
}
@Service
public class UserServiceImpl implements UserService{
	@Override
	public String doSome() {
		System.out.println("doSome...");
		return "hello";
	}

	@Override
	public String say() {
		System.out.println("say...");	
		return null;
	}
}

2.创建切面类

@Aspect
@Component
public class MyAspect {
	//第一个 "*"表示任意的返回值    service包下的任意包下的doSome方法
	@Before("execution(* com.i.service.*.doSome(..))")		
	public void beforeMethod(){
		System.out.println("前置通知");
	}
}

3.配置文件中配置

<?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:c="http://www.springframework.org/schema/c"
	xmlns:p="http://www.springframework.org/schema/p"
	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/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
		
		<!-- 注册目标对象 -->
		<bean id="UserServiceImpl" class="com.i.service.impl.UserServiceImpl"/>
		<!-- 注册切面类 -->
		<bean id="myAspect" class="com.i.advice.MyAspect"/>
		
		<!-- 开启AOP注解的支持     注册自动代理  -->
		<aop:aspectj-autoproxy/>
</beans>

测试
在这里插入图片描述后置通知

@Aspect
@Component
public class MyAspect {
	
	//后置通知     returning可以获取目标对象的返回结果
	@AfterReturning(value="execution(* com.i.service.*.doSome(..))",returning="msg")
	public void beforeMethod(Object msg){
		System.out.println("后置通知..."+msg);
	}
	
}

测试
在这里插入图片描述
环绕通知

切面类

@Aspect
@Component
public class MyAspect {
	@Around("execution(* *..service.*.**(..))")
	public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable{
		System.out.println("环绕通知...前");
		Object res = pjp.proceed();
		System.out.println("环绕通知...后"+res);
		res=res+"...aaa";
		return res;
	}
}

测试
在这里插入图片描述
环绕通知

切面类

@Aspect
@Component
public class MyAspect {

	@AfterThrowing("execution(* *..service.*.*(..))")
	public void throwsMethod(){
		System.out.println("出BUG啦!!!");
	}
}

在这里插入图片描述最终通知
 与try…catch中的 finally关键字一样,一定会执行
切面方法

@Aspect
@Component
public class MyAspect {

	@After("execution(* *..service.*.**(..))")
	public void after(){
		System.out.println("最终通知");
	}
}

在这里插入图片描述

XML配置文件方式

接口和实现类还是用上个案例的

切面类

public class MyAspect {

	//前置通知
	public void beforeMethod(){
		System.out.println("前置通知");
	}
	
	//后置通知
	//@AfterReturning(value="execution(* com.i.service.*.doSome(..))",returning="msg")
	public void afterMethod(Object msg){
		System.out.println("后置通知..."+msg);
	}
	
	//环绕通知
	public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable{
		System.out.println("环绕通知...前");
		Object res = pjp.proceed();
		System.out.println("环绕通知...后"+res);
		res=res+"...aaa";
		return res;
	}
	
	//异常通知
	public void throwsMethod(){
		System.out.println("出BUG啦!!!");
	}
	
	//最终通知
	public void after(){
		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:c="http://www.springframework.org/schema/c"
	xmlns:p="http://www.springframework.org/schema/p"
	xmlns:aop="http://www.springframework.org/schema/aop"
	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-4.3.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
		
		<!-- 注册目标对象 -->
		<bean id="userServiceImpl" class="com.i.service.impl.UserServiceImpl"/>
		<!-- 注册切面类 -->
		<bean id="myAspect" class="com.i.advice.MyAspect"/>
		
		<!-- AOP配置 -->
		<aop:config>
			<!-- 配置切面 -->
			<aop:aspect ref="myAspect">
				<!-- 配置切入点 -->
				<aop:pointcut id="pointcut" expression="execution(* com.i.service.*.*(..))"/>
				<!-- 配置通知类型 -->
				<aop:before method="beforeMethod" pointcut-ref="pointcut"/>
			</aop:aspect>
		</aop:config>
</beans>

在这里插入图片描述

后置通知

<!-- AOP配置 -->
		<aop:config>
			<!-- 配置切面 -->
			<aop:aspect ref="myAspect">
				<!-- 配置切入点 -->
				<aop:pointcut id="pointcut" expression="execution(* com.i.service.*.*(..))"/>
				<!-- 配置通知类型  前置通知 -->
					<!-- <aop:before method="beforeMethod" pointcut-ref="pointcut"/> -->
					
				<!-- 后置通知 returning 属性的值必须和切面类中对应方法的形参同名 -->
				<aop:after-returning method="afterMethod" pointcut-ref="pointcut" returning="msg"/>
			</aop:aspect>
		</aop:config>

在这里插入图片描述

环绕通知

<!-- AOP配置 -->
		<aop:config>
			<!-- 配置切面 -->
			<aop:aspect ref="myAspect">
				<!-- 配置切入点 -->
				<aop:pointcut id="pointcut" expression="execution(* com.i.service.*.*(..))"/>
				<!-- 配置通知类型  前置通知 -->
				<!-- <aop:before method="beforeMethod" pointcut-ref="pointcut"/> -->
				
				<!-- 后置通知 returning 属性的值必须和切面类中对应方法的形参同名 -->
				<!-- <aop:after-returning method="afterMethod" pointcut-ref="pointcut" returning="msg"/> -->
				
				<!-- 环绕通知 -->
				<aop:around method="aroundAdvice" pointcut-ref="pointcut"/>
			</aop:aspect>
		</aop:config>

在这里插入图片描述

异常通知

<!-- AOP配置 -->
		<aop:config>
			<!-- 配置切面 -->
			<aop:aspect ref="myAspect">
				<!-- 配置切入点 -->
				<aop:pointcut id="pointcut" expression="execution(* com.i.service.*.*(..))"/>
				<!-- 配置通知类型  前置通知 -->
				<!-- <aop:before method="beforeMethod" pointcut-ref="pointcut"/> -->
				
				<!-- 后置通知 returning 属性的值必须和切面类中对应方法的形参同名 -->
				<!-- <aop:after-returning method="afterMethod" pointcut-ref="pointcut" returning="msg"/> -->
				
				<!-- 环绕通知 -->
				<!-- <aop:around method="aroundAdvice" pointcut-ref="pointcut"/> -->
				
				<!-- 异常通知  throwing 为对应方法的形参-->
				<aop:after-throwing method="throwsMethod" pointcut-ref="pointcut" throwing="e"/>
			</aop:aspect>
		</aop:config>

在这里插入图片描述最终通知

<!-- AOP配置 -->
		<aop:config>
			<!-- 配置切面 -->
			<aop:aspect ref="myAspect">
				<!-- 配置切入点 -->
				<aop:pointcut id="pointcut" expression="execution(* com.i.service.*.*(..))"/>
				<!-- 配置通知类型  前置通知 -->
				<!-- <aop:before method="beforeMethod" pointcut-ref="pointcut"/> -->
				
				<!-- 后置通知 returning 属性的值必须和切面类中对应方法的形参同名 -->
				<!-- <aop:after-returning method="afterMethod" pointcut-ref="pointcut" returning="msg"/> -->
				
				<!-- 环绕通知 -->
				<!-- <aop:around method="aroundAdvice" pointcut-ref="pointcut"/> -->
				
				<!-- 异常通知 -->
				<aop:after-throwing method="throwsMethod" pointcut-ref="pointcut" throwing="e"/>
				
				<!-- 最终通知 -->
				<aop:after method="after" pointcut-ref="pointcut"/>
			</aop:aspect>
		</aop:config>

在这里插入图片描述

JAVA中的乱码

1.项目编码
2. 文件本身的编码(jsp或者html的head中指定的编码)
3. GET请求乱码(修改Tomcat配置)
4. POST请求乱码(Java代码解决,可以使用过滤器统一解决)
5. 响应乱码(修改响应编码,主要是修改content-type)
6. 数据库乱码:

  1. 确认乱码根源(数据库乱码还是java中乱码)
  2. 通过在java中打印日志,确认乱码的根源
  3. 第三种情况,数据在从Java到数据库实例的过程中乱码了,此时修改数据库连接地址即可解决: dbc:mysql:///test01?useUnicode=true&characterEncoding=utf-8
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值