Spring之AOP详解

15 篇文章 0 订阅
12 篇文章 0 订阅

AOP介绍

AOP,既面向切面编程,可以说是OOP(面向对象编程)的补充和完善
面向切面是面向对象中的一种方式,在代码执行过程中,动态嵌入其他代码,叫做面向切面编程,常见使用场景: 日志 ; 事务; 数据库操作,等

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

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

AOP的实现方式

AOP介绍

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

AOP术语介绍

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

基于Schema-based方式实现

需要添加的jar包
在这里插入图片描述

前置通知

1.创建目标接口和实现类

package com.sxt.service;
/**
 * service层
 * @author Administrator
 *
 */
public interface UserService {
	public void say(String msg);
	
	public String dosame(String msg);

}

package com.sxt.service.impl;

import com.sxt.service.UserService;
/**
 * service实现类
 * @author Administrator
 *
 */
public class UserServiceImpl implements UserService{

	@Override
	public void say(String msg) {
		System.out.println("say---"+msg+1/0);
		
	}

	@Override
	public String dosame(String msg) {
		System.out.println("dosame---"+msg);
		return "hello";
	}

}

2.创建切面类

package com.sxt.advice;

import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;
/**
 * 前置通知
 * @author Administrator
 *
 */
public class Before implements MethodBeforeAdvice{

	@Override
	public void before(Method method, Object[] args, Object target) throws Throwable {
		System.out.println("前置通知执行了..."+args[0]+"  "+method.getName());
		
	}

}

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: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">
		<!-- 注册user实现类 -->
		<bean class="com.sxt.service.impl.UserServiceImpl" id="userService"></bean>
		<!-- 注册前置通知 -->
		<bean class="com.sxt.advice.Before" id="before"></bean>
		<!-- 注册代理类 -->
		<bean class="org.springframework.aop.framework.ProxyFactoryBean" id="proxyFactoryBean">
			<!-- 设置注入目标对象 -->
			<property name="target" ref="userService"/>
			<!-- 注册目标接口 -->
			<property name="interfaces" value="com.sxt.service.UserService"/>
			<!-- 注册前置通知 -->
			<property name="interceptorNames">
				<list>
				<!-- 前置通知执行 -->
					<value>before</value>
				</list>
			</property>
		</bean>
</beans>

5.测试

package com.sxt;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.sxt.service.UserService;

public class Test {

	public static void main(String[] args) {
		ApplicationContext ac = new ClassPathXmlApplicationContext("application.xml");
		UserService bean = ac.getBean("proxyFactoryBean",UserService.class);
		bean.say("hello");
		System.out.println(bean.dosame("haha"));
	}

}

在这里插入图片描述

后置通知

1.接口和之前一样
2.创建后置通知切面类

package com.sxt.advice;

import java.lang.reflect.Method;

import org.springframework.aop.AfterReturningAdvice;
import org.springframework.stereotype.Component;
/**
 * 后置通知
 * @author Administrator
 *
 */
@Component
public class After implements AfterReturningAdvice{

	@Override
	public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
		System.out.println("后置通知执行了"+returnValue);
		
	}

}

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: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">
		<!-- 开启扫描 -->
		<context:component-scan base-package="com.sxt.advice"></context:component-scan>
		<!-- 注册user实现类 -->
		<bean class="com.sxt.service.impl.UserServiceImpl" id="userService"></bean>
		<!-- 注册代理类 -->
		<bean class="org.springframework.aop.framework.ProxyFactoryBean" id="proxyFactoryBean">
			<!-- 设置注入目标对象 -->
			<property name="target" ref="userService"/>
			<!-- 注册目标接口 -->
			<property name="interfaces" value="com.sxt.service.UserService"/>
			<property name="interceptorNames">
				<list>
					<!-- 后置通知执行 -->
					 <value>after</value> 
				</list>
			</property>
		</bean>
</beans>

测试

在这里插入图片描述

环绕通知

1.创建环绕通知切面类

package com.sxt.advice;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.stereotype.Component;
/**
 * 环绕通知
 * @author Administrator
 *
 */
@Component
public class Method implements MethodInterceptor{
	/**
	 * 可以增强目标对象的返回结果
	 */
	@Override
	public Object invoke(MethodInvocation arg0) throws Throwable {
		System.out.println("执行前");
		Object proceed = arg0.proceed();
		if(proceed != null){
			proceed = proceed.toString().toUpperCase();
			
		}
		System.out.println("执行后");
		return proceed;
	}

}

2.配置文件中配置

<?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: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">
		<!-- 开启扫描 -->
		<context:component-scan base-package="com.sxt.advice"></context:component-scan>
		<!-- 注册user实现类 -->
		<bean class="com.sxt.service.impl.UserServiceImpl" id="userService"></bean>
		<!-- 注册代理类 -->
		<bean class="org.springframework.aop.framework.ProxyFactoryBean" id="proxyFactoryBean">
			<!-- 设置注入目标对象 -->
			<property name="target" ref="userService"/>
			<!-- 注册目标接口 -->
			<property name="interfaces" value="com.sxt.service.UserService"/>
			<!-- 注册前置通知 -->
			<property name="interceptorNames">
				<list>
					<!-- 环绕通知执行 -->
					<value>method</value>
				</list>
			</property>
		</bean>
</beans>

3.测试
在这里插入图片描述

异常通知

1.创建异常通知切面类

package com.sxt.advice;

import org.springframework.aop.ThrowsAdvice;
import org.springframework.stereotype.Component;
/**
 * 异常通知
 * @author Administrator
 *
 */
@Component
public class Throw implements ThrowsAdvice{
	/**
	 * 目标对象异常产生后处理的方法
	 * @param ex
	 */
	public void afterThrowing(Exception ex){
		System.out.println("异常通知。。。"+ex.getMessage());
	}
}

ThrowsAdvice接口没有定义方法,是个标志接口,在注释中有提示

2.配置文件配置

<?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: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">
		<!-- 开启扫描 -->
		<context:component-scan base-package="com.sxt.advice"></context:component-scan>
		<!-- 注册user实现类 -->
		<bean class="com.sxt.service.impl.UserServiceImpl" id="userService"></bean>
		<!-- 注册前置通知 -->
		<bean class="com.sxt.advice.Before" id="before"></bean>
		<!-- 注册代理类 -->
		<bean class="org.springframework.aop.framework.ProxyFactoryBean" id="proxyFactoryBean">
			<!-- 设置注入目标对象 -->
			<property name="target" ref="userService"/>
			<!-- 注册目标接口 -->
			<property name="interfaces" value="com.sxt.service.UserService"/>
			<!-- 注册前置通知 -->
			<property name="interceptorNames">
				<list>
				<!-- 前置通知执行 -->
					<!-- <value>before</value> -->
					<!-- 后置通知执行 -->
					<!-- <value>after</value> -->
					<!-- 环绕通知执行 -->
					<!-- <value>method</value> -->
					<!-- 异常通知执行 -->
					 <value>throw</value> 
				</list>
			</property>
		</bean>
</beans>

3.测试

在这里插入图片描述

基于aspectJ方式实现

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

aspectJ中的通知类型

通知类型说明
前置通知目标方法执行之前调用
后置通知目标方法执行完成之后调用
环绕通知目标方法执行前后都会调用方法,且能增强结果
异常处理通知目标方法出现异常调用
最终通知无论程序执行是否正常,该通知都会执行。类似于try…catch中finally代码块

AspectJ的切入点表达式

在这里插入图片描述
切入点表达式要匹配的对象就是目标方法的方法名。所以,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包
在这里插入图片描述

注解方式

1.接口实现类同上

2.创建切面类

package com.sxt.advice;

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.springframework.stereotype.Component;


@Aspect
@Component
public class Advice {
	/**
	 * 前置通知
	 */
	@Before(value="execution(* com.sxt.service.impl.*.*(..))")
	public void before(){
		//System.out.println("前置通知----");
	}
	/**
	 * 后置通知
	 * returning:指定接收返回结果的变量
	 */
	@AfterReturning(value="execution(* com.sxt..*(..))",returning="msg")
	public void afterReturing(Object msg){
		//System.out.println("后置通知:"+msg);
	}
	
	/**
	 * 异常通知
	 */
	@AfterThrowing(value="execution(* com.sxt..*(..))",throwing="ex")
	public void throwse(Exception ex){
		//System.out.println("异常通知"+ex);
	}
	/**
	 * 环绕通知
	 * @throws Throwable 
	 */
	@Around(value="execution(* com.sxt..*(..))")
	public Object around(ProceedingJoinPoint p) throws Throwable{
		System.out.println("环绕前");
		Object proceed = p.proceed();
		if(proceed != null){
			proceed = proceed.toString().toUpperCase();
		}
		System.out.println("环绕后");
		return proceed;
		
	}
	/**
	 * 最终通知
	 */
	@After(value="execution(* com.sxt..*(..))")
	public void after(){
		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: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-4.3.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">

	<!-- 开启扫描 -->
 	<context:component-scan base-package="com.sxt.*"></context:component-scan>
	
	<!-- AspectJ 开启注解的方式 -->
	<aop:aspectj-autoproxy/>
</beans>

效果图同上

XML实现

接口实现类还是使用开始的案例

切面类

package com.sxt.advice;

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.springframework.stereotype.Component;


public class Advice {
	/**
	 * 前置通知
	 */
	
	public void before(){
		//System.out.println("前置通知----");
	}
	/**
	 * 后置通知
	 * returning:指定接收返回结果的变量
	 */
	
	public void afterReturing(Object msg){
		//System.out.println("后置通知:"+msg);
	}
	
	/**
	 * 异常通知
	 */
	
	public void throwse(Exception ex){
		//System.out.println("异常通知"+ex);
	}
	/**
	 * 环绕通知
	 * @throws Throwable 
	 */
	
	public Object around(ProceedingJoinPoint p) throws Throwable{
		System.out.println("环绕前");
		Object proceed = p.proceed();
		if(proceed != null){
			proceed = proceed.toString().toUpperCase();
		}
		System.out.println("环绕后");
		return proceed;
		
	}
	/**
	 * 最终通知
	 */
	
	public void after(){
		System.out.println("最终通知");
	}
}


配置文件修改

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	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-4.3.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">

	<!-- 注册user实现类 -->
		<bean class="com.sxt.service.impl.UserServiceImpl"></bean>
	<!-- 注册advice -->
		<bean class="com.sxt.advice.Advice" id="advice"></bean>
	<!-- AspectJ 开启注解的方式 -->
	<aop:config>
	<!-- 配置切入点 -->
		<aop:pointcut expression="execution(* com.sxt.service.impl.*.*(..))" id="pointcut"/>
		<!-- 配置切面类 -->
		<aop:aspect ref="advice">
		<!-- 最终通知 -->
		<aop:after method="after" pointcut-ref="pointcut"/>
		</aop:aspect>
	</aop:config>
</beans>

<aop:after method=“after” pointcut-ref=“pointcut”/>将after改为切面类中的其他通知的方法名即可实现,这里就不一一例举了

JdbcTemplate

JdbcTemplate就是采用AOP思想,将Jdbc操作中的模板代码全部简化,开发者只需要完成最最核心的SQL以及结果的检索。
使用步骤:

导入jar包

在这里插入图片描述

1.创建user对象

package com.sxt.bean;

public class User {

	private int id;
	
	private String username;
	
	private String password;

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	@Override
	public String toString() {
		return "User [id=" + id + ", username=" + username + ", password=" + password + "]";
	}
	
	
}

2.创建接口和方法

package com.sxt.dao;

public interface UserDao {
	public void add();
	public void update();
	public void delete();
	public void query();
}

3.创建接口实现类

package com.sxt.dao.impl;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.stereotype.Repository;

import com.sxt.bean.User;
import com.sxt.dao.UserDao;
@Repository
public class UserDaoImpl implements UserDao {
	@Autowired
	private JdbcTemplate template;

	@Override
	public void add() {
		String sql = "insert t_user(username,password)values(?,?)";
		int i = template.update(sql,"aa","11");
		System.out.println("影响一行"+i);

	}

	@Override
	public void update() {
		String sql = "update t_user set username=?,password=? where id=?";
		int i = template.update(sql,"bb","22",2);
		System.out.println("影响一行"+i);

	}

	@Override
	public void delete() {
		String sql = "delete from t_user where id=?";
		int i = template.update(sql,2);
		System.out.println("影响一行"+i);

	}

	@Override
	public void query() {
		String sql = "select * from t_user";
		List<User> query = template.query(sql, new BeanPropertyRowMapper<User>(User.class));
		for (User user : query) {
			System.out.println(user);
		}
		
	}

}

4.配置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: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-4.3.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
		<!-- 开启注解 -->
	<context:component-scan base-package="com.sxt.dao.*"></context:component-scan>
	<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource" id="dataSource">
			<!-- 设置注入 -->
			<property name="driverClassName" value="com.mysql.jdbc.Driver "/>
			<property name="url" value="jdbc:mysql://localhost:3306/pms?characterEncoding=utf-8"/>
			<property name="username" value="root"/>
			<property name="password" value="123456"/>
	</bean>
	<bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
		<constructor-arg ref="dataSource"/>
	</bean>
</beans>

测试

package com.sxt;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.sxt.dao.UserDao;

public class Test {
public static void main(String[] args) {
	ApplicationContext ac = new ClassPathXmlApplicationContext("application.xml");
	UserDao bean = ac.getBean(UserDao.class);
	bean.add();
}
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值